Vidalia 0.2.12

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