Vidalia  0.2.21
ServerPage.cpp
Go to the documentation of this file.
1 /*
2 ** This file is part of Vidalia, and is subject to the license terms in the
3 ** LICENSE file, found in the top level directory of this distribution. If you
4 ** did not receive the LICENSE file with this file, you may obtain it from the
5 ** Vidalia source package distributed by the Vidalia Project at
6 ** http://www.torproject.org/projects/vidalia.html. No part of Vidalia,
7 ** including this file, may be copied, modified, propagated, or distributed
8 ** except according to the terms described in the LICENSE file.
9 */
10 
11 /*
12 ** \file ServerPage.cpp
13 ** \brief Tor server configuration options
14 */
15 
16 #include "config.h"
17 #include "ServerPage.h"
18 #include "Vidalia.h"
19 #include "VMessageBox.h"
20 #include "ConfigDialog.h"
21 #include "IpValidator.h"
22 #include "PortValidator.h"
23 #include "DomainValidator.h"
24 #include "NicknameValidator.h"
25 #include "BridgeUsageDialog.h"
26 
27 #include "html.h"
28 #include "stringutil.h"
29 
30 #if defined(USE_MINIUPNPC)
31 #include "UPNPTestDialog.h"
32 #endif
33 
34 
35 #include <QClipboard>
36 #include <QMessageBox>
37 
38 /* These are completely made up values (in bytes/sec). */
39 #define CABLE256_AVG_RATE (32*1024)
40 #define CABLE256_MAX_RATE (64*1024)
41 #define CABLE512_AVG_RATE (64*1024)
42 #define CABLE512_MAX_RATE (128*1024)
43 #define CABLE768_AVG_RATE (96*1024)
44 #define CABLE768_MAX_RATE (192*1024)
45 #define T1_AVG_RATE (192*1024)
46 #define T1_MAX_RATE (384*1024)
47 #define HIGHBW_AVG_RATE (5120*1024)
48 #define HIGHBW_MAX_RATE (10240*1024)
49 /** Minimum allowed bandwidth rate (20KB) */
50 #define MIN_BANDWIDTH_RATE 20
51 /** Maximum bandwidth rate. This is limited to 2147483646 bytes,
52  * or 2097151 kilobytes. (2147483646/1024) */
53 #define MAX_BANDWIDTH_RATE 2097151
54 
55 /** Ports represented by the "Websites" checkbox. (80) */
56 #define PORTS_HTTP (QStringList() << "80")
57 /** Ports represented by the "Secure Websites" checkbox. (443) */
58 #define PORTS_HTTPS (QStringList() << "443")
59 /** Ports represented by the "Retrieve Mail" checkbox. (110,143,993,995) */
60 #define PORTS_MAIL (QStringList() << "110" << "143" << "993" << "995")
61 /** Ports represented by the "Instant Messaging" checkbox.
62  * (703,1863,5050,5190,5222,8300,8888) */
63 #define PORTS_IM (QStringList() << "706" << "1863" << "5050" << "5190" \
64  << "5222" << "5223" << "8300" << "8888")
65 /** Ports represented by the "Internet Relay Chat" checkbox.
66  * (6660-6669,6697,7000-7001) */
67 #define PORTS_IRC (QStringList() << "6660-6669" << "6697" << "7000-7001")
68 
69 
70 /** Constructor */
71 ServerPage::ServerPage(QWidget *parent)
72 : ConfigPage(parent, "Server")
73 {
74  /* Invoke the Qt Designer generated object setup routine */
75  ui.setupUi(this);
76 
77  /* Create ServerSettings object */
79 
80  /* Bind events to actions */
81  connect(ui.btnRateHelp, SIGNAL(clicked()), this, SLOT(bandwidthHelp()));
82  connect(ui.btnExitHelp, SIGNAL(clicked()), this, SLOT(exitPolicyHelp()));
83  connect(ui.btnUpnpHelp, SIGNAL(clicked()), this, SLOT(upnpHelp()));
84  connect(ui.cmboRate, SIGNAL(currentIndexChanged(int)),
85  this, SLOT(rateChanged(int)));
86  connect(ui.lineAvgRateLimit, SIGNAL(editingFinished()),
87  this, SLOT(customRateChanged()));
88  connect(ui.lineMaxRateLimit, SIGNAL(editingFinished()),
89  this, SLOT(customRateChanged()));
90  connect(ui.rdoClientMode, SIGNAL(toggled(bool)),
91  this, SLOT(serverModeChanged(bool)));
92  connect(ui.rdoNonExitMode, SIGNAL(toggled(bool)),
93  this, SLOT(serverModeChanged(bool)));
94  connect(ui.rdoServerMode, SIGNAL(toggled(bool)),
95  this, SLOT(serverModeChanged(bool)));
96  connect(ui.rdoBridgeMode, SIGNAL(toggled(bool)),
97  this, SLOT(serverModeChanged(bool)));
98  connect(Vidalia::torControl(), SIGNAL(authenticated()),
99  this, SLOT(onAuthenticated()));
100  connect(Vidalia::torControl(), SIGNAL(disconnected()),
101  this, SLOT(onDisconnected()));
102  connect(ui.btnCopyBridgeIdentity, SIGNAL(clicked()),
103  this, SLOT(copyBridgeIdentity()));
104  connect(ui.lblBridgeUsage, SIGNAL(linkActivated(QString)),
105  this, SLOT(linkActivated(QString)));
106  connect(ui.lblWhatsThis, SIGNAL(linkActivated(QString)),
107  this, SLOT(linkActivated(QString)));
108 
109  /* Set validators for address, mask and various port number fields */
110  ui.lineServerNickname->setValidator(new NicknameValidator(this));
111  ui.lineServerPort->setValidator(new QIntValidator(1, 65535, this));
112  ui.lineDirPort->setValidator(new QIntValidator(1, 65535, this));
113  ui.lineAvgRateLimit->setValidator(
114  new QIntValidator(MIN_BANDWIDTH_RATE, MAX_BANDWIDTH_RATE, this));
115  ui.lineMaxRateLimit->setValidator(
116  new QIntValidator(MIN_BANDWIDTH_RATE, MAX_BANDWIDTH_RATE, this));
117 
118 #if defined(USE_MINIUPNPC)
119  connect(ui.btnTestUpnp, SIGNAL(clicked()), this, SLOT(testUpnp()));
120 #else
121  ui.chkEnableUpnp->setVisible(false);
122  ui.btnTestUpnp->setVisible(false);
123 #endif
124 
125  _tmpDirPort = "9030";
126  _tmpMirror = true;
127 }
128 
129 /** Destructor */
131 {
132  delete _settings;
133 }
134 
135 /** Called when the user changes the UI translation. */
136 void
138 {
139  ui.retranslateUi(this);
140 }
141 
142 /** Called when Vidalia has authenticated to Tor. If the user's Tor is not
143  * recent enough, this disables the bridge server option and displays a
144  * warning if the user had previously configured Tor as a bridge. */
145 void
147 {
148  quint32 torVersion = Vidalia::torControl()->getTorVersion();
149  if (torVersion < 0x020008) {
150  ui.rdoBridgeMode->setEnabled(false);
151  if (ui.rdoBridgeMode->isChecked()) {
152  int ret = VMessageBox::warning(this,
153  tr("Bridge Support Unavailable"),
154  p(tr("You have configured Tor to act as a bridge relay "
155  "for censored users, but your version of Tor does not "
156  "support bridges.")) +
157  p(tr("Please upgrade your Tor software or configure Tor to "
158  "act as a normal Tor relay.")),
159  VMessageBox::ShowSettings|VMessageBox::Default,
161  if (ret == VMessageBox::ShowSettings) {
162  ConfigDialog *dialog = dynamic_cast<ConfigDialog *>(window());
163  if (dialog)
165  }
166  }
167  }
168 }
169 
170 /** Called when Vidalia disconnects from Tor. This method reenables the bridge
171  * server option. */
172 void
174 {
175  ui.rdoBridgeMode->setEnabled(true);
176 }
177 
178 /** Copies the user's bridge relay identity to the clipboard. */
179 void
181 {
182  QString bridge = ui.lblBridgeIdentity->text();
183  if (!bridge.isEmpty())
184  vApp->clipboard()->setText(bridge);
185 }
186 
187 /** Loads the user's bridge relay identity into the appropriate widgets. If
188  * the user's bridge is not running, then "Not Running" will be displayed.
189  * Otherwise, either the bridge's "address:port", "fingerprint", or
190  * "address:port fingerprint" will be displayed, depending on whether our
191  * GETCONF and GETINFO commands are successful. */
192 void
194 {
196  QString bridge, address, orPort, fingerprint;
197 
198  if (tc->isConnected()) {
199  tc->getInfo("address", address);
200  tc->getInfo("fingerprint", fingerprint);
201  tc->getConf("ORPort", orPort);
202 
203  if (!address.isEmpty() && !orPort.isEmpty() && orPort != "0")
204  bridge = address + ":" + orPort + " ";
205  if (!fingerprint.isEmpty())
206  bridge += fingerprint;
207  bridge = bridge.trimmed();
208  }
209 
210  ui.lblBridgeIdentity->setText(bridge.isEmpty()
211  ? tr("Your bridge relay is not running.")
212  : bridge);
213  ui.lblYourBridgeRelayIs->setEnabled(!bridge.isEmpty());
214  ui.lblBridgeIdentity->setEnabled(!bridge.isEmpty());
215  ui.btnCopyBridgeIdentity->setEnabled(!bridge.isEmpty());
216 }
217 
218 /** Called when the user toggles any one of the server mode radio buttons
219  * and hides or displays the server configuration tabs appropriately. */
220 void
222 {
223  Q_UNUSED(enabled);
224  bool bridgeEnabled = ui.rdoBridgeMode->isChecked();
225  bool relayEnabled = ui.rdoServerMode->isChecked() ||
226  ui.rdoNonExitMode->isChecked();
227 
228  /* Show the tab menu only if the user is running a normal relay or a bridge
229  * relay. */
230  ui.tabsMenu->setVisible(relayEnabled || bridgeEnabled);
231 
232  /* Disable the Exit Policies tab when bridge relay mode is selected */
233  ui.tabsMenu->setTabEnabled(2, !bridgeEnabled);
234 
235  /* Display the widgets that show the user their bridge identity if bridge
236  * relay mode is selected. */
237  ui.lblYourBridgeRelayIs->setVisible(bridgeEnabled);
238  ui.lblBridgeIdentity->setVisible(bridgeEnabled);
239  ui.btnCopyBridgeIdentity->setVisible(bridgeEnabled);
240  ui.chkPublishBridgeAddress->setVisible(bridgeEnabled);
241  ui.lblBridgeUsage->setVisible(bridgeEnabled
242  && Vidalia::torControl()->isConnected());
243 
244  if(bridgeEnabled) {
245  if(ui.lineDirPort->text().length() != 0) {
246  _tmpDirPort = ui.lineDirPort->text();
247  _tmpMirror = ui.chkMirrorDirectory->isChecked();
248  }
249  ui.lineDirPort->clear();
250  ui.chkMirrorDirectory->setChecked(false);
251  } else {
252  ui.lineDirPort->setText(_tmpDirPort);
253  ui.chkMirrorDirectory->setChecked(_tmpMirror);
254  }
255 
256  ui.chkMirrorDirectory->setEnabled(!bridgeEnabled);
257  if(ui.chkMirrorDirectory->isChecked()) {
258  ui.lblDirPort->setEnabled(!bridgeEnabled);
259  ui.lineDirPort->setEnabled(!bridgeEnabled);
260  }
261 
262  /* Disable the Exit Policies tab when bridge or non-exit relay mode is
263  * selected */
264  ui.tabsMenu->setTabEnabled(2, !bridgeEnabled and !ui.rdoNonExitMode->isChecked());
265 }
266 
267 /** Returns true if the user has changed their server settings since the
268  * last time they were applied to Tor. */
269 bool
271 {
273 }
274 
275 /** Applies the server configuration settings to Tor. Returns true if the
276  * settings were applied successfully. Otherwise, <b>errmsg</b> is
277  * set and false is returned. */
278 bool
279 ServerPage::apply(QString &errmsg)
280 {
281  return _settings->apply(&errmsg);
282 }
283 
284 /** Returns true if the user has changed their server settings since the
285  * last time they were applied to Tor. */
286 void
288 {
289  _settings->revert();
290 }
291 
292 /** Saves changes made to settings on the Server settings page. */
293 bool
294 ServerPage::save(QString &errmsg)
295 {
296  /* Force the bandwidth rate limits to validate */
298 
299  if (ui.rdoServerMode->isChecked() ||
300  ui.rdoNonExitMode->isChecked() ||
301  ui.rdoBridgeMode->isChecked()) {
302  /* A server must have an ORPort and a nickname */
303  if (ui.lineServerPort->text().isEmpty() ||
304  ui.lineServerNickname->text().isEmpty()) {
305  errmsg = tr("You must specify at least a relay nickname and port.");
306  return false;
307  }
308  /* If the bandwidth rates aren't set, use some defaults before saving */
309  if (ui.lineAvgRateLimit->text().isEmpty()) {
310  ui.lineAvgRateLimit->setText(QString::number(2097152/1024) /* 2MB */);
311  }
312  if (ui.lineMaxRateLimit->text().isEmpty()) {
313  ui.lineMaxRateLimit->setText(QString::number(5242880/1024) /* 5MB */);
314  }
315  }
316 
317  /* "Server" is enabled whether we're a bridge or normal relay. "Bridge" is
318  * only enabled if we're a bridge (obviously). */
319  _settings->setServerEnabled(ui.rdoServerMode->isChecked()
320  || ui.rdoNonExitMode->isChecked()
321  || ui.rdoBridgeMode->isChecked());
322  _settings->setNonExitEnabled(ui.rdoNonExitMode->isChecked());
323  _settings->setBridgeEnabled(ui.rdoBridgeMode->isChecked());
324  if (ui.rdoBridgeMode->isChecked())
325  _settings->setPublishServerDescriptor(ui.chkPublishBridgeAddress->isChecked());
326 
327  /* Save the rest of the server settings. */
328  _settings->setNickname(ui.lineServerNickname->text());
329  _settings->setORPort(ui.lineServerPort->text().toUInt());
330  if (!ui.rdoBridgeMode->isChecked()) {
331  _settings->setDirPort(ui.lineDirPort->text().toUInt());
332  _settings->setDirectoryMirror(ui.chkMirrorDirectory->isChecked());
333  } else {
335  }
336  _settings->setContactInfo(ui.lineServerContact->text());
339 
340 #if defined(USE_MINIUPNPC)
341  _settings->setUpnpEnabled(ui.chkEnableUpnp->isChecked());
342 #endif
343 
344  return true;
345 }
346 
347 /** Loads previously saved settings */
348 void
350 {
351  if (_settings->isBridgeEnabled())
352  ui.rdoBridgeMode->setChecked(true);
353  else if (_settings->isNonExitEnabled()) {
354  if(_settings->getExitPolicy().toString() != "reject *:*")
355  ui.rdoServerMode->setChecked(true);
356  else
357  ui.rdoNonExitMode->setChecked(true);
358  } else if (_settings->isServerEnabled()) {
359  if(_settings->getExitPolicy().toString() == "reject *:*")
360  ui.rdoNonExitMode->setChecked(true);
361  else
362  ui.rdoServerMode->setChecked(true);
363  } else
364  ui.rdoClientMode->setChecked(true);
365 
366  ui.lineServerNickname->setText(_settings->getNickname());
367  ui.lineServerPort->setText(QString::number(_settings->getORPort()));
368  ui.lineDirPort->setText(QString::number(_settings->getDirPort()));
369  ui.lineServerContact->setText(_settings->getContactInfo());
370  ui.chkMirrorDirectory->setChecked(_settings->isDirectoryMirror());
371  ui.lblBridgeUsage->setVisible(_settings->isBridgeEnabled()
373  ui.chkPublishBridgeAddress->setChecked(_settings->publishServerDescriptor());
374 
378 
379 #if defined(USE_MINIUPNPC)
380  ui.chkEnableUpnp->setChecked(_settings->isUpnpEnabled());
381 #endif
382 }
383 
384 /** Shows exit policy related help information */
385 void
387 {
388  emit helpRequested("server.exitpolicy");
389 }
390 
391 /** Shows the bandwidth rate limiting help information */
392 void
394 {
395  emit helpRequested("server.bandwidth");
396 }
397 
398 /** Loads the server's bandwidth average and burst limits. */
399 void
401 {
402  quint32 avgRate = _settings->getBandwidthAvgRate();
403  quint32 maxRate = _settings->getBandwidthBurstRate();
404 
405  if (avgRate == CABLE256_AVG_RATE &&
406  maxRate == CABLE256_MAX_RATE) {
407  /* Cable/DSL 256 Kbps */
408  ui.cmboRate->setCurrentIndex(CableDsl256);
409  } else if (avgRate == CABLE512_AVG_RATE &&
410  maxRate == CABLE512_MAX_RATE) {
411  /* Cable/DSL 512 Kbps */
412  ui.cmboRate->setCurrentIndex(CableDsl512);
413  } else if (avgRate == CABLE768_AVG_RATE &&
414  maxRate == CABLE768_MAX_RATE) {
415  /* Cable/DSL 768 Kbps */
416  ui.cmboRate->setCurrentIndex(CableDsl768);
417  } else if (avgRate == T1_AVG_RATE &&
418  maxRate == T1_MAX_RATE) {
419  /* T1/Cable/DSL 1.5 Mbps */
420  ui.cmboRate->setCurrentIndex(T1CableDsl1500);
421  } else if (avgRate == HIGHBW_AVG_RATE &&
422  maxRate == HIGHBW_MAX_RATE) {
423  /* > 1.5 Mbps */
424  ui.cmboRate->setCurrentIndex(GreaterThan1500);
425  } else {
426  /* Custom bandwidth limits */
427  ui.cmboRate->setCurrentIndex(CustomBwLimits);
428  }
429  /* Fill in the custom bandwidth limit boxes */
430  ui.lineAvgRateLimit->setText(QString::number(avgRate/1024));
431  ui.lineMaxRateLimit->setText(QString::number(maxRate/1024));
432 }
433 
434 /** Saves the server's bandwidth average and burst limits. */
435 void
437 {
438  quint32 avgRate, maxRate;
439 
440  switch (ui.cmboRate->currentIndex()) {
441  case CableDsl256: /* Cable/DSL 256 Kbps */
442  avgRate = CABLE256_AVG_RATE;
443  maxRate = CABLE256_MAX_RATE;
444  break;
445  case CableDsl512: /* Cable/DSL 512 Kbps */
446  avgRate = CABLE512_AVG_RATE;
447  maxRate = CABLE512_MAX_RATE;
448  break;
449  case CableDsl768: /* Cable/DSL 768 Kbps */
450  avgRate = CABLE768_AVG_RATE;
451  maxRate = CABLE768_MAX_RATE;
452  break;
453  case T1CableDsl1500: /* T1/Cable/DSL 1.5 Mbps */
454  avgRate = T1_AVG_RATE;
455  maxRate = T1_MAX_RATE;
456  break;
457  case GreaterThan1500: /* > 1.5 Mbps */
458  avgRate = HIGHBW_AVG_RATE;
459  maxRate = HIGHBW_MAX_RATE;
460  break;
461  default: /* Custom bandwidth limits */
462  avgRate = (quint32)(ui.lineAvgRateLimit->text().toUInt()*1024);
463  maxRate = (quint32)(ui.lineMaxRateLimit->text().toUInt()*1024);
464  break;
465  }
466  _settings->setBandwidthAvgRate(avgRate);
468 }
469 
470 /** */
471 void
473 {
474  ExitPolicy exitPolicy = _settings->getExitPolicy();
475 
476  if (exitPolicy.contains(Policy(Policy::RejectAll))) {
477  /* If the policy ends with reject *:*, check if the policy explicitly
478  * accepts these ports */
479  ui.chkWebsites->setChecked(exitPolicy.acceptsPorts(PORTS_HTTP));
480  ui.chkSecWebsites->setChecked(exitPolicy.acceptsPorts(PORTS_HTTPS));
481  ui.chkMail->setChecked(exitPolicy.acceptsPorts(PORTS_MAIL));
482  ui.chkIRC->setChecked(exitPolicy.acceptsPorts(PORTS_IRC));
483  ui.chkIM->setChecked(exitPolicy.acceptsPorts(PORTS_IM));
484  ui.chkMisc->setChecked(false);
485  } else {
486  /* If the exit policy ends with accept *:*, check if the policy explicitly
487  * rejects these ports */
488  ui.chkWebsites->setChecked(!exitPolicy.rejectsPorts(PORTS_HTTP));
489  ui.chkSecWebsites->setChecked(!exitPolicy.rejectsPorts(PORTS_HTTPS));
490  ui.chkMail->setChecked(!exitPolicy.rejectsPorts(PORTS_MAIL));
491  ui.chkIRC->setChecked(!exitPolicy.rejectsPorts(PORTS_IRC));
492  ui.chkIM->setChecked(!exitPolicy.rejectsPorts(PORTS_IM));
493  ui.chkMisc->setChecked(true);
494  }
495 }
496 
497 /** */
498 void
500 {
501  ExitPolicy *exitPolicy;
502  if(ui.rdoNonExitMode->isChecked()) {
503  exitPolicy = new ExitPolicy(ExitPolicy::Middleman);
504  } else {
505  exitPolicy = new ExitPolicy();
506  bool rejectUnchecked = ui.chkMisc->isChecked();
507 
508  /* If misc is checked, then reject unchecked items and leave the default exit
509  * policy alone. Else, accept only checked items and end with reject *:*,
510  * replacing the default exit policy. */
511  if (ui.chkWebsites->isChecked() && !rejectUnchecked) {
512  exitPolicy->addAcceptedPorts(PORTS_HTTP);
513  } else if (!ui.chkWebsites->isChecked() && rejectUnchecked) {
514  exitPolicy->addRejectedPorts(PORTS_HTTP);
515  }
516  if (ui.chkSecWebsites->isChecked() && !rejectUnchecked) {
517  exitPolicy->addAcceptedPorts(PORTS_HTTPS);
518  } else if (!ui.chkSecWebsites->isChecked() && rejectUnchecked) {
519  exitPolicy->addRejectedPorts(PORTS_HTTPS);
520  }
521  if (ui.chkMail->isChecked() && !rejectUnchecked) {
522  exitPolicy->addAcceptedPorts(PORTS_MAIL);
523  } else if (!ui.chkMail->isChecked() && rejectUnchecked) {
524  exitPolicy->addRejectedPorts(PORTS_MAIL);
525  }
526  if (ui.chkIRC->isChecked() && !rejectUnchecked) {
527  exitPolicy->addAcceptedPorts(PORTS_IRC);
528  } else if (!ui.chkIRC->isChecked() && rejectUnchecked) {
529  exitPolicy->addRejectedPorts(PORTS_IRC);
530  }
531  if (ui.chkIM->isChecked() && !rejectUnchecked) {
532  exitPolicy->addAcceptedPorts(PORTS_IM);
533  } else if (!ui.chkIM->isChecked() && rejectUnchecked) {
534  exitPolicy->addRejectedPorts(PORTS_IM);
535  }
536  if (!ui.chkMisc->isChecked()) {
537  exitPolicy->addPolicy(Policy(Policy::RejectAll));
538  }
539  }
540 
541  _settings->setExitPolicy(*exitPolicy);
542 }
543 
544 /** Called when the user selects a new value from the rate combo box. */
545 void
547 {
548  /* If the "Custom" option is selected, show the custom bandwidth
549  * limits form. */
550  ui.frmCustomRate->setVisible(index == CustomBwLimits);
551 }
552 
553 /** Called when the user edits the long-term average or maximum bandwidth limit.
554  * This ensures that the average bandwidth rate is greater than MIN_RATE
555  * (20KB/s) and that the max rate is greater than the average rate. */
556 void
558 {
559  /* Make sure the average rate isn't too low or too high */
560  quint32 avgRate = (quint32)ui.lineAvgRateLimit->text().toUInt();
561  if (avgRate < MIN_BANDWIDTH_RATE) {
562  ui.lineAvgRateLimit->setText(QString::number(MIN_BANDWIDTH_RATE));
563  }
564  if (avgRate > MAX_BANDWIDTH_RATE) {
565  ui.lineAvgRateLimit->setText(QString::number(MAX_BANDWIDTH_RATE));
566  }
567  /* Ensure the max burst rate is greater than the average rate but less than
568  * the maximum allowed rate. */
569  quint32 burstRate = (quint32)ui.lineMaxRateLimit->text().toUInt();
570  if (avgRate > burstRate) {
571  ui.lineMaxRateLimit->setText(QString::number(avgRate));
572  }
573  if (burstRate > MAX_BANDWIDTH_RATE) {
574  ui.lineMaxRateLimit->setText(QString::number(MAX_BANDWIDTH_RATE));
575  }
576 }
577 
578 /** Tests automatic port forwarding using UPnP. */
579 void
581 {
582 #if defined(USE_MINIUPNPC)
583  UPNPTestDialog dlg(ui.lineServerPort->text().toUInt(),
584  ui.lineDirPort->text().toUInt(), this);
585 
586  connect(&dlg, SIGNAL(help()), this, SLOT(upnpHelp()));
587 
588  dlg.exec();
589 #endif
590 }
591 
592 /** Called when the user clicks the UPnP test dialog's help button. */
593 void
595 {
596  emit helpRequested("server.upnp");
597 }
598 
599 /** Called when the user clicks on a QLabel containing a hyperlink. */
600 void
601 ServerPage::linkActivated(const QString &url)
602 {
603  if (!url.compare("#bridgeUsage"))
605  else if(!url.compare("#bridgeHelp"))
606  emit helpRequested("bridges.about");
607 }
608 
609 /** Retrieves bridge usage history from Tor, parses and validates it, and
610  * then displays it in a new dialog. */
611 void
613 {
614  QString info;
615  QMessageBox dlg(this);
616 
617  info = Vidalia::torControl()->getInfo("status/clients-seen").toString();
618  if (info.isEmpty()) {
619  goto none;
620  } else {
621  QDateTime timeStarted;
622  QHash<QString,int> countrySummary;
623  QHash<QString,QString> keyvals;
624  BridgeUsageDialog dlg(this);
625  bool ok;
626 
627  keyvals = string_parse_keyvals(info, &ok);
628  if (!ok || !keyvals.contains("TimeStarted")
629  || !keyvals.contains("CountrySummary"))
630  goto err;
631 
632  timeStarted = QDateTime::fromString(keyvals.value("TimeStarted"),
633  "yyyy-MM-dd HH:mm:ss");
634  if (!timeStarted.isValid())
635  goto err;
636 
637  // Default is LocalTime, force UTC
638  timeStarted.setTimeSpec(Qt::UTC);
639 
640  QStringList summary = keyvals.value("CountrySummary")
641  .split(",", QString::SkipEmptyParts);
642  if (summary.isEmpty()) {
643  goto none;
644  } else {
645  foreach (QString pair, summary) {
646  QStringList parts = pair.split("=");
647  if (parts.size() != 2)
648  goto err;
649 
650  countrySummary.insert(parts.at(0).toUpper(), parts.at(1).toInt(&ok));
651  if (!ok)
652  goto err;
653  }
654 
655  dlg.update(timeStarted, countrySummary);
656  dlg.exec();
657  }
658  }
659  return;
660 
661 none:
662  dlg.setIcon(QMessageBox::Information);
663  dlg.setWindowTitle(tr("No Recent Usage"));
664  dlg.setText(tr("No clients have used your relay recently."));
665  dlg.setInformativeText(tr("Leave your relay running so clients have "
666  "a better chance of finding and using it."));
667  dlg.setStandardButtons(QMessageBox::Ok);
668  dlg.exec();
669  return;
670 
671 err:
672  dlg.setIcon(QMessageBox::Warning);
673  dlg.setWindowTitle(tr("Bridge History"));
674  dlg.setText(tr("Vidalia was unable to retrieve your bridge's usage "
675  "history."));
676  dlg.setInformativeText(tr("Tor returned an improperly formatted "
677  "response when Vidalia requested your "
678  "bridge's usage history."));
679  dlg.setDetailedText(tr("The returned response was: %1").arg(info));
680  dlg.setStandardButtons(QMessageBox::Ok);
681  dlg.exec();
682 }
683