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