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 4227 2010-02-25 04:03:32Z 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 #else
00576   _trayIcon.setIcon(QIcon(iconFile));
00577 #endif
00578 }
00579 
00580 /** Respond to a double-click on the tray icon by opening the Control Panel
00581  * window. */
00582 void
00583 MainWindow::trayIconActivated(QSystemTrayIcon::ActivationReason reason)
00584 {
00585   if (reason == QSystemTrayIcon::DoubleClick)
00586     setVisible(true);
00587 }
00588 
00589 /** Start a web browser when given the directory containing the executable and profile */
00590 void
00591 MainWindow::launchBrowserFromDirectory()
00592 {
00593   VidaliaSettings settings;
00594 
00595   QString browserDirectory = settings.getBrowserDirectory();
00596   QString browserDirectoryFilename = settings.getBrowserExecutable();
00597 
00598   /* Set TZ=UTC (to stop leaking timezone information) and
00599    * MOZ_NO_REMOTE=1 (to allow multiple instances of Firefox */
00600   QStringList env = QProcess::systemEnvironment();
00601   env << "TZ=UTC";
00602   env << "MOZ_NO_REMOTE=1";
00603   _browserProcess->setEnvironment(env);
00604 
00605   /* The browser is in <browserDirectory>/App/Firefox/<browserDirectoryFilename> */
00606   QString browserExecutable =
00607     QDir::toNativeSeparators(browserDirectory + "/App/Firefox/" + browserDirectoryFilename);
00608   /* The profile is in <browserDirectory>/Data/profile */
00609   QString profileDir =
00610     QDir::toNativeSeparators(browserDirectory + "/Data/profile");
00611 
00612   /* Copy the profile directory if it's not already there */
00613   QDir browserDirObj = QDir(browserDirectory);
00614 
00615   /* Copy the profile directory if it's not already there */
00616   if (!browserDirObj.exists("Data/profile")) {
00617     browserDirObj.mkdir("Data/profile");
00618     copy_dir(browserDirectory + "/App/DefaultData/profile", browserDirectory + "/Data/profile");
00619   }
00620 
00621   /* Copy the plugins directory if it's not already there */
00622   if (!browserDirObj.exists("Data/plugins")) {
00623     browserDirObj.mkdir("Data/plugins");
00624     copy_dir(browserDirectory + "/App/DefaultData/plugins", browserDirectory + "/Data/plugins");
00625   }
00626 
00627   /* Build the command line arguments */
00628   QStringList commandLine;
00629   // Is this better or worse than MOZ_NO_REMOTE?
00630   //commandLine << "-no-remote";
00631   commandLine << "-profile";
00632   commandLine << profileDir;
00633 
00634   /* Launch the browser */
00635   _browserProcess->start(browserExecutable, commandLine);
00636 }
00637 
00638 /** Starts the web browser and IM client, if appropriately configured */
00639 void
00640 MainWindow::startSubprocesses()
00641 {
00642   VidaliaSettings settings;
00643   QString subprocess;
00644 
00645   /* Launch the web browser */
00646   if (!(subprocess = settings.getBrowserDirectory()).isEmpty()) {
00647     /* The user has set BrowserDirectory; use this */
00648     launchBrowserFromDirectory();
00649   } else if (!(subprocess = settings.getBrowserExecutable()).isEmpty()) {
00650     /* BrowserDirectory is not set, but BrowserExecutable is; use this */
00651     _browserProcess->setEnvironment(QProcess::systemEnvironment() << "TZ=UTC");
00652     _browserProcess->start(subprocess, QStringList());
00653   }
00654 
00655   /* Launch the IM client */
00656   subprocess = settings.getIMExecutable();
00657 
00658   if (!subprocess.isEmpty())
00659     _imProcess->start(subprocess, QStringList());
00660 }
00661 
00662 /** Called when browser or IM client have exited */
00663 void
00664 MainWindow::onSubprocessFinished(int exitCode, QProcess::ExitStatus exitStatus)
00665 {
00666   Q_UNUSED(exitCode)
00667   Q_UNUSED(exitStatus)
00668 
00669   /* Get path to browser and IM client */
00670   VidaliaSettings settings;
00671   QString browserExecutable = settings.getBrowserExecutable();
00672   QString browserDirectory = settings.getBrowserDirectory();
00673   QString imExecutable = settings.getIMExecutable();
00674 
00675   /* A subprocess is finished if it successfully exited or was never asked to start */
00676   bool browserDone = (browserExecutable.isEmpty()
00677                         && browserDirectory.isEmpty())
00678                         || _browserProcess->isDone();
00679   bool imDone = imExecutable.isEmpty() || _imProcess->isDone();
00680 
00681   /* Exit if both subprocesses are finished */
00682   if (browserDone && imDone) {
00683     if (browserDirectory.isEmpty()) {
00684       /* We are using the standard launcher, exit immediately */
00685       vApp->quit();
00686     } else {
00687       /* We are using the alternate launcher, wait until the browser has really died */
00688       QTimer *browserWatcher = new QTimer(this);
00689       connect(browserWatcher, SIGNAL(timeout()), this, SLOT(onCheckForBrowser()));
00690       browserWatcher->start(2000);
00691     }
00692   }
00693 }
00694 
00695 /** Called periodically to check if the browser is running. If it is not,
00696  * exit Vidalia cleanly */
00697 void
00698 MainWindow::onCheckForBrowser()
00699 {
00700 /* This only works on Windows for now */
00701 #if defined(Q_OS_WIN)
00702 
00703   VidaliaSettings settings;
00704   QString browserDirectoryFilename = settings.getBrowserExecutable();
00705 
00706   /* Get list of running processes */
00707   QHash<qint64, QString> procList = win32_process_list();
00708 
00709   /* On old versions of Windows win32_process_list() will return
00710      an empty list. In this case, just keep Vidalia open */
00711   if (procList.isEmpty()) {
00712     return;
00713   }
00714 
00715   /* Loop over all processes or until we find <browserDirectoryFilename> */
00716   QHashIterator<qint64, QString> i(procList);
00717   while (i.hasNext()) {
00718     i.next();
00719     if (i.value().toLower() == browserDirectoryFilename) {
00720       /* The browser is still running, so Vidalia should keep running too */
00721       return;
00722     }
00723   }
00724 
00725   /* The browser isn't running, exit Vidalia */
00726   vApp->quit();
00727 #endif  
00728 }
00729 
00730 /** Called when the web browser failed to start, for example, because the path
00731  * specified to the web browser executable didn't lead to an executable. */
00732 void
00733 MainWindow::onBrowserFailed(QString errmsg)
00734 {
00735   Q_UNUSED(errmsg);
00736  
00737   /* Display an error message and see if the user wants some help */
00738   VMessageBox::warning(this, tr("Error starting web browser"),
00739               tr("Vidalia was unable to start the configured web browser"),
00740               VMessageBox::Ok|VMessageBox::Default|VMessageBox::Escape);
00741 }
00742 
00743 /** Called when the IM client failed to start, for example, because the path
00744  * specified to the IM client executable didn't lead to an executable. */
00745 void
00746 MainWindow::onIMFailed(QString errmsg)
00747 {
00748   Q_UNUSED(errmsg);
00749  
00750   /* Display an error message and see if the user wants some help */
00751   VMessageBox::warning(this, tr("Error starting IM client"),
00752               tr("Vidalia was unable to start the configured IM client"),
00753               VMessageBox::Ok|VMessageBox::Default|VMessageBox::Escape);
00754 }
00755 
00756 /** Starts the proxy server, if appropriately configured */
00757 void
00758 MainWindow::startProxy()
00759 {
00760   VidaliaSettings settings;
00761   QString executable = settings.getProxyExecutable();
00762   _proxyProcess->start(executable, settings.getProxyExecutableArguments());
00763 }
00764 
00765 /** Called when the proxy server fails to start, for example, because
00766  * the path specified didn't lead to an executable. */
00767 void
00768 MainWindow::onProxyFailed(QString errmsg)
00769 {
00770   Q_UNUSED(errmsg);
00771  
00772   /* Display an error message and see if the user wants some help */
00773   VMessageBox::warning(this, tr("Error starting proxy server"),
00774               tr("Vidalia was unable to start the configured proxy server"),
00775               VMessageBox::Ok|VMessageBox::Default|VMessageBox::Escape);
00776 }
00777 
00778 /** Called when Tor's bootstrapping status changes. <b>bse</b> represents
00779  * Tor's current estimate of its bootstrapping progress. */
00780 void
00781 MainWindow::bootstrapStatusChanged(const BootstrapStatus &bs)
00782 {
00783   int percentComplete = STARTUP_PROGRESS_BOOTSTRAPPING + bs.percentComplete();
00784   bool warn = (bs.severity() == tc::WarnSeverity && 
00785                bs.recommendedAction() != BootstrapStatus::RecommendIgnore);
00786 
00787   QString description;
00788   switch (bs.status()) {
00789     case BootstrapStatus::ConnectingToDirMirror:
00790       description = tr("Connecting to a relay directory");
00791       break;
00792     case BootstrapStatus::HandshakingWithDirMirror:
00793     case BootstrapStatus::CreatingOneHopCircuit:
00794       description = tr("Establishing an encrypted directory connection");
00795       break;
00796     case BootstrapStatus::RequestingNetworkStatus:
00797       description = tr("Retrieving network status");
00798       break;
00799     case BootstrapStatus::LoadingNetworkStatus:
00800       description = tr("Loading network status");
00801       break;
00802     case BootstrapStatus::LoadingAuthorityCertificates:
00803       description = tr("Loading authority certificates");
00804       break;
00805     case BootstrapStatus::RequestingDescriptors:
00806       description = tr("Requesting relay information");
00807       break;
00808     case BootstrapStatus::LoadingDescriptors:
00809       description = tr("Loading relay information");
00810       break;
00811     case BootstrapStatus::ConnectingToEntryGuard:
00812       description = tr("Connecting to the Tor network");
00813       break;
00814     case BootstrapStatus::HandshakingWithEntryGuard:
00815     case BootstrapStatus::EstablishingCircuit:
00816       description = tr("Establishing a Tor circuit");
00817       break;
00818     case BootstrapStatus::BootstrappingDone:
00819       description = tr("Connected to the Tor network!");
00820       warn = false; /* probably false anyway */
00821       break;
00822     default:
00823       description = tr("Unrecognized startup status");
00824   }
00825   if (warn) {
00826     QString reason;
00827     /* Is it really a good idea to translate these? */
00828     switch (bs.reason()) {
00829       case tc::MiscellaneousReason:
00830         reason = tr("miscellaneous");
00831         break;
00832       case tc::IdentityMismatch:
00833         reason = tr("identity mismatch");
00834         break;
00835       case tc::ConnectionDone:
00836         reason = tr("done");
00837         break;
00838       case tc::ConnectionRefused:
00839         reason = tr("connection refused");
00840         break;
00841       case tc::ConnectionTimeout:
00842         reason = tr("connection timeout");
00843         break;
00844       case tc::ConnectionIoError:
00845         reason = tr("read/write error");
00846         break;
00847       case tc::NoRouteToHost:
00848         reason = tr("no route to host");
00849         break;
00850       case tc::ResourceLimitReached:
00851         reason = tr("insufficient resources");
00852         break;
00853       default:
00854         reason = tr("unknown");
00855     }
00856     description += tr(" failed (%1)").arg(reason);
00857   }
00858   setStartupProgress(percentComplete, description);
00859 }
00860 
00861 /** Updates the UI to reflect Tor's current <b>status</b>. Returns the
00862  * previously set TorStatus value.*/
00863 MainWindow::TorStatus
00864 MainWindow::updateTorStatus(TorStatus status)
00865 {
00866   QString statusText, actionText;
00867   QString trayIconFile, statusIconFile;
00868   TorStatus prevStatus = _status;
00869  
00870   vNotice("Tor status changed from '%1' to '%2'.")
00871     .arg(toString(prevStatus)).arg(toString(status));
00872   _status = status;
00873 
00874   if (status == Stopped) {
00875       statusText = tr("Tor is not running");
00876       actionText = tr("Start Tor");
00877       trayIconFile = IMG_TOR_STOPPED;
00878       statusIconFile = IMG_TOR_STOPPED_48;
00879       _actionStartStopTor->setEnabled(true);
00880       _actionStartStopTor->setText(actionText);
00881       _actionStartStopTor->setIcon(QIcon(IMG_START_TOR_16));
00882       ui.lblStartStopTor->setEnabled(true);
00883       ui.lblStartStopTor->setText(actionText);
00884       ui.lblStartStopTor->setPixmap(QPixmap(IMG_START_TOR_48));
00885       ui.lblStartStopTor->setStatusTip(actionText);
00886 
00887       /* XXX: This might need to be smarter if we ever start connecting other
00888        * slots to these triggered() and clicked() signals. */
00889       QObject::disconnect(_actionStartStopTor, SIGNAL(triggered()), this, 0);
00890       QObject::disconnect(ui.lblStartStopTor, SIGNAL(clicked()), this, 0);
00891       connect(_actionStartStopTor, SIGNAL(triggered()), this, SLOT(start()));
00892       connect(ui.lblStartStopTor, SIGNAL(clicked()), this, SLOT(start()));
00893       setStartupProgressVisible(false);
00894   } else if (status == Stopping) {
00895       if (_delayedShutdownStarted) {
00896         statusText = tr("Your relay is shutting down.\n" 
00897                         "Click 'Stop' again to stop your relay now.");
00898       } else {
00899         statusText = tr("Tor is shutting down");
00900       }
00901       trayIconFile = IMG_TOR_STOPPING;
00902       statusIconFile = IMG_TOR_STOPPING_48;
00903       
00904       ui.lblStartStopTor->setStatusTip(tr("Stop Tor Now"));
00905   } else if (status == Started) {
00906       actionText = tr("Stop Tor");
00907       _actionStartStopTor->setEnabled(true);
00908       _actionStartStopTor->setText(actionText);
00909       _actionStartStopTor->setIcon(QIcon(IMG_STOP_TOR_16));
00910       ui.lblStartStopTor->setEnabled(true);
00911       ui.lblStartStopTor->setText(actionText);
00912       ui.lblStartStopTor->setPixmap(QPixmap(IMG_STOP_TOR_48));
00913       ui.lblStartStopTor->setStatusTip(actionText);
00914             
00915       /* XXX: This might need to be smarter if we ever start connecting other
00916        * slots to these triggered() and clicked() signals. */
00917       QObject::disconnect(_actionStartStopTor, SIGNAL(triggered()), this, 0);
00918       QObject::disconnect(ui.lblStartStopTor, SIGNAL(clicked()), this, 0);
00919       connect(_actionStartStopTor, SIGNAL(triggered()), this, SLOT(stop()));
00920       connect(ui.lblStartStopTor, SIGNAL(clicked()), this, SLOT(stop()));
00921   } else if (status == Starting)  {
00922       statusText = tr("Starting the Tor software");
00923       trayIconFile = IMG_TOR_STARTING;
00924       statusIconFile = IMG_TOR_STARTING_48;
00925       _actionStartStopTor->setEnabled(false);
00926       ui.lblStartStopTor->setText(tr("Starting Tor"));
00927       ui.lblStartStopTor->setEnabled(false);
00928       ui.lblStartStopTor->setStatusTip(statusText);
00929       setStartupProgressVisible(true);
00930       setStartupProgress(STARTUP_PROGRESS_STARTING, statusText);
00931   } else if (status == CircuitEstablished) {
00932       statusText = tr("Connected to the Tor network!");
00933       trayIconFile = IMG_TOR_RUNNING;
00934       statusIconFile = IMG_TOR_RUNNING_48;
00935       setStartupProgressVisible(false);
00936   }
00937 
00938   /* Update the tray icon */
00939   if (!trayIconFile.isEmpty()) {
00940     setTrayIcon(trayIconFile);
00941   }
00942   /* Update the status banner on the control panel */
00943   if (!statusIconFile.isEmpty())
00944     ui.lblTorStatusImg->setPixmap(QPixmap(statusIconFile));
00945   if (!statusText.isEmpty()) {
00946     _trayIcon.setToolTip(statusText);
00947     ui.lblTorStatus->setText(statusText);
00948   }
00949   return prevStatus;
00950 }
00951 
00952 /** Called when the "show on startup" checkbox is toggled. */
00953 void
00954 MainWindow::toggleShowOnStartup(bool checked)
00955 {
00956   VidaliaSettings settings;
00957   settings.setShowMainWindowAtStart(checked);
00958 }
00959 
00960 /** Sets the visibility of the startup status description and progress bar to
00961  * <b>visible</b>. */
00962 void
00963 MainWindow::setStartupProgressVisible(bool visible)
00964 {
00965   /* XXX: We force a repaint() to make sure the progress bar and onion status
00966    * icon don't overlap briefly. This is pretty hacktastic. */
00967   if (visible) {
00968     ui.lblTorStatus->setVisible(false);
00969     ui.lblTorStatusImg->setVisible(false);
00970     repaint(ui.grpStatus->rect());
00971     ui.lblStartupProgress->setVisible(true);
00972     ui.progressBar->setVisible(true);
00973   } else {
00974     ui.lblStartupProgress->setVisible(false);
00975     ui.progressBar->setVisible(false);
00976     repaint(ui.grpStatus->rect());
00977     ui.lblTorStatus->setVisible(true);
00978     ui.lblTorStatusImg->setVisible(true);
00979   }
00980 }
00981 
00982 /** Sets the progress bar completion value to <b>progressValue</b> and sets
00983  * the status text to <b>description</b>. */
00984 void
00985 MainWindow::setStartupProgress(int progressValue,
00986                                const QString &description)
00987 {
00988   ui.progressBar->setValue(progressValue);
00989   ui.lblStartupProgress->setText(description);
00990   _trayIcon.setToolTip(description);
00991 }
00992 
00993 /** Attempts to start Tor. If Tor fails to start, then startFailed() will be
00994  * called with an error message containing the reason. */
00995 void 
00996 MainWindow::start()
00997 {
00998   TorSettings settings;
00999   QStringList args;
01000 
01001   updateTorStatus(Starting);
01002 
01003   /* Check if Tor is already running separately */
01004   if (net_test_connect(settings.getControlAddress(),
01005                        settings.getControlPort())) {
01006     started();
01007     return;
01008   }
01009 
01010   /* Make sure the torrc we want to use really exists. */
01011   QString torrc = settings.getTorrc();
01012   if (!torrc.isEmpty()) {
01013     if (!QFileInfo(torrc).exists())
01014       touch_file(torrc, true);
01015     args << "-f" << torrc;
01016   }
01017 
01018   /* Specify Tor's data directory, if different from the default */
01019   QString dataDirectory = settings.getDataDirectory();
01020   if (!dataDirectory.isEmpty())
01021     args << "DataDirectory" << expand_filename(dataDirectory);
01022   
01023   /* Add the intended control port value */
01024   quint16 controlPort = settings.getControlPort();
01025   if (controlPort)
01026     args << "ControlPort" << QString::number(controlPort);
01027   
01028   /* Add the control port authentication arguments */
01029   switch (settings.getAuthenticationMethod()) {
01030     case TorSettings::PasswordAuth:
01031       if (! vApp->readPasswordFromStdin()) {
01032         if (settings.useRandomPassword()) {
01033           _controlPassword = TorSettings::randomPassword();
01034           _useSavedPassword = false;
01035         } else {
01036           _controlPassword = settings.getControlPassword();
01037           _useSavedPassword = true;
01038         }
01039       }
01040       args << "HashedControlPassword"
01041            << TorSettings::hashPassword(_controlPassword);
01042       break;
01043     case TorSettings::CookieAuth:
01044       args << "CookieAuthentication"  << "1";
01045       break;
01046     default:
01047       args << "CookieAuthentication"  << "0";
01048   }
01049 
01050   /* This doesn't get set to false until Tor is actually up and running, so we
01051    * don't yell at users twice if their Tor doesn't even start, due to the fact
01052    * that QProcess::stopped() is emitted even if the process didn't even
01053    * start. */
01054   _isIntentionalExit = true;
01055   /* Kick off the Tor process */
01056   _torControl->start(settings.getExecutable(), args);
01057 }
01058 
01059 /** Called when the Tor process fails to start, for example, because the path
01060  * specified to the Tor executable didn't lead to an executable. */
01061 void
01062 MainWindow::startFailed(QString errmsg)
01063 {
01064   /* We don't display the error message for now, because the error message
01065    * that Qt gives us in this instance is almost always "Unknown Error". That
01066    * will make users sad. */
01067   Q_UNUSED(errmsg);
01068  
01069   updateTorStatus(Stopped);
01070 
01071   /* Display an error message and see if the user wants some help */
01072   int response = VMessageBox::warning(this, tr("Error Starting Tor"),
01073                    tr("Vidalia was unable to start Tor. Check your settings "
01074                         "to ensure the correct name and location of your Tor "
01075                         "executable is specified."),
01076                    VMessageBox::ShowSettings|VMessageBox::Default,
01077                    VMessageBox::Cancel|VMessageBox::Escape,
01078                    VMessageBox::Help);
01079 
01080   if (response == VMessageBox::ShowSettings) {
01081     /* Show the settings dialog so the user can make sure they're pointing to
01082      * the correct Tor. */
01083      showConfigDialog();
01084   } else if (response == VMessageBox::Help) {
01085     /* Show troubleshooting information about starting Tor */
01086     showHelpDialog("troubleshooting.start");
01087   }
01088 }
01089 
01090 /** Slot: Called when the Tor process is started. It will connect the control
01091  * socket and set the icons and tooltips accordingly. */
01092 void 
01093 MainWindow::started()
01094 {
01095   TorSettings settings;
01096 
01097   updateTorStatus(Started);
01098 
01099   /* Now that Tor is running, we want to know if it dies when we didn't want
01100    * it to. */
01101   _isIntentionalExit = false;
01102   /* We haven't started a delayed shutdown yet. */
01103   _delayedShutdownStarted = false;
01104   /* Remember whether we started Tor or not */
01105   _isVidaliaRunningTor = _torControl->isVidaliaRunningTor();
01106   /* Try to connect to Tor's control port */
01107   _torControl->connect(settings.getControlAddress(),
01108                        settings.getControlPort());
01109   setStartupProgress(STARTUP_PROGRESS_CONNECTING, tr("Connecting to Tor"));
01110 }
01111 
01112 /** Called when the connection to the control socket fails. The reason will be
01113  * given in the errmsg parameter. */
01114 void
01115 MainWindow::connectFailed(QString errmsg)
01116 {
01117   /* Ok, ok. It really isn't going to connect. I give up. */
01118   int response = VMessageBox::warning(this, 
01119                    tr("Connection Error"), p(errmsg),
01120                    VMessageBox::Ok|VMessageBox::Default|VMessageBox::Escape, 
01121                    VMessageBox::Retry, VMessageBox::Help);
01122 
01123 
01124   if (response == VMessageBox::Retry) {
01125     /* Let's give it another try. */
01126     TorSettings settings;
01127     _torControl->connect(settings.getControlAddress(),
01128                          settings.getControlPort());
01129   } else {
01130     /* Show the help browser (if requested) */
01131     if (response == VMessageBox::Help)
01132       showHelpDialog("troubleshooting.connect");
01133     /* Since Vidalia can't connect, we can't really do much, so stop Tor. */
01134     _torControl->stop();
01135   }
01136 }
01137 
01138 /** Disconnects the control socket and stops the Tor process. */
01139 bool
01140 MainWindow::stop()
01141 {
01142   ServerSettings server(_torControl);
01143   QString errmsg;
01144   TorStatus prevStatus;
01145   bool rc;
01146 
01147   /* If we're running a server, give users the option of terminating
01148    * gracefully so clients have time to find new servers. */
01149   if (server.isServerEnabled() && !_delayedShutdownStarted) {
01150     /* Ask the user if they want to shutdown nicely. */
01151     int response = VMessageBox::question(this, tr("Relaying is Enabled"),
01152                      tr("You are currently running a relay. "
01153                         "Terminating your relay will interrupt any "
01154                         "open connections from clients.\n\n"
01155                         "Would you like to shutdown gracefully and "
01156                         "give clients time to find a new relay?"),
01157                         VMessageBox::Yes|VMessageBox::Default, 
01158                         VMessageBox::No, 
01159                         VMessageBox::Cancel|VMessageBox::Escape);
01160     if (response == VMessageBox::Yes)
01161       _delayedShutdownStarted = true;
01162     else if (response == VMessageBox::Cancel)
01163       return false;
01164   }
01165   
01166   prevStatus = updateTorStatus(Stopping);  
01167   if (_delayedShutdownStarted) {
01168     /* Start a delayed shutdown */
01169     rc = _torControl->signal(TorSignal::Shutdown, &errmsg);
01170   } else {
01171     /* We want Tor to stop now, regardless of whether we're a server. */
01172     _isIntentionalExit = true;
01173     rc = _torControl->stop(&errmsg);
01174   }
01175   
01176   if (!rc) {
01177     /* We couldn't tell Tor to stop, for some reason. */
01178     int response = VMessageBox::warning(this, tr("Error Shutting Down"),
01179                      p(tr("Vidalia was unable to stop the Tor software.")) 
01180                        + p(errmsg),
01181                      VMessageBox::Ok|VMessageBox::Default|VMessageBox::Escape, 
01182                      VMessageBox::Help);
01183       
01184     if (response == VMessageBox::Help) {
01185       /* Show some troubleshooting help */
01186       showHelpDialog("troubleshooting.stop");
01187     }
01188     /* Tor is still running since stopping failed */
01189     _isIntentionalExit = false;
01190     _delayedShutdownStarted = false;
01191     updateTorStatus(prevStatus);
01192   }
01193   return rc;
01194 }
01195 
01196 /** Slot: Called when the Tor process has exited. It will adjust the tray
01197  * icons and tooltips accordingly. */
01198 void 
01199 MainWindow::stopped(int exitCode, QProcess::ExitStatus exitStatus)
01200 {
01201   updateTorStatus(Stopped);
01202 
01203   /* If we didn't intentionally close Tor, then check to see if it crashed or
01204    * if it closed itself and returned an error code. */
01205   if (!_isIntentionalExit) {
01206     /* A quick overview of Tor's code tells me that if it catches a SIGTERM or
01207      * SIGINT, Tor will exit(0). We might need to change this warning message
01208      * if this turns out to not be the case. */
01209     if (exitStatus == QProcess::CrashExit || exitCode != 0) {
01210       int ret = VMessageBox::warning(this, tr("Unexpected Error"),
01211                   tr("Vidalia detected that the Tor software exited "
01212                      "unexpectedly.\n\n"
01213                      "Please check the message log for recent "
01214                      "warning or error messages."),
01215                   VMessageBox::Ok|VMessageBox::Escape, 
01216                   VMessageBox::ShowLog|VMessageBox::Default,
01217                   VMessageBox::Help);
01218       if (ret == VMessageBox::ShowLog)
01219         _messageLog->showWindow();  
01220       else if (ret == VMessageBox::Help)
01221         showHelpDialog("troubleshooting.torexited");
01222     }
01223   }
01224 }
01225 
01226 /** Called when the control socket has successfully connected to Tor. */
01227 void
01228 MainWindow::connected()
01229 {
01230   authenticate();
01231 }
01232 
01233 /** Called when Vidalia wants to disconnect from a Tor it did not start. */
01234 void
01235 MainWindow::disconnect()
01236 {
01237   _torControl->disconnect();
01238 }
01239 
01240 /** Called when the control socket has been disconnected. */
01241 void
01242 MainWindow::disconnected()
01243 {
01244   if (!_isVidaliaRunningTor) {
01245     /* If we didn't start our own Tor process, interpret losing the control
01246      * connection as "Tor is stopped". */
01247     updateTorStatus(Stopped);
01248   }
01249   
01250   /*XXX We should warn here if we get disconnected when we didn't intend to */
01251   _actionNewIdentity->setEnabled(false);
01252   ui.lblNewIdentity->setEnabled(false);
01253   _isVidaliaRunningTor = false;
01254 }
01255 
01256 /** Attempts to authenticate to Tor's control port, depending on the
01257  * authentication method specified in TorSettings::getAuthenticationMethod().
01258  */
01259 bool
01260 MainWindow::authenticate()
01261 {
01262   TorSettings::AuthenticationMethod authMethod;
01263   TorSettings settings;
01264   ProtocolInfo pi;
01265   
01266   updateTorStatus(Authenticating);
01267   setStartupProgress(STARTUP_PROGRESS_AUTHENTICATING,
01268                      tr("Authenticating to Tor"));
01269 
01270   authMethod = settings.getAuthenticationMethod(); 
01271   pi = _torControl->protocolInfo();
01272   if (!pi.isEmpty()) {
01273     QStringList authMethods = pi.authMethods();
01274     if (authMethods.contains("COOKIE"))
01275       authMethod = TorSettings::CookieAuth;
01276     else if (authMethods.contains("HASHEDPASSWORD"))
01277       authMethod = TorSettings::PasswordAuth;
01278     else if (authMethods.contains("NULL"))
01279       authMethod = TorSettings::NullAuth;
01280   }
01281   
01282   if (authMethod == TorSettings::CookieAuth) {
01283     /* Try to load an auth cookie and send it to Tor */
01284     QByteArray cookie = loadControlCookie(pi.cookieAuthFile());
01285     while (cookie.isEmpty()) {
01286       /* Prompt the user to find their control_auth_cookie */
01287       int ret = VMessageBox::question(this,
01288                   tr("Cookie Authentication Required"),
01289                   p(tr("The Tor software requires Vidalia to send the "
01290                        "contents of an authentication cookie, but Vidalia "
01291                        "was unable to find one."))
01292                   + p(tr("Would you like to browse for the file "
01293                          "'control_auth_cookie' yourself?")),
01294                 VMessageBox::Browse|VMessageBox::Default,
01295                 VMessageBox::Cancel|VMessageBox::Escape);
01296       
01297       if (ret == VMessageBox::Cancel)
01298         goto cancel;
01299       QString cookieDir = QFileDialog::getOpenFileName(this,
01300                             tr("Data Directory"),
01301                             settings.getDataDirectory(),
01302                             tr("Control Cookie (control_auth_cookie)"));
01303       if (cookieDir.isEmpty())
01304         goto cancel;
01305       cookieDir = QFileInfo(cookieDir).absolutePath();
01306       cookie = loadControlCookie(cookieDir);
01307     }
01308     vNotice("Authenticating using 'cookie' authentication.");
01309     return _torControl->authenticate(cookie);
01310   } else if (authMethod == TorSettings::PasswordAuth) {
01311     /* Get the control password and send it to Tor */
01312     vNotice("Authenticating using 'hashed password' authentication.");
01313     if (_useSavedPassword) {
01314       TorSettings settings;
01315       _controlPassword = settings.getControlPassword();
01316     }
01317     return _torControl->authenticate(_controlPassword);
01318   }
01319   /* No authentication. Send an empty password. */
01320   vNotice("Authenticating using 'null' authentication.");
01321   return _torControl->authenticate(QString(""));
01322 
01323 cancel:
01324   vWarn("Cancelling control authentication attempt.");
01325   if (_isVidaliaRunningTor)
01326     stop();
01327   else
01328     disconnect();
01329   return false;
01330 }
01331 
01332 /** Called when Vidalia has successfully authenticated to Tor. */
01333 void
01334 MainWindow::authenticated()
01335 {
01336   ServerSettings serverSettings(_torControl);
01337   QString errmsg;
01338 
01339   updateTorStatus(Authenticated);
01340   
01341   /* If Tor doesn't have bootstrapping events, then update the current
01342    * status string and bump the progress bar along a bit. */
01343   if (_torControl->getTorVersion() < 0x020101) {
01344     setStartupProgress(STARTUP_PROGRESS_CIRCUITBUILD,
01345                        tr("Connecting to the Tor network"));
01346   }
01347   
01348   /* Let people click on their beloved "New Identity" button */
01349   _actionNewIdentity->setEnabled(true);
01350   ui.lblNewIdentity->setEnabled(true);
01351 
01352   /* Register for any pertinent asynchronous events. */
01353   if (!_torControl->setEvents(&errmsg)) {
01354     VMessageBox::warning(this, tr("Error Registering for Events"),
01355       p(tr("Vidalia was unable to register for some events. "
01356            "Many of Vidalia's features may be unavailable."))
01357          + p(errmsg),
01358       VMessageBox::Ok);
01359   } else {
01360     /* Stop reading from Tor's stdout immediately, since we successfully
01361      * registered for Tor events, including any desired log events. */
01362     _torControl->closeTorStdout();
01363   }
01364 
01365   /* Configure UPnP port forwarding if needed */
01366   serverSettings.configurePortForwarding();
01367 
01368   /* Check if Tor has a circuit established */
01369   if (_torControl->isCircuitEstablished())
01370     circuitEstablished();
01371   /* Check the status of Tor's version */
01372   if (_torControl->getTorVersion() >= 0x020001)
01373     checkTorVersion();
01374   if (_torControl->getTorVersion() >= 0x020102) {
01375     BootstrapStatus status = _torControl->bootstrapStatus();
01376     if (status.isValid())
01377       bootstrapStatusChanged(status);
01378   }
01379 }
01380 
01381 /** Called when Vidalia fails to authenticate to Tor. The failure reason is
01382  * specified in <b>errmsg</b>. */
01383 void
01384 MainWindow::authenticationFailed(QString errmsg)
01385 {
01386   bool retry = false;
01387   
01388   vWarn("Authentication failed: %1").arg(errmsg);
01389 
01390   /* Parsing log messages is evil, but we're left with little option */
01391   if (errmsg.contains("Password did not match")) {
01392     ControlPasswordInputDialog dlg;
01393     connect(&dlg, SIGNAL(helpRequested(QString)),
01394             this, SLOT(showHelpDialog(QString)));
01395 
01396     qint64 torPid = 0;
01397 
01398 #if defined(Q_OS_WIN32)
01399     QHash<qint64, QString> procs = process_list();
01400     foreach (qint64 pid, procs.keys()) {
01401       if (! procs.value(pid).compare("tor.exe", Qt::CaseInsensitive)) {
01402         torPid = pid;
01403         break;
01404       }
01405     }
01406     dlg.setResetEnabled(torPid > 0);
01407 #else
01408     dlg.setResetEnabled(false);
01409 #endif
01410 
01411     int ret = dlg.exec();
01412     if (ret == QDialogButtonBox::Ok) {
01413       if (dlg.isSavePasswordChecked()) {
01414         TorSettings settings;
01415         settings.setAuthenticationMethod(TorSettings::PasswordAuth);
01416         settings.setUseRandomPassword(false);
01417         settings.setControlPassword(dlg.password());
01418         _useSavedPassword = true;
01419       } else {
01420         _controlPassword = dlg.password();
01421         _useSavedPassword = false;
01422       }
01423       retry = true;
01424     } else if (ret == QDialogButtonBox::Reset) {
01425       if (! process_kill(torPid)) {
01426         VMessageBox::warning(this,
01427           tr("Password Reset Failed"),
01428           p(tr("Vidalia tried to reset Tor's control password, but was not "
01429                "able to restart the Tor software. Please check your Task "
01430                "Manager to ensure there are no other Tor processes running.")),
01431                VMessageBox::Ok|VMessageBox::Default);
01432       } else {
01433         retry = true;
01434       }
01435     }
01436   } else {
01437     /* Something else went wrong */
01438     int ret = VMessageBox::warning(this, 
01439                 tr("Authentication Error"),
01440                 p(tr("Vidalia was unable to authenticate to the Tor software. "
01441                      "(%1)").arg(errmsg)) + 
01442                 p(tr("Please check your control port authentication "
01443                      "settings.")),
01444                 VMessageBox::ShowSettings|VMessageBox::Default,
01445                 VMessageBox::Cancel|VMessageBox::Escape);
01446     
01447     if (ret == VMessageBox::ShowSettings)
01448       showConfigDialog(ConfigDialog::Advanced);
01449   }
01450   
01451   if (_torControl->isRunning())
01452     if (_isVidaliaRunningTor) 
01453       stop();
01454     else
01455       disconnect();
01456   if (retry)
01457     start();
01458 }
01459 
01460 /** Searches for and attempts to load the control authentication cookie. This
01461  * assumes the cookie is named 'control_auth_cookie'. If <b>cookiePath</b> is
01462  * empty, this method will search some default locations depending on the
01463  * current platform. <b>cookiePath</b> can point to either a cookie file or a
01464  * directory containing the cookie file. */
01465 QByteArray
01466 MainWindow::loadControlCookie(QString cookiePath)
01467 {
01468   QFile authCookie;
01469   QStringList pathList;
01470 
01471   if (!cookiePath.isEmpty()) {
01472     pathList << cookiePath;
01473   } else {
01474     /* Try some default locations */
01475     TorSettings settings;
01476     QString dataDir = settings.getDataDirectory();
01477     if (!dataDir.isEmpty())
01478       pathList << dataDir;
01479       
01480 #if defined(Q_WS_WIN)
01481     pathList << expand_filename("%APPDATA%\\Tor");
01482 #else
01483     pathList << expand_filename("~/.tor");
01484 #endif
01485   }
01486   
01487   /* Search for the cookie file */
01488   foreach (QString path, pathList) {
01489     QString cookieFile = QFileInfo(path).isFile() ?
01490                           path : path + "/control_auth_cookie";
01491     vDebug("Checking for authentication cookie in '%1'").arg(cookieFile);
01492     if (!QFileInfo(cookieFile).exists())
01493       continue;
01494     
01495     authCookie.setFileName(cookieFile);
01496     if (authCookie.open(QIODevice::ReadOnly)) {
01497       vInfo("Reading authentication cookie from '%1'").arg(cookieFile);
01498       return authCookie.readAll();
01499     } else {
01500       vWarn("Couldn't open cookie file '%1': %2")
01501         .arg(cookieFile).arg(authCookie.errorString());
01502     }
01503   }
01504   vWarn("Couldn't find a readable authentication cookie.");
01505   return QByteArray();
01506 }
01507 
01508 /** Called when Tor has successfully established a circuit. */
01509 void
01510 MainWindow::circuitEstablished()
01511 {
01512   updateTorStatus(CircuitEstablished);
01513   setStartupProgress(ui.progressBar->maximum(),
01514                      tr("Connected to the Tor network!"));
01515   startSubprocesses();
01516 
01517 #if defined(USE_AUTOUPDATE)
01518   VidaliaSettings settings;
01519   if (settings.isAutoUpdateEnabled()) {
01520     QDateTime lastCheckedAt = settings.lastCheckedForUpdates();
01521     if (UpdateProcess::shouldCheckForUpdates(lastCheckedAt)) {
01522       /* Initiate a background check for updates now */
01523       _updateTimer.stop();
01524       checkForUpdates();
01525     }
01526   }
01527 #endif
01528 }
01529 
01530 /** Checks the status of the current version of Tor to see if it's old,
01531  * unrecommended, or obsolete. */
01532 void
01533 MainWindow::checkTorVersion()
01534 {
01535   QString status;
01536   if (_torControl->getInfo("status/version/current", status)) {
01537     if (!status.compare("old", Qt::CaseInsensitive)
01538           || !status.compare("unrecommended", Qt::CaseInsensitive)
01539           || !status.compare("obsolete", Qt::CaseInsensitive)) {
01540       displayTorVersionWarning();
01541     }
01542   }
01543 }
01544 
01545 /** Called when Tor thinks its version is old or unrecommended, and displays
01546  * a message notifying the user. */
01547 void
01548 MainWindow::dangerousTorVersion(tc::TorVersionStatus reason,
01549                                 const QString &current,
01550                                 const QStringList &recommended)
01551 {
01552   Q_UNUSED(current);
01553   Q_UNUSED(recommended);
01554 
01555   if (reason == tc::ObsoleteTorVersion
01556         || reason == tc::UnrecommendedTorVersion)
01557     displayTorVersionWarning();
01558 }
01559 
01560 /** Called when Tor thinks its version is old or unrecommended, and displays a
01561  * message notifying the user. */
01562 void
01563 MainWindow::displayTorVersionWarning()
01564 {
01565   static bool alreadyWarned = false;
01566 
01567   if (!alreadyWarned) {
01568 #if !defined(USE_AUTOUPDATE)
01569     QString website = "https://www.torproject.org/";
01570 # if QT_VERSION >= 0x040200
01571     website = QString("<a href=\"%1\">%1</a>").arg(website);
01572 # endif
01573 
01574     VMessageBox::information(this, tr("Tor Update Available"),
01575       p(tr("The currently installed version of Tor is out of date or no longer "
01576            "recommended. Please visit the Tor website to download the latest "
01577            "version.")) + p(tr("Tor website: %1").arg(website)),
01578       VMessageBox::Ok);
01579 #else
01580     int ret = VMessageBox::information(this,
01581                 tr("Tor Update Available"),
01582                 p(tr("The currently installed version of Tor is out of date "
01583                      "or no longer recommended."))
01584                   + p(tr("Would you like to check if a newer package is "
01585                          "available for installation?")),
01586                 VMessageBox::Yes|VMessageBox::Default,
01587                 VMessageBox::No|VMessageBox::Escape);
01588 
01589     if (ret == VMessageBox::Yes)
01590       checkForUpdatesWithUi();
01591 #endif
01592     alreadyWarned = true;
01593   }
01594 }
01595 
01596 /** Called when Tor thinks the user has tried to connect to a port that
01597  * typically is used for unencrypted applications. Warns the user and allows
01598  * them to ignore future warnings on <b>port</b>. It is possible that Tor
01599  * will produce multiple asynchronous status events warning of dangerous ports
01600  * while the message box is displayed (for example, while the user is away
01601  * from the keyboard), so subsequent messages will be discarded until the
01602  * first message box is dismissed. */
01603 void
01604 MainWindow::warnDangerousPort(quint16 port, bool rejected)
01605 {
01606   static QMessageBox *dlg = 0;
01607 
01608   /* Don't display another message box until the first one is dismissed */
01609   if (dlg)
01610     return;
01611 
01612   QString application;
01613   switch (port) {
01614     case  23:
01615      application = tr(", probably Telnet, ");
01616      break;
01617 
01618     case 109:
01619     case 110:
01620     case 143:
01621       application = tr(", probably an email client, "); 
01622       break;
01623 
01624     default:
01625       application = " ";
01626   }
01627 
01628   QString text = tr("One of your applications%1appears to be making a "
01629                     "potentially unencrypted and unsafe connection to port %2.")
01630                     .arg(application).arg(port);
01631 
01632   QString extraText = p(tr("Anything sent over this connection could be "
01633                            "monitored. Please check your application's "
01634                            "configuration and use only encrypted protocols, "
01635                            "such as SSL, if possible."));
01636   if (rejected) {
01637     extraText.append(p(tr("Tor has automatically closed your connection in "
01638                           "order to protect your anonymity.")));
01639   }
01640 
01641   dlg = new QMessageBox(QMessageBox::Warning,
01642                         tr("Potentially Unsafe Connection"), text,
01643                         QMessageBox::Ok | QMessageBox::Ignore);
01644   dlg->setInformativeText(extraText);
01645   dlg->setDefaultButton(QMessageBox::Ok);
01646   dlg->setEscapeButton(QMessageBox::Ok);
01647 
01648   int ret = dlg->exec();
01649   if (ret == QMessageBox::Ignore) {
01650     TorControl *tc = Vidalia::torControl();
01651     TorSettings settings;
01652     QStringList portList;
01653     QList<quint16> ports;
01654     int idx;
01655 
01656     ports = settings.getWarnPlaintextPorts();
01657     idx   = ports.indexOf(port);
01658     if (idx >= 0) {
01659       ports.removeAt(idx);
01660       settings.setWarnPlaintextPorts(ports);
01661 
01662       foreach (quint16 port, ports) {
01663         portList << QString::number(port);
01664       }
01665       tc->setConf("WarnPlaintextPorts", portList.join(","));
01666       portList.clear();
01667     }
01668 
01669     ports = settings.getRejectPlaintextPorts();
01670     idx   = ports.indexOf(port);
01671     if (idx >= 0) {
01672       ports.removeAt(idx);
01673       settings.setRejectPlaintextPorts(ports);
01674 
01675       foreach (quint16 port, ports) {
01676         portList << QString::number(port);
01677       }
01678       tc->setConf("RejectPlaintextPorts", portList.join(","));
01679     }
01680   }
01681   delete dlg;
01682   dlg = 0;
01683 }
01684 
01685 /** Creates and displays Vidalia's About dialog. */
01686 void
01687 MainWindow::showAboutDialog()
01688 {
01689   AboutDialog dlg(this);
01690   dlg.exec();
01691 }
01692 
01693 /** Displays the help browser and displays the most recently viewed help
01694  * topic. */
01695 void
01696 MainWindow::showHelpDialog()
01697 {
01698   showHelpDialog(QString());
01699 }
01700 
01701 /**< Shows the help browser and displays the given help <b>topic</b>. */
01702 void
01703 MainWindow::showHelpDialog(const QString &topic)
01704 {
01705   static HelpBrowser *helpBrowser = 0;
01706   if (!helpBrowser)
01707     helpBrowser = new HelpBrowser(this);
01708   helpBrowser->showWindow(topic);
01709 }
01710 
01711 /** Creates and displays the Configuration dialog with the current page set to
01712  * <b>page</b>. */
01713 void
01714 MainWindow::showConfigDialog(ConfigDialog::Page page)
01715 {
01716   _configDialog->showWindow(page);
01717 }
01718 
01719 /** Displays the Configuration dialog, set to the Server page. */
01720 void
01721 MainWindow::showServerConfigDialog()
01722 {
01723   showConfigDialog(ConfigDialog::Server);
01724 }
01725 
01726 /** Called when the user selects the "New Identity" action from the menu. */
01727 void
01728 MainWindow::newIdentity()
01729 {
01730   QString errmsg;
01731 
01732   /* Send the NEWNYM signal. If message balloons are supported and the NEWNYM
01733    * is successful, we will show the result as a balloon. Otherwise, we'll 
01734    * just use a message box. */
01735   if (_torControl->signal(TorSignal::NewNym, &errmsg)) {
01736     /* NEWNYM signal was successful */
01737     QString title = tr("New Identity");
01738     QString message = tr("All subsequent connections will "
01739                          "appear to be different than your "
01740                          "old connections.");
01741 
01742     /* Disable the New Identity button for MIN_NEWIDENTITY_INTERVAL */
01743     _actionNewIdentity->setEnabled(false);
01744     ui.lblNewIdentity->setEnabled(false);
01745     QTimer::singleShot(MIN_NEWIDENTITY_INTERVAL, 
01746                        this, SLOT(enableNewIdentity()));
01747 
01748     if (QSystemTrayIcon::supportsMessages())
01749       _trayIcon.showMessage(title, message, QSystemTrayIcon::Information);
01750     else
01751       VMessageBox::information(this, title, message, VMessageBox::Ok);
01752   } else {
01753     /* NEWNYM signal failed */
01754     VMessageBox::warning(this, 
01755       tr("Failed to Create New Identity"), errmsg, VMessageBox::Ok);
01756   }
01757 }
01758 
01759 /** Re-enables the 'New Identity' button after a delay from the previous time
01760  * 'New Identity' was used. */
01761 void
01762 MainWindow::enableNewIdentity()
01763 {
01764   if (_torControl->isConnected()) {
01765     _actionNewIdentity->setEnabled(true);
01766     ui.lblNewIdentity->setEnabled(true);
01767   }
01768 }
01769 
01770 /** Converts a TorStatus enum value to a string for debug logging purposes. */
01771 QString
01772 MainWindow::toString(TorStatus status)
01773 {
01774   switch (status) {
01775     /* These strings only appear in debug logs, so they should not be
01776      * translated. */
01777     case Unset:     return "Unset";
01778     case Stopping:  return "Stopping";
01779     case Stopped:   return "Stopped";
01780     case Starting:  return "Starting";
01781     case Started:   return "Started";
01782     case Authenticating:  return "Authenticating";
01783     case Authenticated:   return "Authenticated";
01784     case CircuitEstablished: return "Circuit Established";
01785     default: break;
01786   }
01787   return "Unknown";
01788 }
01789 
01790 #if defined(USE_MINIUPNPC)
01791 /** Called when a UPnP error occurs. */
01792 void
01793 MainWindow::upnpError(UPNPControl::UPNPError error)
01794 {
01795   Q_UNUSED(error);
01796 
01797 #if 0
01798   /* XXX: Is there a better way to do this? Currently, this could get called
01799    * if there is an error when testing UPnP support, and again when attempting
01800    * to reset the UPnP state when the test dialog is closed. The user would
01801    * not be amused with all the warning dialogs. */
01802 
01803   VMessageBox::warning(this,
01804     tr("Port Forwarding Failed"),
01805     p(tr("Vidalia was unable to configure automatic port forwarding."))
01806       + p(UPNPControl::Instance()->errorString()),
01807     VMessageBox::Ok);
01808 #endif
01809 }
01810 #endif
01811 
01812 #if defined(USE_AUTOUPDATE)
01813 /** Called when the user clicks the 'Check Now' button in the General
01814  * settings page. */
01815 void
01816 MainWindow::checkForUpdatesWithUi()
01817 {
01818   checkForUpdates(true);
01819 }
01820 
01821 /** Called when the update interval timer expires, notifying Vidalia that
01822  * we should check for updates again. */
01823 void
01824 MainWindow::checkForUpdates(bool showProgress)
01825 {
01826   VidaliaSettings settings;
01827 
01828   if (_updateProcess.isRunning()) {
01829     if (showProgress) {
01830       /* A check for updates is already in progress, so just bring the update
01831        * progress dialog into focus.
01832        */
01833       _updateProgressDialog.show();
01834     }
01835   } else {
01836     /* If Tor is running and bootstrapped, then use Tor to check for updates */
01837     if (_torControl->isRunning() && _torControl->circuitEstablished())
01838       _updateProcess.setSocksPort(_torControl->getSocksPort());
01839     else
01840       _updateProcess.setSocksPort(0);
01841 
01842     /* Initialize the UpdateProgressDialog and display it, if necessary. */
01843     _updateProgressDialog.setStatus(UpdateProgressDialog::CheckingForUpdates);
01844     if (showProgress)
01845       _updateProgressDialog.show();
01846 
01847     /* Initiate a check for available software updates. This check will
01848      * be done in the background, notifying the user only if there are
01849      * updates to be installed.
01850      */
01851     _updateProcess.checkForUpdates(UpdateProcess::TorBundleInfo);
01852 
01853     /* Remember when we last checked for software updates */
01854     settings.setLastCheckedForUpdates(QDateTime::currentDateTime().toUTC());
01855 
01856     /* Restart the "Check for Updates" timer */
01857     _updateTimer.start(UpdateProcess::checkForUpdatesInterval() * 1000);
01858   }
01859 }
01860 
01861 /** Called when the check for software updates fails. */
01862 void
01863 MainWindow::checkForUpdatesFailed(const QString &errmsg)
01864 {
01865   if (_updateProgressDialog.isVisible()) {
01866     _updateProgressDialog.hide();
01867     VMessageBox::warning(this, tr("Update Failed"), errmsg,
01868                          VMessageBox::Ok);
01869   }
01870 }
01871 
01872 /** Called when there is an update available for installation. */
01873 void
01874 MainWindow::updatesAvailable(UpdateProcess::BundleInfo bi,
01875                              const PackageList &packageList)
01876 {
01877   vInfo("%1 software update(s) available").arg(packageList.size());
01878   if (packageList.size() > 0) {
01879     UpdatesAvailableDialog dlg(packageList, &_updateProgressDialog);
01880 
01881     switch (dlg.exec()) {
01882       case UpdatesAvailableDialog::InstallUpdatesNow:
01883         installUpdates(bi);
01884         break;
01885 
01886       default:
01887         _updateProgressDialog.hide();
01888         break;
01889     }
01890   } else {
01891     if (_updateProgressDialog.isVisible()) {
01892       _updateProgressDialog.hide();
01893       VMessageBox::information(this, tr("Your software is up to date"),
01894                                tr("There are no new Tor software packages "
01895                                   "available for your computer at this time."),
01896                                VMessageBox::Ok);
01897     }
01898   }
01899 }
01900 
01901 /** Stops Tor (if necessary), installs any available for <b>bi</b>, and
01902  * restarts Tor (if necessary). */
01903 void
01904 MainWindow::installUpdates(UpdateProcess::BundleInfo bi)
01905 {
01906   _updateProgressDialog.setStatus(UpdateProgressDialog::InstallingUpdates);
01907   _updateProgressDialog.show();
01908 
01909   if (_isVidaliaRunningTor) {
01910     _restartTorAfterUpgrade = true;
01911     _isIntentionalExit = true;
01912     _torControl->stop();
01913   } else {
01914     _restartTorAfterUpgrade = false;
01915   }
01916   _updateProcess.installUpdates(bi);
01917 }
01918 
01919 /** Called when all <b>numUpdates</b> software updates have been installed
01920  * successfully. */
01921 void
01922 MainWindow::updatesInstalled(int numUpdates)
01923 {
01924   _updateProgressDialog.setStatus(UpdateProgressDialog::UpdatesInstalled);
01925   _updateProgressDialog.show();
01926 
01927   if (_restartTorAfterUpgrade)
01928     start();
01929 }
01930 
01931 /** Called when an update fails to install. <b>errmsg</b> contains details
01932  * about the failure. */
01933 void
01934 MainWindow::installUpdatesFailed(const QString &errmsg)
01935 {
01936   _updateProgressDialog.hide();
01937 
01938   VMessageBox::warning(this, tr("Installation Failed"),
01939                        p(tr("Vidalia was unable to install your software updates."))
01940                          + p(tr("The following error occurred:")) 
01941                          + p(errmsg),
01942                        VMessageBox::Ok);
01943 
01944   if (_restartTorAfterUpgrade)
01945     start();
01946 }
01947 
01948 #endif
01949 
Generated on Mon Aug 30 23:09:49 2010 for Vidalia by  doxygen 1.6.3