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