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