Vidalia
0.2.17
|
00001 /* 00002 ** This file is part of Vidalia, and is subject to the license terms in the 00003 ** LICENSE file, found in the top level directory of this distribution. If you 00004 ** did not receive the LICENSE file with this file, you may obtain it from the 00005 ** Vidalia source package distributed by the Vidalia Project at 00006 ** http://www.torproject.org/projects/vidalia.html. No part of Vidalia, 00007 ** including this file, may be copied, modified, propagated, or distributed 00008 ** except according to the terms described in the LICENSE file. 00009 */ 00010 00011 /* 00012 ** \file NetworkPage.cpp 00013 ** \brief Network and firewall configuration options 00014 */ 00015 00016 #include "NetworkPage.h" 00017 #include "NetworkSettings.h" 00018 #include "VMessageBox.h" 00019 #include "Vidalia.h" 00020 #include "BridgeDownloaderProgressDialog.h" 00021 #include "DomainValidator.h" 00022 00023 #include "stringutil.h" 00024 00025 #include <QMenu> 00026 #include <QIntValidator> 00027 #include <QClipboard> 00028 #include <QHostAddress> 00029 #include <QRegExp> 00030 #include <QMessageBox> 00031 00032 #define IMG_COPY ":/images/22x22/edit-copy.png" 00033 00034 00035 /** Constructor */ 00036 NetworkPage::NetworkPage(QWidget *parent) 00037 : ConfigPage(parent, "Network") 00038 { 00039 /* Invoke the Qt Designer generated object setup routine */ 00040 ui.setupUi(this); 00041 00042 connect(ui.btnAddBridge, SIGNAL(clicked()), this, SLOT(addBridge())); 00043 connect(ui.btnRemoveBridge, SIGNAL(clicked()), this, SLOT(removeBridge())); 00044 connect(ui.btnCopyBridge, SIGNAL(clicked()), 00045 this, SLOT(copySelectedBridgesToClipboard())); 00046 connect(ui.listBridges, SIGNAL(customContextMenuRequested(QPoint)), 00047 this, SLOT(bridgeContextMenuRequested(QPoint))); 00048 connect(ui.listBridges, SIGNAL(itemSelectionChanged()), 00049 this, SLOT(bridgeSelectionChanged())); 00050 connect(ui.lineBridge, SIGNAL(returnPressed()), this, SLOT(addBridge())); 00051 connect(ui.lblHelpFindBridges, SIGNAL(linkActivated(QString)), 00052 this, SLOT(onLinkActivated(QString))); 00053 connect(ui.btnFindBridges, SIGNAL(clicked()), this, SLOT(findBridges())); 00054 connect(ui.cmboProxyType, SIGNAL(currentIndexChanged(int)), 00055 this, SLOT(proxyTypeChanged(int))); 00056 00057 ui.lineProxyAddress->setValidator(new DomainValidator(this)); 00058 ui.lineProxyPort->setValidator(new QIntValidator(1, 65535, this)); 00059 00060 vApp->createShortcut(QKeySequence(QKeySequence::Copy), 00061 ui.listBridges, this, 00062 SLOT(copySelectedBridgesToClipboard())); 00063 00064 if (! BridgeDownloader::isMethodSupported(BridgeDownloader::DownloadMethodHttps)) { 00065 ui.btnFindBridges->setVisible(false); 00066 ui.lblHelpFindBridges->setText( 00067 tr("<a href=\"bridges.finding\">How can I find bridges?</a>")); 00068 _bridgeDownloader = 0; 00069 } else { 00070 _bridgeDownloader = new BridgeDownloader(this); 00071 connect(_bridgeDownloader, SIGNAL(bridgeRequestFinished(QStringList)), 00072 this, SLOT(bridgeRequestFinished(QStringList))); 00073 } 00074 00075 #if defined(Q_WS_MAC) 00076 /* On OS X, the network page looks better without frame titles. Everywhere 00077 * else needs titles or else there's a break in the frame border. */ 00078 ui.grpProxySettings->setTitle(""); 00079 ui.grpFirewallSettings->setTitle(""); 00080 ui.grpBridgeSettings->setTitle(""); 00081 #endif 00082 } 00083 00084 /** Called when the user changes the UI translation. */ 00085 void 00086 NetworkPage::retranslateUi() 00087 { 00088 ui.retranslateUi(this); 00089 } 00090 00091 /** Applies the network configuration settings to Tor. Returns true if the * 00092 * settings were applied successfully. Otherwise, <b>errmsg</b> is set and * 00093 * false is returned. */ 00094 bool 00095 NetworkPage::apply(QString &errmsg) 00096 { 00097 return NetworkSettings(Vidalia::torControl()).apply(&errmsg); 00098 } 00099 00100 /** Returns true if the user has changed their server settings since the * 00101 * last time they were applied to Tor. */ 00102 bool 00103 NetworkPage::changedSinceLastApply() 00104 { 00105 return NetworkSettings(Vidalia::torControl()).changedSinceLastApply(); 00106 } 00107 00108 /** Reverts the server configuration settings to their values at the last * 00109 * time they were successfully applied to Tor. */ 00110 void 00111 NetworkPage::revert() 00112 { 00113 NetworkSettings settings(Vidalia::torControl()); 00114 settings.revert(); 00115 } 00116 00117 /** Called when a link in a label is clicked. <b>url</b> is the target of 00118 * the clicked link. */ 00119 void 00120 NetworkPage::onLinkActivated(const QString &url) 00121 { 00122 emit helpRequested(url); 00123 } 00124 00125 /** Adds a bridge to the bridge list box. */ 00126 void 00127 NetworkPage::addBridge() 00128 { 00129 QString input = ui.lineBridge->text().trimmed(); 00130 00131 if (input.isEmpty()) 00132 return; 00133 if (!ui.listBridges->findItems(input, Qt::MatchFixedString).isEmpty()) 00134 return; /* duplicate bridge */ 00135 00136 ui.listBridges->addItem(input); 00137 ui.lineBridge->clear(); 00138 } 00139 00140 /** Removes one or more selected bridges from the bridge list box. */ 00141 void 00142 NetworkPage::removeBridge() 00143 { 00144 qDeleteAll(ui.listBridges->selectedItems()); 00145 } 00146 00147 /** Copies all selected bridges to the clipboard. */ 00148 void 00149 NetworkPage::copySelectedBridgesToClipboard() 00150 { 00151 QString contents; 00152 00153 foreach (QListWidgetItem *item, ui.listBridges->selectedItems()) { 00154 #if defined(Q_WS_WIN) 00155 contents += item->text() + "\r\n"; 00156 #else 00157 contents += item->text() + "\n"; 00158 #endif 00159 } 00160 if (!contents.isEmpty()) 00161 vApp->clipboard()->setText(contents.trimmed()); 00162 } 00163 00164 /** Called when the user right-clicks on a bridge and displays a context 00165 * menu. */ 00166 void 00167 NetworkPage::bridgeContextMenuRequested(const QPoint &pos) 00168 { 00169 QMenu menu(this); 00170 00171 QListWidgetItem *item = ui.listBridges->itemAt(pos); 00172 if (!item) 00173 return; 00174 00175 QAction *copyAction = 00176 new QAction(QIcon(IMG_COPY), tr("Copy (Ctrl+C)"), &menu); 00177 connect(copyAction, SIGNAL(triggered()), 00178 this, SLOT(copySelectedBridgesToClipboard())); 00179 00180 menu.addAction(copyAction); 00181 menu.exec(ui.listBridges->mapToGlobal(pos)); 00182 } 00183 00184 /** Called when the user changes which bridges they have selected. */ 00185 void 00186 NetworkPage::bridgeSelectionChanged() 00187 { 00188 bool enabled = !ui.listBridges->selectedItems().isEmpty(); 00189 ui.btnCopyBridge->setEnabled(enabled); 00190 ui.btnRemoveBridge->setEnabled(enabled); 00191 } 00192 00193 /** Saves changes made to settings on the Firewall settings page. */ 00194 bool 00195 NetworkPage::save(QString &errmsg) 00196 { 00197 NetworkSettings settings(Vidalia::torControl()); 00198 QString addr; 00199 QString user, pass; 00200 NetworkSettings::ProxyType proxy = NetworkSettings::NoProxy; 00201 QStringList bridgeList; 00202 QList<quint16> reachablePorts; 00203 bool ok; 00204 00205 if (ui.chkUseProxy->isChecked()) { 00206 if (ui.lineProxyAddress->text().isEmpty() 00207 || ui.lineProxyPort->text().isEmpty()) { 00208 errmsg = tr("You must specify both an IP address or hostname and a " 00209 "port number to configure Tor to use a proxy to access " 00210 "the Internet."); 00211 return false; 00212 } 00213 if (ui.cmboProxyType->currentIndex() < 0) { 00214 errmsg = tr("You must select the proxy type."); 00215 return false; 00216 } 00217 } 00218 if (ui.chkFascistFirewall->isChecked() 00219 && ui.lineReachablePorts->text().isEmpty()) { 00220 errmsg = tr("You must specify one or more ports to which your " 00221 "firewall allows you to connect."); 00222 return false; 00223 } 00224 00225 if (ui.chkUseProxy->isChecked()) { 00226 if (!ui.lineProxyAddress->text().isEmpty()) { 00227 addr = ui.lineProxyAddress->text(); 00228 if (!ui.lineProxyPort->text().isEmpty()) 00229 addr += ":" + ui.lineProxyPort->text(); 00230 } 00231 00232 user = ui.lineProxyUsername->text(); 00233 pass = ui.lineProxyPassword->text(); 00234 00235 QVariant data; 00236 int type; 00237 00238 data = ui.cmboProxyType->itemData(ui.cmboProxyType->currentIndex()); 00239 Q_ASSERT(data.isValid()); 00240 type = data.toInt(); 00241 Q_ASSERT(type >= NetworkSettings::ProxyTypeMin && 00242 type <= NetworkSettings::ProxyTypeMax); 00243 proxy = static_cast<NetworkSettings::ProxyType>(type); 00244 } 00245 00246 settings.setProxyType(proxy); 00247 settings.setProxyAddress(addr); 00248 settings.setProxyUsername(user); 00249 settings.setProxyPassword(pass); 00250 00251 /* Save the reachable port settings */ 00252 settings.setFascistFirewall(ui.chkFascistFirewall->isChecked()); 00253 foreach (QString portString, 00254 ui.lineReachablePorts->text().split(",", QString::SkipEmptyParts)) { 00255 quint32 port = portString.toUInt(&ok); 00256 if (!ok || port < 1 || port > 65535) { 00257 errmsg = tr("'%1' is not a valid port number.").arg(portString); 00258 return false; 00259 } 00260 reachablePorts << (quint16)port; 00261 } 00262 settings.setReachablePorts(reachablePorts); 00263 00264 if (ui.chkUseBridges->isChecked()) { 00265 if (ui.listBridges->count() < 1) { 00266 errmsg = tr("You must specify one or more bridges."); 00267 return false; 00268 } 00269 } 00270 00271 /* Save the bridge settings */ 00272 settings.setUseBridges(ui.chkUseBridges->isChecked()); 00273 for (int i = 0; i < ui.listBridges->count(); i++) 00274 bridgeList << ui.listBridges->item(i)->text(); 00275 settings.setBridgeList(bridgeList); 00276 00277 return true; 00278 } 00279 00280 /** Loads previously saved settings */ 00281 void 00282 NetworkPage::load() 00283 { 00284 NetworkSettings settings(Vidalia::torControl()); 00285 QStringList reachablePortStrings; 00286 NetworkSettings::ProxyType proxyType; 00287 00288 /* Load proxy settings */ 00289 proxyType = settings.getProxyType(); 00290 ui.chkUseProxy->setChecked(proxyType != NetworkSettings::NoProxy); 00291 QStringList proxy = settings.getProxyAddress().split(":"); 00292 if (proxy.size() >= 1) 00293 ui.lineProxyAddress->setText(proxy.at(0)); 00294 if (proxy.size() >= 2) 00295 ui.lineProxyPort->setText(proxy.at(1)); 00296 ui.lineProxyUsername->setText(settings.getProxyUsername()); 00297 ui.lineProxyPassword->setText(settings.getProxyPassword()); 00298 00299 /* SOCKS options are only available on Tor >= 0.2.2.1-alpha, so don't show 00300 * them if Tor is running and its version is less than that. */ 00301 ui.cmboProxyType->clear(); 00302 if (!vApp->torControl()->isRunning() 00303 || vApp->torControl()->getTorVersion() >= 0x020201) { 00304 ui.cmboProxyType->addItem(tr("SOCKS 4"), NetworkSettings::Socks4Proxy); 00305 ui.cmboProxyType->addItem(tr("SOCKS 5"), NetworkSettings::Socks5Proxy); 00306 } else if (proxyType == NetworkSettings::Socks4Proxy 00307 || proxyType == NetworkSettings::Socks5Proxy) { 00308 /* Disable proxy if the settings include a SOCKS proxy and our version of 00309 * Tor is not compatible. */ 00310 proxyType = NetworkSettings::NoProxy; 00311 ui.chkUseProxy->setChecked(false); 00312 } 00313 ui.cmboProxyType->addItem(tr("HTTP / HTTPS"), 00314 NetworkSettings::HttpHttpsProxy); 00315 00316 ui.cmboProxyType->setCurrentIndex(ui.cmboProxyType->findData(proxyType)); 00317 00318 /* Load firewall settings */ 00319 ui.chkFascistFirewall->setChecked(settings.getFascistFirewall()); 00320 QList<quint16> reachablePorts = settings.getReachablePorts(); 00321 foreach (quint16 port, reachablePorts) { 00322 reachablePortStrings << QString::number(port); 00323 } 00324 ui.lineReachablePorts->setText(reachablePortStrings.join(",")); 00325 00326 /* Load bridge settings */ 00327 ui.chkUseBridges->setChecked(settings.getUseBridges()); 00328 ui.listBridges->clear(); 00329 ui.listBridges->addItems(settings.getBridgeList()); 00330 } 00331 00332 /** Called when the user clicks the "Find Bridges Now" button. 00333 * Attempts to establish an HTTPS connection to bridges.torproject.org 00334 * and download one or more bridge addresses. */ 00335 void 00336 NetworkPage::findBridges() 00337 { 00338 BridgeDownloaderProgressDialog *dlg = new BridgeDownloaderProgressDialog(this); 00339 00340 connect(_bridgeDownloader, SIGNAL(statusChanged(QString)), 00341 dlg, SLOT(setStatus(QString))); 00342 connect(_bridgeDownloader, SIGNAL(downloadProgress(qint64, qint64)), 00343 dlg, SLOT(setDownloadProgress(qint64, qint64))); 00344 connect(_bridgeDownloader, SIGNAL(bridgeRequestFailed(QString)), 00345 dlg, SLOT(bridgeRequestFailed(QString))); 00346 connect(_bridgeDownloader, SIGNAL(bridgeRequestFinished(QStringList)), 00347 dlg, SLOT(bridgeRequestFinished(QStringList))); 00348 connect(dlg, SIGNAL(retry()), this, SLOT(startBridgeRequest())); 00349 00350 startBridgeRequest(); 00351 switch (dlg->exec()) { 00352 case QDialogButtonBox::Cancel: 00353 _bridgeDownloader->cancelBridgeRequest(); 00354 break; 00355 00356 case QDialogButtonBox::Help: 00357 emit helpRequested("bridges.finding"); 00358 break; 00359 } 00360 00361 delete dlg; 00362 } 00363 00364 /** Starts a new request for additional bridge addresses. */ 00365 void 00366 NetworkPage::startBridgeRequest() 00367 { 00368 if (ui.chkUseProxy->isChecked() && 00369 ui.cmboProxyType->currentIndex() == NetworkSettings::HttpHttpsProxy) { 00370 _bridgeDownloader->setProxy(ui.lineProxyAddress->text(), 00371 ui.lineProxyPort->text().toUInt(), 00372 ui.lineProxyUsername->text(), 00373 ui.lineProxyPassword->text()); 00374 } 00375 00376 _bridgeDownloader->downloadBridges(BridgeDownloader::DownloadMethodHttps); 00377 } 00378 00379 /** Called when a previous bridge request initiated by the findBridges() 00380 * method has completed. <b>bridges</b> contains a list of all bridges 00381 * received. */ 00382 void 00383 NetworkPage::bridgeRequestFinished(const QStringList &bridges) 00384 { 00385 bool foundNewBridges = false; 00386 QString normalized; 00387 00388 foreach (QString bridge, bridges) { 00389 QString address = normalized.split(" ").at(0); 00390 if (ui.listBridges->findItems(address, Qt::MatchContains).isEmpty()) { 00391 ui.listBridges->addItem(normalized); 00392 foundNewBridges = true; 00393 } 00394 } 00395 00396 if (! foundNewBridges) { 00397 QMessageBox dlg(this); 00398 dlg.setIcon(QMessageBox::Information); 00399 dlg.setText(tr("No new bridges are currently available. You can either " 00400 "wait a while and try again, or try another method of " 00401 "finding new bridges.")); 00402 dlg.setInformativeText(tr("Click Help to see other methods of finding " 00403 "new bridges.")); 00404 dlg.setStandardButtons(QMessageBox::Ok | QMessageBox::Help); 00405 00406 if (dlg.exec() == QMessageBox::Help) 00407 emit helpRequested("bridges.finding"); 00408 } 00409 } 00410 00411 /** Disable proxy username and password fields when the user wants to use 00412 * a SOCKS 4 proxy. */ 00413 void 00414 NetworkPage::proxyTypeChanged(int selection) 00415 { 00416 QVariant data = ui.cmboProxyType->itemData(selection); 00417 00418 if (data.isValid() 00419 && data.toInt() == NetworkSettings::Socks4Proxy) { 00420 ui.lineProxyUsername->setEnabled(false); 00421 ui.lineProxyPassword->setEnabled(false); 00422 } else { 00423 ui.lineProxyUsername->setEnabled(true); 00424 ui.lineProxyPassword->setEnabled(true); 00425 } 00426 } 00427