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

Generated on 31 Mar 2010 for Vidalia by  doxygen 1.6.1