00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #include <QtGui>
00022 #include <QTimer>
00023 #include <vidalia.h>
00024 #include <file.h>
00025 #include <html.h>
00026 #include <stringutil.h>
00027 #include <net.h>
00028 #include <clientstatusevent.h>
00029 #include <dangerousversionevent.h>
00030 #include <vmessagebox.h>
00031
00032 #include "mainwindow.h"
00033
00034 #define IMG_BWGRAPH ":/images/16x16/utilities-system-monitor.png"
00035 #define IMG_CONTROL_PANEL ":/images/16x16/system-run.png"
00036 #define IMG_MESSAGELOG ":/images/16x16/format-justify-fill.png"
00037 #define IMG_CONFIG ":/images/16x16/preferences-system.png"
00038 #define IMG_IDENTITY ":/images/16x16/view-media-artist.png"
00039 #define IMG_HELP ":/images/16x16/system-help.png"
00040 #define IMG_ABOUT ":/images/16x16/help-about.png"
00041 #define IMG_EXIT ":/images/16x16/application-exit.png"
00042 #define IMG_NETWORK ":/images/16x16/applications-internet.png"
00043
00044 #define IMG_START_TOR_16 ":/images/16x16/media-playback-start.png"
00045 #define IMG_STOP_TOR_16 ":/images/16x16/media-playback-stop.png"
00046 #define IMG_START_TOR_48 ":/images/48x48/media-playback-start.png"
00047 #define IMG_STOP_TOR_48 ":/images/48x48/media-playback-stop.png"
00048 #define IMG_TOR_STOPPED_48 ":/images/48x48/tor-off.png"
00049 #define IMG_TOR_RUNNING_48 ":/images/48x48/tor-on.png"
00050 #define IMG_TOR_STARTING_48 ":/images/48x48/tor-starting.png"
00051 #define IMG_TOR_STOPPING_48 ":/images/48x48/tor-stopping.png"
00052
00053
00054
00055
00056 #if defined(Q_WS_WIN)
00057
00058 #define IMG_TOR_STOPPED ":/images/16x16/tor-off.png"
00059 #define IMG_TOR_RUNNING ":/images/16x16/tor-on.png"
00060 #define IMG_TOR_STARTING ":/images/16x16/tor-starting.png"
00061 #define IMG_TOR_STOPPING ":/images/16x16/tor-stopping.png"
00062 #elif defined(Q_WS_MAC)
00063
00064
00065 #define IMG_TOR_STOPPED "tor-off"
00066 #define IMG_TOR_RUNNING "tor-on"
00067 #define IMG_TOR_STARTING "tor-starting"
00068 #define IMG_TOR_STOPPING "tor-stopping"
00069 #else
00070
00071 #define IMG_TOR_STOPPED ":/images/22x22/tor-off.png"
00072 #define IMG_TOR_RUNNING ":/images/22x22/tor-on.png"
00073 #define IMG_TOR_STARTING ":/images/22x22/tor-starting.png"
00074 #define IMG_TOR_STOPPING ":/images/22x22/tor-stopping.png"
00075 #endif
00076
00077
00078 #define MIN_NEWIDENTITY_INTERVAL (10*1000)
00079
00080
00081 #define STARTUP_PROGRESS_STARTING 0
00082 #define STARTUP_PROGRESS_CONNECTING 10
00083 #define STARTUP_PROGRESS_AUTHENTICATING 20
00084 #define STARTUP_PROGRESS_BOOTSTRAPPING 30
00085 #define STARTUP_PROGRESS_CIRCUITBUILD 75
00086 #define STARTUP_PROGRESS_MAXIMUM (STARTUP_PROGRESS_BOOTSTRAPPING+100)
00087
00088
00089
00090
00091 MainWindow::MainWindow()
00092 : VidaliaWindow("MainWindow")
00093 {
00094 VidaliaSettings settings;
00095
00096 ui.setupUi(this);
00097
00098
00099 _messageLog = new MessageLog();
00100 _bandwidthGraph = new BandwidthGraph();
00101 _netViewer = new NetViewer();
00102 _configDialog = new ConfigDialog();
00103 connect(_messageLog, SIGNAL(helpRequested(QString)),
00104 this, SLOT(showHelpDialog(QString)));
00105 connect(_netViewer, SIGNAL(helpRequested(QString)),
00106 this, SLOT(showHelpDialog(QString)));
00107 connect(_configDialog, SIGNAL(helpRequested(QString)),
00108 this, SLOT(showHelpDialog(QString)));
00109
00110
00111 createActions();
00112
00113
00114 createTrayIcon();
00115
00116 _status = Unset;
00117 updateTorStatus(Stopped);
00118
00119
00120 _torControl = Vidalia::torControl();
00121 connect(_torControl, SIGNAL(started()), this, SLOT(started()));
00122 connect(_torControl, SIGNAL(startFailed(QString)),
00123 this, SLOT(startFailed(QString)));
00124 connect(_torControl, SIGNAL(stopped(int, QProcess::ExitStatus)),
00125 this, SLOT(stopped(int, QProcess::ExitStatus)));
00126 connect(_torControl, SIGNAL(connected()), this, SLOT(connected()));
00127 connect(_torControl, SIGNAL(disconnected()), this, SLOT(disconnected()));
00128 connect(_torControl, SIGNAL(connectFailed(QString)),
00129 this, SLOT(connectFailed(QString)));
00130 connect(_torControl, SIGNAL(authenticated()), this, SLOT(authenticated()));
00131 connect(_torControl, SIGNAL(authenticationFailed(QString)),
00132 this, SLOT(authenticationFailed(QString)));
00133 _torControl->setEvent(TorEvents::ClientStatus, this, true);
00134 _torControl->setEvent(TorEvents::GeneralStatus, this, true);
00135
00136
00137 _browserProcess = new HelperProcess(this);
00138 connect(_browserProcess, SIGNAL(finished(int, QProcess::ExitStatus)),
00139 this, SLOT(onSubprocessFinished(int, QProcess::ExitStatus)));
00140 connect(_browserProcess, SIGNAL(startFailed(QString)),
00141 this, SLOT(onBrowserFailed(QString)));
00142 _browserProcess->setEnvironment(QProcess::systemEnvironment() << "TZ=UTC");
00143
00144
00145 _imProcess = new HelperProcess(this);
00146 connect(_imProcess, SIGNAL(finished(int, QProcess::ExitStatus)),
00147 this, SLOT(onSubprocessFinished(int, QProcess::ExitStatus)));
00148 connect(_imProcess, SIGNAL(startFailed(QString)),
00149 this, SLOT(onIMFailed(QString)));
00150
00151
00152 _proxyProcess = new HelperProcess(this);
00153 connect(_proxyProcess, SIGNAL(startFailed(QString)),
00154 this, SLOT(onProxyFailed(QString)));
00155
00156
00157 connect(vApp, SIGNAL(running()), this, SLOT(running()));
00158 connect(vApp, SIGNAL(shutdown()), this, SLOT(shutdown()));
00159
00160 #if defined(USE_MINIUPNPC)
00161
00162 connect(UPNPControl::instance(), SIGNAL(error(UPNPControl::UPNPError)),
00163 this, SLOT(upnpError(UPNPControl::UPNPError)));
00164 #endif
00165
00166 if (TrayIcon::isTrayIconSupported()) {
00167
00168 _trayIcon.show();
00169
00170 ui.chkShowOnStartup->setChecked(settings.showMainWindowAtStart());
00171 if (ui.chkShowOnStartup->isChecked())
00172 show();
00173 } else {
00174
00175 ui.chkShowOnStartup->hide();
00176 ui.btnHide->hide();
00177 setMinimumHeight(height()-ui.btnHide->height());
00178 setMaximumHeight(height()-ui.btnHide->height());
00179 show();
00180 }
00181 }
00182
00183
00184 MainWindow::~MainWindow()
00185 {
00186 _trayIcon.hide();
00187 delete _messageLog;
00188 delete _bandwidthGraph;
00189 delete _netViewer;
00190 delete _configDialog;
00191 }
00192
00193
00194 void
00195 MainWindow::customEvent(QEvent *event)
00196 {
00197 if (event->type() == CustomEventType::ClientStatusEvent) {
00198 ClientStatusEvent *cse = dynamic_cast<ClientStatusEvent *>(event);
00199 if (!cse)
00200 return;
00201
00202 if (cse->status() == ClientStatusEvent::CircuitEstablished) {
00203 circuitEstablished();
00204 cse->accept();
00205 } else if (cse->status() == ClientStatusEvent::Bootstrap) {
00206 BootstrapStatusEvent *bse = dynamic_cast<BootstrapStatusEvent *>(cse);
00207 if (bse)
00208 bootstrapStatusChanged(bse->status());
00209 cse->accept();
00210 }
00211 } else if (event->type() == CustomEventType::GeneralStatusEvent) {
00212 GeneralStatusEvent *gse = dynamic_cast<GeneralStatusEvent *>(event);
00213 if (!gse)
00214 return;
00215
00216 if (gse->status() == GeneralStatusEvent::DangerousTorVersion) {
00217 DangerousVersionEvent *dve = dynamic_cast<DangerousVersionEvent *>(gse);
00218 if (dve && (dve->reason() == DangerousVersionEvent::ObsoleteVersion
00219 || dve->reason() == DangerousVersionEvent::UnrecommendedVersion)) {
00220 dangerousTorVersion();
00221 }
00222 gse->accept();
00223 }
00224 }
00225 }
00226
00227
00228
00229 void
00230 MainWindow::running()
00231 {
00232 VidaliaSettings settings;
00233 if (_torControl->isRunning()) {
00234
00235
00236 updateTorStatus(Starting);
00237
00238 started();
00239 } else if (settings.runTorAtStart()) {
00240
00241 start();
00242 }
00243
00244
00245 if (settings.runProxyAtStart())
00246 startProxy();
00247 }
00248
00249
00250
00251 void
00252 MainWindow::shutdown()
00253 {
00254 if (_torControl->isVidaliaRunningTor()) {
00255
00256 _torControl->stop();
00257 }
00258
00259
00260 ServerSettings settings(_torControl);
00261 settings.cleanupPortForwarding();
00262
00263 if (_proxyProcess->state() != QProcess::NotRunning) {
00264
00265
00266 _proxyProcess->kill();
00267 }
00268
00269
00270 QObject::disconnect(_torControl, 0, 0, 0);
00271
00272
00273 QCoreApplication::quit();
00274 }
00275
00276
00277
00278
00279 void
00280 MainWindow::close()
00281 {
00282 if (_torControl->isVidaliaRunningTor()) {
00283
00284
00285
00286 ServerSettings settings(_torControl);
00287 if (_torControl->isConnected() && settings.isServerEnabled()) {
00288 connect(_torControl, SIGNAL(stopped()), this, SLOT(shutdown()));
00289 if (!stop())
00290 QObject::disconnect(_torControl, SIGNAL(stopped()), this, SLOT(shutdown()));
00291 return;
00292 }
00293 }
00294
00295 shutdown();
00296 }
00297
00298
00299
00300 void
00301 MainWindow::createActions()
00302 {
00303 _startStopAct = new QAction(QIcon(IMG_START_TOR_16), tr("Start Tor"), this);
00304 connect(_startStopAct, SIGNAL(triggered()), this, SLOT(start()));
00305
00306 _exitAct = new QAction(QIcon(IMG_EXIT), tr("Exit"), this);
00307 connect(_exitAct, SIGNAL(triggered()), this, SLOT(close()));
00308
00309 _bandwidthAct = new QAction(QIcon(IMG_BWGRAPH), tr("Bandwidth Graph"), this);
00310 connect(_bandwidthAct, SIGNAL(triggered()),
00311 _bandwidthGraph, SLOT(showWindow()));
00312 connect(ui.lblBandwidthGraph, SIGNAL(clicked()),
00313 _bandwidthGraph, SLOT(showWindow()));
00314
00315 _messageAct = new QAction(QIcon(IMG_MESSAGELOG), tr("Message Log"), this);
00316 connect(_messageAct, SIGNAL(triggered()),
00317 _messageLog, SLOT(showWindow()));
00318 connect(ui.lblMessageLog, SIGNAL(clicked()),
00319 _messageLog, SLOT(showWindow()));
00320
00321 _networkAct = new QAction(QIcon(IMG_NETWORK), tr("Network Map"), this);
00322 connect(_networkAct, SIGNAL(triggered()),
00323 _netViewer, SLOT(showWindow()));
00324 connect(ui.lblViewNetwork, SIGNAL(clicked()),
00325 _netViewer, SLOT(showWindow()));
00326
00327 _controlPanelAct = new QAction(QIcon(IMG_CONTROL_PANEL),
00328 tr("Control Panel"), this);
00329 connect(_controlPanelAct, SIGNAL(triggered()), this, SLOT(show()));
00330
00331 _configAct = new QAction(QIcon(IMG_CONFIG), tr("Settings"), this);
00332 connect(_configAct, SIGNAL(triggered()), this, SLOT(showConfigDialog()));
00333
00334 _aboutAct = new QAction(QIcon(IMG_ABOUT), tr("About"), this);
00335 connect(_aboutAct, SIGNAL(triggered()), this, SLOT(showAboutDialog()));
00336
00337 _helpAct = new QAction(QIcon(IMG_HELP), tr("Help"), this);
00338 connect(_helpAct, SIGNAL(triggered()), this, SLOT(showHelpDialog()));
00339 connect(ui.lblHelpBrowser, SIGNAL(clicked()), this, SLOT(showHelpDialog()));
00340
00341 _newIdentityAct = new QAction(QIcon(IMG_IDENTITY), tr("New Identity"), this);
00342 _newIdentityAct->setEnabled(false);
00343 connect(_newIdentityAct, SIGNAL(triggered()), this, SLOT(newIdentity()));
00344 }
00345
00346
00347
00348 void
00349 MainWindow::createTrayIcon()
00350 {
00351
00352 createMenuBar();
00353
00354 _trayIcon.setContextMenu(createTrayMenu());
00355 connect(&_trayIcon, SIGNAL(doubleClicked()), this, SLOT(show()));
00356 }
00357
00358
00359
00360 QMenu*
00361 MainWindow::createTrayMenu()
00362 {
00363 QMenu *menu = new QMenu(this);
00364 menu->addAction(_startStopAct);
00365 menu->addSeparator();
00366 menu->addAction(_bandwidthAct);
00367 menu->addAction(_messageAct);
00368 menu->addAction(_networkAct);
00369 menu->addAction(_newIdentityAct);
00370 menu->addSeparator();
00371 menu->addAction(_controlPanelAct);
00372
00373 #if !defined(Q_WS_MAC)
00374
00375
00376 menu->addAction(_configAct);
00377 menu->addAction(_helpAct);
00378 menu->addAction(_aboutAct);
00379 menu->addSeparator();
00380 menu->addAction(_exitAct);
00381 #endif
00382 return menu;
00383 }
00384
00385
00386
00387
00388 void
00389 MainWindow::createMenuBar()
00390 {
00391 #if defined(Q_WS_MAC)
00392
00393
00394
00395 _startStopAct->setShortcut(tr("Ctrl+T"));
00396 _bandwidthAct->setShortcut(tr("Ctrl+B"));
00397 _messageAct->setShortcut(tr("Ctrl+L"));
00398 _networkAct->setShortcut(tr("Ctrl+N"));
00399 _helpAct->setShortcut(tr("Ctrl+?"));
00400 _newIdentityAct->setShortcut(tr("Ctrl+I"));
00401 _controlPanelAct->setShortcut(tr("Ctrl+P"));
00402
00403
00404
00405 _exitAct->setText("exit");
00406 _configAct->setText("config");
00407 _aboutAct->setText("about");
00408
00409
00410
00411 QMenuBar *menuBar = new QMenuBar();
00412 QMenu *fileMenu = menuBar->addMenu(tr("File"));
00413 fileMenu->addAction(_exitAct);
00414
00415 QMenu *torMenu = menuBar->addMenu(tr("Tor"));
00416 torMenu->addAction(_startStopAct);
00417 torMenu->addSeparator();
00418 torMenu->addAction(_newIdentityAct);
00419
00420 QMenu *viewMenu = menuBar->addMenu(tr("View"));
00421 viewMenu->addAction(_controlPanelAct);
00422 viewMenu->addSeparator();
00423 viewMenu->addAction(_bandwidthAct);
00424 viewMenu->addAction(_messageAct);
00425 viewMenu->addAction(_networkAct);
00426 viewMenu->addAction(_configAct);
00427
00428 QMenu *helpMenu = menuBar->addMenu(tr("Help"));
00429 _helpAct->setText(tr("Vidalia Help"));
00430 helpMenu->addAction(_helpAct);
00431 helpMenu->addAction(_aboutAct);
00432
00433 setMenuBar(menuBar);
00434 #endif
00435 }
00436
00437
00438 void
00439 MainWindow::startSubprocesses()
00440 {
00441 VidaliaSettings settings;
00442 QString executable = settings.getBrowserExecutable();
00443
00444 if (!executable.isEmpty())
00445 _browserProcess->start(executable, QStringList());
00446
00447 executable = settings.getIMExecutable();
00448
00449 if (!executable.isEmpty())
00450 _imProcess->start(executable, QStringList());
00451
00452 }
00453
00454
00455 void
00456 MainWindow::onSubprocessFinished(int exitCode, QProcess::ExitStatus exitStatus)
00457 {
00458 Q_UNUSED(exitCode)
00459 Q_UNUSED(exitStatus)
00460
00461
00462 VidaliaSettings settings;
00463 QString browserExecutable = settings.getBrowserExecutable();
00464 QString imExecutable = settings.getIMExecutable();
00465
00466
00467 bool browserDone = browserExecutable.isEmpty() || _browserProcess->isDone();
00468 bool imDone = imExecutable.isEmpty() || _imProcess->isDone();
00469
00470
00471 if (browserDone && imDone)
00472 shutdown();
00473 }
00474
00475
00476
00477 void
00478 MainWindow::onBrowserFailed(QString errmsg)
00479 {
00480 Q_UNUSED(errmsg);
00481
00482
00483 VMessageBox::warning(this, tr("Error starting web browser"),
00484 tr("Vidalia was unable to start the configured web browser"),
00485 VMessageBox::Ok|VMessageBox::Default|VMessageBox::Escape);
00486 }
00487
00488
00489
00490 void
00491 MainWindow::onIMFailed(QString errmsg)
00492 {
00493 Q_UNUSED(errmsg);
00494
00495
00496 VMessageBox::warning(this, tr("Error starting IM client"),
00497 tr("Vidalia was unable to start the configured IM client"),
00498 VMessageBox::Ok|VMessageBox::Default|VMessageBox::Escape);
00499 }
00500
00501
00502 void
00503 MainWindow::startProxy()
00504 {
00505 VidaliaSettings settings;
00506 QString executable = settings.getProxyExecutable();
00507 _proxyProcess->start(executable, settings.getProxyExecutableArguments());
00508 }
00509
00510
00511
00512 void
00513 MainWindow::onProxyFailed(QString errmsg)
00514 {
00515 Q_UNUSED(errmsg);
00516
00517
00518 VMessageBox::warning(this, tr("Error starting proxy server"),
00519 tr("Vidalia was unable to start the configured proxy server"),
00520 VMessageBox::Ok|VMessageBox::Default|VMessageBox::Escape);
00521 }
00522
00523
00524
00525 void
00526 MainWindow::bootstrapStatusChanged(const BootstrapStatus &bs)
00527 {
00528 int percentComplete = STARTUP_PROGRESS_BOOTSTRAPPING + bs.percentComplete();
00529 bool warn = (bs.severity() == tc::SeverityWarn &&
00530 bs.recommendedAction() != BootstrapStatus::RecommendIgnore);
00531
00532 QString description;
00533 switch (bs.status()) {
00534 case BootstrapStatus::ConnectingToDirMirror:
00535 description = tr("Connecting to a relay directory");
00536 break;
00537 case BootstrapStatus::HandshakingWithDirMirror:
00538 case BootstrapStatus::CreatingOneHopCircuit:
00539 description = tr("Establishing an encrypted directory connection");
00540 break;
00541 case BootstrapStatus::RequestingNetworkStatus:
00542 description = tr("Retrieving network status");
00543 break;
00544 case BootstrapStatus::LoadingNetworkStatus:
00545 description = tr("Loading network status");
00546 break;
00547 case BootstrapStatus::LoadingAuthorityCertificates:
00548 description = tr("Loading authority certificates");
00549 break;
00550 case BootstrapStatus::RequestingDescriptors:
00551 description = tr("Requesting relay information");
00552 break;
00553 case BootstrapStatus::LoadingDescriptors:
00554 description = tr("Loading relay information");
00555 break;
00556 case BootstrapStatus::ConnectingToEntryGuard:
00557 description = tr("Connecting to the Tor network");
00558 break;
00559 case BootstrapStatus::HandshakingWithEntryGuard:
00560 case BootstrapStatus::EstablishingCircuit:
00561 description = tr("Establishing a Tor circuit");
00562 break;
00563 case BootstrapStatus::BootstrappingDone:
00564 description = tr("Connected to the Tor network!");
00565 warn = false;
00566 break;
00567 default:
00568 description = tr("Unrecognized startup status");
00569 }
00570 if (warn) {
00571 QString reason;
00572
00573 switch (bs.reason()) {
00574 case tc::MiscellaneousReason:
00575 reason = tr("miscellaneous");
00576 break;
00577 case tc::IdentityMismatch:
00578 reason = tr("identity mismatch");
00579 break;
00580 case tc::ConnectionDone:
00581 reason = tr("done");
00582 break;
00583 case tc::ConnectionRefused:
00584 reason = tr("connection refused");
00585 break;
00586 case tc::ConnectionTimeout:
00587 reason = tr("connection timeout");
00588 break;
00589 case tc::ConnectionIoError:
00590 reason = tr("read/write error");
00591 break;
00592 case tc::NoRouteToHost:
00593 reason = tr("no route to host");
00594 break;
00595 case tc::ResourceLimitReached:
00596 reason = tr("insufficient resources");
00597 break;
00598 default:
00599 reason = tr("unknown");
00600 }
00601 description += tr(" failed (%1)").arg(reason);
00602 }
00603 setStartupProgress(percentComplete, description);
00604 }
00605
00606
00607
00608 MainWindow::TorStatus
00609 MainWindow::updateTorStatus(TorStatus status)
00610 {
00611 QString statusText, actionText;
00612 QString trayIconFile, statusIconFile;
00613 TorStatus prevStatus = _status;
00614
00615 vNotice("Tor status changed from '%1' to '%2'.")
00616 .arg(toString(prevStatus)).arg(toString(status));
00617 _status = status;
00618
00619 if (status == Stopped) {
00620 statusText = tr("Tor is not running");
00621 actionText = tr("Start Tor");
00622 trayIconFile = IMG_TOR_STOPPED;
00623 statusIconFile = IMG_TOR_STOPPED_48;
00624 _startStopAct->setEnabled(true);
00625 _startStopAct->setText(actionText);
00626 _startStopAct->setIcon(QIcon(IMG_START_TOR_16));
00627 ui.lblStartStopTor->setEnabled(true);
00628 ui.lblStartStopTor->setText(actionText);
00629 ui.lblStartStopTor->setPixmap(QPixmap(IMG_START_TOR_48));
00630 ui.lblStartStopTor->setStatusTip(actionText);
00631
00632
00633
00634 QObject::disconnect(_startStopAct, SIGNAL(triggered()), this, 0);
00635 QObject::disconnect(ui.lblStartStopTor, SIGNAL(clicked()), this, 0);
00636 connect(_startStopAct, SIGNAL(triggered()), this, SLOT(start()));
00637 connect(ui.lblStartStopTor, SIGNAL(clicked()), this, SLOT(start()));
00638 setStartupProgressVisible(false);
00639 } else if (status == Stopping) {
00640 if (_delayedShutdownStarted) {
00641 statusText = tr("Your relay is shutting down.\n"
00642 "Click 'Stop' again to stop your relay now.");
00643 } else {
00644 statusText = tr("Tor is shutting down");
00645 }
00646 trayIconFile = IMG_TOR_STOPPING;
00647 statusIconFile = IMG_TOR_STOPPING_48;
00648
00649 ui.lblStartStopTor->setStatusTip(tr("Stop Tor Now"));
00650 } else if (status == Started) {
00651 actionText = tr("Stop Tor");
00652 _startStopAct->setEnabled(true);
00653 _startStopAct->setText(actionText);
00654 _startStopAct->setIcon(QIcon(IMG_STOP_TOR_16));
00655 ui.lblStartStopTor->setEnabled(true);
00656 ui.lblStartStopTor->setText(actionText);
00657 ui.lblStartStopTor->setPixmap(QPixmap(IMG_STOP_TOR_48));
00658 ui.lblStartStopTor->setStatusTip(actionText);
00659
00660
00661
00662 QObject::disconnect(_startStopAct, SIGNAL(triggered()), this, 0);
00663 QObject::disconnect(ui.lblStartStopTor, SIGNAL(clicked()), this, 0);
00664 connect(_startStopAct, SIGNAL(triggered()), this, SLOT(stop()));
00665 connect(ui.lblStartStopTor, SIGNAL(clicked()), this, SLOT(stop()));
00666 } else if (status == Starting) {
00667 statusText = tr("Starting the Tor software");
00668 trayIconFile = IMG_TOR_STARTING;
00669 statusIconFile = IMG_TOR_STARTING_48;
00670 _startStopAct->setEnabled(false);
00671 ui.lblStartStopTor->setText(tr("Starting Tor"));
00672 ui.lblStartStopTor->setEnabled(false);
00673 ui.lblStartStopTor->setStatusTip(statusText);
00674 setStartupProgressVisible(true);
00675 setStartupProgress(STARTUP_PROGRESS_STARTING, statusText);
00676
00677 } else if (status == CircuitEstablished) {
00678 statusText = tr("Tor is Running");
00679 trayIconFile = IMG_TOR_RUNNING;
00680 statusIconFile = IMG_TOR_RUNNING_48;
00681 QTimer::singleShot(3000, this, SLOT(hideStartupProgress()));
00682 }
00683
00684
00685 if (!trayIconFile.isEmpty()) {
00686 _trayIcon.setIcon(trayIconFile);
00687 }
00688
00689 if (!statusIconFile.isEmpty())
00690 ui.lblTorStatusImg->setPixmap(QPixmap(statusIconFile));
00691 if (!statusText.isEmpty()) {
00692 _trayIcon.setToolTip(statusText);
00693 ui.lblTorStatus->setText(statusText);
00694 }
00695 return prevStatus;
00696 }
00697
00698
00699 void
00700 MainWindow::toggleShowOnStartup(bool checked)
00701 {
00702 VidaliaSettings settings;
00703 settings.setShowMainWindowAtStart(checked);
00704 }
00705
00706
00707
00708 void
00709 MainWindow::setStartupProgressVisible(bool visible)
00710 {
00711
00712
00713 if (visible) {
00714 ui.lblTorStatus->setVisible(false);
00715 ui.lblTorStatusImg->setVisible(false);
00716 repaint(ui.grpStatus->rect());
00717 ui.lblStartupProgress->setVisible(true);
00718 ui.progressBar->setVisible(true);
00719 } else {
00720 ui.lblStartupProgress->setVisible(false);
00721 ui.progressBar->setVisible(false);
00722 repaint(ui.grpStatus->rect());
00723 ui.lblTorStatus->setVisible(true);
00724 ui.lblTorStatusImg->setVisible(true);
00725 }
00726 }
00727
00728
00729 void
00730 MainWindow::hideStartupProgress()
00731 {
00732 setStartupProgressVisible(false);
00733 }
00734
00735
00736
00737 void
00738 MainWindow::setStartupProgress(int progressValue,
00739 const QString &description)
00740 {
00741 ui.progressBar->setValue(progressValue);
00742 ui.lblStartupProgress->setText(description);
00743 _trayIcon.setToolTip(description);
00744 }
00745
00746
00747
00748 void
00749 MainWindow::start()
00750 {
00751 TorSettings settings;
00752 QStringList args;
00753
00754 updateTorStatus(Starting);
00755
00756
00757 if (net_test_connect(settings.getControlAddress(),
00758 settings.getControlPort())) {
00759 _controlPassword = settings.getControlPassword();
00760 started();
00761 return;
00762 }
00763
00764
00765 QString torrc = settings.getTorrc();
00766 if (!torrc.isEmpty()) {
00767 if (!QFileInfo(torrc).exists())
00768 touch_file(torrc, true);
00769 args << "-f" << torrc;
00770 }
00771
00772
00773 QString dataDirectory = settings.getDataDirectory();
00774 if (!dataDirectory.isEmpty())
00775 args << "DataDirectory" << expand_filename(dataDirectory);
00776
00777
00778 quint16 controlPort = settings.getControlPort();
00779 if (controlPort)
00780 args << "ControlPort" << QString::number(controlPort);
00781
00782
00783 switch (settings.getAuthenticationMethod()) {
00784 case TorSettings::PasswordAuth:
00785 if (settings.useRandomPassword())
00786 _controlPassword = TorSettings::randomPassword();
00787 else
00788 _controlPassword = settings.getControlPassword();
00789 args << "HashedControlPassword"
00790 << TorSettings::hashPassword(_controlPassword)
00791 << "CookieAuthentication" << "0";
00792 break;
00793 case TorSettings::CookieAuth:
00794 args << "CookieAuthentication" << "1"
00795 << "HashedControlPassword" << "";
00796 break;
00797 default:
00798 args << "CookieAuthentication" << "0"
00799 << "HashedControlPassword" << "";
00800 }
00801
00802
00803 QString user = settings.getUser();
00804 if (!user.isEmpty())
00805 args << "User" << user;
00806 QString group = settings.getGroup();
00807 if (!group.isEmpty())
00808 args << "Group" << group;
00809
00810
00811
00812
00813
00814 _isIntentionalExit = true;
00815
00816 _torControl->start(settings.getExecutable(), args);
00817 }
00818
00819
00820
00821 void
00822 MainWindow::startFailed(QString errmsg)
00823 {
00824
00825
00826
00827 Q_UNUSED(errmsg);
00828
00829 updateTorStatus(Stopped);
00830
00831
00832 int response = VMessageBox::warning(this, tr("Error Starting Tor"),
00833 tr("Vidalia was unable to start Tor. Check your settings "
00834 "to ensure the correct name and location of your Tor "
00835 "executable is specified."),
00836 VMessageBox::ShowSettings|VMessageBox::Default,
00837 VMessageBox::Cancel|VMessageBox::Escape,
00838 VMessageBox::Help);
00839
00840 if (response == VMessageBox::ShowSettings) {
00841
00842
00843 showConfigDialog();
00844 } else if (response == VMessageBox::Help) {
00845
00846 showHelpDialog("troubleshooting.start");
00847 }
00848 }
00849
00850
00851
00852 void
00853 MainWindow::started()
00854 {
00855 TorSettings settings;
00856
00857 updateTorStatus(Started);
00858
00859
00860
00861 _isIntentionalExit = false;
00862
00863 _delayedShutdownStarted = false;
00864
00865 _isVidaliaRunningTor = _torControl->isVidaliaRunningTor();
00866
00867 _torControl->connect(settings.getControlAddress(),
00868 settings.getControlPort());
00869 setStartupProgress(STARTUP_PROGRESS_CONNECTING, tr("Connecting to Tor"));
00870 }
00871
00872
00873
00874 void
00875 MainWindow::connectFailed(QString errmsg)
00876 {
00877
00878 int response = VMessageBox::warning(this,
00879 tr("Connection Error"), p(errmsg),
00880 VMessageBox::Ok|VMessageBox::Default|VMessageBox::Escape,
00881 VMessageBox::Retry, VMessageBox::Help);
00882
00883
00884 if (response == VMessageBox::Retry) {
00885
00886 TorSettings settings;
00887 _torControl->connect(settings.getControlAddress(),
00888 settings.getControlPort());
00889 } else {
00890
00891 if (response == VMessageBox::Help)
00892 showHelpDialog("troubleshooting.connect");
00893
00894 _torControl->stop();
00895 }
00896 }
00897
00898
00899 bool
00900 MainWindow::stop()
00901 {
00902 ServerSettings server(_torControl);
00903 QString errmsg;
00904 TorStatus prevStatus;
00905 bool rc;
00906
00907
00908
00909 if (server.isServerEnabled() && !_delayedShutdownStarted) {
00910
00911 int response = VMessageBox::question(this, tr("Relaying is Enabled"),
00912 tr("You are currently running a relay. "
00913 "Terminating your relay will interrupt any "
00914 "open connections from clients.\n\n"
00915 "Would you like to shutdown gracefully and "
00916 "give clients time to find a new relay?"),
00917 VMessageBox::Yes|VMessageBox::Default,
00918 VMessageBox::No,
00919 VMessageBox::Cancel|VMessageBox::Escape);
00920 if (response == VMessageBox::Yes)
00921 _delayedShutdownStarted = true;
00922 else if (response == VMessageBox::Cancel)
00923 return false;
00924 }
00925
00926 prevStatus = updateTorStatus(Stopping);
00927 if (_delayedShutdownStarted) {
00928
00929 rc = _torControl->signal(TorSignal::Shutdown, &errmsg);
00930 } else {
00931
00932 _isIntentionalExit = true;
00933 rc = _torControl->stop(&errmsg);
00934 }
00935
00936 if (!rc) {
00937
00938 int response = VMessageBox::warning(this, tr("Error Shutting Down"),
00939 p(tr("Vidalia was unable to stop the Tor software."))
00940 + p(errmsg),
00941 VMessageBox::Ok|VMessageBox::Default|VMessageBox::Escape,
00942 VMessageBox::Help);
00943
00944 if (response == VMessageBox::Help) {
00945
00946 showHelpDialog("troubleshooting.stop");
00947 }
00948
00949 _isIntentionalExit = false;
00950 _delayedShutdownStarted = false;
00951 updateTorStatus(prevStatus);
00952 }
00953 return rc;
00954 }
00955
00956
00957
00958 void
00959 MainWindow::stopped(int exitCode, QProcess::ExitStatus exitStatus)
00960 {
00961 updateTorStatus(Stopped);
00962
00963
00964
00965 if (!_isIntentionalExit) {
00966
00967
00968
00969 if (exitStatus == QProcess::CrashExit || exitCode != 0) {
00970 int ret = VMessageBox::warning(this, tr("Unexpected Error"),
00971 tr("Vidalia detected that the Tor software exited "
00972 "unexpectedly.\n\n"
00973 "Please check the message log for recent "
00974 "warning or error messages."),
00975 VMessageBox::Ok|VMessageBox::Escape,
00976 VMessageBox::ShowLog|VMessageBox::Default,
00977 VMessageBox::Help);
00978 if (ret == VMessageBox::ShowLog)
00979 _messageLog->showWindow();
00980 else if (ret == VMessageBox::Help)
00981 showHelpDialog("troubleshooting.torexited");
00982 }
00983 }
00984 }
00985
00986
00987 void
00988 MainWindow::connected()
00989 {
00990 authenticate();
00991 }
00992
00993
00994 void
00995 MainWindow::disconnect()
00996 {
00997 _torControl->disconnect();
00998 }
00999
01000
01001 void
01002 MainWindow::disconnected()
01003 {
01004 if (!_isVidaliaRunningTor) {
01005
01006
01007 updateTorStatus(Stopped);
01008 }
01009
01010
01011 _newIdentityAct->setEnabled(false);
01012 ui.lblNewIdentity->setEnabled(false);
01013 _isVidaliaRunningTor = false;
01014 }
01015
01016
01017
01018
01019 bool
01020 MainWindow::authenticate()
01021 {
01022 TorSettings::AuthenticationMethod authMethod;
01023 TorSettings settings;
01024 ProtocolInfo pi;
01025
01026 updateTorStatus(Authenticating);
01027 setStartupProgress(STARTUP_PROGRESS_AUTHENTICATING,
01028 tr("Authenticating to Tor"));
01029
01030 authMethod = settings.getAuthenticationMethod();
01031 pi = _torControl->protocolInfo();
01032 if (!pi.isEmpty()) {
01033 QStringList authMethods = pi.authMethods();
01034 if (authMethods.contains("COOKIE"))
01035 authMethod = TorSettings::CookieAuth;
01036 else if (authMethods.contains("HASHEDPASSWORD"))
01037 authMethod = TorSettings::PasswordAuth;
01038 else if (authMethods.contains("NULL"))
01039 authMethod = TorSettings::NullAuth;
01040 }
01041
01042 if (authMethod == TorSettings::CookieAuth) {
01043
01044 QByteArray cookie = loadControlCookie(pi.cookieAuthFile());
01045 while (cookie.isEmpty()) {
01046
01047 int ret = VMessageBox::question(this,
01048 tr("Cookie Authentication Required"),
01049 p(tr("The Tor software requires Vidalia to send the "
01050 "contents of an authentication cookie, but Vidalia "
01051 "was unable to find one."))
01052 + p(tr("Would you like to browse for the file "
01053 "'control_auth_cookie' yourself?")),
01054 VMessageBox::Browse|VMessageBox::Default,
01055 VMessageBox::Cancel|VMessageBox::Escape);
01056
01057 if (ret == VMessageBox::Cancel)
01058 goto cancel;
01059 QString cookieDir = QFileDialog::getOpenFileName(this,
01060 tr("Data Directory"),
01061 settings.getDataDirectory(),
01062 tr("Control Cookie (control_auth_cookie)"));
01063 if (cookieDir.isEmpty())
01064 goto cancel;
01065 cookieDir = QFileInfo(cookieDir).absolutePath();
01066 cookie = loadControlCookie(cookieDir);
01067 }
01068 vNotice("Authenticating using 'cookie' authentication.");
01069 return _torControl->authenticate(cookie);
01070 } else if (authMethod == TorSettings::PasswordAuth) {
01071
01072 vNotice("Authenticating using 'hashed password' authentication.");
01073 return _torControl->authenticate(_controlPassword);
01074 }
01075
01076 vNotice("Authenticating using 'null' authentication.");
01077 return _torControl->authenticate(QString(""));
01078
01079 cancel:
01080 vWarn("Cancelling control authentication attempt.");
01081 if (_isVidaliaRunningTor)
01082 stop();
01083 else
01084 disconnect();
01085 return false;
01086 }
01087
01088
01089 void
01090 MainWindow::authenticated()
01091 {
01092 ServerSettings serverSettings(_torControl);
01093 QString errmsg;
01094
01095 updateTorStatus(Authenticated);
01096
01097
01098
01099 if (_torControl->getTorVersion() < 0x020101) {
01100 setStartupProgress(STARTUP_PROGRESS_CIRCUITBUILD,
01101 tr("Connecting to the Tor network"));
01102 }
01103
01104
01105 _newIdentityAct->setEnabled(true);
01106 ui.lblNewIdentity->setEnabled(true);
01107
01108
01109 if (!_torControl->setEvents(&errmsg)) {
01110 VMessageBox::warning(this, tr("Error Registering for Events"),
01111 p(tr("Vidalia was unable to register for some events. "
01112 "Many of Vidalia's features may be unavailable."))
01113 + p(errmsg),
01114 VMessageBox::Ok);
01115 }
01116
01117
01118 serverSettings.configurePortForwarding();
01119
01120
01121 if (_torControl->circuitEstablished())
01122 circuitEstablished();
01123
01124 if (_torControl->getTorVersion() >= 0x020001)
01125 checkTorVersion();
01126 if (_torControl->getTorVersion() >= 0x020102) {
01127 BootstrapStatus status = _torControl->bootstrapStatus();
01128 if (status.isValid())
01129 bootstrapStatusChanged(status);
01130 }
01131 }
01132
01133
01134
01135 void
01136 MainWindow::authenticationFailed(QString errmsg)
01137 {
01138 bool retry = false;
01139
01140 vWarn("Authentication failed: %1").arg(errmsg);
01141
01142
01143 if (errmsg.contains("Password did not match")) {
01144
01145 QString password = QInputDialog::getText(this,
01146 tr("Password Authentication Required"),
01147 tr("Please enter your control password (not the hash):"),
01148 QLineEdit::Password);
01149 if (!password.isEmpty()) {
01150
01151
01152 TorSettings settings;
01153 settings.setAuthenticationMethod(TorSettings::PasswordAuth);
01154 settings.setControlPassword(password);
01155 settings.setUseRandomPassword(false);
01156 retry = true;
01157 }
01158 } else {
01159
01160 int ret = VMessageBox::warning(this,
01161 tr("Authentication Error"),
01162 p(tr("Vidalia was unable to authenticate to the Tor software. "
01163 "(%1)").arg(errmsg)) +
01164 p(tr("Please check your control port authentication "
01165 "settings.")),
01166 VMessageBox::ShowSettings|VMessageBox::Default,
01167 VMessageBox::Cancel|VMessageBox::Escape);
01168
01169 if (ret == VMessageBox::ShowSettings)
01170 showConfigDialog(ConfigDialog::Advanced);
01171 }
01172
01173 if (_torControl->isRunning())
01174 if (_isVidaliaRunningTor)
01175 stop();
01176 else
01177 disconnect();
01178 if (retry)
01179 start();
01180 }
01181
01182
01183
01184
01185
01186
01187 QByteArray
01188 MainWindow::loadControlCookie(QString cookiePath)
01189 {
01190 QFile authCookie;
01191 QStringList pathList;
01192
01193 if (!cookiePath.isEmpty()) {
01194 pathList << cookiePath;
01195 } else {
01196
01197 TorSettings settings;
01198 QString dataDir = settings.getDataDirectory();
01199 if (!dataDir.isEmpty())
01200 pathList << dataDir;
01201
01202 #if defined(Q_WS_WIN)
01203 pathList << expand_filename("%APPDATA%\\Tor");
01204 #else
01205 pathList << expand_filename("~/.tor");
01206 #endif
01207 }
01208
01209
01210 foreach (QString path, pathList) {
01211 QString cookieFile = QFileInfo(path).isFile() ?
01212 path : path + "/control_auth_cookie";
01213 vDebug("Checking for authentication cookie in '%1'").arg(cookieFile);
01214 if (!QFileInfo(cookieFile).exists())
01215 continue;
01216
01217 authCookie.setFileName(cookieFile);
01218 if (authCookie.open(QIODevice::ReadOnly)) {
01219 vInfo("Reading authentication cookie from '%1'").arg(cookieFile);
01220 return authCookie.readAll();
01221 } else {
01222 vWarn("Couldn't open cookie file '%1': %2")
01223 .arg(cookieFile).arg(authCookie.errorString());
01224 }
01225 }
01226 vWarn("Couldn't find a readable authentication cookie.");
01227 return QByteArray();
01228 }
01229
01230
01231 void
01232 MainWindow::circuitEstablished()
01233 {
01234 updateTorStatus(CircuitEstablished);
01235 setStartupProgress(ui.progressBar->maximum(),
01236 tr("Connected to the Tor network!"));
01237 startSubprocesses();
01238 }
01239
01240
01241
01242 void
01243 MainWindow::checkTorVersion()
01244 {
01245 QString status;
01246 if (_torControl->getInfo("status/version/current", status)) {
01247 if (!status.compare("old", Qt::CaseInsensitive)
01248 || !status.compare("unrecommended", Qt::CaseInsensitive)
01249 || !status.compare("obsolete", Qt::CaseInsensitive)) {
01250 dangerousTorVersion();
01251 }
01252 }
01253 }
01254
01255
01256
01257 void
01258 MainWindow::dangerousTorVersion()
01259 {
01260 static bool alreadyWarned = false;
01261
01262 if (!alreadyWarned) {
01263 QString website = "https://www.torproject.org/";
01264 #if QT_VERSION >= 0x040200
01265 website = QString("<a href=\"%1\">%1</a>").arg(website);
01266 #endif
01267
01268 VMessageBox::information(this,
01269 tr("Tor Update Available"),
01270 p(tr("The currently installed version of Tor is out of date or no longer "
01271 "recommended. Please visit the Tor website to download the latest "
01272 "version.")) + p(tr("Tor website: %1").arg(website)),
01273 VMessageBox::Ok);
01274 alreadyWarned = true;
01275 }
01276 }
01277
01278
01279 void
01280 MainWindow::showAboutDialog()
01281 {
01282 static AboutDialog *aboutDialog = 0;
01283 if (!aboutDialog)
01284 aboutDialog = new AboutDialog(this);
01285 aboutDialog->showWindow();
01286 }
01287
01288
01289
01290 void
01291 MainWindow::showHelpDialog()
01292 {
01293 showHelpDialog(QString());
01294 }
01295
01296
01297 void
01298 MainWindow::showHelpDialog(const QString &topic)
01299 {
01300 static HelpBrowser *helpBrowser = 0;
01301 if (!helpBrowser)
01302 helpBrowser = new HelpBrowser(this);
01303 helpBrowser->showWindow(topic);
01304 }
01305
01306
01307
01308 void
01309 MainWindow::showConfigDialog(ConfigDialog::Page page)
01310 {
01311 _configDialog->showWindow(page);
01312 }
01313
01314
01315 void
01316 MainWindow::showServerConfigDialog()
01317 {
01318 showConfigDialog(ConfigDialog::Server);
01319 }
01320
01321
01322 void
01323 MainWindow::newIdentity()
01324 {
01325 QString errmsg;
01326
01327
01328
01329
01330 if (_torControl->signal(TorSignal::NewNym, &errmsg)) {
01331
01332 QString title = tr("New Identity");
01333 QString message = tr("All subsequent connections will "
01334 "appear to be different than your "
01335 "old connections.");
01336
01337
01338 _newIdentityAct->setEnabled(false);
01339 ui.lblNewIdentity->setEnabled(false);
01340 QTimer::singleShot(MIN_NEWIDENTITY_INTERVAL,
01341 this, SLOT(enableNewIdentity()));
01342
01343 if (TrayIcon::supportsBalloonMessages())
01344 _trayIcon.showBalloonMessage(title, message, TrayIcon::Information);
01345 else
01346 VMessageBox::information(this, title, message, VMessageBox::Ok);
01347 } else {
01348
01349 VMessageBox::warning(this,
01350 tr("Failed to Create New Identity"), errmsg, VMessageBox::Ok);
01351 }
01352 }
01353
01354
01355
01356 void
01357 MainWindow::enableNewIdentity()
01358 {
01359 if (_torControl->isConnected()) {
01360 _newIdentityAct->setEnabled(true);
01361 ui.lblNewIdentity->setEnabled(true);
01362 }
01363 }
01364
01365
01366 QString
01367 MainWindow::toString(TorStatus status)
01368 {
01369 switch (status) {
01370
01371
01372 case Unset: return "Unset";
01373 case Stopping: return "Stopping";
01374 case Stopped: return "Stopped";
01375 case Starting: return "Starting";
01376 case Started: return "Started";
01377 case Authenticating: return "Authenticating";
01378 case Authenticated: return "Authenticated";
01379 case CircuitEstablished: return "Circuit Established";
01380 default: break;
01381 }
01382 return "Unknown";
01383 }
01384
01385 #if defined(USE_MINIUPNPC)
01386
01387 void
01388 MainWindow::upnpError(UPNPControl::UPNPError error)
01389 {
01390 Q_UNUSED(error);
01391
01392 #if 0
01393
01394
01395
01396
01397
01398 VMessageBox::warning(this,
01399 tr("Port Forwarding Failed"),
01400 p(tr("Vidalia was unable to configure automatic port forwarding."))
01401 + p(UPNPControl::Instance()->errorString()),
01402 VMessageBox::Ok);
01403 #endif
01404 }
01405 #endif
01406