mainwindow.cpp

Go to the documentation of this file.
00001 /*
00002 **  This file is part of Vidalia, and is subject to the license terms in the
00003 **  LICENSE file, found in the top level directory of this distribution. If you
00004 **  did not receive the LICENSE file with this file, you may obtain it from the
00005 **  Vidalia source package distributed by the Vidalia Project at
00006 **  http://www.vidalia-project.net/. No part of Vidalia, including this file,
00007 **  may be copied, modified, propagated, or distributed except according to the
00008 **  terms described in the LICENSE file.
00009 */
00010 
00011 /*
00012 ** \file mainwindow.cpp
00013 ** \version $Id: mainwindow.cpp 2916 2008-07-25 12:40:23Z edmanm $
00014 ** \brief Main (hidden) window. Creates tray menu and child windows
00015 **
00016 ** Implements the main window. The main window is a hidden window that serves
00017 ** as the parent of the tray icon and popup menu, as well as other application
00018 ** dialogs.
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 //#define //ANIM_PROCESS_WORKING  ":/images/32x32/process-working.png"
00054 
00055 /* Decide which of our four sets of tray icons to use. */
00056 #if defined(Q_WS_WIN)
00057 /* QSystemTrayIcon on Windows wants 16x16 .png files */
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 /* On Mac, we always go straight to Carbon to load our dock images 
00064  * from .icns files */
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 /* On X11, we just use always the 22x22 .png files */
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 /** Only allow 'New Identity' to be clicked once every 10 seconds. */
00078 #define MIN_NEWIDENTITY_INTERVAL   (10*1000)
00079 
00080 /* Startup progress milestones */
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 /** Default constructor. It installs an icon in the system tray area and
00090  * creates the popup menu associated with that icon. */
00091 MainWindow::MainWindow()
00092 : VidaliaWindow("MainWindow")
00093 {
00094   VidaliaSettings settings;
00095 
00096   ui.setupUi(this);
00097 
00098   /* Create all the dialogs of which we only want one instance */
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   /* Create the actions that will go in the tray menu */
00111   createActions();
00112   /* Creates a tray icon with a context menu and adds it to the system's
00113    * notification area. */
00114   createTrayIcon();
00115   /* Start with Tor initially stopped */
00116   _status = Unset;
00117   updateTorStatus(Stopped);
00118   
00119   /* Create a new TorControl object, used to communicate with Tor */
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   /* Create a new HelperProcess object, used to start the web browser */
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   /* Create a new HelperProcess object, used to start the web browser */
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   /* Create a new HelperProcess object, used to start the proxy server */
00152   _proxyProcess = new HelperProcess(this);
00153   connect(_proxyProcess, SIGNAL(startFailed(QString)),
00154            this, SLOT(onProxyFailed(QString)));
00155 
00156   /* Catch signals when the application is running or shutting down */
00157   connect(vApp, SIGNAL(running()), this, SLOT(running()));
00158   connect(vApp, SIGNAL(shutdown()), this, SLOT(shutdown()));
00159 
00160 #if defined(USE_MINIUPNPC)
00161   /* Catch UPnP-related signals */
00162   connect(UPNPControl::instance(), SIGNAL(error(UPNPControl::UPNPError)),
00163          this, SLOT(upnpError(UPNPControl::UPNPError)));
00164 #endif
00165 
00166   if (TrayIcon::isTrayIconSupported()) {
00167     /* Make the tray icon visible */
00168     _trayIcon.show();
00169     /* Check if we are supposed to show our main window on startup */
00170     ui.chkShowOnStartup->setChecked(settings.showMainWindowAtStart());
00171     if (ui.chkShowOnStartup->isChecked())
00172       show();
00173   } else {
00174     /* Don't let people hide the main window, since that's all they have. */
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 /** Destructor. */
00184 MainWindow::~MainWindow()
00185 {
00186   _trayIcon.hide();
00187   delete _messageLog;
00188   delete _bandwidthGraph;
00189   delete _netViewer;
00190   delete _configDialog;
00191 }
00192 
00193 /** Catches and processes Tor client and general status events. */
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 /** Called when the application has started and the main event loop is
00228  * running. */
00229 void
00230 MainWindow::running()
00231 {
00232   VidaliaSettings settings;
00233   if (_torControl->isRunning()) {
00234     /* Tor may be already running, but we still need to connect to it. So,
00235      * update our status now. */ 
00236     updateTorStatus(Starting);
00237     /* Tor was already running */
00238     started();
00239   } else if (settings.runTorAtStart()) {
00240     /* If we're supposed to start Tor when Vidalia starts, then do it now */
00241     start();
00242   }
00243 
00244   /* Start the proxy server, if configured */
00245   if (settings.runProxyAtStart())
00246     startProxy();
00247 }
00248 
00249 /** Terminate the Tor process if it is being run under Vidalia, disconnect all
00250  * TorControl signals, and exit Vidalia. */
00251 void
00252 MainWindow::shutdown()
00253 {
00254   if (_torControl->isVidaliaRunningTor()) {
00255     /* Kill our Tor process now */ 
00256     _torControl->stop();
00257   }
00258 
00259   /* Disable port forwarding */
00260   ServerSettings settings(_torControl);
00261   settings.cleanupPortForwarding();
00262 
00263   if (_proxyProcess->state() != QProcess::NotRunning) {
00264     /* Close the proxy server (Polipo ignores the WM_CLOSE event sent by
00265      * terminate() so we have to kill() it) */
00266     _proxyProcess->kill();
00267   }
00268 
00269   /* Disconnect all of the TorControl object's signals */
00270   QObject::disconnect(_torControl, 0, 0, 0);
00271 
00272   /* And then quit for real */
00273   QCoreApplication::quit();
00274 }
00275 
00276 /** Called when the application is closing, by selecting "Exit" from the tray
00277  * menu. If we're running a Tor server, then ask if we want to kill Tor now,
00278  * or do a delayed shutdown. */
00279 void
00280 MainWindow::close()
00281 {
00282   if (_torControl->isVidaliaRunningTor()) {
00283     /* If we're running a server currently, ask if we want to do a delayed
00284      * shutdown. If we do, then close Vidalia only when Tor stops. Otherwise,
00285      * kill Tor and bail now. */
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   /* Shut down Tor (if necessary) and exit Vidalia */
00295   shutdown();
00296 }
00297 
00298 /** Create and bind actions to events. Setup for initial
00299  * tray menu configuration. */
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 /** Creates a tray icon with a context menu and adds it to the system
00347  * notification area. On Mac, we also set up an application menubar. */
00348 void
00349 MainWindow::createTrayIcon()
00350 {
00351   /* Create the default menu bar (Mac) */
00352   createMenuBar();
00353   /* Create a tray menu and add it to the tray icon */
00354   _trayIcon.setContextMenu(createTrayMenu());
00355   connect(&_trayIcon, SIGNAL(doubleClicked()), this, SLOT(show()));
00356 }
00357 
00358 /** Creates a QMenu object that contains QActions which compose the system 
00359  * tray menu. */
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   /* These aren't added to the dock menu on Mac, since they are in the
00375    * standard Mac locations in the menu bar. */
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 /** Creates a new menubar with no parent, so Qt will use this as the "default
00386  * menubar" on Mac. This adds on to the existing actions from the createMens()
00387  * method. */
00388 void
00389 MainWindow::createMenuBar()
00390 {
00391 #if defined(Q_WS_MAC)
00392   /* Mac users sure like their shortcuts. Actions NOT mentioned below
00393    * don't explicitly need shortcuts, since they are merged to the default
00394    * menubar and get the default shortcuts anyway. */
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   /* Force Qt to put merge the Exit, Configure, and About menubar options into
00404    * the default menu, even if Vidalia is currently not speaking English. */
00405   _exitAct->setText("exit");
00406   _configAct->setText("config");
00407   _aboutAct->setText("about");
00408   
00409   /* The File, Help, and Configure menus will get merged into the application
00410    * menu by Qt. */
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 /** Starts the web browser and IM client, if appropriately configured */
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 /** Called when browser or IM client have exited */
00455 void
00456 MainWindow::onSubprocessFinished(int exitCode, QProcess::ExitStatus exitStatus)
00457 {
00458   Q_UNUSED(exitCode)
00459   Q_UNUSED(exitStatus)
00460 
00461   /* Get path to browser and IM client */
00462   VidaliaSettings settings;
00463   QString browserExecutable = settings.getBrowserExecutable();
00464   QString imExecutable = settings.getIMExecutable();
00465 
00466   /* A subprocess is finished if it successfully exited or was never asked to start */
00467   bool browserDone = browserExecutable.isEmpty() || _browserProcess->isDone();
00468   bool imDone = imExecutable.isEmpty() || _imProcess->isDone();
00469 
00470   /* Exit if both subprocesses are finished */
00471   if (browserDone && imDone)
00472     shutdown();
00473 }
00474 
00475 /** Called when the web browser failed to start, for example, because the path
00476  * specified to the web browser executable didn't lead to an executable. */
00477 void
00478 MainWindow::onBrowserFailed(QString errmsg)
00479 {
00480   Q_UNUSED(errmsg);
00481  
00482   /* Display an error message and see if the user wants some help */
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 /** Called when the IM client failed to start, for example, because the path
00489  * specified to the IM client executable didn't lead to an executable. */
00490 void
00491 MainWindow::onIMFailed(QString errmsg)
00492 {
00493   Q_UNUSED(errmsg);
00494  
00495   /* Display an error message and see if the user wants some help */
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 /** Starts the proxy server, if appropriately configured */
00502 void
00503 MainWindow::startProxy()
00504 {
00505   VidaliaSettings settings;
00506   QString executable = settings.getProxyExecutable();
00507   _proxyProcess->start(executable, settings.getProxyExecutableArguments());
00508 }
00509 
00510 /** Called when the proxy server fails to start, for example, because
00511  * the path specified didn't lead to an executable. */
00512 void
00513 MainWindow::onProxyFailed(QString errmsg)
00514 {
00515   Q_UNUSED(errmsg);
00516  
00517   /* Display an error message and see if the user wants some help */
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 /** Called when Tor's bootstrapping status changes. <b>bse</b> represents
00524  * Tor's current estimate of its bootstrapping progress. */
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; /* probably false anyway */
00566       break;
00567     default:
00568       description = tr("Unrecognized startup status");
00569   }
00570   if (warn) {
00571     QString reason;
00572     /* Is it really a good idea to translate these? */
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 /** Updates the UI to reflect Tor's current <b>status</b>. Returns the
00607  * previously set TorStatus value.*/
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       /* XXX: This might need to be smarter if we ever start connecting other
00633        * slots to these triggered() and clicked() signals. */
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       /* XXX: This might need to be smarter if we ever start connecting other
00661        * slots to these triggered() and clicked() signals. */
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       //ui.lblStartStopTor->setAnimation(QPixmap(ANIM_PROCESS_WORKING));
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   /* Update the tray icon */
00685   if (!trayIconFile.isEmpty()) {
00686     _trayIcon.setIcon(trayIconFile);
00687   }
00688   /* Update the status banner on the control panel */
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 /** Called when the "show on startup" checkbox is toggled. */
00699 void
00700 MainWindow::toggleShowOnStartup(bool checked)
00701 {
00702   VidaliaSettings settings;
00703   settings.setShowMainWindowAtStart(checked);
00704 }
00705 
00706 /** Sets the visibility of the startup status description and progress bar to
00707  * <b>visible</b>. */
00708 void
00709 MainWindow::setStartupProgressVisible(bool visible)
00710 {
00711   /* XXX: We force a repaint() to make sure the progress bar and onion status
00712    * icon don't overlap briefly. This is pretty hacktastic. */
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 /** Hides the startup status text and progress bar. */
00729 void
00730 MainWindow::hideStartupProgress()
00731 {
00732   setStartupProgressVisible(false);
00733 }
00734 
00735 /** Sets the progress bar completion value to <b>progressValue</b> and sets
00736  * the status text to <b>description</b>. */
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 /** Attempts to start Tor. If Tor fails to start, then startFailed() will be
00747  * called with an error message containing the reason. */
00748 void 
00749 MainWindow::start()
00750 {
00751   TorSettings settings;
00752   QStringList args;
00753 
00754   updateTorStatus(Starting);
00755   
00756   /* Check if Tor is already running separately */
00757   if (net_test_connect(settings.getControlAddress(),
00758                        settings.getControlPort())) {
00759     _controlPassword = settings.getControlPassword();
00760     started();
00761     return;
00762   }
00763 
00764   /* Make sure the torrc we want to use really exists. */
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   /* Specify Tor's data directory, if different from the default */
00773   QString dataDirectory = settings.getDataDirectory();
00774   if (!dataDirectory.isEmpty())
00775     args << "DataDirectory" << expand_filename(dataDirectory);
00776   
00777   /* Add the intended control port value */
00778   quint16 controlPort = settings.getControlPort();
00779   if (controlPort)
00780     args << "ControlPort" << QString::number(controlPort);
00781   
00782   /* Add the control port authentication arguments */
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   /* Add custom user and group information (if specified) */
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   /* This doesn't get set to false until Tor is actually up and running, so we
00811    * don't yell at users twice if their Tor doesn't even start, due to the fact
00812    * that QProcess::stopped() is emitted even if the process didn't even
00813    * start. */
00814   _isIntentionalExit = true;
00815   /* Kick off the Tor process */
00816   _torControl->start(settings.getExecutable(), args);
00817 }
00818 
00819 /** Called when the Tor process fails to start, for example, because the path
00820  * specified to the Tor executable didn't lead to an executable. */
00821 void
00822 MainWindow::startFailed(QString errmsg)
00823 {
00824   /* We don't display the error message for now, because the error message
00825    * that Qt gives us in this instance is almost always "Unknown Error". That
00826    * will make users sad. */
00827   Q_UNUSED(errmsg);
00828  
00829   updateTorStatus(Stopped);
00830 
00831   /* Display an error message and see if the user wants some help */
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     /* Show the settings dialog so the user can make sure they're pointing to
00842      * the correct Tor. */
00843      showConfigDialog();
00844   } else if (response == VMessageBox::Help) {
00845     /* Show troubleshooting information about starting Tor */
00846     showHelpDialog("troubleshooting.start");
00847   }
00848 }
00849 
00850 /** Slot: Called when the Tor process is started. It will connect the control
00851  * socket and set the icons and tooltips accordingly. */
00852 void 
00853 MainWindow::started()
00854 {
00855   TorSettings settings;
00856 
00857   updateTorStatus(Started);
00858 
00859   /* Now that Tor is running, we want to know if it dies when we didn't want
00860    * it to. */
00861   _isIntentionalExit = false;
00862   /* We haven't started a delayed shutdown yet. */
00863   _delayedShutdownStarted = false;
00864   /* Remember whether we started Tor or not */
00865   _isVidaliaRunningTor = _torControl->isVidaliaRunningTor();
00866   /* Try to connect to Tor's control port */
00867   _torControl->connect(settings.getControlAddress(),
00868                        settings.getControlPort());
00869   setStartupProgress(STARTUP_PROGRESS_CONNECTING, tr("Connecting to Tor"));
00870 }
00871 
00872 /** Called when the connection to the control socket fails. The reason will be
00873  * given in the errmsg parameter. */
00874 void
00875 MainWindow::connectFailed(QString errmsg)
00876 {
00877   /* Ok, ok. It really isn't going to connect. I give up. */
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     /* Let's give it another try. */
00886     TorSettings settings;
00887     _torControl->connect(settings.getControlAddress(),
00888                          settings.getControlPort());
00889   } else {
00890     /* Show the help browser (if requested) */
00891     if (response == VMessageBox::Help)
00892       showHelpDialog("troubleshooting.connect");
00893     /* Since Vidalia can't connect, we can't really do much, so stop Tor. */
00894     _torControl->stop();
00895   }
00896 }
00897 
00898 /** Disconnects the control socket and stops the Tor process. */
00899 bool
00900 MainWindow::stop()
00901 {
00902   ServerSettings server(_torControl);
00903   QString errmsg;
00904   TorStatus prevStatus;
00905   bool rc;
00906 
00907   /* If we're running a server, give users the option of terminating
00908    * gracefully so clients have time to find new servers. */
00909   if (server.isServerEnabled() && !_delayedShutdownStarted) {
00910     /* Ask the user if they want to shutdown nicely. */
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     /* Start a delayed shutdown */
00929     rc = _torControl->signal(TorSignal::Shutdown, &errmsg);
00930   } else {
00931     /* We want Tor to stop now, regardless of whether we're a server. */
00932     _isIntentionalExit = true;
00933     rc = _torControl->stop(&errmsg);
00934   }
00935   
00936   if (!rc) {
00937     /* We couldn't tell Tor to stop, for some reason. */
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       /* Show some troubleshooting help */
00946       showHelpDialog("troubleshooting.stop");
00947     }
00948     /* Tor is still running since stopping failed */
00949     _isIntentionalExit = false;
00950     _delayedShutdownStarted = false;
00951     updateTorStatus(prevStatus);
00952   }
00953   return rc;
00954 }
00955 
00956 /** Slot: Called when the Tor process has exited. It will adjust the tray
00957  * icons and tooltips accordingly. */
00958 void 
00959 MainWindow::stopped(int exitCode, QProcess::ExitStatus exitStatus)
00960 {
00961   updateTorStatus(Stopped);
00962 
00963   /* If we didn't intentionally close Tor, then check to see if it crashed or
00964    * if it closed itself and returned an error code. */
00965   if (!_isIntentionalExit) {
00966     /* A quick overview of Tor's code tells me that if it catches a SIGTERM or
00967      * SIGINT, Tor will exit(0). We might need to change this warning message
00968      * if this turns out to not be the case. */
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 /** Called when the control socket has successfully connected to Tor. */
00987 void
00988 MainWindow::connected()
00989 {
00990   authenticate();
00991 }
00992 
00993 /** Called when Vidalia wants to disconnect from a Tor it did not start. */
00994 void
00995 MainWindow::disconnect()
00996 {
00997   _torControl->disconnect();
00998 }
00999 
01000 /** Called when the control socket has been disconnected. */
01001 void
01002 MainWindow::disconnected()
01003 {
01004   if (!_isVidaliaRunningTor) {
01005     /* If we didn't start our own Tor process, interpret losing the control
01006      * connection as "Tor is stopped". */
01007     updateTorStatus(Stopped);
01008   }
01009   
01010   /*XXX We should warn here if we get disconnected when we didn't intend to */
01011   _newIdentityAct->setEnabled(false);
01012   ui.lblNewIdentity->setEnabled(false);
01013   _isVidaliaRunningTor = false;
01014 }
01015 
01016 /** Attempts to authenticate to Tor's control port, depending on the
01017  * authentication method specified in TorSettings::getAuthenticationMethod().
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     /* Try to load an auth cookie and send it to Tor */
01044     QByteArray cookie = loadControlCookie(pi.cookieAuthFile());
01045     while (cookie.isEmpty()) {
01046       /* Prompt the user to find their control_auth_cookie */
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     /* Get the control password and send it to Tor */
01072     vNotice("Authenticating using 'hashed password' authentication.");
01073     return _torControl->authenticate(_controlPassword);
01074   }
01075   /* No authentication. Send an empty password. */
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 /** Called when Vidalia has successfully authenticated to Tor. */
01089 void
01090 MainWindow::authenticated()
01091 {
01092   ServerSettings serverSettings(_torControl);
01093   QString errmsg;
01094 
01095   updateTorStatus(Authenticated);
01096   
01097   /* If Tor doesn't have bootstrapping events, then update the current
01098    * status string and bump the progress bar along a bit. */
01099   if (_torControl->getTorVersion() < 0x020101) {
01100     setStartupProgress(STARTUP_PROGRESS_CIRCUITBUILD,
01101                        tr("Connecting to the Tor network"));
01102   }
01103   
01104   /* Let people click on their beloved "New Identity" button */
01105   _newIdentityAct->setEnabled(true);
01106   ui.lblNewIdentity->setEnabled(true);
01107 
01108   /* Register for any pertinent asynchronous events. */
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   /* Configure UPnP port forwarding if needed */
01118   serverSettings.configurePortForwarding();
01119 
01120   /* Check if Tor has a circuit established */
01121   if (_torControl->circuitEstablished())
01122     circuitEstablished();
01123   /* Check the status of Tor's version */
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 /** Called when Vidalia fails to authenticate to Tor. The failure reason is
01134  * specified in <b>errmsg</b>. */
01135 void
01136 MainWindow::authenticationFailed(QString errmsg)
01137 {
01138   bool retry = false;
01139   
01140   vWarn("Authentication failed: %1").arg(errmsg);
01141 
01142   /* Parsing log messages is evil, but we're left with little option */
01143   if (errmsg.contains("Password did not match")) {
01144     /* Bad password, so prompt for a new one. */
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       /* XXX: We should ask the user if they really want to save the password
01151        * they just typed in. */
01152       TorSettings settings;
01153       settings.setAuthenticationMethod(TorSettings::PasswordAuth);
01154       settings.setControlPassword(password);
01155       settings.setUseRandomPassword(false);
01156       retry = true;
01157     }
01158   } else {
01159     /* Something else went wrong */
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 /** Searches for and attempts to load the control authentication cookie. This
01183  * assumes the cookie is named 'control_auth_cookie'. If <b>cookiePath</b> is
01184  * empty, this method will search some default locations depending on the
01185  * current platform. <b>cookiePath</b> can point to either a cookie file or a
01186  * directory containing the cookie file. */
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     /* Try some default locations */
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   /* Search for the cookie file */
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 /** Called when Tor has successfully established a circuit. */
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 /** Checks the status of the current version of Tor to see if it's old,
01241  * unrecommended, or obsolete. */
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 /** Called when Tor thinks its version is old or unrecommended, and displays a
01256  * message notifying the user. */
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 /** Creates and displays Vidalia's About dialog. */
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 /** Displays the help browser and displays the most recently viewed help
01289  * topic. */
01290 void
01291 MainWindow::showHelpDialog()
01292 {
01293   showHelpDialog(QString());
01294 }
01295 
01296 /**< Shows the help browser and displays the given help <b>topic</b>. */
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 /** Creates and displays the Configuration dialog with the current page set to
01307  * <b>page</b>. */
01308 void
01309 MainWindow::showConfigDialog(ConfigDialog::Page page)
01310 {
01311   _configDialog->showWindow(page);
01312 }
01313 
01314 /** Displays the Configuration dialog, set to the Server page. */
01315 void
01316 MainWindow::showServerConfigDialog()
01317 {
01318   showConfigDialog(ConfigDialog::Server);
01319 }
01320 
01321 /** Called when the user selects the "New Identity" action from the menu. */
01322 void
01323 MainWindow::newIdentity()
01324 {
01325   QString errmsg;
01326 
01327   /* Send the NEWNYM signal. If message balloons are supported and the NEWNYM
01328    * is successful, we will show the result as a balloon. Otherwise, we'll 
01329    * just use a message box. */
01330   if (_torControl->signal(TorSignal::NewNym, &errmsg)) {
01331     /* NEWNYM signal was successful */
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     /* Disable the New Identity button for MIN_NEWIDENTITY_INTERVAL */
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     /* NEWNYM signal failed */
01349     VMessageBox::warning(this, 
01350       tr("Failed to Create New Identity"), errmsg, VMessageBox::Ok);
01351   }
01352 }
01353 
01354 /** Re-enables the 'New Identity' button after a delay from the previous time
01355  * 'New Identity' was used. */
01356 void
01357 MainWindow::enableNewIdentity()
01358 {
01359   if (_torControl->isConnected()) {
01360     _newIdentityAct->setEnabled(true);
01361     ui.lblNewIdentity->setEnabled(true);
01362   }
01363 }
01364 
01365 /** Converts a TorStatus enum value to a string for debug logging purposes. */
01366 QString
01367 MainWindow::toString(TorStatus status)
01368 {
01369   switch (status) {
01370     /* These strings only appear in debug logs, so they should not be
01371      * translated. */
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 /** Called when a UPnP error occurs. */
01387 void
01388 MainWindow::upnpError(UPNPControl::UPNPError error)
01389 {
01390   Q_UNUSED(error);
01391 
01392 #if 0
01393   /* XXX: Is there a better way to do this? Currently, this could get called
01394    * if there is an error when testing UPnP support, and again when attempting
01395    * to reset the UPnP state when the test dialog is closed. The user would
01396    * not be amused with all the warning dialogs. */
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 

Generated on Sat Aug 16 17:38:35 2008 for Vidalia by  doxygen 1.5.6