Vidalia
0.2.17
|
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