Vidalia 0.2.10

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 4407 2010-08-26 16:55:53Z 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 "MainWindow.h"
00022 #include "Vidalia.h"
00023 #include "VMessageBox.h"
00024 #include "ControlPasswordInputDialog.h"
00025 #include "TorSettings.h"
00026 #include "ServerSettings.h"
00027 #ifdef USE_AUTOUPDATE
00028 #include "UpdatesAvailableDialog.h"
00029 #endif
00030 
00031 #include "ProtocolInfo.h"
00032 
00033 #include "net.h"
00034 #include "file.h"
00035 #include "html.h"
00036 #include "stringutil.h"
00037 #include "procutil.h"
00038 
00039 #include <QMenuBar>
00040 #include <QTimer>
00041 #include <QTextStream>
00042 
00043 #define IMG_BWGRAPH        ":/images/16x16/utilities-system-monitor.png"
00044 #define IMG_CONTROL_PANEL  ":/images/16x16/system-run.png"
00045 #define IMG_MESSAGELOG     ":/images/16x16/format-justify-fill.png"
00046 #define IMG_CONFIG         ":/images/16x16/preferences-system.png"
00047 #define IMG_IDENTITY       ":/images/16x16/view-media-artist.png"
00048 #define IMG_HELP           ":/images/16x16/system-help.png"
00049 #define IMG_ABOUT          ":/images/16x16/help-about.png"
00050 #define IMG_EXIT           ":/images/16x16/application-exit.png"
00051 #define IMG_NETWORK        ":/images/16x16/applications-internet.png"
00052 
00053 #define IMG_START_TOR_16     ":/images/16x16/media-playback-start.png"
00054 #define IMG_STOP_TOR_16      ":/images/16x16/media-playback-stop.png"
00055 #define IMG_START_TOR_48     ":/images/48x48/media-playback-start.png"
00056 #define IMG_STOP_TOR_48      ":/images/48x48/media-playback-stop.png"
00057 #define IMG_TOR_STOPPED_48   ":/images/48x48/tor-off.png"
00058 #define IMG_TOR_RUNNING_48   ":/images/48x48/tor-on.png"
00059 #define IMG_TOR_STARTING_48  ":/images/48x48/tor-starting.png"
00060 #define IMG_TOR_STOPPING_48  ":/images/48x48/tor-stopping.png"
00061 
00062 /* Decide which of our four sets of tray icons to use. */
00063 #if defined(Q_WS_WIN)
00064 /* QSystemTrayIcon on Windows wants 16x16 .png files */
00065 #define IMG_TOR_STOPPED  ":/images/16x16/tor-off.png"
00066 #define IMG_TOR_RUNNING  ":/images/16x16/tor-on.png"
00067 #define IMG_TOR_STARTING ":/images/16x16/tor-starting.png"
00068 #define IMG_TOR_STOPPING ":/images/16x16/tor-stopping.png"
00069 #elif defined(Q_WS_MAC)
00070 /* On Mac, the dock icons look best at 128x128, otherwise they get blurry
00071  * if resized from a smaller image */
00072 #define IMG_TOR_STOPPED    ":/images/128x128/tor-off.png"
00073 #define IMG_TOR_RUNNING    ":/images/128x128/tor-on.png"
00074 #define IMG_TOR_STARTING   ":/images/128x128/tor-starting.png"
00075 #define IMG_TOR_STOPPING   ":/images/128x128/tor-stopping.png"
00076 void qt_mac_set_dock_menu(QMenu *menu);
00077 #else
00078 /* On X11, we just use always the 22x22 .png files */
00079 #define IMG_TOR_STOPPED    ":/images/22x22/tor-off.png"
00080 #define IMG_TOR_RUNNING    ":/images/22x22/tor-on.png"
00081 #define IMG_TOR_STARTING   ":/images/22x22/tor-starting.png"
00082 #define IMG_TOR_STOPPING   ":/images/22x22/tor-stopping.png"
00083 #endif
00084 
00085 /** Only allow 'New Identity' to be clicked once every 10 seconds. */
00086 #define MIN_NEWIDENTITY_INTERVAL   (10*1000)
00087 
00088 /* Startup progress milestones */
00089 #define STARTUP_PROGRESS_STARTING          0
00090 #define STARTUP_PROGRESS_CONNECTING       10
00091 #define STARTUP_PROGRESS_AUTHENTICATING   20
00092 #define STARTUP_PROGRESS_BOOTSTRAPPING    30
00093 #define STARTUP_PROGRESS_CIRCUITBUILD     75
00094 #define STARTUP_PROGRESS_MAXIMUM          (STARTUP_PROGRESS_BOOTSTRAPPING+100)
00095 
00096 /** Default constructor. It installs an icon in the system tray area and
00097  * creates the popup menu associated with that icon. */
00098 MainWindow::MainWindow()
00099 : VidaliaWindow("MainWindow")
00100 {
00101   VidaliaSettings settings;
00102 
00103   ui.setupUi(this);
00104 
00105   /* Pressing 'Esc' or 'Ctrl+W' will close the window */
00106   Vidalia::createShortcut("Ctrl+W", this, ui.btnHide, SLOT(click()));
00107   Vidalia::createShortcut("Esc", this, ui.btnHide, SLOT(click()));
00108 
00109   /* Create all the dialogs of which we only want one instance */
00110   _messageLog     = new MessageLog();
00111   _bandwidthGraph = new BandwidthGraph();
00112   _netViewer      = new NetViewer();
00113   _configDialog   = new ConfigDialog();
00114   _menuBar        = 0;
00115   connect(_messageLog, SIGNAL(helpRequested(QString)),
00116           this, SLOT(showHelpDialog(QString)));
00117   connect(_netViewer, SIGNAL(helpRequested(QString)),
00118           this, SLOT(showHelpDialog(QString)));
00119   connect(_configDialog, SIGNAL(helpRequested(QString)),
00120           this, SLOT(showHelpDialog(QString)));
00121 
00122   /* Create the actions that will go in the tray menu */
00123   createActions();
00124   /* Creates a tray icon with a context menu and adds it to the system's
00125    * notification area. */
00126   createTrayIcon();
00127   /* Start with Tor initially stopped */
00128   _status = Unset;
00129   _isVidaliaRunningTor = false;
00130   updateTorStatus(Stopped);
00131 
00132   /* Create a new TorControl object, used to communicate with Tor */
00133   _torControl = Vidalia::torControl(); 
00134   connect(_torControl, SIGNAL(started()), this, SLOT(started()));
00135   connect(_torControl, SIGNAL(startFailed(QString)),
00136           this, SLOT(startFailed(QString)));
00137   connect(_torControl, SIGNAL(stopped(int, QProcess::ExitStatus)),
00138           this, SLOT(stopped(int, QProcess::ExitStatus)));
00139   connect(_torControl, SIGNAL(connected()), this, SLOT(connected()));
00140   connect(_torControl, SIGNAL(disconnected()), this, SLOT(disconnected()));
00141   connect(_torControl, SIGNAL(connectFailed(QString)), 
00142           this, SLOT(connectFailed(QString)));
00143   connect(_torControl, SIGNAL(authenticated()), this, SLOT(authenticated()));
00144   connect(_torControl, SIGNAL(authenticationFailed(QString)),
00145           this, SLOT(authenticationFailed(QString)));
00146 
00147   _torControl->setEvent(TorEvents::GeneralStatus);
00148   connect(_torControl, SIGNAL(dangerousTorVersion(tc::TorVersionStatus,
00149                                                   QString, QStringList)),
00150           this, SLOT(dangerousTorVersion(tc::TorVersionStatus,
00151                                          QString, QStringList)));
00152 
00153   _torControl->setEvent(TorEvents::ClientStatus);
00154   connect(_torControl, SIGNAL(bootstrapStatusChanged(BootstrapStatus)),
00155           this, SLOT(bootstrapStatusChanged(BootstrapStatus)));
00156   connect(_torControl, SIGNAL(circuitEstablished()),
00157           this, SLOT(circuitEstablished()));
00158   connect(_torControl, SIGNAL(dangerousPort(quint16, bool)),
00159           this, SLOT(warnDangerousPort(quint16, bool)));
00160 
00161   /* Create a new HelperProcess object, used to start the web browser */
00162   _browserProcess = new HelperProcess(this);
00163   connect(_browserProcess, SIGNAL(finished(int, QProcess::ExitStatus)),
00164            this, SLOT(onSubprocessFinished(int, QProcess::ExitStatus)));
00165   connect(_browserProcess, SIGNAL(startFailed(QString)),
00166            this, SLOT(onBrowserFailed(QString)));
00167 
00168   /* Create a new HelperProcess object, used to start the IM client */
00169   _imProcess = new HelperProcess(this);
00170   connect(_imProcess, SIGNAL(finished(int, QProcess::ExitStatus)),
00171            this, SLOT(onSubprocessFinished(int, QProcess::ExitStatus)));
00172   connect(_imProcess, SIGNAL(startFailed(QString)),
00173            this, SLOT(onIMFailed(QString)));
00174 
00175   /* Create a new HelperProcess object, used to start the proxy server */
00176   _proxyProcess = new HelperProcess(this);
00177   connect(_proxyProcess, SIGNAL(startFailed(QString)),
00178            this, SLOT(onProxyFailed(QString)));
00179 
00180   /* Catch signals when the application is running or shutting down */
00181   connect(vApp, SIGNAL(running()), this, SLOT(running()));
00182   connect(vApp, SIGNAL(aboutToQuit()), this, SLOT(aboutToQuit()));
00183 
00184 #if defined(USE_AUTOUPDATE)
00185   /* Create a timer used to remind us to check for software updates */
00186   connect(&_updateTimer, SIGNAL(timeout()), this, SLOT(checkForUpdates()));
00187 
00188   /* Also check for updates in the foreground when the user clicks the
00189    * "Check Now" button in the config dialog. */
00190   connect(_configDialog, SIGNAL(checkForUpdates()),
00191           this, SLOT(checkForUpdatesWithUi()));
00192 
00193   /* The rest of these slots are called as the update process executes. */
00194   connect(&_updateProcess, SIGNAL(downloadProgress(QString,int,int)),
00195           &_updateProgressDialog, SLOT(setDownloadProgress(QString,int,int)));
00196   connect(&_updateProcess, SIGNAL(updatesAvailable(UpdateProcess::BundleInfo,PackageList)),
00197           this, SLOT(updatesAvailable(UpdateProcess::BundleInfo,PackageList)));
00198   connect(&_updateProcess, SIGNAL(updatesInstalled(int)),
00199           this, SLOT(updatesInstalled(int)));
00200   connect(&_updateProcess, SIGNAL(installUpdatesFailed(QString)),
00201           this, SLOT(installUpdatesFailed(QString)));
00202   connect(&_updateProgressDialog, SIGNAL(cancelUpdate()),
00203           &_updateProcess, SLOT(cancel()));
00204 #endif
00205 
00206 #if defined(USE_MINIUPNPC)
00207   /* Catch UPnP-related signals */
00208   connect(UPNPControl::instance(), SIGNAL(error(UPNPControl::UPNPError)),
00209          this, SLOT(upnpError(UPNPControl::UPNPError)));
00210 #endif
00211 
00212   ui.chkShowOnStartup->setChecked(settings.showMainWindowAtStart());
00213   if (ui.chkShowOnStartup->isChecked())
00214     show(); 
00215   /* Optimistically hope that the tray icon gets added. */
00216   _trayIcon.show();
00217 }
00218 
00219 /** Destructor. */
00220 MainWindow::~MainWindow()
00221 {
00222   _trayIcon.hide();
00223   delete _messageLog;
00224   delete _bandwidthGraph;
00225   delete _netViewer;
00226   delete _configDialog;
00227 }
00228 
00229 void
00230 MainWindow::setVisible(bool visible)
00231 {
00232   if (visible) {
00233     /* In Gnome, will hide buttons if Vidalia is run on startup. */
00234     if (!QSystemTrayIcon::isSystemTrayAvailable()) {
00235       /* Don't let people hide the main window, since that's all they have. */
00236       ui.chkShowOnStartup->hide();
00237       ui.btnHide->hide();
00238       /* Causes window to not appear in Enlightenment. */
00239       //setMinimumHeight(height()-ui.btnHide->height());
00240       //setMaximumHeight(height()-ui.btnHide->height());
00241     }
00242   }
00243   VidaliaWindow::setVisible(visible);
00244 }
00245 
00246 void
00247 MainWindow::retranslateUi()
00248 {
00249   ui.retranslateUi(this);
00250 
00251   updateTorStatus(_status);
00252   if (_status == Stopped) {
00253     _actionStartStopTor->setText(tr("Start Tor"));
00254     ui.lblStartStopTor->setText(tr("Start Tor"));
00255   } else if (_status == Starting) {
00256     _actionStartStopTor->setText(tr("Starting Tor"));
00257     ui.lblStartStopTor->setText(tr("Starting Tor"));
00258   } else {
00259     _actionStartStopTor->setText(tr("Stop Tor"));
00260     ui.lblStartStopTor->setText(tr("Stop Tor"));
00261   }
00262 
00263   _actionShowBandwidth->setText(tr("Bandwidth Graph"));
00264   _actionShowMessageLog->setText(tr("Message Log"));
00265   _actionShowNetworkMap->setText(tr("Network Map"));
00266   _actionShowControlPanel->setText(tr("Control Panel"));
00267   _actionShowHelp->setText(tr("Help"));
00268   _actionNewIdentity->setText(tr("New Identity"));
00269 
00270 #if !defined(Q_WS_MAC)
00271   _actionShowAbout->setText(tr("About"));
00272   _actionShowConfig->setText(tr("Settings"));
00273   _actionExit->setText(tr("Exit"));
00274 #else
00275   createMenuBar();
00276 #endif
00277 }
00278 
00279 /** Called when the application has started and the main event loop is
00280  * running. */
00281 void
00282 MainWindow::running()
00283 {
00284   VidaliaSettings settings;
00285 
00286   if (vApp->readPasswordFromStdin()) {
00287     QTextStream in(stdin);
00288     in >> _controlPassword;
00289     _useSavedPassword = false;
00290   } else {
00291     /* Initialize _useSavedPassword to true. If Tor is already running when
00292      * Vidalia starts, then there is no point in generating a random password.
00293      * If Tor is not already running, then this will be set according to the
00294      * current configuration in the start() method.
00295      */
00296     _useSavedPassword = true;
00297   }
00298 
00299   if (settings.runTorAtStart()) {
00300     /* If we're supposed to start Tor when Vidalia starts, then do it now */
00301     start();
00302   }
00303 
00304   /* Start the proxy server, if configured */
00305   if (settings.runProxyAtStart())
00306     startProxy();
00307 
00308 #if defined(USE_AUTOUPDATE)
00309   if (settings.isAutoUpdateEnabled()) {
00310     QDateTime lastCheckedAt = settings.lastCheckedForUpdates();
00311     if (UpdateProcess::shouldCheckForUpdates(lastCheckedAt)) {
00312       if (settings.runTorAtStart() && ! _torControl->isCircuitEstablished()) {
00313         /* We started Tor but it hasn't bootstrapped yet, so give it a bit
00314          * before we decide to check for updates. If Tor manages to build a
00315          * circuit before this timer times out, we will stop the timer and
00316          * launch a check for updates immediately. (see circuitEstablished()).
00317          */
00318         _updateTimer.start(5*60*1000);
00319       } else {
00320         /* Initiate a background check for updates now */
00321         checkForUpdates();
00322       }
00323     } else {
00324       /* Schedule the next time to check for updates */
00325       QDateTime nextCheckAt = UpdateProcess::nextCheckForUpdates(lastCheckedAt);
00326       QDateTime now = QDateTime::currentDateTime().toUTC();
00327 
00328       vInfo("Last checked for software updates at %1. Will check again at %2.")
00329         .arg(lastCheckedAt.toLocalTime().toString("dd-MM-yyyy hh:mm:ss"))
00330         .arg(nextCheckAt.toLocalTime().toString("dd-MM-yyyy hh:mm:ss"));
00331       _updateTimer.start((nextCheckAt.toTime_t() - now.toTime_t()) * 1000);
00332     }
00333   }
00334 #endif
00335 }
00336 
00337 /** Terminate the Tor process if it is being run under Vidalia, disconnect all
00338  * TorControl signals, and exit Vidalia. */
00339 void
00340 MainWindow::aboutToQuit()
00341 {
00342   vNotice("Cleaning up before exiting.");
00343 
00344   if (_torControl->isVidaliaRunningTor()) {
00345     /* Kill our Tor process now */ 
00346     _torControl->stop();
00347   }
00348 
00349   /* Disable port forwarding */
00350   ServerSettings settings(_torControl);
00351   settings.cleanupPortForwarding();
00352 
00353   if (_proxyProcess->state() != QProcess::NotRunning) {
00354     /* Close the proxy server (Polipo ignores the WM_CLOSE event sent by
00355      * terminate() so we have to kill() it) */
00356     _proxyProcess->kill();
00357   }
00358 
00359   /* Kill the browser and IM client if using the new launcher */
00360   VidaliaSettings vidalia_settings;
00361 
00362   if (! vidalia_settings.getBrowserDirectory().isEmpty()) {
00363     /* Disconnect the finished signals so that we won't try to exit Vidalia again */
00364     QObject::disconnect(_browserProcess, SIGNAL(finished(int, QProcess::ExitStatus)), 0, 0);
00365     QObject::disconnect(_imProcess, SIGNAL(finished(int, QProcess::ExitStatus)), 0, 0);
00366 
00367     /* Use QProcess terminate function */
00368     if (_browserProcess->state() == QProcess::Running)
00369       _browserProcess->terminate();
00370 
00371 #if defined(Q_OS_WIN)
00372     /* Kill any processes which might have been forked off */
00373     win32_end_process_by_filename(vidalia_settings.getBrowserExecutable());
00374 #endif
00375 
00376     if (_imProcess->state() == QProcess::Running)
00377       _imProcess->terminate();    
00378   }
00379 
00380   /* Disconnect all of the TorControl object's signals */
00381   QObject::disconnect(_torControl, 0, 0, 0);
00382 }
00383 
00384 /** Called when the application is closing, by selecting "Exit" from the tray
00385  * menu. If we're running a Tor server, then ask if we want to kill Tor now,
00386  * or do a delayed shutdown. */
00387 void
00388 MainWindow::close()
00389 {
00390   if (_torControl->isVidaliaRunningTor()) {
00391     /* If we're running a server currently, ask if we want to do a delayed
00392      * shutdown. If we do, then close Vidalia only when Tor stops. Otherwise,
00393      * kill Tor and bail now. */
00394     ServerSettings settings(_torControl);
00395     if (_torControl->isConnected() && settings.isServerEnabled()) {
00396       connect(_torControl, SIGNAL(stopped()), vApp, SLOT(quit()));
00397       if (!stop())
00398         QObject::disconnect(_torControl, SIGNAL(stopped()), vApp, SLOT(quit()));
00399       return;
00400     }
00401   }
00402   vApp->quit();
00403 }
00404 
00405 /** Create and bind actions to events. Setup for initial
00406  * tray menu configuration. */
00407 void 
00408 MainWindow::createActions()
00409 {
00410   _actionStartStopTor = new QAction(tr("Start Tor"), this);
00411   connect(_actionStartStopTor, SIGNAL(triggered()), this, SLOT(start()));
00412 
00413   _actionExit = new QAction(tr("Exit"), this);
00414   connect(_actionExit, SIGNAL(triggered()), this, SLOT(close()));
00415 
00416   _actionShowBandwidth = new QAction(tr("Bandwidth Graph"), this);
00417   connect(_actionShowBandwidth, SIGNAL(triggered()), 
00418           _bandwidthGraph, SLOT(showWindow()));
00419   connect(ui.lblBandwidthGraph, SIGNAL(clicked()),
00420           _bandwidthGraph, SLOT(showWindow()));
00421 
00422   _actionShowMessageLog = new QAction(tr("Message Log"), this);
00423   connect(_actionShowMessageLog, SIGNAL(triggered()),
00424           _messageLog, SLOT(showWindow()));
00425   connect(ui.lblMessageLog, SIGNAL(clicked()),
00426           _messageLog, SLOT(showWindow()));
00427 
00428   _actionShowNetworkMap = new QAction(tr("Network Map"), this);
00429   connect(_actionShowNetworkMap, SIGNAL(triggered()), 
00430           _netViewer, SLOT(showWindow()));
00431   connect(ui.lblViewNetwork, SIGNAL(clicked()),
00432           _netViewer, SLOT(showWindow()));
00433 
00434   _actionShowControlPanel = new QAction(tr("Control Panel"), this);
00435   connect(_actionShowControlPanel, SIGNAL(triggered()), this, SLOT(show()));
00436 
00437   _actionShowConfig = new QAction(tr("Settings"), this);
00438   connect(_actionShowConfig, SIGNAL(triggered()), this, SLOT(showConfigDialog()));
00439   
00440   _actionShowAbout = new QAction(tr("About"), this);
00441   connect(_actionShowAbout, SIGNAL(triggered()), this, SLOT(showAboutDialog()));
00442 
00443   _actionShowHelp = new QAction(tr("Help"), this);
00444   connect(_actionShowHelp, SIGNAL(triggered()), this, SLOT(showHelpDialog()));
00445   connect(ui.lblHelpBrowser, SIGNAL(clicked()), this, SLOT(showHelpDialog()));
00446 
00447   _actionNewIdentity = new QAction(tr("New Identity"), this);
00448   _actionNewIdentity->setEnabled(false);
00449   connect(_actionNewIdentity, SIGNAL(triggered()), this, SLOT(newIdentity()));
00450 
00451 #if !defined(Q_WS_MAC)
00452   /* Don't give the menu items icons on OS X, since they end up in the
00453    * application menu bar. Menu bar items on OS X typically do not have
00454    * icons. */
00455   _actionStartStopTor->setIcon(QIcon(IMG_START_TOR_16));
00456   _actionExit->setIcon(QIcon(IMG_EXIT));
00457   _actionShowBandwidth->setIcon(QIcon(IMG_BWGRAPH));
00458   _actionShowMessageLog->setIcon(QIcon(IMG_MESSAGELOG));
00459   _actionShowNetworkMap->setIcon(QIcon(IMG_NETWORK));
00460   _actionShowControlPanel->setIcon(QIcon(IMG_CONTROL_PANEL));
00461   _actionShowConfig->setIcon(QIcon(IMG_CONFIG));
00462   _actionShowAbout->setIcon(QIcon(IMG_ABOUT));
00463   _actionShowHelp->setIcon(QIcon(IMG_HELP));
00464   _actionNewIdentity->setIcon(QIcon(IMG_IDENTITY));
00465 #endif
00466 }
00467 
00468 /** Creates a tray icon with a context menu and adds it to the system
00469  * notification area. On Mac, we also set up an application menubar. */
00470 void
00471 MainWindow::createTrayIcon()
00472 {
00473   QMenu *menu = createTrayMenu();
00474 
00475   /* Add the menu it to the tray icon */
00476   _trayIcon.setContextMenu(menu);
00477 
00478   connect(&_trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)),
00479           this, SLOT(trayIconActivated(QSystemTrayIcon::ActivationReason)));
00480 
00481 #if defined(Q_WS_MAC)
00482   createMenuBar();
00483   qt_mac_set_dock_menu(menu);
00484 #endif
00485 }
00486 
00487 /** Creates a QMenu object that contains QActions which compose the system 
00488  * tray menu. */
00489 QMenu* 
00490 MainWindow::createTrayMenu()
00491 {
00492   QMenu *menu = new QMenu(this);
00493   menu->addAction(_actionStartStopTor);
00494   menu->addSeparator();
00495   menu->addAction(_actionShowBandwidth);
00496   menu->addAction(_actionShowMessageLog);
00497   menu->addAction(_actionShowNetworkMap);
00498   menu->addAction(_actionNewIdentity);
00499   menu->addSeparator();
00500   menu->addAction(_actionShowControlPanel);
00501   
00502 #if !defined(Q_WS_MAC)
00503   /* These aren't added to the dock menu on Mac, since they are in the
00504    * standard Mac locations in the menu bar. */
00505   menu->addAction(_actionShowConfig);
00506   menu->addAction(_actionShowHelp);
00507   menu->addAction(_actionShowAbout);
00508   menu->addSeparator();
00509   menu->addAction(_actionExit);
00510 #endif
00511   return menu;
00512 }
00513 
00514 /** Creates a new menubar with no parent, so Qt will use this as the "default
00515  * menubar" on Mac. This adds on to the existing actions from the createMens()
00516  * method. */
00517 void
00518 MainWindow::createMenuBar()
00519 {
00520 #if defined(Q_WS_MAC)
00521   /* Mac users sure like their shortcuts. Actions NOT mentioned below
00522    * don't explicitly need shortcuts, since they are merged to the default
00523    * menubar and get the default shortcuts anyway. */
00524   _actionStartStopTor->setShortcut(tr("Ctrl+T"));
00525   _actionShowBandwidth->setShortcut(tr("Ctrl+B"));
00526   _actionShowMessageLog->setShortcut(tr("Ctrl+L"));
00527   _actionShowNetworkMap->setShortcut(tr("Ctrl+N"));
00528   _actionShowHelp->setShortcut(tr("Ctrl+?"));
00529   _actionNewIdentity->setShortcut(tr("Ctrl+I"));
00530   _actionShowControlPanel->setShortcut(tr("Ctrl+P"));
00531 
00532   /* Force Qt to put merge the Exit, Configure, and About menubar options into
00533    * the default menu, even if Vidalia is currently not speaking English. */
00534   _actionShowConfig->setText("config");
00535   _actionShowConfig->setMenuRole(QAction::PreferencesRole);
00536   _actionShowAbout->setText("about");
00537   _actionShowAbout->setMenuRole(QAction::AboutRole);
00538   _actionExit->setText("quit");
00539   _actionExit->setMenuRole(QAction::QuitRole);
00540 
00541   /* The File, Help, and Configure menus will get merged into the application
00542    * menu by Qt. */
00543   if (_menuBar)
00544     delete _menuBar;
00545   _menuBar = new QMenuBar(0);
00546   QMenu *fileMenu = _menuBar->addMenu("File");
00547   fileMenu->addAction(_actionExit);
00548   fileMenu->addAction(_actionShowConfig);
00549 
00550   QMenu *torMenu = _menuBar->addMenu(tr("Tor"));
00551   torMenu->addAction(_actionStartStopTor);
00552   torMenu->addSeparator();
00553   torMenu->addAction(_actionNewIdentity);
00554 
00555   QMenu *viewMenu = _menuBar->addMenu(tr("View"));
00556   viewMenu->addAction(_actionShowControlPanel);
00557   viewMenu->addSeparator();
00558   viewMenu->addAction(_actionShowBandwidth);
00559   viewMenu->addAction(_actionShowMessageLog);
00560   viewMenu->addAction(_actionShowNetworkMap);
00561   
00562   QMenu *helpMenu = _menuBar->addMenu(tr("Help"));
00563   _actionShowHelp->setText(tr("Vidalia Help"));
00564   helpMenu->addAction(_actionShowHelp);
00565   helpMenu->addAction(_actionShowAbout);
00566 #endif
00567 }
00568 
00569 /** Sets the current tray or dock icon image to <b>iconFile</b>. */
00570 void
00571 MainWindow::setTrayIcon(const QString &iconFile)
00572 {
00573 #if defined(Q_WS_MAC)
00574   QApplication::setWindowIcon(QPixmap(iconFile));
00575 #endif
00576   _trayIcon.setIcon(QIcon(iconFile));
00577 }
00578 
00579 /** Respond to a double-click on the tray icon by opening the Control Panel
00580  * window. */
00581 void
00582 MainWindow::trayIconActivated(QSystemTrayIcon::ActivationReason reason)
00583 {
00584   if (reason == QSystemTrayIcon::DoubleClick)
00585     setVisible(true);
00586 }
00587 
00588 /** Start a web browser when given the directory containing the executable and profile */
00589 void
00590 MainWindow::launchBrowserFromDirectory()
00591 {
00592   VidaliaSettings settings;
00593 
00594   QString browserDirectory = settings.getBrowserDirectory();
00595   QString browserDirectoryFilename = settings.getBrowserExecutable();
00596 
00597   /* Set TZ=UTC (to stop leaking timezone information) and
00598    * MOZ_NO_REMOTE=1 (to allow multiple instances of Firefox */
00599   QStringList env = QProcess::systemEnvironment();
00600   env << "TZ=UTC";
00601   env << "MOZ_NO_REMOTE=1";
00602   _browserProcess->setEnvironment(env);
00603 
00604   /* The browser is in <browserDirectory>/App/Firefox/<browserDirectoryFilename> */
00605   QString browserExecutable =
00606     QDir::toNativeSeparators(browserDirectory + "/App/Firefox/" + browserDirectoryFilename);
00607   /* The profile is in <browserDirectory>/Data/profile */
00608   QString profileDir =
00609     QDir::toNativeSeparators(browserDirectory + "/Data/profile");
00610 
00611   /* Copy the profile directory if it's not already there */
00612   QDir browserDirObj = QDir(browserDirectory);
00613 
00614   /* Copy the profile directory if it's not already there */
00615   if (!browserDirObj.exists("Data/profile")) {
00616     browserDirObj.mkdir("Data/profile");
00617     copy_dir(browserDirectory + "/App/DefaultData/profile", browserDirectory + "/Data/profile");
00618   }
00619 
00620   /* Copy the plugins directory if it's not already there */
00621   if (!browserDirObj.exists("Data/plugins")) {
00622     browserDirObj.mkdir("Data/plugins");
00623     copy_dir(browserDirectory + "/App/DefaultData/plugins", browserDirectory + "/Data/plugins");
00624   }
00625 
00626   /* Build the command line arguments */
00627   QStringList commandLine;
00628   // Is this better or worse than MOZ_NO_REMOTE?
00629   //commandLine << "-no-remote";
00630   commandLine << "-profile";
00631   commandLine << profileDir;
00632 
00633   /* Launch the browser */
00634   _browserProcess->start(browserExecutable, commandLine);
00635 }
00636 
00637 /** Starts the web browser and IM client, if appropriately configured */
00638 void
00639 MainWindow::startSubprocesses()
00640 {
00641   VidaliaSettings settings;
00642   QString subprocess;
00643 
00644   /* Launch the web browser */
00645   if (!(subprocess = settings.getBrowserDirectory()).isEmpty()) {
00646     /* The user has set BrowserDirectory; use this */
00647     launchBrowserFromDirectory();
00648   } else if (!(subprocess = settings.getBrowserExecutable()).isEmpty()) {
00649     /* BrowserDirectory is not set, but BrowserExecutable is; use this */
00650     _browserProcess->setEnvironment(QProcess::systemEnvironment() << "TZ=UTC");
00651     _browserProcess->start(subprocess, QStringList());
00652   }
00653 
00654   /* Launch the IM client */
00655   subprocess = settings.getIMExecutable();
00656 
00657   if (!subprocess.isEmpty())
00658     _imProcess->start(subprocess, QStringList());
00659 }
00660 
00661 /** Called when browser or IM client have exited */
00662 void
00663 MainWindow::onSubprocessFinished(int exitCode, QProcess::ExitStatus exitStatus)
00664 {
00665   Q_UNUSED(exitCode)
00666   Q_UNUSED(exitStatus)
00667 
00668   /* Get path to browser and IM client */
00669   VidaliaSettings settings;
00670   QString browserExecutable = settings.getBrowserExecutable();
00671   QString browserDirectory = settings.getBrowserDirectory();
00672   QString imExecutable = settings.getIMExecutable();
00673 
00674   /* A subprocess is finished if it successfully exited or was never asked to start */
00675   bool browserDone = (browserExecutable.isEmpty()
00676                         && browserDirectory.isEmpty())
00677                         || _browserProcess->isDone();
00678   bool imDone = imExecutable.isEmpty() || _imProcess->isDone();
00679 
00680   /* Exit if both subprocesses are finished */
00681   if (browserDone && imDone) {
00682     if (browserDirectory.isEmpty()) {
00683       /* We are using the standard launcher, exit immediately */
00684       vApp->quit();
00685     } else {
00686       /* We are using the alternate launcher, wait until the browser has really died */
00687       QTimer *browserWatcher = new QTimer(this);
00688       connect(browserWatcher, SIGNAL(timeout()), this, SLOT(onCheckForBrowser()));
00689       browserWatcher->start(2000);
00690     }
00691   }
00692 }
00693 
00694 /** Called periodically to check if the browser is running. If it is not,
00695  * exit Vidalia cleanly */
00696 void
00697 MainWindow::onCheckForBrowser()
00698 {
00699 /* This only works on Windows for now */
00700 #if defined(Q_OS_WIN)
00701 
00702   VidaliaSettings settings;
00703   QString browserDirectoryFilename = settings.getBrowserExecutable();
00704 
00705   /* Get list of running processes */
00706   QHash<qint64, QString> procList = win32_process_list();
00707 
00708   /* On old versions of Windows win32_process_list() will return
00709      an empty list. In this case, just keep Vidalia open */
00710   if (procList.isEmpty()) {
00711     return;
00712   }
00713 
00714   /* Loop over all processes or until we find <browserDirectoryFilename> */
00715   QHashIterator<qint64, QString> i(procList);
00716   while (i.hasNext()) {
00717     i.next();
00718     if (i.value().toLower() == browserDirectoryFilename) {
00719       /* The browser is still running, so Vidalia should keep running too */
00720       return;
00721     }
00722   }
00723 
00724   /* The browser isn't running, exit Vidalia */
00725   vApp->quit();
00726 #endif  
00727 }
00728 
00729 /** Called when the web browser failed to start, for example, because the path
00730  * specified to the web browser executable didn't lead to an executable. */
00731 void
00732 MainWindow::onBrowserFailed(QString errmsg)
00733 {
00734   Q_UNUSED(errmsg);
00735  
00736   /* Display an error message and see if the user wants some help */
00737   VMessageBox::warning(this, tr("Error starting web browser"),
00738               tr("Vidalia was unable to start the configured web browser"),
00739               VMessageBox::Ok|VMessageBox::Default|VMessageBox::Escape);
00740 }
00741 
00742 /** Called when the IM client failed to start, for example, because the path
00743  * specified to the IM client executable didn't lead to an executable. */
00744 void
00745 MainWindow::onIMFailed(QString errmsg)
00746 {
00747   Q_UNUSED(errmsg);
00748  
00749   /* Display an error message and see if the user wants some help */
00750   VMessageBox::warning(this, tr("Error starting IM client"),
00751               tr("Vidalia was unable to start the configured IM client"),
00752               VMessageBox::Ok|VMessageBox::Default|VMessageBox::Escape);
00753 }
00754 
00755 /** Starts the proxy server, if appropriately configured */
00756 void
00757 MainWindow::startProxy()
00758 {
00759   VidaliaSettings settings;
00760   QString executable = settings.getProxyExecutable();
00761   _proxyProcess->start(executable, settings.getProxyExecutableArguments());
00762 }
00763 
00764 /** Called when the proxy server fails to start, for example, because
00765  * the path specified didn't lead to an executable. */
00766 void
00767 MainWindow::onProxyFailed(QString errmsg)
00768 {
00769   Q_UNUSED(errmsg);
00770  
00771   /* Display an error message and see if the user wants some help */
00772   VMessageBox::warning(this, tr("Error starting proxy server"),
00773               tr("Vidalia was unable to start the configured proxy server"),
00774               VMessageBox::Ok|VMessageBox::Default|VMessageBox::Escape);
00775 }
00776 
00777 /** Called when Tor's bootstrapping status changes. <b>bse</b> represents
00778  * Tor's current estimate of its bootstrapping progress. */
00779 void
00780 MainWindow::bootstrapStatusChanged(const BootstrapStatus &bs)
00781 {
00782   int percentComplete = STARTUP_PROGRESS_BOOTSTRAPPING + bs.percentComplete();
00783   bool warn = (bs.severity() == tc::WarnSeverity && 
00784                bs.recommendedAction() != BootstrapStatus::RecommendIgnore);
00785 
00786   QString description;
00787   switch (bs.status()) {
00788     case BootstrapStatus::ConnectingToDirMirror:
00789       description = tr("Connecting to a relay directory");
00790       break;
00791     case BootstrapStatus::HandshakingWithDirMirror:
00792     case BootstrapStatus::CreatingOneHopCircuit:
00793       description = tr("Establishing an encrypted directory connection");
00794       break;
00795     case BootstrapStatus::RequestingNetworkStatus:
00796       description = tr("Retrieving network status");
00797       break;
00798     case BootstrapStatus::LoadingNetworkStatus:
00799       description = tr("Loading network status");
00800       break;
00801     case BootstrapStatus::LoadingAuthorityCertificates:
00802       description = tr("Loading authority certificates");
00803       break;
00804     case BootstrapStatus::RequestingDescriptors:
00805       description = tr("Requesting relay information");
00806       break;
00807     case BootstrapStatus::LoadingDescriptors:
00808       description = tr("Loading relay information");
00809       break;
00810     case BootstrapStatus::ConnectingToEntryGuard:
00811       description = tr("Connecting to the Tor network");
00812       break;
00813     case BootstrapStatus::HandshakingWithEntryGuard:
00814     case BootstrapStatus::EstablishingCircuit:
00815       description = tr("Establishing a Tor circuit");
00816       break;
00817     case BootstrapStatus::BootstrappingDone:
00818       description = tr("Connected to the Tor network!");
00819       warn = false; /* probably false anyway */
00820       break;
00821     default:
00822       description = tr("Unrecognized startup status");
00823   }
00824   if (warn) {
00825     QString reason;
00826     /* Is it really a good idea to translate these? */
00827     switch (bs.reason()) {
00828       case tc::MiscellaneousReason:
00829         reason = tr("miscellaneous");
00830         break;
00831       case tc::IdentityMismatch:
00832         reason = tr("identity mismatch");
00833         break;
00834       case tc::ConnectionDone:
00835         reason = tr("done");
00836         break;
00837       case tc::ConnectionRefused:
00838         reason = tr("connection refused");
00839         break;
00840       case tc::ConnectionTimeout:
00841         reason = tr("connection timeout");
00842         break;
00843       case tc::ConnectionIoError:
00844         reason = tr("read/write error");
00845         break;
00846       case tc::NoRouteToHost:
00847         reason = tr("no route to host");
00848         break;
00849       case tc::ResourceLimitReached:
00850         reason = tr("insufficient resources");
00851         break;
00852       default:
00853         reason = tr("unknown");
00854     }
00855     description += tr(" failed (%1)").arg(reason);
00856   }
00857   setStartupProgress(percentComplete, description);
00858 }
00859 
00860 /** Updates the UI to reflect Tor's current <b>status</b>. Returns the
00861  * previously set TorStatus value.*/
00862 MainWindow::TorStatus
00863 MainWindow::updateTorStatus(TorStatus status)
00864 {
00865   QString statusText, actionText;
00866   QString trayIconFile, statusIconFile;
00867   TorStatus prevStatus = _status;
00868  
00869   vNotice("Tor status changed from '%1' to '%2'.")
00870     .arg(toString(prevStatus)).arg(toString(status));
00871   _status = status;
00872 
00873   if (status == Stopped) {
00874       statusText = tr("Tor is not running");
00875       actionText = tr("Start Tor");
00876       trayIconFile = IMG_TOR_STOPPED;
00877       statusIconFile = IMG_TOR_STOPPED_48;
00878       _actionStartStopTor->setEnabled(true);
00879       _actionStartStopTor->setText(actionText);
00880       _actionStartStopTor->setIcon(QIcon(IMG_START_TOR_16));
00881       ui.lblStartStopTor->setEnabled(true);
00882       ui.lblStartStopTor->setText(actionText);
00883       ui.lblStartStopTor->setPixmap(QPixmap(IMG_START_TOR_48));
00884       ui.lblStartStopTor->setStatusTip(actionText);
00885 
00886       /* XXX: This might need to be smarter if we ever start connecting other
00887        * slots to these triggered() and clicked() signals. */
00888       QObject::disconnect(_actionStartStopTor, SIGNAL(triggered()), this, 0);
00889       QObject::disconnect(ui.lblStartStopTor, SIGNAL(clicked()), this, 0);
00890       connect(_actionStartStopTor, SIGNAL(triggered()), this, SLOT(start()));
00891       connect(ui.lblStartStopTor, SIGNAL(clicked()), this, SLOT(start()));
00892       setStartupProgressVisible(false);
00893   } else if (status == Stopping) {
00894       if (_delayedShutdownStarted) {
00895         statusText = tr("Your relay is shutting down.\n" 
00896                         "Click 'Stop' again to stop your relay now.");
00897       } else {
00898         statusText = tr("Tor is shutting down");
00899       }
00900       trayIconFile = IMG_TOR_STOPPING;
00901       statusIconFile = IMG_TOR_STOPPING_48;
00902       
00903       ui.lblStartStopTor->setStatusTip(tr("Stop Tor Now"));
00904   } else if (status == Started) {
00905       actionText = tr("Stop Tor");
00906       _actionStartStopTor->setEnabled(true);
00907       _actionStartStopTor->setText(actionText);
00908       _actionStartStopTor->setIcon(QIcon(IMG_STOP_TOR_16));
00909       ui.lblStartStopTor->setEnabled(true);
00910       ui.lblStartStopTor->setText(actionText);
00911       ui.lblStartStopTor->setPixmap(QPixmap(IMG_STOP_TOR_48));
00912       ui.lblStartStopTor->setStatusTip(actionText);
00913             
00914       /* XXX: This might need to be smarter if we ever start connecting other
00915        * slots to these triggered() and clicked() signals. */
00916       QObject::disconnect(_actionStartStopTor, SIGNAL(triggered()), this, 0);
00917       QObject::disconnect(ui.lblStartStopTor, SIGNAL(clicked()), this, 0);
00918       connect(_actionStartStopTor, SIGNAL(triggered()), this, SLOT(stop()));
00919       connect(ui.lblStartStopTor, SIGNAL(clicked()), this, SLOT(stop()));
00920   } else if (status == Starting)  {
00921       statusText = tr("Starting the Tor software");
00922       trayIconFile = IMG_TOR_STARTING;
00923       statusIconFile = IMG_TOR_STARTING_48;
00924       _actionStartStopTor->setEnabled(false);
00925       ui.lblStartStopTor->setText(tr("Starting Tor"));
00926       ui.lblStartStopTor->setEnabled(false);
00927       ui.lblStartStopTor->setStatusTip(statusText);
00928       setStartupProgressVisible(true);
00929       setStartupProgress(STARTUP_PROGRESS_STARTING, statusText);
00930   } else if (status == CircuitEstablished) {
00931       statusText = tr("Connected to the Tor network!");
00932       trayIconFile = IMG_TOR_RUNNING;
00933       statusIconFile = IMG_TOR_RUNNING_48;
00934       setStartupProgressVisible(false);
00935   }
00936 
00937   /* Update the tray icon */
00938   if (!trayIconFile.isEmpty()) {
00939     setTrayIcon(trayIconFile);
00940   }
00941   /* Update the status banner on the control panel */
00942   if (!statusIconFile.isEmpty())
00943     ui.lblTorStatusImg->setPixmap(QPixmap(statusIconFile));
00944   if (!statusText.isEmpty()) {
00945     _trayIcon.setToolTip(statusText);
00946     ui.lblTorStatus->setText(statusText);
00947   }
00948   return prevStatus;
00949 }
00950 
00951 /** Called when the "show on startup" checkbox is toggled. */
00952 void
00953 MainWindow::toggleShowOnStartup(bool checked)
00954 {
00955   VidaliaSettings settings;
00956   settings.setShowMainWindowAtStart(checked);
00957 }
00958 
00959 /** Sets the visibility of the startup status description and progress bar to
00960  * <b>visible</b>. */
00961 void
00962 MainWindow::setStartupProgressVisible(bool visible)
00963 {
00964   /* XXX: We force a repaint() to make sure the progress bar and onion status
00965    * icon don't overlap briefly. This is pretty hacktastic. */
00966   if (visible) {
00967     ui.lblTorStatus->setVisible(false);
00968     ui.lblTorStatusImg->setVisible(false);
00969     repaint(ui.grpStatus->rect());
00970     ui.lblStartupProgress->setVisible(true);
00971     ui.progressBar->setVisible(true);
00972   } else {
00973     ui.lblStartupProgress->setVisible(false);
00974     ui.progressBar->setVisible(false);
00975     repaint(ui.grpStatus->rect());
00976     ui.lblTorStatus->setVisible(true);
00977     ui.lblTorStatusImg->setVisible(true);
00978   }
00979 }
00980 
00981 /** Sets the progress bar completion value to <b>progressValue</b> and sets
00982  * the status text to <b>description</b>. */
00983 void
00984 MainWindow::setStartupProgress(int progressValue,
00985                                const QString &description)
00986 {
00987   ui.progressBar->setValue(progressValue);
00988   ui.lblStartupProgress->setText(description);
00989   _trayIcon.setToolTip(description);
00990 }
00991 
00992 /** Attempts to start Tor. If Tor fails to start, then startFailed() will be
00993  * called with an error message containing the reason. */
00994 void 
00995 MainWindow::start()
00996 {
00997   TorSettings settings;
00998   QStringList args;
00999 
01000   updateTorStatus(Starting);
01001 
01002   /* Check if Tor is already running separately */
01003   if (net_test_connect(settings.getControlAddress(),
01004                        settings.getControlPort())) {
01005     started();
01006     return;
01007   }
01008 
01009   /* Make sure the torrc we want to use really exists. */
01010   QString torrc = settings.getTorrc();
01011   if (!torrc.isEmpty()) {
01012     if (!QFileInfo(torrc).exists())
01013       touch_file(torrc, true);
01014     args << "-f" << torrc;
01015   }
01016 
01017   /* Specify Tor's data directory, if different from the default */
01018   QString dataDirectory = settings.getDataDirectory();
01019   if (!dataDirectory.isEmpty())
01020     args << "DataDirectory" << expand_filename(dataDirectory);
01021   
01022   /* Add the intended control port value */
01023   quint16 controlPort = settings.getControlPort();
01024   if (controlPort)
01025     args << "ControlPort" << QString::number(controlPort);
01026   
01027   /* Add the control port authentication arguments */
01028   switch (settings.getAuthenticationMethod()) {
01029     case TorSettings::PasswordAuth:
01030       if (! vApp->readPasswordFromStdin()) {
01031         if (settings.useRandomPassword()) {
01032           _controlPassword = TorSettings::randomPassword();
01033           _useSavedPassword = false;
01034         } else {
01035           _controlPassword = settings.getControlPassword();
01036           _useSavedPassword = true;
01037         }
01038       }
01039       args << "HashedControlPassword"
01040            << TorSettings::hashPassword(_controlPassword);
01041       break;
01042     case TorSettings::CookieAuth:
01043       args << "CookieAuthentication"  << "1";
01044       break;
01045     default:
01046       args << "CookieAuthentication"  << "0";
01047   }
01048 
01049   /* This doesn't get set to false until Tor is actually up and running, so we
01050    * don't yell at users twice if their Tor doesn't even start, due to the fact
01051    * that QProcess::stopped() is emitted even if the process didn't even
01052    * start. */
01053   _isIntentionalExit = true;
01054   /* Kick off the Tor process */
01055   _torControl->start(settings.getExecutable(), args);
01056 }
01057 
01058 /** Called when the Tor process fails to start, for example, because the path
01059  * specified to the Tor executable didn't lead to an executable. */
01060 void
01061 MainWindow::startFailed(QString errmsg)
01062 {
01063   /* We don't display the error message for now, because the error message
01064    * that Qt gives us in this instance is almost always "Unknown Error". That
01065    * will make users sad. */
01066   Q_UNUSED(errmsg);
01067  
01068   updateTorStatus(Stopped);
01069 
01070   /* Display an error message and see if the user wants some help */
01071   int response = VMessageBox::warning(this, tr("Error Starting Tor"),
01072                    tr("Vidalia was unable to start Tor. Check your settings "
01073                         "to ensure the correct name and location of your Tor "
01074                         "executable is specified."),
01075                    VMessageBox::ShowSettings|VMessageBox::Default,
01076                    VMessageBox::Cancel|VMessageBox::Escape,
01077                    VMessageBox::Help);
01078 
01079   if (response == VMessageBox::ShowSettings) {
01080     /* Show the settings dialog so the user can make sure they're pointing to
01081      * the correct Tor. */
01082      showConfigDialog();
01083   } else if (response == VMessageBox::Help) {
01084     /* Show troubleshooting information about starting Tor */
01085     showHelpDialog("troubleshooting.start");
01086   }
01087 }
01088 
01089 /** Slot: Called when the Tor process is started. It will connect the control
01090  * socket and set the icons and tooltips accordingly. */
01091 void 
01092 MainWindow::started()
01093 {
01094   TorSettings settings;
01095 
01096   updateTorStatus(Started);
01097 
01098   /* Now that Tor is running, we want to know if it dies when we didn't want
01099    * it to. */
01100   _isIntentionalExit = false;
01101   /* We haven't started a delayed shutdown yet. */
01102   _delayedShutdownStarted = false;
01103   /* Remember whether we started Tor or not */
01104   _isVidaliaRunningTor = _torControl->isVidaliaRunningTor();
01105   /* Try to connect to Tor's control port */
01106   _torControl->connect(settings.getControlAddress(),
01107                        settings.getControlPort());
01108   setStartupProgress(STARTUP_PROGRESS_CONNECTING, tr("Connecting to Tor"));
01109 }
01110 
01111 /** Called when the connection to the control socket fails. The reason will be
01112  * given in the errmsg parameter. */
01113 void
01114 MainWindow::connectFailed(QString errmsg)
01115 {
01116   /* Ok, ok. It really isn't going to connect. I give up. */
01117   int response = VMessageBox::warning(this, 
01118                    tr("Connection Error"), p(errmsg),
01119                    VMessageBox::Ok|VMessageBox::Default|VMessageBox::Escape, 
01120                    VMessageBox::Retry, VMessageBox::Help);
01121 
01122 
01123   if (response == VMessageBox::Retry) {
01124     /* Let's give it another try. */
01125     TorSettings settings;
01126     _torControl->connect(settings.getControlAddress(),
01127                          settings.getControlPort());
01128   } else {
01129     /* Show the help browser (if requested) */
01130     if (response == VMessageBox::Help)
01131       showHelpDialog("troubleshooting.connect");
01132     /* Since Vidalia can't connect, we can't really do much, so stop Tor. */
01133     _torControl->stop();
01134   }
01135 }
01136 
01137 /** Disconnects the control socket and stops the Tor process. */
01138 bool
01139 MainWindow::stop()
01140 {
01141   ServerSettings server(_torControl);
01142   QString errmsg;
01143   TorStatus prevStatus;
01144   bool rc;
01145 
01146   /* If we're running a server, give users the option of terminating
01147    * gracefully so clients have time to find new servers. */
01148   if (server.isServerEnabled() && !_delayedShutdownStarted) {
01149     /* Ask the user if they want to shutdown nicely. */
01150     int response = VMessageBox::question(this, tr("Relaying is Enabled"),
01151                      tr("You are currently running a relay. "
01152                         "Terminating your relay will interrupt any "
01153                         "open connections from clients.\n\n"
01154                         "Would you like to shutdown gracefully and "
01155                         "give clients time to find a new relay?"),
01156                         VMessageBox::Yes|VMessageBox::Default, 
01157                         VMessageBox::No, 
01158                         VMessageBox::Cancel|VMessageBox::Escape);
01159     if (response == VMessageBox::Yes)
01160       _delayedShutdownStarted = true;
01161     else if (response == VMessageBox::Cancel)
01162       return false;
01163   }
01164   
01165   prevStatus = updateTorStatus(Stopping);  
01166   if (_delayedShutdownStarted) {
01167     /* Start a delayed shutdown */
01168     rc = _torControl->signal(TorSignal::Shutdown, &errmsg);
01169   } else {
01170     /* We want Tor to stop now, regardless of whether we're a server. */
01171     _isIntentionalExit = true;
01172     rc = _torControl->stop(&errmsg);
01173   }
01174   
01175   if (!rc) {
01176     /* We couldn't tell Tor to stop, for some reason. */
01177     int response = VMessageBox::warning(this, tr("Error Shutting Down"),
01178                      p(tr("Vidalia was unable to stop the Tor software.")) 
01179                        + p(errmsg),
01180                      VMessageBox::Ok|VMessageBox::Default|VMessageBox::Escape, 
01181                      VMessageBox::Help);
01182       
01183     if (response == VMessageBox::Help) {
01184       /* Show some troubleshooting help */
01185       showHelpDialog("troubleshooting.stop");
01186     }
01187     /* Tor is still running since stopping failed */
01188     _isIntentionalExit = false;
01189     _delayedShutdownStarted = false;
01190     updateTorStatus(prevStatus);
01191   }
01192   return rc;
01193 }
01194 
01195 /** Slot: Called when the Tor process has exited. It will adjust the tray
01196  * icons and tooltips accordingly. */
01197 void 
01198 MainWindow::stopped(int exitCode, QProcess::ExitStatus exitStatus)
01199 {
01200   updateTorStatus(Stopped);
01201 
01202   /* If we didn't intentionally close Tor, then check to see if it crashed or
01203    * if it closed itself and returned an error code. */
01204   if (!_isIntentionalExit) {
01205     /* A quick overview of Tor's code tells me that if it catches a SIGTERM or
01206      * SIGINT, Tor will exit(0). We might need to change this warning message
01207      * if this turns out to not be the case. */
01208     if (exitStatus == QProcess::CrashExit || exitCode != 0) {
01209       int ret = VMessageBox::warning(this, tr("Unexpected Error"),
01210                   tr("Vidalia detected that the Tor software exited "
01211                      "unexpectedly.\n\n"
01212                      "Please check the message log for recent "
01213                      "warning or error messages."),
01214                   VMessageBox::Ok|VMessageBox::Escape, 
01215                   VMessageBox::ShowLog|VMessageBox::Default,
01216                   VMessageBox::Help);
01217       if (ret == VMessageBox::ShowLog)
01218         _messageLog->showWindow();  
01219       else if (ret == VMessageBox::Help)
01220         showHelpDialog("troubleshooting.torexited");
01221     }
01222   }
01223 }
01224 
01225 /** Called when the control socket has successfully connected to Tor. */
01226 void
01227 MainWindow::connected()
01228 {
01229   authenticate();
01230 }
01231 
01232 /** Called when Vidalia wants to disconnect from a Tor it did not start. */
01233 void
01234 MainWindow::disconnect()
01235 {
01236   _torControl->disconnect();
01237 }
01238 
01239 /** Called when the control socket has been disconnected. */
01240 void
01241 MainWindow::disconnected()
01242 {
01243   if (!_isVidaliaRunningTor) {
01244     /* If we didn't start our own Tor process, interpret losing the control
01245      * connection as "Tor is stopped". */
01246     updateTorStatus(Stopped);
01247   }
01248   
01249   /*XXX We should warn here if we get disconnected when we didn't intend to */
01250   _actionNewIdentity->setEnabled(false);
01251   ui.lblNewIdentity->setEnabled(false);
01252   _isVidaliaRunningTor = false;
01253 }
01254 
01255 /** Attempts to authenticate to Tor's control port, depending on the
01256  * authentication method specified in TorSettings::getAuthenticationMethod().
01257  */
01258 bool
01259 MainWindow::authenticate()
01260 {
01261   TorSettings::AuthenticationMethod authMethod;
01262   TorSettings settings;
01263   ProtocolInfo pi;
01264   
01265   updateTorStatus(Authenticating);
01266   setStartupProgress(STARTUP_PROGRESS_AUTHENTICATING,
01267                      tr("Authenticating to Tor"));
01268 
01269   authMethod = settings.getAuthenticationMethod(); 
01270   pi = _torControl->protocolInfo();
01271   if (!pi.isEmpty()) {
01272     QStringList authMethods = pi.authMethods();
01273     if (authMethods.contains("COOKIE"))
01274       authMethod = TorSettings::CookieAuth;
01275     else if (authMethods.contains("HASHEDPASSWORD"))
01276       authMethod = TorSettings::PasswordAuth;
01277     else if (authMethods.contains("NULL"))
01278       authMethod = TorSettings::NullAuth;
01279   }
01280   
01281   if (authMethod == TorSettings::CookieAuth) {
01282     /* Try to load an auth cookie and send it to Tor */
01283     QByteArray cookie = loadControlCookie(pi.cookieAuthFile());
01284     while (cookie.isEmpty()) {
01285       /* Prompt the user to find their control_auth_cookie */
01286       int ret = VMessageBox::question(this,
01287                   tr("Cookie Authentication Required"),
01288                   p(tr("The Tor software requires Vidalia to send the "
01289                        "contents of an authentication cookie, but Vidalia "
01290                        "was unable to find one."))
01291                   + p(tr("Would you like to browse for the file "
01292                          "'control_auth_cookie' yourself?")),
01293                 VMessageBox::Browse|VMessageBox::Default,
01294                 VMessageBox::Cancel|VMessageBox::Escape);
01295       
01296       if (ret == VMessageBox::Cancel)
01297         goto cancel;
01298       QString cookieDir = QFileDialog::getOpenFileName(this,
01299                             tr("Data Directory"),
01300                             settings.getDataDirectory(),
01301                             tr("Control Cookie (control_auth_cookie)"));
01302       if (cookieDir.isEmpty())
01303         goto cancel;
01304       cookieDir = QFileInfo(cookieDir).absolutePath();
01305       cookie = loadControlCookie(cookieDir);
01306     }
01307     vNotice("Authenticating using 'cookie' authentication.");
01308     return _torControl->authenticate(cookie);
01309   } else if (authMethod == TorSettings::PasswordAuth) {
01310     /* Get the control password and send it to Tor */
01311     vNotice("Authenticating using 'hashed password' authentication.");
01312     if (_useSavedPassword) {
01313       TorSettings settings;
01314       _controlPassword = settings.getControlPassword();
01315     }
01316     return _torControl->authenticate(_controlPassword);
01317   }
01318   /* No authentication. Send an empty password. */
01319   vNotice("Authenticating using 'null' authentication.");
01320   return _torControl->authenticate(QString(""));
01321 
01322 cancel:
01323   vWarn("Cancelling control authentication attempt.");
01324   if (_isVidaliaRunningTor)
01325     stop();
01326   else
01327     disconnect();
01328   return false;
01329 }
01330 
01331 /** Called when Vidalia has successfully authenticated to Tor. */
01332 void
01333 MainWindow::authenticated()
01334 {
01335   ServerSettings serverSettings(_torControl);
01336   QString errmsg;
01337 
01338   updateTorStatus(Authenticated);
01339   
01340   /* If Tor doesn't have bootstrapping events, then update the current
01341    * status string and bump the progress bar along a bit. */
01342   if (_torControl->getTorVersion() < 0x020101) {
01343     setStartupProgress(STARTUP_PROGRESS_CIRCUITBUILD,
01344                        tr("Connecting to the Tor network"));
01345   }
01346   
01347   /* Let people click on their beloved "New Identity" button */
01348   _actionNewIdentity->setEnabled(true);
01349   ui.lblNewIdentity->setEnabled(true);
01350 
01351   /* Register for any pertinent asynchronous events. */
01352   if (!_torControl->setEvents(&errmsg)) {
01353     VMessageBox::warning(this, tr("Error Registering for Events"),
01354       p(tr("Vidalia was unable to register for some events. "
01355            "Many of Vidalia's features may be unavailable."))
01356          + p(errmsg),
01357       VMessageBox::Ok);
01358   } else {
01359     /* Stop reading from Tor's stdout immediately, since we successfully
01360      * registered for Tor events, including any desired log events. */
01361     _torControl->closeTorStdout();
01362   }
01363 
01364   /* Configure UPnP port forwarding if needed */
01365   serverSettings.configurePortForwarding();
01366 
01367   /* Check if Tor has a circuit established */
01368   if (_torControl->isCircuitEstablished())
01369     circuitEstablished();
01370   /* Check the status of Tor's version */
01371   if (_torControl->getTorVersion() >= 0x020001)
01372     checkTorVersion();
01373   if (_torControl->getTorVersion() >= 0x020102) {
01374     BootstrapStatus status = _torControl->bootstrapStatus();
01375     if (status.isValid())
01376       bootstrapStatusChanged(status);
01377   }
01378 }
01379 
01380 /** Called when Vidalia fails to authenticate to Tor. The failure reason is
01381  * specified in <b>errmsg</b>. */
01382 void
01383 MainWindow::authenticationFailed(QString errmsg)
01384 {
01385   bool retry = false;
01386   
01387   vWarn("Authentication failed: %1").arg(errmsg);
01388 
01389   /* Parsing log messages is evil, but we're left with little option */
01390   if (errmsg.contains("Password did not match")) {
01391     ControlPasswordInputDialog dlg;
01392     connect(&dlg, SIGNAL(helpRequested(QString)),
01393             this, SLOT(showHelpDialog(QString)));
01394 
01395     qint64 torPid = 0;
01396 
01397 #if defined(Q_OS_WIN32)
01398     QHash<qint64, QString> procs = process_list();
01399     foreach (qint64 pid, procs.keys()) {
01400       if (! procs.value(pid).compare("tor.exe", Qt::CaseInsensitive)) {
01401         torPid = pid;
01402         break;
01403       }
01404     }
01405     dlg.setResetEnabled(torPid > 0);
01406 #else
01407     dlg.setResetEnabled(false);
01408 #endif
01409 
01410     int ret = dlg.exec();
01411     if (ret == QDialogButtonBox::Ok) {
01412       if (dlg.isSavePasswordChecked()) {
01413         TorSettings settings;
01414         settings.setAuthenticationMethod(TorSettings::PasswordAuth);
01415         settings.setUseRandomPassword(false);
01416         settings.setControlPassword(dlg.password());
01417         _useSavedPassword = true;
01418       } else {
01419         _controlPassword = dlg.password();
01420         _useSavedPassword = false;
01421       }
01422       retry = true;
01423     } else if (ret == QDialogButtonBox::Reset) {
01424       if (! process_kill(torPid)) {
01425         VMessageBox::warning(this,
01426           tr("Password Reset Failed"),
01427           p(tr("Vidalia tried to reset Tor's control password, but was not "
01428                "able to restart the Tor software. Please check your Task "
01429                "Manager to ensure there are no other Tor processes running.")),
01430                VMessageBox::Ok|VMessageBox::Default);
01431       } else {
01432         retry = true;
01433       }
01434     }
01435   } else {
01436     /* Something else went wrong */
01437     int ret = VMessageBox::warning(this, 
01438                 tr("Authentication Error"),
01439                 p(tr("Vidalia was unable to authenticate to the Tor software. "
01440                      "(%1)").arg(errmsg)) + 
01441                 p(tr("Please check your control port authentication "
01442                      "settings.")),
01443                 VMessageBox::ShowSettings|VMessageBox::Default,
01444                 VMessageBox::Cancel|VMessageBox::Escape);
01445     
01446     if (ret == VMessageBox::ShowSettings)
01447       showConfigDialog(ConfigDialog::Advanced);
01448   }
01449   
01450   if (_torControl->isRunning())
01451     if (_isVidaliaRunningTor) 
01452       stop();
01453     else
01454       disconnect();
01455   if (retry)
01456     start();
01457 }
01458 
01459 /** Searches for and attempts to load the control authentication cookie. This
01460  * assumes the cookie is named 'control_auth_cookie'. If <b>cookiePath</b> is
01461  * empty, this method will search some default locations depending on the
01462  * current platform. <b>cookiePath</b> can point to either a cookie file or a
01463  * directory containing the cookie file. */
01464 QByteArray
01465 MainWindow::loadControlCookie(QString cookiePath)
01466 {
01467   QFile authCookie;
01468   QStringList pathList;
01469 
01470   if (!cookiePath.isEmpty()) {
01471     pathList << cookiePath;
01472   } else {
01473     /* Try some default locations */
01474     TorSettings settings;
01475     QString dataDir = settings.getDataDirectory();
01476     if (!dataDir.isEmpty())
01477       pathList << dataDir;
01478       
01479 #if defined(Q_WS_WIN)
01480     pathList << expand_filename("%APPDATA%\\Tor");
01481 #else
01482     pathList << expand_filename("~/.tor");
01483 #endif
01484   }
01485   
01486   /* Search for the cookie file */
01487   foreach (QString path, pathList) {
01488     QString cookieFile = QFileInfo(path).isFile() ?
01489                           path : path + "/control_auth_cookie";
01490     vDebug("Checking for authentication cookie in '%1'").arg(cookieFile);
01491     if (!QFileInfo(cookieFile).exists())
01492       continue;
01493     
01494     authCookie.setFileName(cookieFile);
01495     if (authCookie.open(QIODevice::ReadOnly)) {
01496       vInfo("Reading authentication cookie from '%1'").arg(cookieFile);
01497       return authCookie.readAll();
01498     } else {
01499       vWarn("Couldn't open cookie file '%1': %2")
01500         .arg(cookieFile).arg(authCookie.errorString());
01501     }
01502   }
01503   vWarn("Couldn't find a readable authentication cookie.");
01504   return QByteArray();
01505 }
01506 
01507 /** Called when Tor has successfully established a circuit. */
01508 void
01509 MainWindow::circuitEstablished()
01510 {
01511   updateTorStatus(CircuitEstablished);
01512   setStartupProgress(ui.progressBar->maximum(),
01513                      tr("Connected to the Tor network!"));
01514   startSubprocesses();
01515 
01516 #if defined(USE_AUTOUPDATE)
01517   VidaliaSettings settings;
01518   if (settings.isAutoUpdateEnabled()) {
01519     QDateTime lastCheckedAt = settings.lastCheckedForUpdates();
01520     if (UpdateProcess::shouldCheckForUpdates(lastCheckedAt)) {
01521       /* Initiate a background check for updates now */
01522       _updateTimer.stop();
01523       checkForUpdates();
01524     }
01525   }
01526 #endif
01527 }
01528 
01529 /** Checks the status of the current version of Tor to see if it's old,
01530  * unrecommended, or obsolete. */
01531 void
01532 MainWindow::checkTorVersion()
01533 {
01534   QString status;
01535   if (_torControl->getInfo("status/version/current", status)) {
01536     if (!status.compare("old", Qt::CaseInsensitive)
01537           || !status.compare("unrecommended", Qt::CaseInsensitive)
01538           || !status.compare("obsolete", Qt::CaseInsensitive)) {
01539       displayTorVersionWarning();
01540     }
01541   }
01542 }
01543 
01544 /** Called when Tor thinks its version is old or unrecommended, and displays
01545  * a message notifying the user. */
01546 void
01547 MainWindow::dangerousTorVersion(tc::TorVersionStatus reason,
01548                                 const QString &current,
01549                                 const QStringList &recommended)
01550 {
01551   Q_UNUSED(current);
01552   Q_UNUSED(recommended);
01553 
01554   if (reason == tc::ObsoleteTorVersion
01555         || reason == tc::UnrecommendedTorVersion)
01556     displayTorVersionWarning();
01557 }
01558 
01559 /** Called when Tor thinks its version is old or unrecommended, and displays a
01560  * message notifying the user. */
01561 void
01562 MainWindow::displayTorVersionWarning()
01563 {
01564   static bool alreadyWarned = false;
01565 
01566   if (!alreadyWarned) {
01567 #if !defined(USE_AUTOUPDATE)
01568     QString website = "https://www.torproject.org/";
01569 # if QT_VERSION >= 0x040200
01570     website = QString("<a href=\"%1\">%1</a>").arg(website);
01571 # endif
01572 
01573     VMessageBox::information(this, tr("Tor Update Available"),
01574       p(tr("The currently installed version of Tor is out of date or no longer "
01575            "recommended. Please visit the Tor website to download the latest "
01576            "version.")) + p(tr("Tor website: %1").arg(website)),
01577       VMessageBox::Ok);
01578 #else
01579     int ret = VMessageBox::information(this,
01580                 tr("Tor Update Available"),
01581                 p(tr("The currently installed version of Tor is out of date "
01582                      "or no longer recommended."))
01583                   + p(tr("Would you like to check if a newer package is "
01584                          "available for installation?")),
01585                 VMessageBox::Yes|VMessageBox::Default,
01586                 VMessageBox::No|VMessageBox::Escape);
01587 
01588     if (ret == VMessageBox::Yes)
01589       checkForUpdatesWithUi();
01590 #endif
01591     alreadyWarned = true;
01592   }
01593 }
01594 
01595 /** Called when Tor thinks the user has tried to connect to a port that
01596  * typically is used for unencrypted applications. Warns the user and allows
01597  * them to ignore future warnings on <b>port</b>. It is possible that Tor
01598  * will produce multiple asynchronous status events warning of dangerous ports
01599  * while the message box is displayed (for example, while the user is away
01600  * from the keyboard), so subsequent messages will be discarded until the
01601  * first message box is dismissed. */
01602 void
01603 MainWindow::warnDangerousPort(quint16 port, bool rejected)
01604 {
01605   static QMessageBox *dlg = 0;
01606 
01607   /* Don't display another message box until the first one is dismissed */
01608   if (dlg)
01609     return;
01610 
01611   QString application;
01612   switch (port) {
01613     case  23:
01614      application = tr(", probably Telnet, ");
01615      break;
01616 
01617     case 109:
01618     case 110:
01619     case 143:
01620       application = tr(", probably an email client, "); 
01621       break;
01622 
01623     default:
01624       application = " ";
01625   }
01626 
01627   QString text = tr("One of your applications%1appears to be making a "
01628                     "potentially unencrypted and unsafe connection to port %2.")
01629                     .arg(application).arg(port);
01630 
01631   QString extraText = p(tr("Anything sent over this connection could be "
01632                            "monitored. Please check your application's "
01633                            "configuration and use only encrypted protocols, "
01634                            "such as SSL, if possible."));
01635   if (rejected) {
01636     extraText.append(p(tr("Tor has automatically closed your connection in "
01637                           "order to protect your anonymity.")));
01638   }
01639 
01640   dlg = new QMessageBox(QMessageBox::Warning,
01641                         tr("Potentially Unsafe Connection"), text,
01642                         QMessageBox::Ok | QMessageBox::Ignore);
01643   dlg->setInformativeText(extraText);
01644   dlg->setDefaultButton(QMessageBox::Ok);
01645   dlg->setEscapeButton(QMessageBox::Ok);
01646 
01647   int ret = dlg->exec();
01648   if (ret == QMessageBox::Ignore) {
01649     TorControl *tc = Vidalia::torControl();
01650     TorSettings settings;
01651     QStringList portList;
01652     QList<quint16> ports;
01653     int idx;
01654 
01655     ports = settings.getWarnPlaintextPorts();
01656     idx   = ports.indexOf(port);
01657     if (idx >= 0) {
01658       ports.removeAt(idx);
01659       settings.setWarnPlaintextPorts(ports);
01660 
01661       foreach (quint16 port, ports) {
01662         portList << QString::number(port);
01663       }
01664       tc->setConf("WarnPlaintextPorts", portList.join(","));
01665       portList.clear();
01666     }
01667 
01668     ports = settings.getRejectPlaintextPorts();
01669     idx   = ports.indexOf(port);
01670     if (idx >= 0) {
01671       ports.removeAt(idx);
01672       settings.setRejectPlaintextPorts(ports);
01673 
01674       foreach (quint16 port, ports) {
01675         portList << QString::number(port);
01676       }
01677       tc->setConf("RejectPlaintextPorts", portList.join(","));
01678     }
01679   }
01680   delete dlg;
01681   dlg = 0;
01682 }
01683 
01684 /** Creates and displays Vidalia's About dialog. */
01685 void
01686 MainWindow::showAboutDialog()
01687 {
01688   AboutDialog dlg(this);
01689   dlg.exec();
01690 }
01691 
01692 /** Displays the help browser and displays the most recently viewed help
01693  * topic. */
01694 void
01695 MainWindow::showHelpDialog()
01696 {
01697   showHelpDialog(QString());
01698 }
01699 
01700 /**< Shows the help browser and displays the given help <b>topic</b>. */
01701 void
01702 MainWindow::showHelpDialog(const QString &topic)
01703 {
01704   static HelpBrowser *helpBrowser = 0;
01705   if (!helpBrowser)
01706     helpBrowser = new HelpBrowser(this);
01707   helpBrowser->showWindow(topic);
01708 }
01709 
01710 /** Creates and displays the Configuration dialog with the current page set to
01711  * <b>page</b>. */
01712 void
01713 MainWindow::showConfigDialog(ConfigDialog::Page page)
01714 {
01715   _configDialog->showWindow(page);
01716 }
01717 
01718 /** Displays the Configuration dialog, set to the Server page. */
01719 void
01720 MainWindow::showServerConfigDialog()
01721 {
01722   showConfigDialog(ConfigDialog::Server);
01723 }
01724 
01725 /** Called when the user selects the "New Identity" action from the menu. */
01726 void
01727 MainWindow::newIdentity()
01728 {
01729   QString errmsg;
01730 
01731   /* Send the NEWNYM signal. If message balloons are supported and the NEWNYM
01732    * is successful, we will show the result as a balloon. Otherwise, we'll 
01733    * just use a message box. */
01734   if (_torControl->signal(TorSignal::NewNym, &errmsg)) {
01735     /* NEWNYM signal was successful */
01736     QString title = tr("New Identity");
01737     QString message = tr("All subsequent connections will "
01738                          "appear to be different than your "
01739                          "old connections.");
01740 
01741     /* Disable the New Identity button for MIN_NEWIDENTITY_INTERVAL */
01742     _actionNewIdentity->setEnabled(false);
01743     ui.lblNewIdentity->setEnabled(false);
01744     QTimer::singleShot(MIN_NEWIDENTITY_INTERVAL, 
01745                        this, SLOT(enableNewIdentity()));
01746 
01747     if (QSystemTrayIcon::supportsMessages())
01748       _trayIcon.showMessage(title, message, QSystemTrayIcon::Information);
01749     else
01750       VMessageBox::information(this, title, message, VMessageBox::Ok);
01751   } else {
01752     /* NEWNYM signal failed */
01753     VMessageBox::warning(this, 
01754       tr("Failed to Create New Identity"), errmsg, VMessageBox::Ok);
01755   }
01756 }
01757 
01758 /** Re-enables the 'New Identity' button after a delay from the previous time
01759  * 'New Identity' was used. */
01760 void
01761 MainWindow::enableNewIdentity()
01762 {
01763   if (_torControl->isConnected()) {
01764     _actionNewIdentity->setEnabled(true);
01765     ui.lblNewIdentity->setEnabled(true);
01766   }
01767 }
01768 
01769 /** Converts a TorStatus enum value to a string for debug logging purposes. */
01770 QString
01771 MainWindow::toString(TorStatus status)
01772 {
01773   switch (status) {
01774     /* These strings only appear in debug logs, so they should not be
01775      * translated. */
01776     case Unset:     return "Unset";
01777     case Stopping:  return "Stopping";
01778     case Stopped:   return "Stopped";
01779     case Starting:  return "Starting";
01780     case Started:   return "Started";
01781     case Authenticating:  return "Authenticating";
01782     case Authenticated:   return "Authenticated";
01783     case CircuitEstablished: return "Circuit Established";
01784     default: break;
01785   }
01786   return "Unknown";
01787 }
01788 
01789 #if defined(USE_MINIUPNPC)
01790 /** Called when a UPnP error occurs. */
01791 void
01792 MainWindow::upnpError(UPNPControl::UPNPError error)
01793 {
01794   Q_UNUSED(error);
01795 
01796 #if 0
01797   /* XXX: Is there a better way to do this? Currently, this could get called
01798    * if there is an error when testing UPnP support, and again when attempting
01799    * to reset the UPnP state when the test dialog is closed. The user would
01800    * not be amused with all the warning dialogs. */
01801 
01802   VMessageBox::warning(this,
01803     tr("Port Forwarding Failed"),
01804     p(tr("Vidalia was unable to configure automatic port forwarding."))
01805       + p(UPNPControl::Instance()->errorString()),
01806     VMessageBox::Ok);
01807 #endif
01808 }
01809 #endif
01810 
01811 #if defined(USE_AUTOUPDATE)
01812 /** Called when the user clicks the 'Check Now' button in the General
01813  * settings page. */
01814 void
01815 MainWindow::checkForUpdatesWithUi()
01816 {
01817   checkForUpdates(true);
01818 }
01819 
01820 /** Called when the update interval timer expires, notifying Vidalia that
01821  * we should check for updates again. */
01822 void
01823 MainWindow::checkForUpdates(bool showProgress)
01824 {
01825   VidaliaSettings settings;
01826 
01827   if (_updateProcess.isRunning()) {
01828     if (showProgress) {
01829       /* A check for updates is already in progress, so just bring the update
01830        * progress dialog into focus.
01831        */
01832       _updateProgressDialog.show();
01833     }
01834   } else {
01835     /* If Tor is running and bootstrapped, then use Tor to check for updates */
01836     if (_torControl->isRunning() && _torControl->circuitEstablished())
01837       _updateProcess.setSocksPort(_torControl->getSocksPort());
01838     else
01839       _updateProcess.setSocksPort(0);
01840 
01841     /* Initialize the UpdateProgressDialog and display it, if necessary. */
01842     _updateProgressDialog.setStatus(UpdateProgressDialog::CheckingForUpdates);
01843     if (showProgress)
01844       _updateProgressDialog.show();
01845 
01846     /* Initiate a check for available software updates. This check will
01847      * be done in the background, notifying the user only if there are
01848      * updates to be installed.
01849      */
01850     _updateProcess.checkForUpdates(UpdateProcess::TorBundleInfo);
01851 
01852     /* Remember when we last checked for software updates */
01853     settings.setLastCheckedForUpdates(QDateTime::currentDateTime().toUTC());
01854 
01855     /* Restart the "Check for Updates" timer */
01856     _updateTimer.start(UpdateProcess::checkForUpdatesInterval() * 1000);
01857   }
01858 }
01859 
01860 /** Called when the check for software updates fails. */
01861 void
01862 MainWindow::checkForUpdatesFailed(const QString &errmsg)
01863 {
01864   if (_updateProgressDialog.isVisible()) {
01865     _updateProgressDialog.hide();
01866     VMessageBox::warning(this, tr("Update Failed"), errmsg,
01867                          VMessageBox::Ok);
01868   }
01869 }
01870 
01871 /** Called when there is an update available for installation. */
01872 void
01873 MainWindow::updatesAvailable(UpdateProcess::BundleInfo bi,
01874                              const PackageList &packageList)
01875 {
01876   vInfo("%1 software update(s) available").arg(packageList.size());
01877   if (packageList.size() > 0) {
01878     UpdatesAvailableDialog dlg(packageList, &_updateProgressDialog);
01879 
01880     switch (dlg.exec()) {
01881       case UpdatesAvailableDialog::InstallUpdatesNow:
01882         installUpdates(bi);
01883         break;
01884 
01885       default:
01886         _updateProgressDialog.hide();
01887         break;
01888     }
01889   } else {
01890     if (_updateProgressDialog.isVisible()) {
01891       _updateProgressDialog.hide();
01892       VMessageBox::information(this, tr("Your software is up to date"),
01893                                tr("There are no new Tor software packages "
01894                                   "available for your computer at this time."),
01895                                VMessageBox::Ok);
01896     }
01897   }
01898 }
01899 
01900 /** Stops Tor (if necessary), installs any available for <b>bi</b>, and
01901  * restarts Tor (if necessary). */
01902 void
01903 MainWindow::installUpdates(UpdateProcess::BundleInfo bi)
01904 {
01905   _updateProgressDialog.setStatus(UpdateProgressDialog::InstallingUpdates);
01906   _updateProgressDialog.show();
01907 
01908   if (_isVidaliaRunningTor) {
01909     _restartTorAfterUpgrade = true;
01910     _isIntentionalExit = true;
01911     _torControl->stop();
01912   } else {
01913     _restartTorAfterUpgrade = false;
01914   }
01915   _updateProcess.installUpdates(bi);
01916 }
01917 
01918 /** Called when all <b>numUpdates</b> software updates have been installed
01919  * successfully. */
01920 void
01921 MainWindow::updatesInstalled(int numUpdates)
01922 {
01923   _updateProgressDialog.setStatus(UpdateProgressDialog::UpdatesInstalled);
01924   _updateProgressDialog.show();
01925 
01926   if (_restartTorAfterUpgrade)
01927     start();
01928 }
01929 
01930 /** Called when an update fails to install. <b>errmsg</b> contains details
01931  * about the failure. */
01932 void
01933 MainWindow::installUpdatesFailed(const QString &errmsg)
01934 {
01935   _updateProgressDialog.hide();
01936 
01937   VMessageBox::warning(this, tr("Installation Failed"),
01938                        p(tr("Vidalia was unable to install your software updates."))
01939                          + p(tr("The following error occurred:")) 
01940                          + p(errmsg),
01941                        VMessageBox::Ok);
01942 
01943   if (_restartTorAfterUpgrade)
01944     start();
01945 }
01946 
01947 #endif
01948