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
33bool compareStatusEventItems(const QTreeWidgetItem *a,
34 const QTreeWidgetItem *b)
35{
36 return (*a < *b);
37}
38
40 : QTreeWidget(parent)
41{
44 tc->setEvent(TorEvents::ClientStatus);
45 tc->setEvent(TorEvents::ServerStatus);
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
83void
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
95void
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
112int
114{
115 return _maximumItemCount;
116}
117
118QStringList
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
138QStringList
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
158void
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
178QList<StatusEventItem *>
179StatusEventWidget::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
200void
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
232QPixmap
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
244QPixmap
246 const QString &badge)
247{
248 return StatusEventWidget::addBadgeToPixmap(pixmap, QPixmap(badge));
249}
250
251QPixmap
253 const QString &badge)
254{
255 return StatusEventWidget::addBadgeToPixmap(QPixmap(pixmap), QPixmap(badge));
256}
257
258void
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
286void
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
301void
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
333void
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
343void
344StatusEventWidget::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
355void
356StatusEventWidget::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
392void
393StatusEventWidget::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
421void
422StatusEventWidget::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
461void
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
475void
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
488void
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
501void
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
512void
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
538void
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
549void
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
575void
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
591void
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
bool compareStatusEventItems(const QTreeWidgetItem *a, const QTreeWidgetItem *b)
stop errmsg connect(const QHostAddress &address, quint16 port)
void setIcon(const QPixmap &pixmap)
void setDescription(const QString &description)
void setTimestamp(const QDateTime &timestamp)
void setToolTip(const QString &toolTip)
QString title() const
void setTitle(const QString &title)
QString description() const
void setHelpUrl(const QString &url)
void dangerousPort(quint16 port, bool rejected)
int maximumItemCount() const
void orPortReachabilityFinished(const QHostAddress &ip, quint16 port, bool reachable)
void addNotification(const QPixmap &icon, const QString &title, const QString &description, const QString &helpUrl=QString())
void socksError(tc::SocksError type, const QString &destination)
void externalAddressChanged(const QHostAddress &ip, const QString &hostname)
void serverDescriptorRejected(const QHostAddress &ip, quint16 port, const QString &reason)
void checkingOrPortReachability(const QHostAddress &ip, quint16 port)
void dangerousTorVersion(tc::TorVersionStatus reason, const QString &version, const QStringList &recommended)
void customContextMenuRequested(const QPoint &pos)
void setMaximumItemCount(int maximumItemCount)
void checkingDirPortReachability(const QHostAddress &ip, quint16 port)
QStringList selectedEvents() const
void bug(const QString &reason)
void dirPortReachabilityFinished(const QHostAddress &ip, quint16 port, bool reachable)
static QPixmap addBadgeToPixmap(const QPixmap &pixmap, const QPixmap &badge)
void clockSkewed(int skew, const QString &source)
QList< StatusEventItem * > find(const QString &text, bool highlight=true)
StatusEventWidget(QWidget *parent=0)
virtual void retranslateUi()
void serverDescriptorAccepted(const QHostAddress &ip, quint16 port)
QStringList allEvents() const
quint32 getTorVersion()
Definition: TorControl.cpp:667
QString getTorVersionString()
Definition: TorControl.cpp:659
@ GeneralStatus
Definition: TorEvents.h:56
@ ServerStatus
Definition: TorEvents.h:58
@ ClientStatus
Definition: TorEvents.h:57
static TorControl * torControl()
Definition: Vidalia.h:76
QString i(QString str)
Definition: html.cpp:32
QString b(QString str)
Definition: html.cpp:39
Definition: tcglobal.cpp:19
TorVersionStatus
Definition: tcglobal.h:86
@ UnrecommendedTorVersion
Definition: tcglobal.h:88
@ ObsoleteTorVersion
Definition: tcglobal.h:87
SocksError
Definition: tcglobal.h:79
@ UnknownSocksProtocolError
Definition: tcglobal.h:81
@ DangerousSocksTypeError
Definition: tcglobal.h:80
@ BadSocksHostnameError
Definition: tcglobal.h:82
QString string_wrap(const QString &str, int width, const QString &sep, const QString &le)
Definition: stringutil.cpp:75