Vidalia  0.2.17
ServerPage.cpp
Go to the documentation of this file.
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 ServerPage.cpp
00013 ** \brief Tor server configuration options
00014 */
00015 
00016 #include "config.h"
00017 #include "ServerPage.h"
00018 #include "Vidalia.h"
00019 #include "VMessageBox.h"
00020 #include "ConfigDialog.h"
00021 #include "IpValidator.h"
00022 #include "PortValidator.h"
00023 #include "DomainValidator.h"
00024 #include "NicknameValidator.h"
00025 #include "BridgeUsageDialog.h"
00026 
00027 #include "html.h"
00028 #include "stringutil.h"
00029 
00030 #if defined(USE_MINIUPNPC)
00031 #include "UPNPTestDialog.h"
00032 #endif
00033 
00034 
00035 #include <QClipboard>
00036 #include <QMessageBox>
00037 
00038 /* These are completely made up values (in bytes/sec). */
00039 #define CABLE256_AVG_RATE       (32*1024)
00040 #define CABLE256_MAX_RATE       (64*1024)
00041 #define CABLE512_AVG_RATE       (64*1024)
00042 #define CABLE512_MAX_RATE       (128*1024)
00043 #define CABLE768_AVG_RATE       (96*1024)
00044 #define CABLE768_MAX_RATE       (192*1024)
00045 #define T1_AVG_RATE             (192*1024)
00046 #define T1_MAX_RATE             (384*1024)
00047 #define HIGHBW_AVG_RATE         (5120*1024)
00048 #define HIGHBW_MAX_RATE         (10240*1024)
00049 /** Minimum allowed bandwidth rate (20KB) */
00050 #define MIN_BANDWIDTH_RATE      20
00051 /** Maximum bandwidth rate. This is limited to 2147483646 bytes, 
00052  * or 2097151 kilobytes. (2147483646/1024) */ 
00053 #define MAX_BANDWIDTH_RATE      2097151
00054 
00055 /** Ports represented by the "Websites" checkbox. (80) */
00056 #define PORTS_HTTP   (QStringList() << "80")
00057 /** Ports represented by the "Secure Websites" checkbox. (443) */
00058 #define PORTS_HTTPS  (QStringList() << "443")
00059 /** Ports represented by the "Retrieve Mail" checkbox. (110,143,993,995) */
00060 #define PORTS_MAIL   (QStringList() << "110" << "143" << "993" << "995")
00061 /** Ports represented by the "Instant Messaging" checkbox.
00062  * (703,1863,5050,5190,5222,8300,8888) */
00063 #define PORTS_IM     (QStringList() << "706" << "1863" << "5050" << "5190" \
00064                                     << "5222" << "5223" << "8300" << "8888")
00065 /** Ports represented by the "Internet Relay Chat" checkbox. 
00066  * (6660-6669,6697,7000-7001) */
00067 #define PORTS_IRC    (QStringList() << "6660-6669" << "6697" << "7000-7001")
00068 
00069 
00070 /** Constructor */
00071 ServerPage::ServerPage(QWidget *parent)
00072 : ConfigPage(parent, "Server")
00073 {
00074   /* Invoke the Qt Designer generated object setup routine */
00075   ui.setupUi(this);
00076   
00077   /* Create ServerSettings object */
00078   _settings = new ServerSettings(Vidalia::torControl());
00079 
00080   /* Bind events to actions */
00081   connect(ui.btnRateHelp, SIGNAL(clicked()), this, SLOT(bandwidthHelp()));
00082   connect(ui.btnExitHelp, SIGNAL(clicked()), this, SLOT(exitPolicyHelp()));
00083   connect(ui.btnUpnpHelp, SIGNAL(clicked()), this, SLOT(upnpHelp()));
00084   connect(ui.cmboRate, SIGNAL(currentIndexChanged(int)),
00085                  this, SLOT(rateChanged(int)));
00086   connect(ui.lineAvgRateLimit, SIGNAL(editingFinished()), 
00087                          this, SLOT(customRateChanged()));
00088   connect(ui.lineMaxRateLimit, SIGNAL(editingFinished()), 
00089                          this, SLOT(customRateChanged()));
00090   connect(ui.rdoClientMode, SIGNAL(toggled(bool)),
00091                       this, SLOT(serverModeChanged(bool)));
00092   connect(ui.rdoNonExitMode, SIGNAL(toggled(bool)),
00093                        this, SLOT(serverModeChanged(bool)));
00094   connect(ui.rdoServerMode, SIGNAL(toggled(bool)),
00095                       this, SLOT(serverModeChanged(bool)));
00096   connect(ui.rdoBridgeMode, SIGNAL(toggled(bool)),
00097                       this, SLOT(serverModeChanged(bool)));
00098   connect(Vidalia::torControl(), SIGNAL(authenticated()),
00099                            this, SLOT(onAuthenticated()));
00100   connect(Vidalia::torControl(), SIGNAL(disconnected()),
00101                            this, SLOT(onDisconnected()));
00102   connect(ui.btnCopyBridgeIdentity, SIGNAL(clicked()),
00103                               this, SLOT(copyBridgeIdentity()));
00104   connect(ui.lblBridgeUsage, SIGNAL(linkActivated(QString)),
00105                        this, SLOT(linkActivated(QString)));
00106   connect(ui.lblWhatsThis, SIGNAL(linkActivated(QString)),
00107                        this, SLOT(linkActivated(QString)));
00108 
00109   /* Set validators for address, mask and various port number fields */
00110   ui.lineServerNickname->setValidator(new NicknameValidator(this));
00111   ui.lineServerPort->setValidator(new QIntValidator(1, 65535, this));
00112   ui.lineDirPort->setValidator(new QIntValidator(1, 65535, this));
00113   ui.lineAvgRateLimit->setValidator(
00114     new QIntValidator(MIN_BANDWIDTH_RATE, MAX_BANDWIDTH_RATE, this));
00115   ui.lineMaxRateLimit->setValidator(
00116     new QIntValidator(MIN_BANDWIDTH_RATE, MAX_BANDWIDTH_RATE, this));
00117 
00118 #if defined(USE_MINIUPNPC)
00119   connect(ui.btnTestUpnp, SIGNAL(clicked()), this, SLOT(testUpnp()));
00120 #else
00121   ui.chkEnableUpnp->setVisible(false);
00122   ui.btnTestUpnp->setVisible(false);
00123 #endif
00124 
00125   _tmpDirPort = "9030";
00126   _tmpMirror = true;
00127 }
00128 
00129 /** Destructor */
00130 ServerPage::~ServerPage()
00131 {
00132   delete _settings;
00133 }
00134 
00135 /** Called when the user changes the UI translation. */
00136 void
00137 ServerPage::retranslateUi()
00138 {
00139   ui.retranslateUi(this);
00140 }
00141 
00142 /** Called when Vidalia has authenticated to Tor. If the user's Tor is not
00143  * recent enough, this disables the bridge server option and displays a
00144  * warning if the user had previously configured Tor as a bridge. */
00145 void
00146 ServerPage::onAuthenticated()
00147 {
00148   quint32 torVersion = Vidalia::torControl()->getTorVersion();
00149   if (torVersion < 0x020008) {
00150     ui.rdoBridgeMode->setEnabled(false);
00151     if (ui.rdoBridgeMode->isChecked()) {
00152       int ret = VMessageBox::warning(this,
00153                   tr("Bridge Support Unavailable"),
00154                   p(tr("You have configured Tor to act as a bridge relay "
00155                        "for censored users, but your version of Tor does not "
00156                        "support bridges.")) +
00157                   p(tr("Please upgrade your Tor software or configure Tor to "
00158                        "act as a normal Tor relay.")),
00159                   VMessageBox::ShowSettings|VMessageBox::Default,
00160                   VMessageBox::Cancel);
00161       if (ret == VMessageBox::ShowSettings) {
00162         ConfigDialog *dialog = dynamic_cast<ConfigDialog *>(window());
00163         if (dialog)
00164           dialog->showWindow(ConfigDialog::Server);
00165       }
00166     }
00167   }
00168 }
00169 
00170 /** Called when Vidalia disconnects from Tor. This method reenables the bridge
00171  * server option. */
00172 void
00173 ServerPage::onDisconnected()
00174 {
00175   ui.rdoBridgeMode->setEnabled(true);
00176 }
00177 
00178 /** Copies the user's bridge relay identity to the clipboard. */
00179 void
00180 ServerPage::copyBridgeIdentity()
00181 {
00182   QString bridge = ui.lblBridgeIdentity->text();
00183   if (!bridge.isEmpty())
00184     vApp->clipboard()->setText(bridge);
00185 }
00186 
00187 /** Loads the user's bridge relay identity into the appropriate widgets. If
00188  * the user's bridge is not running, then "Not Running" will be displayed.
00189  * Otherwise, either the bridge's "address:port", "fingerprint", or
00190  * "address:port fingerprint" will be displayed, depending on whether our
00191  * GETCONF and GETINFO commands are successful. */
00192 void
00193 ServerPage::loadBridgeIdentity()
00194 {
00195   TorControl *tc = Vidalia::torControl();
00196   QString bridge, address, orPort, fingerprint;
00197 
00198   if (tc->isConnected()) {
00199     tc->getInfo("address", address);
00200     tc->getInfo("fingerprint", fingerprint);
00201     tc->getConf("ORPort", orPort);
00202   
00203     if (!address.isEmpty() && !orPort.isEmpty() && orPort != "0")
00204       bridge = address + ":" + orPort + " ";
00205     if (!fingerprint.isEmpty())
00206       bridge += fingerprint;
00207     bridge = bridge.trimmed();
00208   }
00209 
00210   ui.lblBridgeIdentity->setText(bridge.isEmpty()
00211                                   ? tr("Your bridge relay is not running.")
00212                                   : bridge);
00213   ui.lblYourBridgeRelayIs->setEnabled(!bridge.isEmpty());
00214   ui.lblBridgeIdentity->setEnabled(!bridge.isEmpty());
00215   ui.btnCopyBridgeIdentity->setEnabled(!bridge.isEmpty());
00216 }
00217 
00218 /** Called when the user toggles any one of the server mode radio buttons
00219  * and hides or displays the server configuration tabs appropriately. */
00220 void
00221 ServerPage::serverModeChanged(bool enabled)
00222 {
00223   Q_UNUSED(enabled);
00224   bool bridgeEnabled = ui.rdoBridgeMode->isChecked();
00225   bool relayEnabled = ui.rdoServerMode->isChecked() ||
00226                       ui.rdoNonExitMode->isChecked();
00227   
00228   /* Show the tab menu only if the user is running a normal relay or a bridge
00229    * relay. */
00230   ui.tabsMenu->setVisible(relayEnabled || bridgeEnabled);
00231   
00232   /* Disable the Exit Policies tab when bridge relay mode is selected */
00233   ui.tabsMenu->setTabEnabled(2, !bridgeEnabled);
00234   
00235   /* Display the widgets that show the user their bridge identity if bridge
00236    * relay mode is selected. */
00237   ui.lblYourBridgeRelayIs->setVisible(bridgeEnabled);
00238   ui.lblBridgeIdentity->setVisible(bridgeEnabled);
00239   ui.btnCopyBridgeIdentity->setVisible(bridgeEnabled);
00240   ui.chkPublishBridgeAddress->setVisible(bridgeEnabled);
00241   ui.lblBridgeUsage->setVisible(bridgeEnabled
00242                                   && Vidalia::torControl()->isConnected());
00243 
00244   if(bridgeEnabled) {
00245     if(ui.lineDirPort->text().length() != 0) {
00246       _tmpDirPort = ui.lineDirPort->text();
00247       _tmpMirror = ui.chkMirrorDirectory->isChecked();
00248     }
00249     ui.lineDirPort->clear();
00250     ui.chkMirrorDirectory->setChecked(false);
00251   } else {
00252     ui.lineDirPort->setText(_tmpDirPort);
00253     ui.chkMirrorDirectory->setChecked(_tmpMirror);
00254   }
00255 
00256   ui.chkMirrorDirectory->setEnabled(!bridgeEnabled);
00257   if(ui.chkMirrorDirectory->isChecked()) {
00258     ui.lblDirPort->setEnabled(!bridgeEnabled);
00259     ui.lineDirPort->setEnabled(!bridgeEnabled);
00260   }
00261 
00262   /* Disable the Exit Policies tab when bridge or non-exit relay mode is 
00263    * selected */
00264   ui.tabsMenu->setTabEnabled(2, !bridgeEnabled and !ui.rdoNonExitMode->isChecked());
00265 }
00266 
00267 /** Returns true if the user has changed their server settings since the
00268  * last time they were applied to Tor. */
00269 bool
00270 ServerPage::changedSinceLastApply()
00271 {
00272   return _settings->changedSinceLastApply();
00273 }
00274 
00275 /** Applies the server configuration settings to Tor. Returns true if the
00276  * settings were applied successfully. Otherwise, <b>errmsg</b> is
00277  * set and false is returned. */
00278 bool
00279 ServerPage::apply(QString &errmsg)
00280 {
00281   return _settings->apply(&errmsg);
00282 }
00283 
00284 /** Returns true if the user has changed their server settings since the
00285  * last time they were applied to Tor. */
00286 void
00287 ServerPage::revert()
00288 {
00289   _settings->revert();
00290 }
00291 
00292 /** Saves changes made to settings on the Server settings page. */
00293 bool
00294 ServerPage::save(QString &errmsg)
00295 {
00296   /* Force the bandwidth rate limits to validate */
00297   customRateChanged();
00298 
00299   if (ui.rdoServerMode->isChecked() || 
00300       ui.rdoNonExitMode->isChecked() ||
00301       ui.rdoBridgeMode->isChecked()) {
00302     /* A server must have an ORPort and a nickname */
00303     if (ui.lineServerPort->text().isEmpty() ||
00304         ui.lineServerNickname->text().isEmpty()) {
00305       errmsg = tr("You must specify at least a relay nickname and port.");
00306       return false;
00307     }
00308     /* If the bandwidth rates aren't set, use some defaults before saving */
00309     if (ui.lineAvgRateLimit->text().isEmpty()) {
00310       ui.lineAvgRateLimit->setText(QString::number(2097152/1024) /* 2MB */);
00311     }
00312     if (ui.lineMaxRateLimit->text().isEmpty()) {
00313       ui.lineMaxRateLimit->setText(QString::number(5242880/1024) /* 5MB */);
00314     }
00315   }
00316   
00317   /* "Server" is enabled whether we're a bridge or normal relay. "Bridge" is
00318    * only enabled if we're a bridge (obviously). */
00319   _settings->setServerEnabled(ui.rdoServerMode->isChecked()
00320                               || ui.rdoNonExitMode->isChecked()
00321                               || ui.rdoBridgeMode->isChecked());
00322   _settings->setNonExitEnabled(ui.rdoNonExitMode->isChecked());
00323   _settings->setBridgeEnabled(ui.rdoBridgeMode->isChecked());
00324   if (ui.rdoBridgeMode->isChecked())
00325     _settings->setPublishServerDescriptor(ui.chkPublishBridgeAddress->isChecked());
00326 
00327   /* Save the rest of the server settings. */
00328   _settings->setNickname(ui.lineServerNickname->text());
00329   _settings->setORPort(ui.lineServerPort->text().toUInt());
00330   if (!ui.rdoBridgeMode->isChecked()) {
00331     _settings->setDirPort(ui.lineDirPort->text().toUInt());
00332     _settings->setDirectoryMirror(ui.chkMirrorDirectory->isChecked());
00333   } else {
00334     _settings->setDirectoryMirror(false);
00335   }
00336   _settings->setContactInfo(ui.lineServerContact->text());
00337   saveBandwidthLimits();
00338   saveExitPolicies();
00339 
00340 #if defined(USE_MINIUPNPC)
00341   _settings->setUpnpEnabled(ui.chkEnableUpnp->isChecked());
00342 #endif
00343 
00344   return true;
00345 }
00346 
00347 /** Loads previously saved settings */
00348 void
00349 ServerPage::load()
00350 {
00351   if (_settings->isBridgeEnabled())
00352     ui.rdoBridgeMode->setChecked(true);
00353   else if (_settings->isNonExitEnabled()) {
00354     if(_settings->getExitPolicy().toString() != "reject *:*")
00355       ui.rdoServerMode->setChecked(true);
00356     else
00357       ui.rdoNonExitMode->setChecked(true);
00358   } else if (_settings->isServerEnabled()) {
00359     if(_settings->getExitPolicy().toString() == "reject *:*")
00360       ui.rdoNonExitMode->setChecked(true);
00361     else
00362       ui.rdoServerMode->setChecked(true);
00363   } else
00364     ui.rdoClientMode->setChecked(true);
00365 
00366   ui.lineServerNickname->setText(_settings->getNickname());
00367   ui.lineServerPort->setText(QString::number(_settings->getORPort()));
00368   ui.lineDirPort->setText(QString::number(_settings->getDirPort()));
00369   ui.lineServerContact->setText(_settings->getContactInfo());
00370   ui.chkMirrorDirectory->setChecked(_settings->isDirectoryMirror());
00371   ui.lblBridgeUsage->setVisible(_settings->isBridgeEnabled()
00372                                   && Vidalia::torControl()->isConnected());
00373   ui.chkPublishBridgeAddress->setChecked(_settings->publishServerDescriptor());
00374 
00375   loadBandwidthLimits();
00376   loadExitPolicies();
00377   loadBridgeIdentity();
00378 
00379 #if defined(USE_MINIUPNPC)
00380   ui.chkEnableUpnp->setChecked(_settings->isUpnpEnabled());
00381 #endif
00382 }
00383 
00384 /** Shows exit policy related help information */
00385 void
00386 ServerPage::exitPolicyHelp()
00387 {
00388   emit helpRequested("server.exitpolicy");
00389 }
00390 
00391 /** Shows the bandwidth rate limiting help information */
00392 void
00393 ServerPage::bandwidthHelp()
00394 {
00395   emit helpRequested("server.bandwidth");
00396 }
00397 
00398 /** Loads the server's bandwidth average and burst limits. */
00399 void
00400 ServerPage::loadBandwidthLimits()
00401 {
00402   quint32 avgRate = _settings->getBandwidthAvgRate();
00403   quint32 maxRate = _settings->getBandwidthBurstRate();
00404 
00405   if (avgRate == CABLE256_AVG_RATE && 
00406       maxRate == CABLE256_MAX_RATE) {
00407     /* Cable/DSL 256 Kbps */
00408     ui.cmboRate->setCurrentIndex(CableDsl256); 
00409   } else if (avgRate == CABLE512_AVG_RATE && 
00410              maxRate == CABLE512_MAX_RATE) {
00411     /* Cable/DSL 512 Kbps */
00412     ui.cmboRate->setCurrentIndex(CableDsl512);
00413   } else if (avgRate == CABLE768_AVG_RATE && 
00414              maxRate == CABLE768_MAX_RATE) {
00415     /* Cable/DSL 768 Kbps */
00416     ui.cmboRate->setCurrentIndex(CableDsl768);
00417   } else if (avgRate == T1_AVG_RATE && 
00418              maxRate == T1_MAX_RATE) {
00419     /* T1/Cable/DSL 1.5 Mbps */
00420     ui.cmboRate->setCurrentIndex(T1CableDsl1500);
00421   } else if (avgRate == HIGHBW_AVG_RATE && 
00422              maxRate == HIGHBW_MAX_RATE) {
00423     /* > 1.5 Mbps */
00424     ui.cmboRate->setCurrentIndex(GreaterThan1500);
00425   } else {
00426     /* Custom bandwidth limits */
00427     ui.cmboRate->setCurrentIndex(CustomBwLimits);
00428   }
00429   /* Fill in the custom bandwidth limit boxes */
00430   ui.lineAvgRateLimit->setText(QString::number(avgRate/1024));
00431   ui.lineMaxRateLimit->setText(QString::number(maxRate/1024));
00432 }
00433 
00434 /** Saves the server's bandwidth average and burst limits. */
00435 void
00436 ServerPage::saveBandwidthLimits()
00437 {
00438   quint32 avgRate, maxRate;
00439 
00440   switch (ui.cmboRate->currentIndex()) {
00441     case CableDsl256: /* Cable/DSL 256 Kbps */
00442       avgRate = CABLE256_AVG_RATE;
00443       maxRate = CABLE256_MAX_RATE;
00444       break;
00445     case CableDsl512: /* Cable/DSL 512 Kbps */
00446       avgRate = CABLE512_AVG_RATE;
00447       maxRate = CABLE512_MAX_RATE;
00448       break;
00449     case CableDsl768: /* Cable/DSL 768 Kbps */
00450       avgRate = CABLE768_AVG_RATE;
00451       maxRate = CABLE768_MAX_RATE;
00452       break;
00453     case T1CableDsl1500: /* T1/Cable/DSL 1.5 Mbps */
00454       avgRate = T1_AVG_RATE;
00455       maxRate = T1_MAX_RATE;
00456       break;
00457     case GreaterThan1500: /* > 1.5 Mbps */
00458       avgRate = HIGHBW_AVG_RATE;
00459       maxRate = HIGHBW_MAX_RATE;
00460       break;
00461     default: /* Custom bandwidth limits */
00462       avgRate = (quint32)(ui.lineAvgRateLimit->text().toUInt()*1024);
00463       maxRate = (quint32)(ui.lineMaxRateLimit->text().toUInt()*1024);
00464       break;
00465   }
00466   _settings->setBandwidthAvgRate(avgRate);
00467   _settings->setBandwidthBurstRate(maxRate);
00468 }
00469 
00470 /** */
00471 void
00472 ServerPage::loadExitPolicies()
00473 {
00474   ExitPolicy exitPolicy = _settings->getExitPolicy();
00475   
00476   if (exitPolicy.contains(Policy(Policy::RejectAll))) {
00477     /* If the policy ends with reject *:*, check if the policy explicitly
00478      * accepts these ports */
00479     ui.chkWebsites->setChecked(exitPolicy.acceptsPorts(PORTS_HTTP));
00480     ui.chkSecWebsites->setChecked(exitPolicy.acceptsPorts(PORTS_HTTPS));
00481     ui.chkMail->setChecked(exitPolicy.acceptsPorts(PORTS_MAIL));
00482     ui.chkIRC->setChecked(exitPolicy.acceptsPorts(PORTS_IRC));
00483     ui.chkIM->setChecked(exitPolicy.acceptsPorts(PORTS_IM));
00484     ui.chkMisc->setChecked(false);
00485   } else {
00486     /* If the exit policy ends with accept *:*, check if the policy explicitly
00487      * rejects these ports */
00488     ui.chkWebsites->setChecked(!exitPolicy.rejectsPorts(PORTS_HTTP));
00489     ui.chkSecWebsites->setChecked(!exitPolicy.rejectsPorts(PORTS_HTTPS));
00490     ui.chkMail->setChecked(!exitPolicy.rejectsPorts(PORTS_MAIL));
00491     ui.chkIRC->setChecked(!exitPolicy.rejectsPorts(PORTS_IRC));
00492     ui.chkIM->setChecked(!exitPolicy.rejectsPorts(PORTS_IM));
00493     ui.chkMisc->setChecked(true);
00494   }
00495 }
00496 
00497 /** */
00498 void
00499 ServerPage::saveExitPolicies()
00500 {
00501   ExitPolicy *exitPolicy;
00502   if(ui.rdoNonExitMode->isChecked()) {
00503     exitPolicy = new ExitPolicy(ExitPolicy::Middleman);
00504   } else {
00505     exitPolicy = new ExitPolicy();
00506     bool rejectUnchecked = ui.chkMisc->isChecked();
00507     
00508     /* If misc is checked, then reject unchecked items and leave the default exit
00509      * policy alone. Else, accept only checked items and end with reject *:*,
00510      * replacing the default exit policy. */
00511     if (ui.chkWebsites->isChecked() && !rejectUnchecked) {
00512       exitPolicy->addAcceptedPorts(PORTS_HTTP);
00513     } else if (!ui.chkWebsites->isChecked() && rejectUnchecked) {
00514       exitPolicy->addRejectedPorts(PORTS_HTTP);
00515     }
00516     if (ui.chkSecWebsites->isChecked() && !rejectUnchecked) {
00517       exitPolicy->addAcceptedPorts(PORTS_HTTPS);
00518     } else if (!ui.chkSecWebsites->isChecked() && rejectUnchecked) {
00519       exitPolicy->addRejectedPorts(PORTS_HTTPS);
00520     }
00521     if (ui.chkMail->isChecked() && !rejectUnchecked) {
00522       exitPolicy->addAcceptedPorts(PORTS_MAIL);
00523     } else if (!ui.chkMail->isChecked() && rejectUnchecked) {
00524       exitPolicy->addRejectedPorts(PORTS_MAIL);
00525     }
00526     if (ui.chkIRC->isChecked() && !rejectUnchecked) {
00527       exitPolicy->addAcceptedPorts(PORTS_IRC);
00528     } else if (!ui.chkIRC->isChecked() && rejectUnchecked) {
00529       exitPolicy->addRejectedPorts(PORTS_IRC);
00530     }
00531     if (ui.chkIM->isChecked() && !rejectUnchecked) {
00532       exitPolicy->addAcceptedPorts(PORTS_IM);
00533     } else if (!ui.chkIM->isChecked() && rejectUnchecked) {
00534       exitPolicy->addRejectedPorts(PORTS_IM);
00535     }
00536     if (!ui.chkMisc->isChecked()) {
00537       exitPolicy->addPolicy(Policy(Policy::RejectAll));
00538     }
00539   }
00540 
00541   _settings->setExitPolicy(*exitPolicy);
00542 }
00543 
00544 /** Called when the user selects a new value from the rate combo box. */
00545 void
00546 ServerPage::rateChanged(int index)
00547 {
00548   /* If the "Custom" option is selected, show the custom bandwidth 
00549    * limits form. */
00550   ui.frmCustomRate->setVisible(index == CustomBwLimits);
00551 }
00552 
00553 /** Called when the user edits the long-term average or maximum bandwidth limit. 
00554  * This ensures that the average bandwidth rate is greater than MIN_RATE
00555  * (20KB/s) and that the max rate is greater than the average rate. */
00556 void
00557 ServerPage::customRateChanged()
00558 {
00559   /* Make sure the average rate isn't too low or too high */
00560   quint32 avgRate = (quint32)ui.lineAvgRateLimit->text().toUInt();
00561   if (avgRate < MIN_BANDWIDTH_RATE) {
00562     ui.lineAvgRateLimit->setText(QString::number(MIN_BANDWIDTH_RATE));    
00563   }
00564   if (avgRate > MAX_BANDWIDTH_RATE) {
00565     ui.lineAvgRateLimit->setText(QString::number(MAX_BANDWIDTH_RATE));
00566   }
00567   /* Ensure the max burst rate is greater than the average rate but less than
00568    * the maximum allowed rate. */
00569   quint32 burstRate = (quint32)ui.lineMaxRateLimit->text().toUInt();
00570   if (avgRate > burstRate) {
00571     ui.lineMaxRateLimit->setText(QString::number(avgRate));
00572   }
00573   if (burstRate > MAX_BANDWIDTH_RATE) {
00574     ui.lineMaxRateLimit->setText(QString::number(MAX_BANDWIDTH_RATE));
00575   }
00576 }
00577 
00578 /** Tests automatic port forwarding using UPnP. */
00579 void
00580 ServerPage::testUpnp()
00581 {
00582 #if defined(USE_MINIUPNPC)
00583   UPNPTestDialog dlg(ui.lineServerPort->text().toUInt(),
00584                      ui.lineDirPort->text().toUInt(), this);
00585   
00586   connect(&dlg, SIGNAL(help()), this, SLOT(upnpHelp()));
00587 
00588   dlg.exec();
00589 #endif
00590 }
00591 
00592 /** Called when the user clicks the UPnP test dialog's help button. */
00593 void
00594 ServerPage::upnpHelp()
00595 {
00596   emit helpRequested("server.upnp");
00597 }
00598 
00599 /** Called when the user clicks on a QLabel containing a hyperlink. */
00600 void
00601 ServerPage::linkActivated(const QString &url)
00602 {
00603   if (!url.compare("#bridgeUsage"))
00604     displayBridgeUsage();
00605   else if(!url.compare("#bridgeHelp"))
00606     emit helpRequested("bridges.about");
00607 }
00608 
00609 /** Retrieves bridge usage history from Tor, parses and validates it, and
00610  * then displays it in a new dialog. */
00611 void
00612 ServerPage::displayBridgeUsage()
00613 {
00614   QString info;
00615   QMessageBox dlg(this);
00616 
00617   info = Vidalia::torControl()->getInfo("status/clients-seen").toString();
00618   if (info.isEmpty()) {
00619     goto none;
00620   } else {
00621     QDateTime timeStarted;
00622     QHash<QString,int> countrySummary;
00623     QHash<QString,QString> keyvals;
00624     BridgeUsageDialog dlg(this);
00625     bool ok;
00626 
00627     keyvals = string_parse_keyvals(info, &ok);
00628     if (!ok || !keyvals.contains("TimeStarted") 
00629             || !keyvals.contains("CountrySummary"))
00630       goto err;
00631 
00632     timeStarted = QDateTime::fromString(keyvals.value("TimeStarted"), 
00633                                         "yyyy-MM-dd HH:mm:ss");
00634     if (!timeStarted.isValid())
00635       goto err;
00636 
00637     // Default is LocalTime, force UTC
00638     timeStarted.setTimeSpec(Qt::UTC);
00639 
00640     QStringList summary = keyvals.value("CountrySummary")
00641                                  .split(",", QString::SkipEmptyParts);
00642     if (summary.isEmpty()) {
00643       goto none;
00644     } else {
00645       foreach (QString pair, summary) {
00646         QStringList parts = pair.split("=");
00647         if (parts.size() != 2)
00648           goto err;
00649 
00650         countrySummary.insert(parts.at(0).toUpper(), parts.at(1).toInt(&ok));
00651         if (!ok)
00652           goto err;
00653       }
00654 
00655       dlg.update(timeStarted, countrySummary);
00656       dlg.exec();
00657     }
00658   }
00659   return;
00660 
00661 none:
00662   dlg.setIcon(QMessageBox::Information);
00663   dlg.setWindowTitle(tr("No Recent Usage"));
00664   dlg.setText(tr("No clients have used your relay recently."));
00665   dlg.setInformativeText(tr("Leave your relay running so clients have "
00666                             "a better chance of finding and using it."));
00667   dlg.setStandardButtons(QMessageBox::Ok);
00668   dlg.exec();
00669   return;
00670 
00671 err:
00672   dlg.setIcon(QMessageBox::Warning);
00673   dlg.setWindowTitle(tr("Bridge History"));
00674   dlg.setText(tr("Vidalia was unable to retrieve your bridge's usage "
00675                  "history."));
00676   dlg.setInformativeText(tr("Tor returned an improperly formatted "
00677                             "response when Vidalia requested your "
00678                             "bridge's usage history."));
00679   dlg.setDetailedText(tr("The returned response was: %1").arg(info));
00680   dlg.setStandardButtons(QMessageBox::Ok);
00681   dlg.exec();
00682 }
00683