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