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