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