Vidalia 0.2.15
StatusEventWidget.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 StatusEventWidget.h
00013 ** \brief Displays information on Tor status events
00014 */
00015 
00016 #include "StatusEventWidget.h"
00017 #include "StatusEventItem.h"
00018 #include "StatusEventItemDelegate.h"
00019 #include "Vidalia.h"
00020 
00021 #include "TorEvents.h"
00022 #include "stringutil.h"
00023 
00024 #include <QTime>
00025 #include <QMenu>
00026 #include <QPainter>
00027 #include <QPixmap>
00028 #include <QStringList>
00029 #include <QObject>
00030 #include <QHeaderView>
00031 #include <QClipboard>
00032 
00033 bool compareStatusEventItems(const QTreeWidgetItem *a,
00034                              const QTreeWidgetItem *b)
00035 {
00036   return (*a < *b);
00037 }
00038 
00039 StatusEventWidget::StatusEventWidget(QWidget *parent)
00040   : QTreeWidget(parent)
00041 {
00042   TorControl *tc = Vidalia::torControl();
00043   tc->setEvent(TorEvents::GeneralStatus);
00044   tc->setEvent(TorEvents::ClientStatus);
00045   tc->setEvent(TorEvents::ServerStatus);
00046 
00047   connect(this, SIGNAL(customContextMenuRequested(QPoint)),
00048           this, SLOT(customContextMenuRequested(QPoint)));
00049   connect(tc, SIGNAL(authenticated()), this, SLOT(authenticated()));
00050   connect(tc, SIGNAL(disconnected()), this, SLOT(disconnected()));
00051   connect(tc, SIGNAL(dangerousTorVersion(tc::TorVersionStatus, QString,
00052                                          QStringList)),
00053           this, SLOT(dangerousTorVersion(tc::TorVersionStatus, QString,
00054                                          QStringList)));
00055   connect(tc, SIGNAL(circuitEstablished()), this, SLOT(circuitEstablished()));
00056   connect(tc, SIGNAL(bug(QString)), this, SLOT(bug(QString)));
00057   connect(tc, SIGNAL(clockSkewed(int, QString)),
00058           this, SLOT(clockSkewed(int, QString)));
00059   connect(tc, SIGNAL(dangerousPort(quint16, bool)),
00060           this, SLOT(dangerousPort(quint16, bool)));
00061   connect(tc, SIGNAL(socksError(tc::SocksError, QString)),
00062           this, SLOT(socksError(tc::SocksError, QString)));
00063   connect(tc, SIGNAL(externalAddressChanged(QHostAddress, QString)),
00064           this, SLOT(externalAddressChanged(QHostAddress, QString)));
00065   connect(tc, SIGNAL(dnsHijacked()), this, SLOT(dnsHijacked()));
00066   connect(tc, SIGNAL(dnsUseless()), this, SLOT(dnsUseless()));
00067   connect(tc, SIGNAL(checkingOrPortReachability(QHostAddress, quint16)),
00068           this, SLOT(checkingOrPortReachability(QHostAddress, quint16)));
00069   connect(tc, SIGNAL(orPortReachabilityFinished(QHostAddress, quint16, bool)),
00070           this, SLOT(orPortReachabilityFinished(QHostAddress, quint16, bool)));
00071   connect(tc, SIGNAL(checkingDirPortReachability(QHostAddress, quint16)),
00072           this, SLOT(checkingDirPortReachability(QHostAddress, quint16)));
00073   connect(tc, SIGNAL(dirPortReachabilityFinished(QHostAddress, quint16, bool)),
00074           this, SLOT(dirPortReachabilityFinished(QHostAddress, quint16, bool)));
00075   connect(tc, SIGNAL(serverDescriptorRejected(QHostAddress, quint16, QString)),
00076           this, SLOT(serverDescriptorRejected(QHostAddress, quint16, QString)));
00077   connect(tc, SIGNAL(serverDescriptorAccepted(QHostAddress, quint16)),
00078           this, SLOT(serverDescriptorAccepted(QHostAddress, quint16)));
00079 
00080   setItemDelegate(new StatusEventItemDelegate(this));
00081 }
00082 
00083 void
00084 StatusEventWidget::retranslateUi()
00085 {
00086   /* XXX: We need to store the untranslated text for each of the status
00087    *      event items, iterate through all items in the list, and then
00088    *      retranslate them. The trick is that some of the messages are
00089    *      generated dynamically based on data sent by Tor (which is NOT
00090    *      translated). Those messages we can't retranslate correctly
00091    *      without also storing the variables used to generate the message.
00092    */
00093 }
00094 
00095 void
00096 StatusEventWidget::setMaximumItemCount(int maximumItemCount)
00097 {
00098   _maximumItemCount = maximumItemCount;
00099 
00100   QTreeWidgetItem *item;
00101   Qt::SortOrder order = header()->sortIndicatorOrder();
00102   while (topLevelItemCount() > _maximumItemCount) {
00103     if (order == Qt::AscendingOrder)
00104       item = takeTopLevelItem(0);
00105     else
00106       item = takeTopLevelItem(topLevelItemCount()-1);
00107     if (item)
00108       delete item;
00109   }
00110 }
00111 
00112 int
00113 StatusEventWidget::maximumItemCount() const
00114 {
00115   return _maximumItemCount;
00116 }
00117 
00118 QStringList
00119 StatusEventWidget::selectedEvents() const
00120 {
00121   QString text;
00122   QStringList out;
00123   QList<QTreeWidgetItem *> items = selectedItems();
00124 
00125   // We have to sort the items since selectedItems() returns the order in
00126   // which the items were selected, not the order in which they appear in the
00127   // current list.
00128   qStableSort(items.begin(), items.end(), compareStatusEventItems);
00129 
00130   for (int i = 0; i < items.size(); i++) {
00131     StatusEventItem *event = dynamic_cast<StatusEventItem *>(items.at(i));
00132     if (event)
00133       out.append(event->toString());
00134   }
00135   return out;
00136 }
00137 
00138 QStringList
00139 StatusEventWidget::allEvents() const
00140 {
00141   QStringList out;
00142   QList<QTreeWidgetItem *> items;
00143 
00144   for (int i = 0; i < topLevelItemCount(); i++)
00145     items.append(topLevelItem(i));
00146 
00147   // Ensure the items are sorted in ascending order according to timestamp
00148   qStableSort(items.begin(), items.end(), compareStatusEventItems);
00149 
00150   for (int i = 0; i < items.size(); i++) {
00151     StatusEventItem *event = dynamic_cast<StatusEventItem *>(items.at(i));
00152     if (event)
00153       out.append(event->toString());
00154   }
00155   return out;
00156 }
00157 
00158 void
00159 StatusEventWidget::customContextMenuRequested(const QPoint &pos)
00160 {
00161   QMenu menu(this);
00162 
00163   StatusEventItem *item = dynamic_cast<StatusEventItem *>(itemAt(pos));
00164   if (! item || ! item->isSelected())
00165     return;
00166 
00167   QAction *copyAction = menu.addAction(QIcon(":/images/22x22/edit-copy.png"),
00168                                        tr("Copy to Clipboard"));
00169 
00170   QAction *action = menu.exec(mapToGlobal(pos));
00171   if (action == copyAction) {
00172     QStringList eventText = selectedEvents();
00173     if (! eventText.isEmpty())
00174       QApplication::clipboard()->setText(eventText.join("\n"));
00175   }
00176 }
00177 
00178 QList<StatusEventItem *>
00179 StatusEventWidget::find(const QString &text, bool highlight)
00180 {
00181   QList<StatusEventItem *> items;
00182 
00183   for (int i = 0; i < topLevelItemCount(); i++) {
00184     StatusEventItem *item = dynamic_cast<StatusEventItem *>(topLevelItem(i));
00185     if (! item)
00186       continue;
00187 
00188     if (item->title().contains(text, Qt::CaseInsensitive)
00189         || item->description().contains(text, Qt::CaseInsensitive)) {
00190       items.append(item);
00191       if (highlight)
00192         item->setSelected(true);
00193     } else if (highlight) {
00194       item->setSelected(false);
00195     }
00196   }
00197   return items;
00198 }
00199 
00200 void
00201 StatusEventWidget::addNotification(const QPixmap &icon,
00202                                    const QString &title,
00203                                    const QString &description,
00204                                    const QString &helpUrl)
00205 {
00206   // Check if we first need to remove the oldest item in the list in order
00207   // to avoid exceeding the maximum number of notification items
00208   if (topLevelItemCount() == maximumItemCount()) {
00209     QTreeWidgetItem *item;
00210     if (header()->sortIndicatorOrder() == Qt::AscendingOrder)
00211       item = takeTopLevelItem(0);
00212     else
00213       item = takeTopLevelItem(topLevelItemCount()-1);
00214     if (item)
00215       delete item;
00216   }
00217 
00218   // Create the new notification item
00219   StatusEventItem *item = new StatusEventItem(this);
00220   item->setTimestamp(QDateTime::currentDateTime());
00221   item->setIcon(icon);
00222   item->setTitle(title);
00223   item->setDescription(description);
00224   item->setHelpUrl(helpUrl);
00225   item->setToolTip(string_wrap(description, 80));
00226 
00227   // Add the new item to the list and ensure it is visible
00228   addTopLevelItem(item);
00229   scrollToItem(item, QAbstractItemView::EnsureVisible);
00230 }
00231 
00232 QPixmap
00233 StatusEventWidget::addBadgeToPixmap(const QPixmap &pixmap,
00234                                     const QPixmap &badge)
00235 {
00236   QPixmap out = pixmap;
00237   QPainter painter(&out);
00238   painter.drawPixmap(pixmap.width() - badge.width(),
00239                      pixmap.height() - badge.height(),
00240                      badge);
00241   return out;
00242 }
00243 
00244 QPixmap
00245 StatusEventWidget::addBadgeToPixmap(const QPixmap &pixmap,
00246                                     const QString &badge)
00247 {
00248   return StatusEventWidget::addBadgeToPixmap(pixmap, QPixmap(badge));
00249 }
00250 
00251 QPixmap
00252 StatusEventWidget::addBadgeToPixmap(const QString &pixmap,
00253                                     const QString &badge)
00254 {
00255   return StatusEventWidget::addBadgeToPixmap(QPixmap(pixmap), QPixmap(badge));
00256 }
00257 
00258 void
00259 StatusEventWidget::authenticated()
00260 {
00261   TorControl *tc = Vidalia::torControl();
00262 
00263   QString version = tc->getTorVersionString();
00264   QPixmap icon = addBadgeToPixmap(":/images/48x48/tor-logo.png",
00265                                   ":/images/32x32/dialog-ok-apply.png");
00266   addNotification(icon,
00267     tr("The Tor Software is Running"),
00268     tr("You are currently running version \"%1\" of the Tor software.")
00269                                                             .arg(version));
00270 
00271   // Check if Tor established a circuit before we were able to authenticate,
00272   // in which case we missed the CIRCUIT_ESTABLISHED event. So fake it.
00273   if (tc->isCircuitEstablished())
00274     circuitEstablished();
00275 
00276   // Check on the status of Tor's version, in case we missed that event too
00277   QString status = tc->getInfo("status/version/current").toString();
00278   if (! status.compare("old", Qt::CaseInsensitive)
00279         || ! status.compare("obsolete", Qt::CaseInsensitive)) {
00280     dangerousTorVersion(tc::ObsoleteTorVersion, version, QStringList());
00281   } else if (! status.compare("unrecommended", Qt::CaseInsensitive)) {
00282     dangerousTorVersion(tc::UnrecommendedTorVersion, version, QStringList());
00283   }
00284 }
00285 
00286 void
00287 StatusEventWidget::disconnected()
00288 {
00289   QPixmap icon = addBadgeToPixmap(":/images/48x48/tor-logo.png",
00290                                   ":/images/32x32/edit-delete.png");
00291 
00292   addNotification(icon,
00293     tr("The Tor Software is not Running"),
00294     tr("Click \"Start Tor\" in the Vidalia Control Panel to restart the Tor "
00295        "software. If Tor exited unexpectedly, select the \"Advanced\" tab "
00296        "above for details about any errors encountered."));
00297 
00298   _squelchDescriptorAcceptedEvent = false;
00299 }
00300 
00301 void
00302 StatusEventWidget::dangerousTorVersion(tc::TorVersionStatus reason,
00303                                        const QString &version,
00304                                        const QStringList &recommended)
00305 {
00306   Q_UNUSED(recommended);
00307   QString description;
00308   QPixmap icon;
00309 
00310   if (reason == tc::UnrecommendedTorVersion) {
00311     icon = addBadgeToPixmap(":/images/48x48/tor-logo.png",
00312                             ":/images/32x32/security-medium.png");
00313 
00314     description =
00315       tr("You are currently running version \"%1\" of the Tor software, which "
00316          "is no longer recommended. Please upgrade to the most recent version "
00317          "of the software, which may contain important security, reliability "
00318          "and performance fixes.").arg(version);
00319   } else if (reason == tc::ObsoleteTorVersion) {
00320     icon = addBadgeToPixmap(":/images/48x48/tor-logo.png",
00321                             ":/images/32x32/security-low.png");
00322 
00323     description =
00324       tr("You are currently running version \"%1\" of the Tor software, which "
00325          "may no longer work with the current Tor network. Please upgrade "
00326          "to the most recent version of the software, which may contain "
00327          "important security, reliability and performance fixes.").arg(version);
00328   }
00329 
00330   addNotification(icon, tr("Your Tor Software is Out-of-date"), description);
00331 }
00332 
00333 void
00334 StatusEventWidget::circuitEstablished()
00335 {
00336   addNotification(QPixmap(":/images/48x48/network-connect.png"),
00337     tr("Connected to the Tor Network"),
00338     tr("We were able to successfully establish a connection to the Tor "
00339        "network. You can now configure your applications to use the Internet "
00340        "anonymously."));
00341 }
00342 
00343 void
00344 StatusEventWidget::bug(const QString &description)
00345 {
00346   QPixmap icon = addBadgeToPixmap(":/images/48x48/tor-logo.png",
00347                                   ":/images/32x32/script-error.png");
00348   addNotification(icon,
00349     tr("Tor Software Error"),
00350     tr("The Tor software encountered an internal bug. Please report the "
00351        "following error message to the Tor developers at bugs.torproject.org: "
00352        "\"%1\"").arg(description));
00353 }
00354 
00355 void
00356 StatusEventWidget::clockSkewed(int skew, const QString &source)
00357 {
00358   if (source.startsWith("OR:", Qt::CaseInsensitive)) {
00359     // Tor versions 0.2.1.19 and earlier, and 0.2.2.1 and earlier, throw
00360     // this message a little too liberally in this case.
00361     quint32 torVersion = Vidalia::torControl()->getTorVersion();
00362     if (torVersion <= 0x00020113)
00363       return;
00364     QString str = Vidalia::torControl()->getTorVersionString();
00365     if (str.startsWith("0.2.2.") && torVersion <= 0x00020201)
00366       return;
00367   }
00368 
00369   QString description;
00370   QPixmap icon = addBadgeToPixmap(":/images/48x48/chronometer.png",
00371                                   ":/images/32x32/dialog-warning.png");
00372 
00373   if (skew < 0) {
00374     description =
00375       tr("Tor has determined that your computer's clock may be set to %1 "
00376          "seconds in the past compared to the source \"%2\". If your "
00377          "clock is not correct, Tor will not be able to function. Please "
00378          "verify your computer displays the correct time.").arg(qAbs(skew))
00379                                                            .arg(source);
00380   } else {
00381     description =
00382       tr("Tor has determined that your computer's clock may be set to %1 "
00383          "seconds in the future compared to the source \"%2\". If "
00384          "your clock is not correct, Tor will not be able to function. Please "
00385          "verify your computer displays the correct time.").arg(qAbs(skew))
00386                                                            .arg(source);
00387   }
00388   addNotification(icon, tr("Your Computer's Clock is Potentially Incorrect"),
00389                   description);
00390 }
00391 
00392 void
00393 StatusEventWidget::dangerousPort(quint16 port, bool rejected)
00394 {
00395   QPixmap icon;
00396   QString description;
00397 
00398   if (rejected) {
00399     icon = addBadgeToPixmap(":/images/48x48/applications-internet.png",
00400                             ":/images/32x32/security-low.png");
00401 
00402     description =
00403       tr("One of the applications on your computer may have attempted to "
00404          "make an unencrypted connection through Tor to port %1. Sending "
00405          "unencrypted information over the Tor network is dangerous and not "
00406          "recommended. For your protection, Tor has automatically closed this "
00407          "connection.").arg(port);
00408   } else {
00409     icon = addBadgeToPixmap(":/images/48x48/applications-internet.png",
00410                             ":/images/32x32/security-medium.png");
00411     description =
00412       tr("One of the applications on your computer may have attempted to "
00413          "make an unencrypted connection through Tor to port %1. Sending "
00414          "unencrypted information over the Tor network is dangerous and not "
00415          "recommended.").arg(port);
00416   }
00417 
00418   addNotification(icon, tr("Potentially Dangerous Connection!"), description);
00419 }
00420 
00421 void
00422 StatusEventWidget::socksError(tc::SocksError type, const QString &destination)
00423 {
00424   QString title, description;
00425   QPixmap icon = QPixmap(":/images/48x48/applications-internet.png");
00426 
00427   if (type == tc::DangerousSocksTypeError) {
00428     icon  = addBadgeToPixmap(icon, ":/images/32x32/security-medium.png");
00429 
00430     title = tr("Potentially Dangerous Connection!");
00431     description =
00432       tr("One of your applications established a connection through Tor "
00433          "to \"%1\" using a protocol that may leak information about your "
00434          "destination. Please ensure you configure your applications to use "
00435          "only SOCKS4a or SOCKS5 with remote hostname resolution.")
00436                                                             .arg(destination);
00437   } else if (type == tc::UnknownSocksProtocolError) {
00438     icon = addBadgeToPixmap(icon, ":/images/32x32/dialog-warning.png");
00439 
00440     title = tr("Unknown SOCKS Protocol");
00441     description =
00442       tr("One of your applications tried to establish a connection through "
00443          "Tor using a protocol that Tor does not understand. Please ensure "
00444          "you configure your applications to use only SOCKS4a or SOCKS5 with "
00445          "remote hostname resolution.");
00446   } else if (type == tc::BadSocksHostnameError) {
00447     icon = addBadgeToPixmap(icon, ":/images/32x32/dialog-warning.png");
00448 
00449     title = tr("Invalid Destination Hostname");
00450     description =
00451       tr("One of your applications tried to establish a connection through "
00452          "Tor to \"%1\", which Tor does not recognize as a valid hostname. "
00453          "Please check your application's configuration.").arg(destination);
00454   } else {
00455     return;
00456   }
00457 
00458   addNotification(icon, title, description);
00459 }
00460 
00461 void
00462 StatusEventWidget::externalAddressChanged(const QHostAddress &ip,
00463                                           const QString &hostname)
00464 {
00465   QString hostString = hostname.isEmpty() ? QString()
00466                                           : QString(" (%1)").arg(hostname);
00467 
00468   addNotification(QPixmap(":/images/48x48/applications-internet.png"),
00469     tr("External IP Address Changed"),
00470     tr("Tor has determined your relay's public IP address is currently %1%2. "
00471        "If that is not correct, please consider setting the 'Address' option "
00472        "in your relay's configuration.").arg(ip.toString()).arg(hostString));
00473 }
00474 
00475 void
00476 StatusEventWidget::dnsHijacked()
00477 {
00478   QPixmap icon = addBadgeToPixmap(":/images/48x48/applications-internet.png",
00479                                   ":/images/32x32/dialog-warning.png");
00480   addNotification(icon,
00481     tr("DNS Hijacking Detected"),
00482     tr("Tor detected that your DNS provider is providing false responses for "
00483        "domains that do not exist. Some ISPs and other DNS providers, such as "
00484        "OpenDNS, are known to do this in order to display their own search or "
00485        "advertising pages."));
00486 }
00487 
00488 void
00489 StatusEventWidget::dnsUseless()
00490 {
00491   QPixmap icon = addBadgeToPixmap(":/images/48x48/applications-internet.png",
00492                                   ":/images/32x32/edit-delete.png");
00493   addNotification(icon,
00494     tr("DNS Hijacking Detected"),
00495     tr("Tor detected that your DNS provider is providing false responses for "
00496        "well known domains. Since clients rely on Tor network relays to "
00497        "provide accurate DNS repsonses, your relay will not be configured as "
00498        "an exit relay."));
00499 }
00500 
00501 void
00502 StatusEventWidget::checkingOrPortReachability(const QHostAddress &ip,
00503                                               quint16 port)
00504 {
00505   addNotification(QPixmap(":/images/48x48/network-wired.png"),
00506     tr("Checking Server Port Reachability"),
00507     tr("Tor is trying to determine if your relay's server port is reachable "
00508        "from the Tor network by connecting to itself at %1:%2. This test "
00509        "could take several minutes.").arg(ip.toString()).arg(port));
00510 }
00511 
00512 void
00513 StatusEventWidget::orPortReachabilityFinished(const QHostAddress &ip,
00514                                               quint16 port,
00515                                               bool reachable)
00516 {
00517   QString title, description;
00518   QPixmap icon = QPixmap(":/images/48x48/network-wired.png");
00519   if (reachable) {
00520     icon = addBadgeToPixmap(icon, ":/images/32x32/dialog-ok-apply.png");
00521     title = tr("Server Port Reachability Test Successful!");
00522     description =
00523       tr("Your relay's server port is reachable from the Tor network!");
00524   } else {
00525     icon = addBadgeToPixmap(icon, ":/images/32x32/dialog-warning.png");
00526     title = tr("Server Port Reachability Test Failed");
00527     description =
00528       tr("Your relay's server port is not reachable by other Tor clients. This "
00529          "can happen if you are behind a router or firewall that requires you "
00530          "to set up port forwarding. If %1:%2 is not your correct IP address "
00531          "and server port, please check your relay's configuration.")
00532                                                 .arg(ip.toString()).arg(port);
00533   }
00534 
00535   addNotification(icon, title, description);
00536 }
00537 
00538 void
00539 StatusEventWidget::checkingDirPortReachability(const QHostAddress &ip,
00540                                                quint16 port)
00541 {
00542   addNotification(QPixmap(":/images/48x48/network-wired.png"),
00543     tr("Checking Directory Port Reachability"),
00544     tr("Tor is trying to determine if your relay's directory port is reachable "
00545        "from the Tor network by connecting to itself at %1:%2. This test "
00546        "could take several minutes.").arg(ip.toString()).arg(port));
00547 }
00548 
00549 void
00550 StatusEventWidget::dirPortReachabilityFinished(const QHostAddress &ip,
00551                                                quint16 port,
00552                                                bool reachable)
00553 {
00554   QString title, description;
00555   QPixmap icon = QPixmap(":/images/48x48/network-wired.png");
00556   if (reachable) {
00557     icon = addBadgeToPixmap(icon, ":/images/32x32/dialog-ok-apply.png");
00558     title = tr("Directory Port Reachability Test Successful!");
00559     description =
00560       tr("Your relay's directory port is reachable from the Tor network!");
00561   } else {
00562     icon = addBadgeToPixmap(icon, ":/images/32x32/dialog-warning.png");
00563     title = tr("Directory Port Reachability Test Failed");
00564     description =
00565       tr("Your relay's directory port is not reachable by other Tor clients. "
00566          "This can happen if you are behind a router or firewall that requires "
00567          "you to set up port forwarding. If %1:%2 is not your correct IP "
00568          "address and directory port, please check your relay's configuration.")
00569                                                 .arg(ip.toString()).arg(port);
00570   }
00571 
00572   addNotification(icon, title, description);
00573 }
00574 
00575 void
00576 StatusEventWidget::serverDescriptorRejected(const QHostAddress &ip,
00577                                             quint16 port,
00578                                             const QString &reason)
00579 {
00580   QPixmap icon =
00581     addBadgeToPixmap(":/images/48x48/preferences-system-network-sharing.png",
00582                      ":/images/32x32/dialog-warning.png");
00583 
00584   addNotification(icon,
00585     tr("Relay Descriptor Rejected"),
00586     tr("Your relay's descriptor, which enables clients to connect to your "
00587        "relay, was rejected by the directory server at %1:%2. The reason "
00588        "given was: %3").arg(ip.toString()).arg(port).arg(reason));
00589 }
00590 
00591 void
00592 StatusEventWidget::serverDescriptorAccepted(const QHostAddress &ip,
00593                                             quint16 port)
00594 {
00595   Q_UNUSED(ip);
00596   Q_UNUSED(port);
00597 
00598   if (_squelchDescriptorAcceptedEvent)
00599     return;
00600    _squelchDescriptorAcceptedEvent = true;
00601 
00602   QPixmap icon =
00603     addBadgeToPixmap(":/images/48x48/preferences-system-network-sharing.png",
00604                      ":/images/32x32/dialog-ok-apply.png");
00605 
00606   addNotification(icon,
00607     tr("Your Relay is Online"),
00608     tr("Your relay is now online and available for Tor clients to use. You "
00609        "should see an increase in network traffic shown by the Bandwidth "
00610        "Graph within a few hours as more clients learn about your relay. "
00611        "Thank you for contributing to the Tor network!"));
00612 }
00613