Vidalia  0.2.15
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     ui.rdoNonExitMode->setChecked(true);
00355   else if (_settings->isServerEnabled())
00356     ui.rdoServerMode->setChecked(true);
00357   else
00358     ui.rdoClientMode->setChecked(true);
00359 
00360   ui.lineServerNickname->setText(_settings->getNickname());
00361   ui.lineServerPort->setText(QString::number(_settings->getORPort()));
00362   ui.lineDirPort->setText(QString::number(_settings->getDirPort()));
00363   ui.lineServerContact->setText(_settings->getContactInfo());
00364   ui.chkMirrorDirectory->setChecked(_settings->isDirectoryMirror());
00365   ui.lblBridgeUsage->setVisible(_settings->isBridgeEnabled()
00366                                   && Vidalia::torControl()->isConnected());
00367   ui.chkPublishBridgeAddress->setChecked(_settings->publishServerDescriptor());
00368 
00369   loadBandwidthLimits();
00370   loadExitPolicies();
00371   loadBridgeIdentity();
00372 
00373 #if defined(USE_MINIUPNPC)
00374   ui.chkEnableUpnp->setChecked(_settings->isUpnpEnabled());
00375 #endif
00376 }
00377 
00378 /** Shows exit policy related help information */
00379 void
00380 ServerPage::exitPolicyHelp()
00381 {
00382   emit helpRequested("server.exitpolicy");
00383 }
00384 
00385 /** Shows the bandwidth rate limiting help information */
00386 void
00387 ServerPage::bandwidthHelp()
00388 {
00389   emit helpRequested("server.bandwidth");
00390 }
00391 
00392 /** Loads the server's bandwidth average and burst limits. */
00393 void
00394 ServerPage::loadBandwidthLimits()
00395 {
00396   quint32 avgRate = _settings->getBandwidthAvgRate();
00397   quint32 maxRate = _settings->getBandwidthBurstRate();
00398 
00399   if (avgRate == CABLE256_AVG_RATE && 
00400       maxRate == CABLE256_MAX_RATE) {
00401     /* Cable/DSL 256 Kbps */
00402     ui.cmboRate->setCurrentIndex(CableDsl256); 
00403   } else if (avgRate == CABLE512_AVG_RATE && 
00404              maxRate == CABLE512_MAX_RATE) {
00405     /* Cable/DSL 512 Kbps */
00406     ui.cmboRate->setCurrentIndex(CableDsl512);
00407   } else if (avgRate == CABLE768_AVG_RATE && 
00408              maxRate == CABLE768_MAX_RATE) {
00409     /* Cable/DSL 768 Kbps */
00410     ui.cmboRate->setCurrentIndex(CableDsl768);
00411   } else if (avgRate == T1_AVG_RATE && 
00412              maxRate == T1_MAX_RATE) {
00413     /* T1/Cable/DSL 1.5 Mbps */
00414     ui.cmboRate->setCurrentIndex(T1CableDsl1500);
00415   } else if (avgRate == HIGHBW_AVG_RATE && 
00416              maxRate == HIGHBW_MAX_RATE) {
00417     /* > 1.5 Mbps */
00418     ui.cmboRate->setCurrentIndex(GreaterThan1500);
00419   } else {
00420     /* Custom bandwidth limits */
00421     ui.cmboRate->setCurrentIndex(CustomBwLimits);
00422   }
00423   /* Fill in the custom bandwidth limit boxes */
00424   ui.lineAvgRateLimit->setText(QString::number(avgRate/1024));
00425   ui.lineMaxRateLimit->setText(QString::number(maxRate/1024));
00426 }
00427 
00428 /** Saves the server's bandwidth average and burst limits. */
00429 void
00430 ServerPage::saveBandwidthLimits()
00431 {
00432   quint32 avgRate, maxRate;
00433 
00434   switch (ui.cmboRate->currentIndex()) {
00435     case CableDsl256: /* Cable/DSL 256 Kbps */
00436       avgRate = CABLE256_AVG_RATE;
00437       maxRate = CABLE256_MAX_RATE;
00438       break;
00439     case CableDsl512: /* Cable/DSL 512 Kbps */
00440       avgRate = CABLE512_AVG_RATE;
00441       maxRate = CABLE512_MAX_RATE;
00442       break;
00443     case CableDsl768: /* Cable/DSL 768 Kbps */
00444       avgRate = CABLE768_AVG_RATE;
00445       maxRate = CABLE768_MAX_RATE;
00446       break;
00447     case T1CableDsl1500: /* T1/Cable/DSL 1.5 Mbps */
00448       avgRate = T1_AVG_RATE;
00449       maxRate = T1_MAX_RATE;
00450       break;
00451     case GreaterThan1500: /* > 1.5 Mbps */
00452       avgRate = HIGHBW_AVG_RATE;
00453       maxRate = HIGHBW_MAX_RATE;
00454       break;
00455     default: /* Custom bandwidth limits */
00456       avgRate = (quint32)(ui.lineAvgRateLimit->text().toUInt()*1024);
00457       maxRate = (quint32)(ui.lineMaxRateLimit->text().toUInt()*1024);
00458       break;
00459   }
00460   _settings->setBandwidthAvgRate(avgRate);
00461   _settings->setBandwidthBurstRate(maxRate);
00462 }
00463 
00464 /** */
00465 void
00466 ServerPage::loadExitPolicies()
00467 {
00468   ExitPolicy exitPolicy = _settings->getExitPolicy();
00469   
00470   if (exitPolicy.contains(Policy(Policy::RejectAll))) {
00471     /* If the policy ends with reject *:*, check if the policy explicitly
00472      * accepts these ports */
00473     ui.chkWebsites->setChecked(exitPolicy.acceptsPorts(PORTS_HTTP));
00474     ui.chkSecWebsites->setChecked(exitPolicy.acceptsPorts(PORTS_HTTPS));
00475     ui.chkMail->setChecked(exitPolicy.acceptsPorts(PORTS_MAIL));
00476     ui.chkIRC->setChecked(exitPolicy.acceptsPorts(PORTS_IRC));
00477     ui.chkIM->setChecked(exitPolicy.acceptsPorts(PORTS_IM));
00478     ui.chkMisc->setChecked(false);
00479   } else {
00480     /* If the exit policy ends with accept *:*, check if the policy explicitly
00481      * rejects these ports */
00482     ui.chkWebsites->setChecked(!exitPolicy.rejectsPorts(PORTS_HTTP));
00483     ui.chkSecWebsites->setChecked(!exitPolicy.rejectsPorts(PORTS_HTTPS));
00484     ui.chkMail->setChecked(!exitPolicy.rejectsPorts(PORTS_MAIL));
00485     ui.chkIRC->setChecked(!exitPolicy.rejectsPorts(PORTS_IRC));
00486     ui.chkIM->setChecked(!exitPolicy.rejectsPorts(PORTS_IM));
00487     ui.chkMisc->setChecked(true);
00488   }
00489 }
00490 
00491 /** */
00492 void
00493 ServerPage::saveExitPolicies()
00494 {
00495   ExitPolicy *exitPolicy;
00496   if(ui.rdoNonExitMode->isChecked()) {
00497     exitPolicy = new ExitPolicy(ExitPolicy::Middleman);
00498   } else {
00499     exitPolicy = new ExitPolicy();
00500     bool rejectUnchecked = ui.chkMisc->isChecked();
00501     
00502     /* If misc is checked, then reject unchecked items and leave the default exit
00503      * policy alone. Else, accept only checked items and end with reject *:*,
00504      * replacing the default exit policy. */
00505     if (ui.chkWebsites->isChecked() && !rejectUnchecked) {
00506       exitPolicy->addAcceptedPorts(PORTS_HTTP);
00507     } else if (!ui.chkWebsites->isChecked() && rejectUnchecked) {
00508       exitPolicy->addRejectedPorts(PORTS_HTTP);
00509     }
00510     if (ui.chkSecWebsites->isChecked() && !rejectUnchecked) {
00511       exitPolicy->addAcceptedPorts(PORTS_HTTPS);
00512     } else if (!ui.chkSecWebsites->isChecked() && rejectUnchecked) {
00513       exitPolicy->addRejectedPorts(PORTS_HTTPS);
00514     }
00515     if (ui.chkMail->isChecked() && !rejectUnchecked) {
00516       exitPolicy->addAcceptedPorts(PORTS_MAIL);
00517     } else if (!ui.chkMail->isChecked() && rejectUnchecked) {
00518       exitPolicy->addRejectedPorts(PORTS_MAIL);
00519     }
00520     if (ui.chkIRC->isChecked() && !rejectUnchecked) {
00521       exitPolicy->addAcceptedPorts(PORTS_IRC);
00522     } else if (!ui.chkIRC->isChecked() && rejectUnchecked) {
00523       exitPolicy->addRejectedPorts(PORTS_IRC);
00524     }
00525     if (ui.chkIM->isChecked() && !rejectUnchecked) {
00526       exitPolicy->addAcceptedPorts(PORTS_IM);
00527     } else if (!ui.chkIM->isChecked() && rejectUnchecked) {
00528       exitPolicy->addRejectedPorts(PORTS_IM);
00529     }
00530     if (!ui.chkMisc->isChecked()) {
00531       exitPolicy->addPolicy(Policy(Policy::RejectAll));
00532     }
00533   }
00534 
00535   _settings->setExitPolicy(*exitPolicy);
00536 }
00537 
00538 /** Called when the user selects a new value from the rate combo box. */
00539 void
00540 ServerPage::rateChanged(int index)
00541 {
00542   /* If the "Custom" option is selected, show the custom bandwidth 
00543    * limits form. */
00544   ui.frmCustomRate->setVisible(index == CustomBwLimits);
00545 }
00546 
00547 /** Called when the user edits the long-term average or maximum bandwidth limit. 
00548  * This ensures that the average bandwidth rate is greater than MIN_RATE
00549  * (20KB/s) and that the max rate is greater than the average rate. */
00550 void
00551 ServerPage::customRateChanged()
00552 {
00553   /* Make sure the average rate isn't too low or too high */
00554   quint32 avgRate = (quint32)ui.lineAvgRateLimit->text().toUInt();
00555   if (avgRate < MIN_BANDWIDTH_RATE) {
00556     ui.lineAvgRateLimit->setText(QString::number(MIN_BANDWIDTH_RATE));    
00557   }
00558   if (avgRate > MAX_BANDWIDTH_RATE) {
00559     ui.lineAvgRateLimit->setText(QString::number(MAX_BANDWIDTH_RATE));
00560   }
00561   /* Ensure the max burst rate is greater than the average rate but less than
00562    * the maximum allowed rate. */
00563   quint32 burstRate = (quint32)ui.lineMaxRateLimit->text().toUInt();
00564   if (avgRate > burstRate) {
00565     ui.lineMaxRateLimit->setText(QString::number(avgRate));
00566   }
00567   if (burstRate > MAX_BANDWIDTH_RATE) {
00568     ui.lineMaxRateLimit->setText(QString::number(MAX_BANDWIDTH_RATE));
00569   }
00570 }
00571 
00572 /** Tests automatic port forwarding using UPnP. */
00573 void
00574 ServerPage::testUpnp()
00575 {
00576 #if defined(USE_MINIUPNPC)
00577   UPNPTestDialog dlg(ui.lineServerPort->text().toUInt(),
00578                      ui.lineDirPort->text().toUInt(), this);
00579   
00580   connect(&dlg, SIGNAL(help()), this, SLOT(upnpHelp()));
00581 
00582   dlg.exec();
00583 #endif
00584 }
00585 
00586 /** Called when the user clicks the UPnP test dialog's help button. */
00587 void
00588 ServerPage::upnpHelp()
00589 {
00590   emit helpRequested("server.upnp");
00591 }
00592 
00593 /** Called when the user clicks on a QLabel containing a hyperlink. */
00594 void
00595 ServerPage::linkActivated(const QString &url)
00596 {
00597   if (!url.compare("#bridgeUsage"))
00598     displayBridgeUsage();
00599   else if(!url.compare("#bridgeHelp"))
00600     emit helpRequested("bridges.about");
00601 }
00602 
00603 /** Retrieves bridge usage history from Tor, parses and validates it, and
00604  * then displays it in a new dialog. */
00605 void
00606 ServerPage::displayBridgeUsage()
00607 {
00608   QString info;
00609   QMessageBox dlg(this);
00610 
00611   info = Vidalia::torControl()->getInfo("status/clients-seen").toString();
00612   if (info.isEmpty()) {
00613     goto none;
00614   } else {
00615     QDateTime timeStarted;
00616     QHash<QString,int> countrySummary;
00617     QHash<QString,QString> keyvals;
00618     BridgeUsageDialog dlg(this);
00619     bool ok;
00620 
00621     keyvals = string_parse_keyvals(info, &ok);
00622     if (!ok || !keyvals.contains("TimeStarted") 
00623             || !keyvals.contains("CountrySummary"))
00624       goto err;
00625 
00626     timeStarted = QDateTime::fromString(keyvals.value("TimeStarted"), 
00627                                         "yyyy-MM-dd HH:mm:ss");
00628     if (!timeStarted.isValid())
00629       goto err;
00630 
00631     // Default is LocalTime, force UTC
00632     timeStarted.setTimeSpec(Qt::UTC);
00633 
00634     QStringList summary = keyvals.value("CountrySummary")
00635                                  .split(",", QString::SkipEmptyParts);
00636     if (summary.isEmpty()) {
00637       goto none;
00638     } else {
00639       foreach (QString pair, summary) {
00640         QStringList parts = pair.split("=");
00641         if (parts.size() != 2)
00642           goto err;
00643 
00644         countrySummary.insert(parts.at(0).toUpper(), parts.at(1).toInt(&ok));
00645         if (!ok)
00646           goto err;
00647       }
00648 
00649       dlg.update(timeStarted, countrySummary);
00650       dlg.exec();
00651     }
00652   }
00653   return;
00654 
00655 none:
00656   dlg.setIcon(QMessageBox::Information);
00657   dlg.setWindowTitle(tr("No Recent Usage"));
00658   dlg.setText(tr("No clients have used your relay recently."));
00659   dlg.setInformativeText(tr("Leave your relay running so clients have "
00660                             "a better chance of finding and using it."));
00661   dlg.setStandardButtons(QMessageBox::Ok);
00662   dlg.exec();
00663   return;
00664 
00665 err:
00666   dlg.setIcon(QMessageBox::Warning);
00667   dlg.setWindowTitle(tr("Bridge History"));
00668   dlg.setText(tr("Vidalia was unable to retrieve your bridge's usage "
00669                  "history."));
00670   dlg.setInformativeText(tr("Tor returned an improperly formatted "
00671                             "response when Vidalia requested your "
00672                             "bridge's usage history."));
00673   dlg.setDetailedText(tr("The returned response was: %1").arg(info));
00674   dlg.setStandardButtons(QMessageBox::Ok);
00675   dlg.exec();
00676 }
00677