Vidalia 0.2.12

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