00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017 #include "NetworkPage.h"
00018 #include "NetworkSettings.h"
00019 #include "VMessageBox.h"
00020 #include "Vidalia.h"
00021 #include "BridgeDownloaderProgressDialog.h"
00022 #include "DomainValidator.h"
00023
00024 #include "stringutil.h"
00025
00026 #include <QMenu>
00027 #include <QIntValidator>
00028 #include <QClipboard>
00029 #include <QHostAddress>
00030 #include <QRegExp>
00031 #include <QMessageBox>
00032
00033 #define IMG_COPY ":/images/22x22/edit-copy.png"
00034
00035
00036
00037 NetworkPage::NetworkPage(QWidget *parent)
00038 : ConfigPage(parent, "Network")
00039 {
00040
00041 ui.setupUi(this);
00042
00043 connect(ui.btnAddBridge, SIGNAL(clicked()), this, SLOT(addBridge()));
00044 connect(ui.btnRemoveBridge, SIGNAL(clicked()), this, SLOT(removeBridge()));
00045 connect(ui.btnCopyBridge, SIGNAL(clicked()),
00046 this, SLOT(copySelectedBridgesToClipboard()));
00047 connect(ui.listBridges, SIGNAL(customContextMenuRequested(QPoint)),
00048 this, SLOT(bridgeContextMenuRequested(QPoint)));
00049 connect(ui.listBridges, SIGNAL(itemSelectionChanged()),
00050 this, SLOT(bridgeSelectionChanged()));
00051 connect(ui.lineBridge, SIGNAL(returnPressed()), this, SLOT(addBridge()));
00052 connect(ui.lblHelpFindBridges, SIGNAL(linkActivated(QString)),
00053 this, SLOT(onLinkActivated(QString)));
00054 connect(ui.btnFindBridges, SIGNAL(clicked()), this, SLOT(findBridges()));
00055 connect(ui.cmboProxyType, SIGNAL(currentIndexChanged(int)),
00056 this, SLOT(proxyTypeChanged(int)));
00057
00058 ui.lineProxyAddress->setValidator(new DomainValidator(this));
00059 ui.lineProxyPort->setValidator(new QIntValidator(1, 65535, this));
00060
00061 vApp->createShortcut(QKeySequence(QKeySequence::Copy),
00062 ui.listBridges, this,
00063 SLOT(copySelectedBridgesToClipboard()));
00064
00065 if (! BridgeDownloader::isMethodSupported(BridgeDownloader::DownloadMethodHttps)) {
00066 ui.btnFindBridges->setVisible(false);
00067 ui.lblHelpFindBridges->setText(
00068 tr("<a href=\"bridges.finding\">How can I find bridges?</a>"));
00069 _bridgeDownloader = 0;
00070 } else {
00071 _bridgeDownloader = new BridgeDownloader(this);
00072 connect(_bridgeDownloader, SIGNAL(bridgeRequestFinished(QStringList)),
00073 this, SLOT(bridgeRequestFinished(QStringList)));
00074 }
00075
00076 #if defined(Q_WS_MAC)
00077
00078
00079 ui.grpProxySettings->setTitle("");
00080 ui.grpFirewallSettings->setTitle("");
00081 ui.grpBridgeSettings->setTitle("");
00082 #endif
00083 }
00084
00085
00086 void
00087 NetworkPage::retranslateUi()
00088 {
00089 ui.retranslateUi(this);
00090 }
00091
00092
00093
00094
00095 bool
00096 NetworkPage::apply(QString &errmsg)
00097 {
00098 return NetworkSettings(Vidalia::torControl()).apply(&errmsg);
00099 }
00100
00101
00102
00103 bool
00104 NetworkPage::changedSinceLastApply()
00105 {
00106 return NetworkSettings(Vidalia::torControl()).changedSinceLastApply();
00107 }
00108
00109
00110
00111 void
00112 NetworkPage::revert()
00113 {
00114 NetworkSettings settings(Vidalia::torControl());
00115 settings.revert();
00116 }
00117
00118
00119
00120 void
00121 NetworkPage::onLinkActivated(const QString &url)
00122 {
00123 emit helpRequested(url);
00124 }
00125
00126
00127
00128
00129
00130
00131 bool
00132 NetworkPage::validateBridge(const QString &bridge, QString *out)
00133 {
00134 QString temp = bridge;
00135 if (temp.startsWith("bridge ", Qt::CaseInsensitive))
00136 temp = temp.remove(0, 7);
00137
00138 QStringList parts = temp.split(" ", QString::SkipEmptyParts);
00139 if (parts.isEmpty())
00140 return false;
00141
00142 QString s = parts.at(0);
00143 QRegExp re("(\\d{1,3}\\.){3}\\d{1,3}(:\\d{1,5})?");
00144 if (re.exactMatch(s)) {
00145 if (s.endsWith(":"))
00146 return false;
00147
00148 int index = s.indexOf(":");
00149 QString host = s.mid(0, index);
00150 if (QHostAddress(host).isNull()
00151 || QHostAddress(host).protocol() != QAbstractSocket::IPv4Protocol) {
00152 return false;
00153 }
00154 if (index > 0) {
00155 QString port = s.mid(index + 1);
00156 if (port.toUInt() < 1 || port.toUInt() > 65535)
00157 return false;
00158 }
00159
00160 temp = s;
00161 if (parts.size() > 1) {
00162 QString fp = static_cast<QStringList>(parts.mid(1)).join("");
00163 if (fp.length() != 40 || !string_is_hex(fp))
00164 return false;
00165 temp += " " + fp.toUpper();
00166 }
00167 } else {
00168 return false;
00169 }
00170 *out = temp;
00171 return true;
00172 }
00173
00174
00175 void
00176 NetworkPage::addBridge()
00177 {
00178 QString bridge;
00179 QString input = ui.lineBridge->text().trimmed();
00180
00181 if (input.isEmpty())
00182 return;
00183 if (!validateBridge(input, &bridge)) {
00184 VMessageBox::warning(this,
00185 tr("Invalid Bridge"),
00186 tr("The specified bridge identifier is not valid."),
00187 VMessageBox::Ok|VMessageBox::Default);
00188 return;
00189 }
00190 if (!ui.listBridges->findItems(bridge, Qt::MatchFixedString).isEmpty())
00191 return;
00192
00193 ui.listBridges->addItem(bridge);
00194 ui.lineBridge->clear();
00195 }
00196
00197
00198 void
00199 NetworkPage::removeBridge()
00200 {
00201 qDeleteAll(ui.listBridges->selectedItems());
00202 }
00203
00204
00205 void
00206 NetworkPage::copySelectedBridgesToClipboard()
00207 {
00208 QString contents;
00209
00210 foreach (QListWidgetItem *item, ui.listBridges->selectedItems()) {
00211 #if defined(Q_WS_WIN)
00212 contents += item->text() + "\r\n";
00213 #else
00214 contents += item->text() + "\n";
00215 #endif
00216 }
00217 if (!contents.isEmpty())
00218 vApp->clipboard()->setText(contents.trimmed());
00219 }
00220
00221
00222
00223 void
00224 NetworkPage::bridgeContextMenuRequested(const QPoint &pos)
00225 {
00226 QMenu menu(this);
00227
00228 QListWidgetItem *item = ui.listBridges->itemAt(pos);
00229 if (!item)
00230 return;
00231
00232 QAction *copyAction =
00233 new QAction(QIcon(IMG_COPY), tr("Copy (Ctrl+C)"), &menu);
00234 connect(copyAction, SIGNAL(triggered()),
00235 this, SLOT(copySelectedBridgesToClipboard()));
00236
00237 menu.addAction(copyAction);
00238 menu.exec(ui.listBridges->mapToGlobal(pos));
00239 }
00240
00241
00242 void
00243 NetworkPage::bridgeSelectionChanged()
00244 {
00245 bool enabled = !ui.listBridges->selectedItems().isEmpty();
00246 ui.btnCopyBridge->setEnabled(enabled);
00247 ui.btnRemoveBridge->setEnabled(enabled);
00248 }
00249
00250
00251 bool
00252 NetworkPage::save(QString &errmsg)
00253 {
00254 NetworkSettings settings(Vidalia::torControl());
00255 QString addr;
00256 QString user, pass;
00257 NetworkSettings::ProxyType proxy = NetworkSettings::NoProxy;
00258 QStringList bridgeList;
00259 QList<quint16> reachablePorts;
00260 bool ok;
00261
00262 if (ui.chkUseProxy->isChecked()) {
00263 if (ui.lineProxyAddress->text().isEmpty()
00264 || ui.lineProxyPort->text().isEmpty()) {
00265 errmsg = tr("You must specify both an IP address or hostname and a "
00266 "port number to configure Tor to use a proxy to access "
00267 "the Internet.");
00268 return false;
00269 }
00270 if (ui.cmboProxyType->currentIndex() < 0) {
00271 errmsg = tr("You must select the proxy type.");
00272 return false;
00273 }
00274 }
00275 if (ui.chkFascistFirewall->isChecked()
00276 && ui.lineReachablePorts->text().isEmpty()) {
00277 errmsg = tr("You must specify one or more ports to which your "
00278 "firewall allows you to connect.");
00279 return false;
00280 }
00281
00282 if (ui.chkUseProxy->isChecked()) {
00283 if (!ui.lineProxyAddress->text().isEmpty()) {
00284 addr = ui.lineProxyAddress->text();
00285 if (!ui.lineProxyPort->text().isEmpty())
00286 addr += ":" + ui.lineProxyPort->text();
00287 }
00288
00289 user = ui.lineProxyUsername->text();
00290 pass = ui.lineProxyPassword->text();
00291
00292 QVariant data;
00293 int type;
00294
00295 data = ui.cmboProxyType->itemData(ui.cmboProxyType->currentIndex());
00296 Q_ASSERT(data.isValid());
00297 type = data.toInt();
00298 Q_ASSERT(type >= NetworkSettings::ProxyTypeMin &&
00299 type <= NetworkSettings::ProxyTypeMax);
00300 proxy = static_cast<NetworkSettings::ProxyType>(type);
00301 }
00302
00303 settings.setProxyType(proxy);
00304 settings.setProxyAddress(addr);
00305 settings.setProxyUsername(user);
00306 settings.setProxyPassword(pass);
00307
00308
00309 settings.setFascistFirewall(ui.chkFascistFirewall->isChecked());
00310 foreach (QString portString,
00311 ui.lineReachablePorts->text().split(",", QString::SkipEmptyParts)) {
00312 quint32 port = portString.toUInt(&ok);
00313 if (!ok || port < 1 || port > 65535) {
00314 errmsg = tr("'%1' is not a valid port number.").arg(portString);
00315 return false;
00316 }
00317 reachablePorts << (quint16)port;
00318 }
00319 settings.setReachablePorts(reachablePorts);
00320
00321
00322 settings.setUseBridges(ui.chkUseBridges->isChecked());
00323 for (int i = 0; i < ui.listBridges->count(); i++)
00324 bridgeList << ui.listBridges->item(i)->text();
00325 settings.setBridgeList(bridgeList);
00326
00327 return true;
00328 }
00329
00330
00331 void
00332 NetworkPage::load()
00333 {
00334 NetworkSettings settings(Vidalia::torControl());
00335 QStringList reachablePortStrings;
00336 NetworkSettings::ProxyType proxyType;
00337
00338
00339 proxyType = settings.getProxyType();
00340 ui.chkUseProxy->setChecked(proxyType != NetworkSettings::NoProxy);
00341 QStringList proxy = settings.getProxyAddress().split(":");
00342 if (proxy.size() >= 1)
00343 ui.lineProxyAddress->setText(proxy.at(0));
00344 if (proxy.size() >= 2)
00345 ui.lineProxyPort->setText(proxy.at(1));
00346 ui.lineProxyUsername->setText(settings.getProxyUsername());
00347 ui.lineProxyPassword->setText(settings.getProxyPassword());
00348
00349
00350
00351 ui.cmboProxyType->clear();
00352 if (!vApp->torControl()->isRunning()
00353 || vApp->torControl()->getTorVersion() >= 0x020201) {
00354 ui.cmboProxyType->addItem(tr("SOCKS 4"), NetworkSettings::Socks4Proxy);
00355 ui.cmboProxyType->addItem(tr("SOCKS 5"), NetworkSettings::Socks5Proxy);
00356 } else if (proxyType == NetworkSettings::Socks4Proxy
00357 || proxyType == NetworkSettings::Socks5Proxy) {
00358
00359
00360 proxyType = NetworkSettings::NoProxy;
00361 ui.chkUseProxy->setChecked(false);
00362 }
00363 ui.cmboProxyType->addItem(tr("HTTP"), NetworkSettings::HttpProxy);
00364 ui.cmboProxyType->addItem(tr("HTTP / HTTPS"),
00365 NetworkSettings::HttpHttpsProxy);
00366
00367 ui.cmboProxyType->setCurrentIndex(ui.cmboProxyType->findData(proxyType));
00368
00369
00370 ui.chkFascistFirewall->setChecked(settings.getFascistFirewall());
00371 QList<quint16> reachablePorts = settings.getReachablePorts();
00372 foreach (quint16 port, reachablePorts) {
00373 reachablePortStrings << QString::number(port);
00374 }
00375 ui.lineReachablePorts->setText(reachablePortStrings.join(","));
00376
00377
00378 ui.chkUseBridges->setChecked(settings.getUseBridges());
00379 ui.listBridges->clear();
00380 ui.listBridges->addItems(settings.getBridgeList());
00381 }
00382
00383
00384
00385
00386 void
00387 NetworkPage::findBridges()
00388 {
00389 BridgeDownloaderProgressDialog *dlg = new BridgeDownloaderProgressDialog(this);
00390
00391 connect(_bridgeDownloader, SIGNAL(statusChanged(QString)),
00392 dlg, SLOT(setStatus(QString)));
00393 connect(_bridgeDownloader, SIGNAL(downloadProgress(int, int)),
00394 dlg, SLOT(setDownloadProgress(int, int)));
00395 connect(_bridgeDownloader, SIGNAL(bridgeRequestFailed(QString)),
00396 dlg, SLOT(bridgeRequestFailed(QString)));
00397 connect(_bridgeDownloader, SIGNAL(bridgeRequestFinished(QStringList)),
00398 dlg, SLOT(bridgeRequestFinished(QStringList)));
00399 connect(dlg, SIGNAL(retry()), this, SLOT(startBridgeRequest()));
00400
00401 startBridgeRequest();
00402 switch (dlg->exec()) {
00403 case QDialogButtonBox::Cancel:
00404 _bridgeDownloader->cancelBridgeRequest();
00405 break;
00406
00407 case QDialogButtonBox::Help:
00408 emit helpRequested("bridges.finding");
00409 break;
00410 }
00411
00412 delete dlg;
00413 }
00414
00415
00416 void
00417 NetworkPage::startBridgeRequest()
00418 {
00419 if (ui.chkUseProxy->isChecked() &&
00420 ui.cmboProxyType->currentIndex() == NetworkSettings::HttpHttpsProxy) {
00421 _bridgeDownloader->setProxy(ui.lineProxyAddress->text(),
00422 ui.lineProxyPort->text().toUInt(),
00423 ui.lineProxyUsername->text(),
00424 ui.lineProxyPassword->text());
00425 }
00426
00427 _bridgeDownloader->downloadBridges(BridgeDownloader::DownloadMethodHttps);
00428 }
00429
00430
00431
00432
00433 void
00434 NetworkPage::bridgeRequestFinished(const QStringList &bridges)
00435 {
00436 bool foundNewBridges = false;
00437 QString normalized;
00438
00439 foreach (QString bridge, bridges) {
00440 if (! validateBridge(bridge, &normalized))
00441 continue;
00442
00443 QString address = normalized.split(" ").at(0);
00444 if (ui.listBridges->findItems(address, Qt::MatchContains).isEmpty()) {
00445 ui.listBridges->addItem(normalized);
00446 foundNewBridges = true;
00447 }
00448 }
00449
00450 if (! foundNewBridges) {
00451 QMessageBox dlg(this);
00452 dlg.setIcon(QMessageBox::Information);
00453 dlg.setText(tr("No new bridges are currently available. You can either "
00454 "wait a while and try again, or try another method of "
00455 "finding new bridges."));
00456 dlg.setInformativeText(tr("Click Help to see other methods of finding "
00457 "new bridges."));
00458 dlg.setStandardButtons(QMessageBox::Ok | QMessageBox::Help);
00459
00460 if (dlg.exec() == QMessageBox::Help)
00461 emit helpRequested("bridges.finding");
00462 }
00463 }
00464
00465
00466
00467 void
00468 NetworkPage::proxyTypeChanged(int selection)
00469 {
00470 QVariant data = ui.cmboProxyType->itemData(selection);
00471
00472 if (data.isValid()
00473 && data.toInt() == NetworkSettings::Socks4Proxy) {
00474 ui.lineProxyUsername->setEnabled(false);
00475 ui.lineProxyPassword->setEnabled(false);
00476 } else {
00477 ui.lineProxyUsername->setEnabled(true);
00478 ui.lineProxyPassword->setEnabled(true);
00479 }
00480 }
00481