Vidalia  0.3.1
StatusEventWidget.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 StatusEventWidget.h
13 ** \brief Displays information on Tor status events
14 */
15 
16 #include "StatusEventWidget.h"
17 #include "StatusEventItem.h"
19 #include "Vidalia.h"
20 
21 #include "TorEvents.h"
22 #include "stringutil.h"
23 
24 #include <QTime>
25 #include <QMenu>
26 #include <QPainter>
27 #include <QPixmap>
28 #include <QStringList>
29 #include <QObject>
30 #include <QHeaderView>
31 #include <QClipboard>
32 
33 bool compareStatusEventItems(const QTreeWidgetItem *a,
34  const QTreeWidgetItem *b)
35 {
36  return (*a < *b);
37 }
38 
40  : QTreeWidget(parent)
41 {
46 
47  connect(this, SIGNAL(customContextMenuRequested(QPoint)),
48  this, SLOT(customContextMenuRequested(QPoint)));
49  connect(tc, SIGNAL(authenticated()), this, SLOT(authenticated()));
50  connect(tc, SIGNAL(disconnected()), this, SLOT(disconnected()));
52  QStringList)),
53  this, SLOT(dangerousTorVersion(tc::TorVersionStatus, QString,
54  QStringList)));
55  connect(tc, SIGNAL(circuitEstablished()), this, SLOT(circuitEstablished()));
56  connect(tc, SIGNAL(bug(QString)), this, SLOT(bug(QString)));
57  connect(tc, SIGNAL(clockSkewed(int, QString)),
58  this, SLOT(clockSkewed(int, QString)));
59  connect(tc, SIGNAL(dangerousPort(quint16, bool)),
60  this, SLOT(dangerousPort(quint16, bool)));
61  connect(tc, SIGNAL(socksError(tc::SocksError, QString)),
62  this, SLOT(socksError(tc::SocksError, QString)));
63  connect(tc, SIGNAL(externalAddressChanged(QHostAddress, QString)),
64  this, SLOT(externalAddressChanged(QHostAddress, QString)));
65  connect(tc, SIGNAL(dnsHijacked()), this, SLOT(dnsHijacked()));
66  connect(tc, SIGNAL(dnsUseless()), this, SLOT(dnsUseless()));
67  connect(tc, SIGNAL(checkingOrPortReachability(QHostAddress, quint16)),
68  this, SLOT(checkingOrPortReachability(QHostAddress, quint16)));
69  connect(tc, SIGNAL(orPortReachabilityFinished(QHostAddress, quint16, bool)),
70  this, SLOT(orPortReachabilityFinished(QHostAddress, quint16, bool)));
71  connect(tc, SIGNAL(checkingDirPortReachability(QHostAddress, quint16)),
72  this, SLOT(checkingDirPortReachability(QHostAddress, quint16)));
73  connect(tc, SIGNAL(dirPortReachabilityFinished(QHostAddress, quint16, bool)),
74  this, SLOT(dirPortReachabilityFinished(QHostAddress, quint16, bool)));
75  connect(tc, SIGNAL(serverDescriptorRejected(QHostAddress, quint16, QString)),
76  this, SLOT(serverDescriptorRejected(QHostAddress, quint16, QString)));
77  connect(tc, SIGNAL(serverDescriptorAccepted(QHostAddress, quint16)),
78  this, SLOT(serverDescriptorAccepted(QHostAddress, quint16)));
79 
80  setItemDelegate(new StatusEventItemDelegate(this));
81 }
82 
83 void
85 {
86  /* XXX: We need to store the untranslated text for each of the status
87  * event items, iterate through all items in the list, and then
88  * retranslate them. The trick is that some of the messages are
89  * generated dynamically based on data sent by Tor (which is NOT
90  * translated). Those messages we can't retranslate correctly
91  * without also storing the variables used to generate the message.
92  */
93 }
94 
95 void
97 {
99 
100  QTreeWidgetItem *item;
101  Qt::SortOrder order = header()->sortIndicatorOrder();
102  while (topLevelItemCount() > _maximumItemCount) {
103  if (order == Qt::AscendingOrder)
104  item = takeTopLevelItem(0);
105  else
106  item = takeTopLevelItem(topLevelItemCount()-1);
107  if (item)
108  delete item;
109  }
110 }
111 
112 int
114 {
115  return _maximumItemCount;
116 }
117 
118 QStringList
120 {
121  QString text;
122  QStringList out;
123  QList<QTreeWidgetItem *> items = selectedItems();
124 
125  // We have to sort the items since selectedItems() returns the order in
126  // which the items were selected, not the order in which they appear in the
127  // current list.
128  qStableSort(items.begin(), items.end(), compareStatusEventItems);
129 
130  for (int i = 0; i < items.size(); i++) {
131  StatusEventItem *event = dynamic_cast<StatusEventItem *>(items.at(i));
132  if (event)
133  out.append(event->toString());
134  }
135  return out;
136 }
137 
138 QStringList
140 {
141  QStringList out;
142  QList<QTreeWidgetItem *> items;
143 
144  for (int i = 0; i < topLevelItemCount(); i++)
145  items.append(topLevelItem(i));
146 
147  // Ensure the items are sorted in ascending order according to timestamp
148  qStableSort(items.begin(), items.end(), compareStatusEventItems);
149 
150  for (int i = 0; i < items.size(); i++) {
151  StatusEventItem *event = dynamic_cast<StatusEventItem *>(items.at(i));
152  if (event)
153  out.append(event->toString());
154  }
155  return out;
156 }
157 
158 void
160 {
161  QMenu menu(this);
162 
163  StatusEventItem *item = dynamic_cast<StatusEventItem *>(itemAt(pos));
164  if (! item || ! item->isSelected())
165  return;
166 
167  QAction *copyAction = menu.addAction(QIcon(":/images/22x22/edit-copy.png"),
168  tr("Copy to Clipboard"));
169 
170  QAction *action = menu.exec(mapToGlobal(pos));
171  if (action == copyAction) {
172  QStringList eventText = selectedEvents();
173  if (! eventText.isEmpty())
174  QApplication::clipboard()->setText(eventText.join("\n"));
175  }
176 }
177 
178 QList<StatusEventItem *>
179 StatusEventWidget::find(const QString &text, bool highlight)
180 {
181  QList<StatusEventItem *> items;
182 
183  for (int i = 0; i < topLevelItemCount(); i++) {
184  StatusEventItem *item = dynamic_cast<StatusEventItem *>(topLevelItem(i));
185  if (! item)
186  continue;
187 
188  if (item->title().contains(text, Qt::CaseInsensitive)
189  || item->description().contains(text, Qt::CaseInsensitive)) {
190  items.append(item);
191  if (highlight)
192  item->setSelected(true);
193  } else if (highlight) {
194  item->setSelected(false);
195  }
196  }
197  return items;
198 }
199 
200 void
202  const QString &title,
203  const QString &description,
204  const QString &helpUrl)
205 {
206  // Check if we first need to remove the oldest item in the list in order
207  // to avoid exceeding the maximum number of notification items
208  if (topLevelItemCount() == maximumItemCount()) {
209  QTreeWidgetItem *item;
210  if (header()->sortIndicatorOrder() == Qt::AscendingOrder)
211  item = takeTopLevelItem(0);
212  else
213  item = takeTopLevelItem(topLevelItemCount()-1);
214  if (item)
215  delete item;
216  }
217 
218  // Create the new notification item
219  StatusEventItem *item = new StatusEventItem(this);
220  item->setTimestamp(QDateTime::currentDateTime());
221  item->setIcon(icon);
222  item->setTitle(title);
223  item->setDescription(description);
224  item->setHelpUrl(helpUrl);
225  item->setToolTip(string_wrap(description, 80));
226 
227  // Add the new item to the list and ensure it is visible
228  addTopLevelItem(item);
229  scrollToItem(item, QAbstractItemView::EnsureVisible);
230 }
231 
232 QPixmap
234  const QPixmap &badge)
235 {
236  QPixmap out = pixmap;
237  QPainter painter(&out);
238  painter.drawPixmap(pixmap.width() - badge.width(),
239  pixmap.height() - badge.height(),
240  badge);
241  return out;
242 }
243 
244 QPixmap
246  const QString &badge)
247 {
248  return StatusEventWidget::addBadgeToPixmap(pixmap, QPixmap(badge));
249 }
250 
251 QPixmap
253  const QString &badge)
254 {
255  return StatusEventWidget::addBadgeToPixmap(QPixmap(pixmap), QPixmap(badge));
256 }
257 
258 void
260 {
262 
263  QString version = tc->getTorVersionString();
264  QPixmap icon = addBadgeToPixmap(":/images/48x48/tor-logo.png",
265  ":/images/32x32/dialog-ok-apply.png");
266  addNotification(icon,
267  tr("The Tor Software is Running"),
268  tr("You are currently running version \"%1\" of the Tor software.")
269  .arg(version));
270 
271  // Check if Tor established a circuit before we were able to authenticate,
272  // in which case we missed the CIRCUIT_ESTABLISHED event. So fake it.
273  if (tc->isCircuitEstablished())
275 
276  // Check on the status of Tor's version, in case we missed that event too
277  QString status = tc->getInfo("status/version/current").toString();
278  if (! status.compare("old", Qt::CaseInsensitive)
279  || ! status.compare("obsolete", Qt::CaseInsensitive)) {
280  dangerousTorVersion(tc::ObsoleteTorVersion, version, QStringList());
281  } else if (! status.compare("unrecommended", Qt::CaseInsensitive)) {
282  dangerousTorVersion(tc::UnrecommendedTorVersion, version, QStringList());
283  }
284 }
285 
286 void
288 {
289  QPixmap icon = addBadgeToPixmap(":/images/48x48/tor-logo.png",
290  ":/images/32x32/edit-delete.png");
291 
292  addNotification(icon,
293  tr("The Tor Software is not Running"),
294  tr("Click \"Start Tor\" in the Vidalia Control Panel to restart the Tor "
295  "software. If Tor exited unexpectedly, select the \"Advanced\" tab "
296  "above for details about any errors encountered."));
297 
299 }
300 
301 void
303  const QString &version,
304  const QStringList &recommended)
305 {
306  Q_UNUSED(recommended);
307  QString description;
308  QPixmap icon;
309 
310  if (reason == tc::UnrecommendedTorVersion) {
311  icon = addBadgeToPixmap(":/images/48x48/tor-logo.png",
312  ":/images/32x32/security-medium.png");
313 
314  description =
315  tr("You are currently running version \"%1\" of the Tor software, which "
316  "is no longer recommended. Please upgrade to the most recent version "
317  "of the software, which may contain important security, reliability "
318  "and performance fixes.").arg(version);
319  } else if (reason == tc::ObsoleteTorVersion) {
320  icon = addBadgeToPixmap(":/images/48x48/tor-logo.png",
321  ":/images/32x32/security-low.png");
322 
323  description =
324  tr("You are currently running version \"%1\" of the Tor software, which "
325  "may no longer work with the current Tor network. Please upgrade "
326  "to the most recent version of the software, which may contain "
327  "important security, reliability and performance fixes.").arg(version);
328  }
329 
330  addNotification(icon, tr("Your Tor Software is Out-of-date"), description);
331 }
332 
333 void
335 {
336  addNotification(QPixmap(":/images/48x48/network-connect.png"),
337  tr("Connected to the Tor Network"),
338  tr("We were able to successfully establish a connection to the Tor "
339  "network. You can now configure your applications to use the Internet "
340  "anonymously."));
341 }
342 
343 void
344 StatusEventWidget::bug(const QString &description)
345 {
346  QPixmap icon = addBadgeToPixmap(":/images/48x48/tor-logo.png",
347  ":/images/32x32/script-error.png");
348  addNotification(icon,
349  tr("Tor Software Error"),
350  tr("The Tor software encountered an internal bug. Please report the "
351  "following error message to the Tor developers at bugs.torproject.org: "
352  "\"%1\"").arg(description));
353 }
354 
355 void
356 StatusEventWidget::clockSkewed(int skew, const QString &source)
357 {
358  if (source.startsWith("OR:", Qt::CaseInsensitive)) {
359  // Tor versions 0.2.1.19 and earlier, and 0.2.2.1 and earlier, throw
360  // this message a little too liberally in this case.
361  quint32 torVersion = Vidalia::torControl()->getTorVersion();
362  if (torVersion <= 0x00020113)
363  return;
364  QString str = Vidalia::torControl()->getTorVersionString();
365  if (str.startsWith("0.2.2.") && torVersion <= 0x00020201)
366  return;
367  }
368 
369  QString description;
370  QPixmap icon = addBadgeToPixmap(":/images/48x48/chronometer.png",
371  ":/images/32x32/dialog-warning.png");
372 
373  if (skew < 0) {
374  description =
375  tr("Tor has determined that your computer's clock may be set to %1 "
376  "seconds in the past compared to the source \"%2\". If your "
377  "clock is not correct, Tor will not be able to function. Please "
378  "verify your computer displays the correct time.").arg(qAbs(skew))
379  .arg(source);
380  } else {
381  description =
382  tr("Tor has determined that your computer's clock may be set to %1 "
383  "seconds in the future compared to the source \"%2\". If "
384  "your clock is not correct, Tor will not be able to function. Please "
385  "verify your computer displays the correct time.").arg(qAbs(skew))
386  .arg(source);
387  }
388  addNotification(icon, tr("Your Computer's Clock is Potentially Incorrect"),
389  description);
390 }
391 
392 void
393 StatusEventWidget::dangerousPort(quint16 port, bool rejected)
394 {
395  QPixmap icon;
396  QString description;
397 
398  if (rejected) {
399  icon = addBadgeToPixmap(":/images/48x48/applications-internet.png",
400  ":/images/32x32/security-low.png");
401 
402  description =
403  tr("One of the applications on your computer may have attempted to "
404  "make an unencrypted connection through Tor to port %1. Sending "
405  "unencrypted information over the Tor network is dangerous and not "
406  "recommended. For your protection, Tor has automatically closed this "
407  "connection.").arg(port);
408  } else {
409  icon = addBadgeToPixmap(":/images/48x48/applications-internet.png",
410  ":/images/32x32/security-medium.png");
411  description =
412  tr("One of the applications on your computer may have attempted to "
413  "make an unencrypted connection through Tor to port %1. Sending "
414  "unencrypted information over the Tor network is dangerous and not "
415  "recommended.").arg(port);
416  }
417 
418  addNotification(icon, tr("Potentially Dangerous Connection!"), description);
419 }
420 
421 void
422 StatusEventWidget::socksError(tc::SocksError type, const QString &destination)
423 {
424  QString title, description;
425  QPixmap icon = QPixmap(":/images/48x48/applications-internet.png");
426 
427  if (type == tc::DangerousSocksTypeError) {
428  icon = addBadgeToPixmap(icon, ":/images/32x32/security-medium.png");
429 
430  title = tr("Potentially Dangerous Connection!");
431  description =
432  tr("One of your applications established a connection through Tor "
433  "to \"%1\" using a protocol that may leak information about your "
434  "destination. Please ensure you configure your applications to use "
435  "only SOCKS4a or SOCKS5 with remote hostname resolution.")
436  .arg(destination);
437  } else if (type == tc::UnknownSocksProtocolError) {
438  icon = addBadgeToPixmap(icon, ":/images/32x32/dialog-warning.png");
439 
440  title = tr("Unknown SOCKS Protocol");
441  description =
442  tr("One of your applications tried to establish a connection through "
443  "Tor using a protocol that Tor does not understand. Please ensure "
444  "you configure your applications to use only SOCKS4a or SOCKS5 with "
445  "remote hostname resolution.");
446  } else if (type == tc::BadSocksHostnameError) {
447  icon = addBadgeToPixmap(icon, ":/images/32x32/dialog-warning.png");
448 
449  title = tr("Invalid Destination Hostname");
450  description =
451  tr("One of your applications tried to establish a connection through "
452  "Tor to \"%1\", which Tor does not recognize as a valid hostname. "
453  "Please check your application's configuration.").arg(destination);
454  } else {
455  return;
456  }
457 
458  addNotification(icon, title, description);
459 }
460 
461 void
463  const QString &hostname)
464 {
465  QString hostString = hostname.isEmpty() ? QString()
466  : QString(" (%1)").arg(hostname);
467 
468  addNotification(QPixmap(":/images/48x48/applications-internet.png"),
469  tr("External IP Address Changed"),
470  tr("Tor has determined your relay's public IP address is currently %1%2. "
471  "If that is not correct, please consider setting the 'Address' option "
472  "in your relay's configuration.").arg(ip.toString()).arg(hostString));
473 }
474 
475 void
477 {
478  QPixmap icon = addBadgeToPixmap(":/images/48x48/applications-internet.png",
479  ":/images/32x32/dialog-warning.png");
480  addNotification(icon,
481  tr("DNS Hijacking Detected"),
482  tr("Tor detected that your DNS provider is providing false responses for "
483  "domains that do not exist. Some ISPs and other DNS providers, such as "
484  "OpenDNS, are known to do this in order to display their own search or "
485  "advertising pages."));
486 }
487 
488 void
490 {
491  QPixmap icon = addBadgeToPixmap(":/images/48x48/applications-internet.png",
492  ":/images/32x32/edit-delete.png");
493  addNotification(icon,
494  tr("DNS Hijacking Detected"),
495  tr("Tor detected that your DNS provider is providing false responses for "
496  "well known domains. Since clients rely on Tor network relays to "
497  "provide accurate DNS repsonses, your relay will not be configured as "
498  "an exit relay."));
499 }
500 
501 void
503  quint16 port)
504 {
505  addNotification(QPixmap(":/images/48x48/network-wired.png"),
506  tr("Checking Server Port Reachability"),
507  tr("Tor is trying to determine if your relay's server port is reachable "
508  "from the Tor network by connecting to itself at %1:%2. This test "
509  "could take several minutes.").arg(ip.toString()).arg(port));
510 }
511 
512 void
514  quint16 port,
515  bool reachable)
516 {
517  QString title, description;
518  QPixmap icon = QPixmap(":/images/48x48/network-wired.png");
519  if (reachable) {
520  icon = addBadgeToPixmap(icon, ":/images/32x32/dialog-ok-apply.png");
521  title = tr("Server Port Reachability Test Successful!");
522  description =
523  tr("Your relay's server port is reachable from the Tor network!");
524  } else {
525  icon = addBadgeToPixmap(icon, ":/images/32x32/dialog-warning.png");
526  title = tr("Server Port Reachability Test Failed");
527  description =
528  tr("Your relay's server port is not reachable by other Tor clients. This "
529  "can happen if you are behind a router or firewall that requires you "
530  "to set up port forwarding. If %1:%2 is not your correct IP address "
531  "and server port, please check your relay's configuration.")
532  .arg(ip.toString()).arg(port);
533  }
534 
535  addNotification(icon, title, description);
536 }
537 
538 void
540  quint16 port)
541 {
542  addNotification(QPixmap(":/images/48x48/network-wired.png"),
543  tr("Checking Directory Port Reachability"),
544  tr("Tor is trying to determine if your relay's directory port is reachable "
545  "from the Tor network by connecting to itself at %1:%2. This test "
546  "could take several minutes.").arg(ip.toString()).arg(port));
547 }
548 
549 void
551  quint16 port,
552  bool reachable)
553 {
554  QString title, description;
555  QPixmap icon = QPixmap(":/images/48x48/network-wired.png");
556  if (reachable) {
557  icon = addBadgeToPixmap(icon, ":/images/32x32/dialog-ok-apply.png");
558  title = tr("Directory Port Reachability Test Successful!");
559  description =
560  tr("Your relay's directory port is reachable from the Tor network!");
561  } else {
562  icon = addBadgeToPixmap(icon, ":/images/32x32/dialog-warning.png");
563  title = tr("Directory Port Reachability Test Failed");
564  description =
565  tr("Your relay's directory port is not reachable by other Tor clients. "
566  "This can happen if you are behind a router or firewall that requires "
567  "you to set up port forwarding. If %1:%2 is not your correct IP "
568  "address and directory port, please check your relay's configuration.")
569  .arg(ip.toString()).arg(port);
570  }
571 
572  addNotification(icon, title, description);
573 }
574 
575 void
577  quint16 port,
578  const QString &reason)
579 {
580  QPixmap icon =
581  addBadgeToPixmap(":/images/48x48/preferences-system-network-sharing.png",
582  ":/images/32x32/dialog-warning.png");
583 
584  addNotification(icon,
585  tr("Relay Descriptor Rejected"),
586  tr("Your relay's descriptor, which enables clients to connect to your "
587  "relay, was rejected by the directory server at %1:%2. The reason "
588  "given was: %3").arg(ip.toString()).arg(port).arg(reason));
589 }
590 
591 void
593  quint16 port)
594 {
595  Q_UNUSED(ip);
596  Q_UNUSED(port);
597 
599  return;
601 
602  QPixmap icon =
603  addBadgeToPixmap(":/images/48x48/preferences-system-network-sharing.png",
604  ":/images/32x32/dialog-ok-apply.png");
605 
606  addNotification(icon,
607  tr("Your Relay is Online"),
608  tr("Your relay is now online and available for Tor clients to use. You "
609  "should see an increase in network traffic shown by the Bandwidth "
610  "Graph within a few hours as more clients learn about your relay. "
611  "Thank you for contributing to the Tor network!"));
612 }
613 
SocksError
Definition: tcglobal.h:79
Definition: tcglobal.cpp:19
QString title() const
quint32 getTorVersion()
Definition: TorControl.cpp:667
int maximumItemCount() const
bool compareStatusEventItems(const QTreeWidgetItem *a, const QTreeWidgetItem *b)
QList< StatusEventItem * > find(const QString &text, bool highlight=true)
void setDescription(const QString &description)
StatusEventWidget(QWidget *parent=0)
void checkingDirPortReachability(const QHostAddress &ip, quint16 port)
void externalAddressChanged(const QHostAddress &ip, const QString &hostname)
void bug(const QString &reason)
void dirPortReachabilityFinished(const QHostAddress &ip, quint16 port, bool reachable)
bool setEvent(TorEvents::Event e, bool add=true, bool set=true, QString *errmsg=0)
Definition: TorControl.cpp:697
QStringList allEvents() const
void checkingOrPortReachability(const QHostAddress &ip, quint16 port)
QString string_wrap(const QString &str, int width, const QString &sep, const QString &le)
Definition: stringutil.cpp:75
bool getInfo(QHash< QString, QString > &map, QString *errmsg=0)
Definition: TorControl.cpp:450
virtual void retranslateUi()
static TorControl * torControl()
Definition: Vidalia.h:76
QString i(QString str)
Definition: html.cpp:32
stop errmsg connect(const QHostAddress &address, quint16 port)
QStringList selectedEvents() const
TorVersionStatus
Definition: tcglobal.h:86
QString getTorVersionString()
Definition: TorControl.cpp:659
bool isCircuitEstablished()
Definition: TorControl.cpp:424
QString description() const
void orPortReachabilityFinished(const QHostAddress &ip, quint16 port, bool reachable)
void setMaximumItemCount(int maximumItemCount)
void dangerousTorVersion(tc::TorVersionStatus reason, const QString &version, const QStringList &recommended)
void dangerousPort(quint16 port, bool rejected)
void customContextMenuRequested(const QPoint &pos)
void clockSkewed(int skew, const QString &source)
void addNotification(const QPixmap &icon, const QString &title, const QString &description, const QString &helpUrl=QString())
void setHelpUrl(const QString &url)
void setIcon(const QPixmap &pixmap)
void serverDescriptorAccepted(const QHostAddress &ip, quint16 port)
QString b(QString str)
Definition: html.cpp:39
void setTimestamp(const QDateTime &timestamp)
void serverDescriptorRejected(const QHostAddress &ip, quint16 port, const QString &reason)
static QPixmap addBadgeToPixmap(const QPixmap &pixmap, const QPixmap &badge)
void setTitle(const QString &title)
void socksError(tc::SocksError type, const QString &destination)
void setToolTip(const QString &toolTip)