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 3276 2008-11-03 00:08:59Z 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 #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 /* 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   /* Pressing 'Esc' or 'Ctrl+W' will close the window */
00099   Vidalia::createShortcut("Ctrl+W", this, ui.btnHide, SLOT(click()));
00100   Vidalia::createShortcut("Esc", this, ui.btnHide, SLOT(click()));
00101 
00102   /* Create all the dialogs of which we only want one instance */
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   /* Create the actions that will go in the tray menu */
00115   createActions();
00116   /* Creates a tray icon with a context menu and adds it to the system's
00117    * notification area. */
00118   createTrayIcon();
00119   /* Start with Tor initially stopped */
00120   _status = Unset;
00121   updateTorStatus(Stopped);
00122   
00123   /* Create a new TorControl object, used to communicate with Tor */
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   /* Create a new HelperProcess object, used to start the web browser */
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   /* Create a new HelperProcess object, used to start the IM client */
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   /* Create a new HelperProcess object, used to start the proxy server */
00156   _proxyProcess = new HelperProcess(this);
00157   connect(_proxyProcess, SIGNAL(startFailed(QString)),
00158            this, SLOT(onProxyFailed(QString)));
00159 
00160   /* Catch signals when the application is running or shutting down */
00161   connect(vApp, SIGNAL(running()), this, SLOT(running()));
00162   connect(vApp, SIGNAL(shutdown()), this, SLOT(shutdown()));
00163 
00164 #if defined(USE_MINIUPNPC)
00165   /* Catch UPnP-related signals */
00166   connect(UPNPControl::instance(), SIGNAL(error(UPNPControl::UPNPError)),
00167          this, SLOT(upnpError(UPNPControl::UPNPError)));
00168 #endif
00169 
00170   if (TrayIcon::isTrayIconSupported()) {
00171     /* Make the tray icon visible */
00172     _trayIcon.show();
00173     /* Check if we are supposed to show our main window on startup */
00174     ui.chkShowOnStartup->setChecked(settings.showMainWindowAtStart());
00175     if (ui.chkShowOnStartup->isChecked())
00176       show();
00177   } else {
00178     /* Don't let people hide the main window, since that's all they have. */
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 /** Destructor. */
00188 MainWindow::~MainWindow()
00189 {
00190   _trayIcon.hide();
00191   delete _messageLog;
00192   delete _bandwidthGraph;
00193   delete _netViewer;
00194   delete _configDialog;
00195 }
00196 
00197 /** Catches and processes Tor client and general status events. */
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 /** Called when the application has started and the main event loop is
00232  * running. */
00233 void
00234 MainWindow::running()
00235 {
00236   VidaliaSettings settings;
00237   if (_torControl->isRunning()) {
00238     /* Tor may be already running, but we still need to connect to it. So,
00239      * update our status now. */ 
00240     updateTorStatus(Starting);
00241     /* Tor was already running */
00242     started();
00243   } else if (settings.runTorAtStart()) {
00244     /* If we're supposed to start Tor when Vidalia starts, then do it now */
00245     start();
00246   }
00247 
00248   /* Start the proxy server, if configured */
00249   if (settings.runProxyAtStart())
00250     startProxy();
00251 }
00252 
00253 /** Terminate the Tor process if it is being run under Vidalia, disconnect all
00254  * TorControl signals, and exit Vidalia. */
00255 void
00256 MainWindow::shutdown()
00257 {
00258   if (_torControl->isVidaliaRunningTor()) {
00259     /* Kill our Tor process now */ 
00260     _torControl->stop();
00261   }
00262 
00263   /* Disable port forwarding */
00264   ServerSettings settings(_torControl);
00265   settings.cleanupPortForwarding();
00266 
00267   if (_proxyProcess->state() != QProcess::NotRunning) {
00268     /* Close the proxy server (Polipo ignores the WM_CLOSE event sent by
00269      * terminate() so we have to kill() it) */
00270     _proxyProcess->kill();
00271   }
00272 
00273   /* Disconnect all of the TorControl object's signals */
00274   QObject::disconnect(_torControl, 0, 0, 0);
00275 
00276   /* And then quit for real */
00277   QCoreApplication::quit();
00278 }
00279 
00280 /** Called when the application is closing, by selecting "Exit" from the tray
00281  * menu. If we're running a Tor server, then ask if we want to kill Tor now,
00282  * or do a delayed shutdown. */
00283 void
00284 MainWindow::close()
00285 {
00286   if (_torControl->isVidaliaRunningTor()) {
00287     /* If we're running a server currently, ask if we want to do a delayed
00288      * shutdown. If we do, then close Vidalia only when Tor stops. Otherwise,
00289      * kill Tor and bail now. */
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   /* Shut down Tor (if necessary) and exit Vidalia */
00299   shutdown();
00300 }
00301 
00302 /** Create and bind actions to events. Setup for initial
00303  * tray menu configuration. */
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   /* Don't give the menu items icons on OS X, since they end up in the
00350    * application menu bar. Menu bar items on OS X typically do not have
00351    * icons. */
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 /** Creates a tray icon with a context menu and adds it to the system
00366  * notification area. On Mac, we also set up an application menubar. */
00367 void
00368 MainWindow::createTrayIcon()
00369 {
00370   /* Create the default menu bar (Mac) */
00371   createMenuBar();
00372   /* Create a tray menu and add it to the tray icon */
00373   _trayIcon.setContextMenu(createTrayMenu());
00374   connect(&_trayIcon, SIGNAL(doubleClicked()), this, SLOT(show()));
00375 }
00376 
00377 /** Creates a QMenu object that contains QActions which compose the system 
00378  * tray menu. */
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   /* These aren't added to the dock menu on Mac, since they are in the
00394    * standard Mac locations in the menu bar. */
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 /** Creates a new menubar with no parent, so Qt will use this as the "default
00405  * menubar" on Mac. This adds on to the existing actions from the createMens()
00406  * method. */
00407 void
00408 MainWindow::createMenuBar()
00409 {
00410 #if defined(Q_WS_MAC)
00411   /* Mac users sure like their shortcuts. Actions NOT mentioned below
00412    * don't explicitly need shortcuts, since they are merged to the default
00413    * menubar and get the default shortcuts anyway. */
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   /* Force Qt to put merge the Exit, Configure, and About menubar options into
00423    * the default menu, even if Vidalia is currently not speaking English. */
00424   _exitAct->setText("exit");
00425   _configAct->setText("config");
00426   _aboutAct->setText("about");
00427   
00428   /* The File, Help, and Configure menus will get merged into the application
00429    * menu by Qt. */
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 /** Starts the web browser and IM client, if appropriately configured */
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 /** Called when browser or IM client have exited */
00472 void
00473 MainWindow::onSubprocessFinished(int exitCode, QProcess::ExitStatus exitStatus)
00474 {
00475   Q_UNUSED(exitCode)
00476   Q_UNUSED(exitStatus)
00477 
00478   /* Get path to browser and IM client */
00479   VidaliaSettings settings;
00480   QString browserExecutable = settings.getBrowserExecutable();
00481   QString imExecutable = settings.getIMExecutable();
00482 
00483   /* A subprocess is finished if it successfully exited or was never asked to start */
00484   bool browserDone = browserExecutable.isEmpty() || _browserProcess->isDone();
00485   bool imDone = imExecutable.isEmpty() || _imProcess->isDone();
00486 
00487   /* Exit if both subprocesses are finished */
00488   if (browserDone && imDone)
00489     shutdown();
00490 }
00491 
00492 /** Called when the web browser failed to start, for example, because the path
00493  * specified to the web browser executable didn't lead to an executable. */
00494 void
00495 MainWindow::onBrowserFailed(QString errmsg)
00496 {
00497   Q_UNUSED(errmsg);
00498  
00499   /* Display an error message and see if the user wants some help */
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 /** Called when the IM client failed to start, for example, because the path
00506  * specified to the IM client executable didn't lead to an executable. */
00507 void
00508 MainWindow::onIMFailed(QString errmsg)
00509 {
00510   Q_UNUSED(errmsg);
00511  
00512   /* Display an error message and see if the user wants some help */
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 /** Starts the proxy server, if appropriately configured */
00519 void
00520 MainWindow::startProxy()
00521 {
00522   VidaliaSettings settings;
00523   QString executable = settings.getProxyExecutable();
00524   _proxyProcess->start(executable, settings.getProxyExecutableArguments());
00525 }
00526 
00527 /** Called when the proxy server fails to start, for example, because
00528  * the path specified didn't lead to an executable. */
00529 void
00530 MainWindow::onProxyFailed(QString errmsg)
00531 {
00532   Q_UNUSED(errmsg);
00533  
00534   /* Display an error message and see if the user wants some help */
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 /** Called when Tor's bootstrapping status changes. <b>bse</b> represents
00541  * Tor's current estimate of its bootstrapping progress. */
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; /* probably false anyway */
00583       break;
00584     default:
00585       description = tr("Unrecognized startup status");
00586   }
00587   if (warn) {
00588     QString reason;
00589     /* Is it really a good idea to translate these? */
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 /** Updates the UI to reflect Tor's current <b>status</b>. Returns the
00624  * previously set TorStatus value.*/
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       /* XXX: This might need to be smarter if we ever start connecting other
00650        * slots to these triggered() and clicked() signals. */
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       /* XXX: This might need to be smarter if we ever start connecting other
00678        * slots to these triggered() and clicked() signals. */
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   /* Update the tray icon */
00701   if (!trayIconFile.isEmpty()) {
00702     _trayIcon.setIcon(trayIconFile);
00703   }
00704   /* Update the status banner on the control panel */
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 /** Called when the "show on startup" checkbox is toggled. */
00715 void
00716 MainWindow::toggleShowOnStartup(bool checked)
00717 {
00718   VidaliaSettings settings;
00719   settings.setShowMainWindowAtStart(checked);
00720 }
00721 
00722 /** Sets the visibility of the startup status description and progress bar to
00723  * <b>visible</b>. */
00724 void
00725 MainWindow::setStartupProgressVisible(bool visible)
00726 {
00727   /* XXX: We force a repaint() to make sure the progress bar and onion status
00728    * icon don't overlap briefly. This is pretty hacktastic. */
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 /** Sets the progress bar completion value to <b>progressValue</b> and sets
00745  * the status text to <b>description</b>. */
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 /** Attempts to start Tor. If Tor fails to start, then startFailed() will be
00756  * called with an error message containing the reason. */
00757 void 
00758 MainWindow::start()
00759 {
00760   TorSettings settings;
00761   QStringList args;
00762 
00763   updateTorStatus(Starting);
00764 
00765   /* Check if Tor is already running separately */
00766   if (net_test_connect(settings.getControlAddress(),
00767                        settings.getControlPort())) {
00768     started();
00769     return;
00770   }
00771 
00772   /* Make sure the torrc we want to use really exists. */
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   /* Specify Tor's data directory, if different from the default */
00781   QString dataDirectory = settings.getDataDirectory();
00782   if (!dataDirectory.isEmpty())
00783     args << "DataDirectory" << expand_filename(dataDirectory);
00784   
00785   /* Add the intended control port value */
00786   quint16 controlPort = settings.getControlPort();
00787   if (controlPort)
00788     args << "ControlPort" << QString::number(controlPort);
00789   
00790   /* Add the control port authentication arguments */
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   /* Add custom user and group information (if specified) */
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   /* This doesn't get set to false until Tor is actually up and running, so we
00822    * don't yell at users twice if their Tor doesn't even start, due to the fact
00823    * that QProcess::stopped() is emitted even if the process didn't even
00824    * start. */
00825   _isIntentionalExit = true;
00826   /* Kick off the Tor process */
00827   _torControl->start(settings.getExecutable(), args);
00828 }
00829 
00830 /** Called when the Tor process fails to start, for example, because the path
00831  * specified to the Tor executable didn't lead to an executable. */
00832 void
00833 MainWindow::startFailed(QString errmsg)
00834 {
00835   /* We don't display the error message for now, because the error message
00836    * that Qt gives us in this instance is almost always "Unknown Error". That
00837    * will make users sad. */
00838   Q_UNUSED(errmsg);
00839  
00840   updateTorStatus(Stopped);
00841 
00842   /* Display an error message and see if the user wants some help */
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     /* Show the settings dialog so the user can make sure they're pointing to
00853      * the correct Tor. */
00854      showConfigDialog();
00855   } else if (response == VMessageBox::Help) {
00856     /* Show troubleshooting information about starting Tor */
00857     showHelpDialog("troubleshooting.start");
00858   }
00859 }
00860 
00861 /** Slot: Called when the Tor process is started. It will connect the control
00862  * socket and set the icons and tooltips accordingly. */
00863 void 
00864 MainWindow::started()
00865 {
00866   TorSettings settings;
00867 
00868   updateTorStatus(Started);
00869 
00870   /* Now that Tor is running, we want to know if it dies when we didn't want
00871    * it to. */
00872   _isIntentionalExit = false;
00873   /* We haven't started a delayed shutdown yet. */
00874   _delayedShutdownStarted = false;
00875   /* Remember whether we started Tor or not */
00876   _isVidaliaRunningTor = _torControl->isVidaliaRunningTor();
00877   /* Try to connect to Tor's control port */
00878   _torControl->connect(settings.getControlAddress(),
00879                        settings.getControlPort());
00880   setStartupProgress(STARTUP_PROGRESS_CONNECTING, tr("Connecting to Tor"));
00881 }
00882 
00883 /** Called when the connection to the control socket fails. The reason will be
00884  * given in the errmsg parameter. */
00885 void
00886 MainWindow::connectFailed(QString errmsg)
00887 {
00888   /* Ok, ok. It really isn't going to connect. I give up. */
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     /* Let's give it another try. */
00897     TorSettings settings;
00898     _torControl->connect(settings.getControlAddress(),
00899                          settings.getControlPort());
00900   } else {
00901     /* Show the help browser (if requested) */
00902     if (response == VMessageBox::Help)
00903       showHelpDialog("troubleshooting.connect");
00904     /* Since Vidalia can't connect, we can't really do much, so stop Tor. */
00905     _torControl->stop();
00906   }
00907 }
00908 
00909 /** Disconnects the control socket and stops the Tor process. */
00910 bool
00911 MainWindow::stop()
00912 {
00913   ServerSettings server(_torControl);
00914   QString errmsg;
00915   TorStatus prevStatus;
00916   bool rc;
00917 
00918   /* If we're running a server, give users the option of terminating
00919    * gracefully so clients have time to find new servers. */
00920   if (server.isServerEnabled() && !_delayedShutdownStarted) {
00921     /* Ask the user if they want to shutdown nicely. */
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     /* Start a delayed shutdown */
00940     rc = _torControl->signal(TorSignal::Shutdown, &errmsg);
00941   } else {
00942     /* We want Tor to stop now, regardless of whether we're a server. */
00943     _isIntentionalExit = true;
00944     rc = _torControl->stop(&errmsg);
00945   }
00946   
00947   if (!rc) {
00948     /* We couldn't tell Tor to stop, for some reason. */
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       /* Show some troubleshooting help */
00957       showHelpDialog("troubleshooting.stop");
00958     }
00959     /* Tor is still running since stopping failed */
00960     _isIntentionalExit = false;
00961     _delayedShutdownStarted = false;
00962     updateTorStatus(prevStatus);
00963   }
00964   return rc;
00965 }
00966 
00967 /** Slot: Called when the Tor process has exited. It will adjust the tray
00968  * icons and tooltips accordingly. */
00969 void 
00970 MainWindow::stopped(int exitCode, QProcess::ExitStatus exitStatus)
00971 {
00972   updateTorStatus(Stopped);
00973 
00974   /* If we didn't intentionally close Tor, then check to see if it crashed or
00975    * if it closed itself and returned an error code. */
00976   if (!_isIntentionalExit) {
00977     /* A quick overview of Tor's code tells me that if it catches a SIGTERM or
00978      * SIGINT, Tor will exit(0). We might need to change this warning message
00979      * if this turns out to not be the case. */
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 /** Called when the control socket has successfully connected to Tor. */
00998 void
00999 MainWindow::connected()
01000 {
01001   authenticate();
01002 }
01003 
01004 /** Called when Vidalia wants to disconnect from a Tor it did not start. */
01005 void
01006 MainWindow::disconnect()
01007 {
01008   _torControl->disconnect();
01009 }
01010 
01011 /** Called when the control socket has been disconnected. */
01012 void
01013 MainWindow::disconnected()
01014 {
01015   if (!_isVidaliaRunningTor) {
01016     /* If we didn't start our own Tor process, interpret losing the control
01017      * connection as "Tor is stopped". */
01018     updateTorStatus(Stopped);
01019   }
01020   
01021   /*XXX We should warn here if we get disconnected when we didn't intend to */
01022   _newIdentityAct->setEnabled(false);
01023   ui.lblNewIdentity->setEnabled(false);
01024   _isVidaliaRunningTor = false;
01025 }
01026 
01027 /** Attempts to authenticate to Tor's control port, depending on the
01028  * authentication method specified in TorSettings::getAuthenticationMethod().
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     /* Try to load an auth cookie and send it to Tor */
01055     QByteArray cookie = loadControlCookie(pi.cookieAuthFile());
01056     while (cookie.isEmpty()) {
01057       /* Prompt the user to find their control_auth_cookie */
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     /* Get the control password and send it to Tor */
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   /* No authentication. Send an empty password. */
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 /** Called when Vidalia has successfully authenticated to Tor. */
01104 void
01105 MainWindow::authenticated()
01106 {
01107   ServerSettings serverSettings(_torControl);
01108   QString errmsg;
01109 
01110   updateTorStatus(Authenticated);
01111   
01112   /* If Tor doesn't have bootstrapping events, then update the current
01113    * status string and bump the progress bar along a bit. */
01114   if (_torControl->getTorVersion() < 0x020101) {
01115     setStartupProgress(STARTUP_PROGRESS_CIRCUITBUILD,
01116                        tr("Connecting to the Tor network"));
01117   }
01118   
01119   /* Let people click on their beloved "New Identity" button */
01120   _newIdentityAct->setEnabled(true);
01121   ui.lblNewIdentity->setEnabled(true);
01122 
01123   /* Register for any pertinent asynchronous events. */
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   /* Configure UPnP port forwarding if needed */
01133   serverSettings.configurePortForwarding();
01134 
01135   /* Check if Tor has a circuit established */
01136   if (_torControl->circuitEstablished())
01137     circuitEstablished();
01138   /* Check the status of Tor's version */
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 /** Called when Vidalia fails to authenticate to Tor. The failure reason is
01149  * specified in <b>errmsg</b>. */
01150 void
01151 MainWindow::authenticationFailed(QString errmsg)
01152 {
01153   bool retry = false;
01154   
01155   vWarn("Authentication failed: %1").arg(errmsg);
01156 
01157   /* Parsing log messages is evil, but we're left with little option */
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     /* Something else went wrong */
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 /** Searches for and attempts to load the control authentication cookie. This
01228  * assumes the cookie is named 'control_auth_cookie'. If <b>cookiePath</b> is
01229  * empty, this method will search some default locations depending on the
01230  * current platform. <b>cookiePath</b> can point to either a cookie file or a
01231  * directory containing the cookie file. */
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     /* Try some default locations */
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   /* Search for the cookie file */
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 /** Called when Tor has successfully established a circuit. */
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 /** Checks the status of the current version of Tor to see if it's old,
01286  * unrecommended, or obsolete. */
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 /** Called when Tor thinks its version is old or unrecommended, and displays a
01301  * message notifying the user. */
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 /** Creates and displays Vidalia's About dialog. */
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 /** Displays the help browser and displays the most recently viewed help
01334  * topic. */
01335 void
01336 MainWindow::showHelpDialog()
01337 {
01338   showHelpDialog(QString());
01339 }
01340 
01341 /**< Shows the help browser and displays the given help <b>topic</b>. */
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 /** Creates and displays the Configuration dialog with the current page set to
01352  * <b>page</b>. */
01353 void
01354 MainWindow::showConfigDialog(ConfigDialog::Page page)
01355 {
01356   _configDialog->showWindow(page);
01357 }
01358 
01359 /** Displays the Configuration dialog, set to the Server page. */
01360 void
01361 MainWindow::showServerConfigDialog()
01362 {
01363   showConfigDialog(ConfigDialog::Server);
01364 }
01365 
01366 /** Called when the user selects the "New Identity" action from the menu. */
01367 void
01368 MainWindow::newIdentity()
01369 {
01370   QString errmsg;
01371 
01372   /* Send the NEWNYM signal. If message balloons are supported and the NEWNYM
01373    * is successful, we will show the result as a balloon. Otherwise, we'll 
01374    * just use a message box. */
01375   if (_torControl->signal(TorSignal::NewNym, &errmsg)) {
01376     /* NEWNYM signal was successful */
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     /* Disable the New Identity button for MIN_NEWIDENTITY_INTERVAL */
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     /* NEWNYM signal failed */
01394     VMessageBox::warning(this, 
01395       tr("Failed to Create New Identity"), errmsg, VMessageBox::Ok);
01396   }
01397 }
01398 
01399 /** Re-enables the 'New Identity' button after a delay from the previous time
01400  * 'New Identity' was used. */
01401 void
01402 MainWindow::enableNewIdentity()
01403 {
01404   if (_torControl->isConnected()) {
01405     _newIdentityAct->setEnabled(true);
01406     ui.lblNewIdentity->setEnabled(true);
01407   }
01408 }
01409 
01410 /** Converts a TorStatus enum value to a string for debug logging purposes. */
01411 QString
01412 MainWindow::toString(TorStatus status)
01413 {
01414   switch (status) {
01415     /* These strings only appear in debug logs, so they should not be
01416      * translated. */
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 /** Called when a UPnP error occurs. */
01432 void
01433 MainWindow::upnpError(UPNPControl::UPNPError error)
01434 {
01435   Q_UNUSED(error);
01436 
01437 #if 0
01438   /* XXX: Is there a better way to do this? Currently, this could get called
01439    * if there is an error when testing UPnP support, and again when attempting
01440    * to reset the UPnP state when the test dialog is closed. The user would
01441    * not be amused with all the warning dialogs. */
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 

Generated on Wed Nov 26 21:02:42 2008 for Vidalia by  doxygen 1.5.7.1