Vidalia 0.3.1
NetViewer.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 NetViewer.cpp
13** \brief Displays a map of the Tor network and the user's circuits
14*/
15
16#include "NetViewer.h"
17#include "RouterInfoDialog.h"
18#include "RouterListItem.h"
19#include "Vidalia.h"
20#include "VMessageBox.h"
21
22#include <QMessageBox>
23#include <QToolBar>
24#include <QHeaderView>
25#include <QCoreApplication>
26
27#define IMG_MOVE ":/images/22x22/move-map.png"
28#define IMG_ZOOMIN ":/images/22x22/zoom-in.png"
29#define IMG_ZOOMOUT ":/images/22x22/zoom-out.png"
30
31#if 0
32/** Number of milliseconds to wait after the arrival of the last descriptor whose
33 * IP needs to be resolved to geographic information, in case more descriptors
34 * arrive. Then we can simply lump the IPs into a single request. */
35#define MIN_RESOLVE_QUEUE_DELAY (10*1000)
36/** Maximum number of milliseconds to wait after the arrival of the first
37 * IP address into the resolve queue, before we flush the entire queue. */
38#define MAX_RESOLVE_QUEUE_DELAY (30*1000)
39#endif
40
41/** Constructor. Loads settings from VidaliaSettings.
42 * \param parent The parent widget of this NetViewer object.\
43 */
44NetViewer::NetViewer(QWidget *parent)
45 : VidaliaTab(tr("Network Map"), "", parent)
46{
47 /* Invoke Qt Designer generated QObject setup routine */
48 ui.setupUi(this);
49
50#if defined(Q_WS_MAC)
51 ui.actionHelp->setShortcut(QString("Ctrl+?"));
52#endif
53
54 /* Pressing 'Esc' or 'Ctrl+W' will close the window */
55// ui.actionClose->setShortcut(QString("Esc"));
56// Vidalia::createShortcut("Ctrl+W", this, ui.actionClose, SLOT(trigger()));
57
58 /* Get the TorControl object */
60 connect(_torControl, SIGNAL(authenticated()),
61 this, SLOT(onAuthenticated()));
62 connect(_torControl, SIGNAL(disconnected()),
63 this, SLOT(onDisconnected()));
64
66 connect(_torControl, SIGNAL(circuitStatusChanged(Circuit)),
67 this, SLOT(addCircuit(Circuit)));
68
70 connect(_torControl, SIGNAL(streamStatusChanged(Stream)),
71 this, SLOT(addStream(Stream)));
72
74 connect(_torControl, SIGNAL(addressMapped(QString, QString, QDateTime)),
75 this, SLOT(addressMapped(QString, QString, QDateTime)));
76
78 connect(_torControl, SIGNAL(newDescriptors(QStringList)),
79 this, SLOT(newDescriptors(QStringList)));
80
81 /* Change the column widths of the tree widgets */
82 ui.treeRouterList->header()->
83 resizeSection(RouterListWidget::StatusColumn, 25);
84 ui.treeRouterList->header()->
85 resizeSection(RouterListWidget::CountryColumn, 25);
86 ui.treeCircuitList->header()->
87 resizeSection(CircuitListWidget::ConnectionColumn, 235);
88
89 /* Create the TorMapWidget and add it to the dialog */
90#if defined(USE_MARBLE)
91 _map = new TorMapWidget();
92 connect(_map, SIGNAL(displayRouterInfo(QString)),
93 this, SLOT(displayRouterInfo(QString)));
94 connect(ui.actionZoomFullScreen, SIGNAL(triggered()),
95 this, SLOT(toggleFullScreen()));
96 Vidalia::createShortcut("ESC", _map, this, SLOT(toggleFullScreen()));
97#else
98 _map = new TorMapImageView();
99 ui.actionZoomFullScreen->setVisible(false);
100#endif
101 ui.gridLayout->addWidget(_map);
102
103 /* Connect zoom buttons to TorMapWidget zoom slots */
104 connect(ui.actionZoomIn, SIGNAL(triggered()), this, SLOT(zoomIn()));
105 connect(ui.actionZoomOut, SIGNAL(triggered()), this, SLOT(zoomOut()));
106 connect(ui.actionZoomToFit, SIGNAL(triggered()), _map, SLOT(zoomToFit()));
107
108 /* Create the timer that will be used to update the router list once every
109 * hour. We still receive the NEWDESC event to get new descriptors, but this
110 * needs to be called to get rid of any descriptors that were removed. */
111 _refreshTimer.setInterval(60*60*1000);
112 connect(&_refreshTimer, SIGNAL(timeout()), this, SLOT(refresh()));
113
114 /* Connect the necessary slots and signals */
115 connect(ui.actionHelp, SIGNAL(triggered()), this, SLOT(help()));
116 connect(ui.actionRefresh, SIGNAL(triggered()), this, SLOT(refresh()));
117 connect(ui.treeRouterList, SIGNAL(routerSelected(QList<RouterDescriptor>)),
118 this, SLOT(routerSelected(QList<RouterDescriptor>)));
119 connect(ui.treeRouterList, SIGNAL(zoomToRouter(QString)),
120 _map, SLOT(zoomToRouter(QString)));
121 connect(ui.treeCircuitList, SIGNAL(circuitSelected(Circuit)),
122 this, SLOT(circuitSelected(Circuit)));
123 connect(ui.treeCircuitList, SIGNAL(circuitRemoved(CircuitId)),
124 _map, SLOT(removeCircuit(CircuitId)));
125 connect(ui.treeCircuitList, SIGNAL(zoomToCircuit(CircuitId)),
126 _map, SLOT(zoomToCircuit(CircuitId)));
127 connect(ui.treeCircuitList, SIGNAL(closeCircuit(CircuitId)),
128 _torControl, SLOT(closeCircuit(CircuitId)));
129 connect(ui.treeCircuitList, SIGNAL(closeStream(StreamId)),
130 _torControl, SLOT(closeStream(StreamId)));
131 connect(ui.lineRouterSearch, SIGNAL(returnPressed()),
132 this, SLOT(onRouterSearch()));
133 connect(ui.lineRouterSearch, SIGNAL(textChanged(QString)),
134 ui.treeRouterList, SLOT(onRouterSearch(QString)));
135
137
138 QToolBar *tb = new QToolBar();
139 tb->addAction(ui.actionRefresh);
140 tb->addAction(ui.actionHelp);
141 tb->addAction(ui.actionZoomIn);
142 tb->addAction(ui.actionZoomOut);
143 tb->addAction(ui.actionZoomToFit);
144 tb->addAction(ui.actionZoomFullScreen);
145
146 tb->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
147 ui.horizontalLayout->addWidget(tb);
148}
149
150/** Called when the user changes the UI translation. */
151void
153{
154 ui.retranslateUi(this);
155 setTitle(tr("Network Map"));
156 ui.treeRouterList->retranslateUi();
157 ui.treeCircuitList->retranslateUi();
158
159 if (ui.treeRouterList->selectedItems().size()) {
160 QList<RouterDescriptor> routers;
161 foreach (QTreeWidgetItem *item, ui.treeRouterList->selectedItems()) {
162 routers << dynamic_cast<RouterListItem *>(item)->descriptor();
163 }
164 ui.textRouterInfo->display(routers);
165 } else if (ui.treeCircuitList->selectedItems().size()) {
166 QList<RouterDescriptor> routers;
167 QTreeWidgetItem *item = ui.treeCircuitList->selectedItems()[0];
168 Circuit circuit = dynamic_cast<CircuitItem*>(item)->circuit();
169 foreach (QString id, circuit.routerIDs()) {
170 RouterListItem *item = ui.treeRouterList->findRouterById(id);
171 if (item)
172 routers.append(item->descriptor());
173 }
174 ui.textRouterInfo->display(routers);
175 }
176}
177
178void
180{
181 VidaliaSettings settings;
182
183#if defined(USE_GEOIP)
184 if (settings.useLocalGeoIpDatabase()) {
185 QString databaseFile = settings.localGeoIpDatabase();
186 if (! databaseFile.isEmpty()) {
187 _geoip.setLocalDatabase(databaseFile);
189 vInfo("Using local database file for relay mapping: %1")
190 .arg(databaseFile);
191 return;
192 }
193 }
194#endif
195 vInfo("Using Tor's GeoIP database for country-level relay mapping.");
197}
198
199/** Loads data into map, lists and starts timer when we get connected*/
200void
202{
203 refresh();
204 _refreshTimer.start();
205 ui.actionRefresh->setEnabled(true);
206}
207
208/** Clears map, lists and stops timer when we get disconnected */
209void
211{
212 clear();
213 _refreshTimer.stop();
214 ui.actionRefresh->setEnabled(false);
215}
216
217/** Reloads the lists of routers, circuits that Tor knows about */
218void
220{
221 /* Don't let the user refresh while we're refreshing. */
222 ui.actionRefresh->setEnabled(false);
223
224 /* Clear the data */
225 clear();
226
227 /* Load router information */
229 /* Load existing address mappings */
231 /* Load Circuits and Streams information */
233
234 /* Ok, they can refresh again. */
235 ui.actionRefresh->setEnabled(true);
236}
237
238/** Clears the lists and the map */
239void
241{
242 /* Clear the network map */
243 _map->clear();
244 _map->update();
245 /* Clear the address map */
246 _addressMap.clear();
247 /* Clear the lists of routers, circuits, and streams */
248 ui.treeRouterList->clearRouters();
249 ui.treeCircuitList->clearCircuits();
250 ui.textRouterInfo->clear();
251}
252
253/** Called when the search of a router is triggered by the signal
254 * returnPressed from the search field. */
255void
257{
258 ui.treeRouterList->searchNextRouter(ui.lineRouterSearch->text());
259}
260
261/** Loads a list of all current address mappings. */
262void
264{
265 /* We store the reverse address mappings, so we can go from a numeric value
266 * back to a likely more meaningful hostname to display for the user. */
268}
269
270/** Loads a list of all current circuits and streams. */
271void
273{
274 /* Load all circuits */
275 CircuitList circuits = _torControl->getCircuits();
276 foreach (Circuit circuit, circuits) {
277 addCircuit(circuit);
278 }
279 /* Now load all streams */
280 StreamList streams = _torControl->getStreams();
281 foreach (Stream stream, streams) {
282 addStream(stream);
283 }
284
285 /* Update the map */
286 _map->update();
287}
288
289/** Adds <b>circuit</b> to the map and the list */
290void
292{
293 /* Add the circuit to the list of all current circuits */
294 ui.treeCircuitList->addCircuit(circuit);
295 /* Plot the circuit on the map */
296 _map->addCircuit(circuit.id(), circuit.routerIDs());
297}
298
299/** Adds <b>stream</b> to its associated circuit on the list of all circuits. */
300void
302{
303 /* If the new stream's target has an IP address instead of a host name,
304 * check our cache for an existing reverse address mapping. */
305 if (stream.status() == Stream::New) {
306 QString target = stream.targetAddress();
307 if (! QHostAddress(target).isNull() && _addressMap.isMapped(target)) {
308 /* Replace the IP address in the stream event with the original
309 * hostname */
310 ui.treeCircuitList->addStream(
311 Stream(stream.id(), stream.status(), stream.circuitId(),
312 _addressMap.mappedTo(target), stream.targetPort()));
313 }
314 } else {
315 ui.treeCircuitList->addStream(stream);
316 }
317}
318
319void
320NetViewer::addressMapped(const QString &from, const QString &to,
321 const QDateTime &expires)
322{
323 _addressMap.add(to, from, expires);
324}
325
326/** Called when the user selects the "Help" action from the toolbar. */
327void
329{
330 emit helpRequested("netview");
331}
332
333/** Retrieves a list of all running routers from Tor and their descriptors,
334 * and adds them to the RouterListWidget. */
335void
337{
338 NetworkStatus networkStatus = _torControl->getNetworkStatus();
339 foreach (RouterStatus rs, networkStatus) {
340 if (!rs.isRunning())
341 continue;
342
344 if (!rd.isEmpty())
345 addRouter(rd);
346
347 QCoreApplication::processEvents();
348 }
349}
350
351/** Adds a router to our list of servers and retrieves geographic location
352 * information for the server. */
353void
355{
356 /* Add the descriptor to the list of server */
357 RouterListItem *item = ui.treeRouterList->addRouter(rd);
358 if (! item)
359 return;
360
361 /* Attempt to map this relay to an approximate geographic location. The
362 * accuracy of the result depends on the database information currently
363 * available to the GeoIP resolver. */
364 if (! item->location().isValid() || rd.ip() != item->location().ip()) {
365 GeoIpRecord location = _geoip.resolve(rd.ip());
366 if (location.isValid()) {
367 item->setLocation(location);
368 _map->addRouter(rd, location);
369 }
370 }
371}
372
373/** Called when a NEWDESC event arrives. Retrieves new router descriptors
374 * for the router identities given in <b>ids</b> and updates the router
375 * list and network map. */
376void
377NetViewer::newDescriptors(const QStringList &ids)
378{
379 foreach (QString id, ids) {
381 if (!rd.isEmpty())
382 addRouter(rd); /* Updates the existing entry */
383 }
384}
385
386/** Called when the user selects a circuit from the circuit and streams
387 * list. */
388void
390{
391 /* Clear any selected items. */
392 ui.treeRouterList->deselectAll();
393 ui.textRouterInfo->clear();
394 _map->deselectAll();
395
396 /* Select the items on the map and in the list */
397 _map->selectCircuit(circuit.id());
398
399 QList<RouterDescriptor> routers;
400
401 foreach (QString id, circuit.routerIDs()) {
402 /* Try to find and select each router in the path */
403 RouterListItem *item = ui.treeRouterList->findRouterById(id);
404 if (item)
405 routers.append(item->descriptor());
406 }
407
408 ui.textRouterInfo->display(routers);
409}
410
411/** Called when the user selects one or more routers from the router list. */
412void
413NetViewer::routerSelected(const QList<RouterDescriptor> &routers)
414{
415 _map->deselectAll();
416 ui.textRouterInfo->clear();
417 ui.textRouterInfo->display(routers);
418
419 /* XXX: Ideally we would also be able to select multiple pinpoints on the
420 * map. But our current map sucks and you can't even tell when one is
421 * selected anyway. Worry about this when we actually get to Marble.
422 */
423 if (routers.size() == 1)
424 _map->selectRouter(routers[0].id());
425}
426
427/** Called when the user selects a router on the network map. Displays a
428 * dialog with detailed information for the router specified by
429 * <b>id</b>.*/
430void
432{
433 RouterInfoDialog dlg(_map->isFullScreen() ? static_cast<QWidget*>(_map)
434 : static_cast<QWidget*>(this));
435
436 /* Fetch the specified router's descriptor */
437 QStringList rd = _torControl->getRouterDescriptorText(id);
438 if (rd.isEmpty()) {
439 VMessageBox::warning(this, tr("Relay Not Found"),
440 tr("No details on the selected relay are available."),
442 return;
443 }
444
445 /* Fetch the router's network status information */
447
448 dlg.setRouterInfo(rd, rs);
449
450 /* Populate the UI with information learned from a previous GeoIP request */
451 RouterListItem *item = ui.treeRouterList->findRouterById(id);
452 if (item)
453 dlg.setLocation(item->location().toString());
454 else
455 dlg.setLocation(tr("Unknown"));
456
457 dlg.exec();
458}
459
460/* XXX: The following zoomIn() and zoomOut() slots are a hack. MarbleWidget
461 * does have zoomIn() and zoomOut() slots to which we could connect the
462 * buttons, but these slots currently don't force a repaint. So to see
463 * the zoom effect, the user has to click on the map after clicking one
464 * of the zoom buttons. Instead, we use the zoomViewBy() method, which
465 * DOES force a repaint.
466 */
467/** Called when the user clicks the "Zoom In" button. */
468void
470{
471#if defined(USE_MARBLE)
472 _map->zoomViewBy(40);
473#else
474 _map->zoomIn();
475#endif
476}
477
478/** Called when the user clicks the "Zoom Out" button. */
479void
481{
482#if defined(USE_MARBLE)
483 _map->zoomViewBy(-40);
484#else
485 _map->zoomOut();
486#endif
487}
488
489/** Called when the user clicks "Full Screen" or presses Escape on the map.
490 * Toggles the map between normal and a full screen viewing modes. */
491void
493{
494 if (_map->isFullScreen()) {
495 /* Disabling full screen mode. Put the map back in its container. */
496 ui.gridLayout->addWidget(_map);
497 _map->setWindowState(_map->windowState() & ~Qt::WindowFullScreen);
498 } else {
499 /* Enabling full screen mode. Remove the map from the QGridLayout
500 * container and set its window state to full screen. */
501 _map->setParent(0);
502 _map->setWindowState(_map->windowState() | Qt::WindowFullScreen);
503 _map->show();
504 }
505}
506
QList< Circuit > CircuitList
Definition: Circuit.h:81
QString CircuitId
Definition: Circuit.h:24
QList< RouterStatus > NetworkStatus
Definition: RouterStatus.h:97
QString StreamId
Definition: Stream.h:28
QList< Stream > StreamList
Definition: Stream.h:97
stop errmsg connect(const QHostAddress &address, quint16 port)
#define vInfo(fmt)
Definition: Vidalia.h:40
void add(const QString &from, const QString &to, const QDateTime &expires)
Definition: AddressMap.cpp:29
QString mappedTo(const QString &addr) const
Definition: AddressMap.cpp:85
bool isMapped(const QString &addr) const
Definition: AddressMap.cpp:76
AddressMap reverse() const
Definition: AddressMap.cpp:94
CircuitId id() const
Definition: Circuit.h:51
QStringList routerIDs() const
Definition: Circuit.h:61
QString toString() const
Definition: GeoIpRecord.cpp:64
QHostAddress ip() const
Definition: GeoIpRecord.h:44
bool isValid() const
Definition: GeoIpRecord.cpp:56
void setUseLocalDatabase(bool useLocalDatabase)
bool setLocalDatabase(const QString &databaseFile)
GeoIpRecord resolve(const QHostAddress &ip)
void clear()
Definition: NetViewer.cpp:240
QTimer _refreshTimer
Definition: NetViewer.h:123
void loadNetworkStatus()
Definition: NetViewer.cpp:336
void zoomOut()
Definition: NetViewer.cpp:480
void onDisconnected()
Definition: NetViewer.cpp:210
GeoIpResolver _geoip
Definition: NetViewer.h:125
TorControl * _torControl
Definition: NetViewer.h:121
AddressMap _addressMap
Definition: NetViewer.h:127
void displayRouterInfo(const QString &id)
Definition: NetViewer.cpp:431
void help()
Definition: NetViewer.cpp:328
void routerSelected(const QList< RouterDescriptor > &routers)
Definition: NetViewer.cpp:413
void addressMapped(const QString &from, const QString &to, const QDateTime &expires)
Definition: NetViewer.cpp:320
void addStream(const Stream &stream)
Definition: NetViewer.cpp:301
Ui::NetViewer ui
Definition: NetViewer.h:137
void onAuthenticated()
Definition: NetViewer.cpp:201
void refresh()
Definition: NetViewer.cpp:219
void loadConnections()
Definition: NetViewer.cpp:272
void onRouterSearch()
Definition: NetViewer.cpp:256
void newDescriptors(const QStringList &ids)
Definition: NetViewer.cpp:377
TorMapImageView * _map
Definition: NetViewer.h:133
void toggleFullScreen()
Definition: NetViewer.cpp:492
NetViewer(QWidget *parent=0)
Definition: NetViewer.cpp:44
void loadAddressMap()
Definition: NetViewer.cpp:263
void zoomIn()
Definition: NetViewer.cpp:469
void retranslateUi()
Definition: NetViewer.cpp:152
void addRouter(const RouterDescriptor &rd)
Definition: NetViewer.cpp:354
void setupGeoIpResolver()
Definition: NetViewer.cpp:179
void circuitSelected(const Circuit &circuit)
Definition: NetViewer.cpp:389
void addCircuit(const Circuit &circuit)
Definition: NetViewer.cpp:291
QHostAddress ip() const
void setLocation(const QString &location)
void setRouterInfo(const QStringList &desc, const RouterStatus &status)
RouterDescriptor descriptor() const
void setLocation(const GeoIpRecord &geoip)
GeoIpRecord location() const
bool isRunning() const
Definition: RouterStatus.h:69
QString id() const
Definition: RouterStatus.h:51
Definition: Stream.h:32
StreamId id() const
Definition: Stream.h:68
CircuitId circuitId() const
Definition: Stream.h:74
quint16 targetPort() const
Definition: Stream.h:80
Status status() const
Definition: Stream.h:70
QString targetAddress() const
Definition: Stream.h:78
@ New
Definition: Stream.h:39
RouterStatus getRouterStatus(const QString &id, QString *errmsg=0)
Definition: TorControl.cpp:971
AddressMap getAddressMap(AddressMap::AddressMapType type=AddressMap::AddressMapAll, QString *errmsg=0)
NetworkStatus getNetworkStatus(QString *errmsg=0)
Definition: TorControl.cpp:981
QStringList getRouterDescriptorText(const QString &id, QString *errmsg=0)
Definition: TorControl.cpp:953
CircuitList getCircuits(QString *errmsg=0)
bool setEvent(TorEvents::Event e, bool add=true, bool set=true, QString *errmsg=0)
Definition: TorControl.cpp:697
RouterDescriptor getRouterDescriptor(const QString &id, QString *errmsg=0)
Definition: TorControl.cpp:962
StreamList getStreams(QString *errmsg=0)
@ AddressMap
Definition: TorEvents.h:55
@ StreamStatus
Definition: TorEvents.h:52
@ NewDescriptor
Definition: TorEvents.h:54
@ CircuitStatus
Definition: TorEvents.h:51
void selectCircuit(const CircuitId &circid)
void addCircuit(const CircuitId &circid, const QStringList &path)
void addRouter(const RouterDescriptor &desc, const GeoIpRecord &geoip)
void selectRouter(const QString &id)
static int warning(QWidget *parent, QString caption, QString text, int button0, int button1=NoButton, int button2=NoButton)
static void createShortcut(const QKeySequence &key, QWidget *sender, QObject *receiver, const char *slot)
Definition: Vidalia.cpp:402
static TorControl * torControl()
Definition: Vidalia.h:76
bool useLocalGeoIpDatabase() const
QString localGeoIpDatabase() const
void helpRequested(const QString &topic)
void setTitle(const QString &title)
Definition: VidaliaTab.h:36
void zoomIn()
Definition: ZImageView.cpp:296
void zoomOut()
Definition: ZImageView.cpp:303