commit 3d4bbd50e6b7d8f66d04ca2c59295fd99d68c6d4 Merge: cf0296a 8d691c7 Author: Tomas Touceda chiiph@gentoo.org Date: Tue May 3 11:14:00 2011 -0300
Merge branch 'master' into alpha
Conflicts: src/vidalia/MainWindow.cpp
src/vidalia/MainWindow.cpp | 14 +++++++++++++- src/vidalia/config/TorSettings.cpp | 27 +++++++++++++++++++++++++++ src/vidalia/config/TorSettings.h | 9 +++++++++ 3 files changed, 49 insertions(+), 1 deletions(-)
diff --cc src/vidalia/MainWindow.cpp index a3d3ae6,29c1330..540af15 --- a/src/vidalia/MainWindow.cpp +++ b/src/vidalia/MainWindow.cpp @@@ -559,284 -400,295 +559,296 @@@ MainWindow::start( return; } } - vApp->quit(); -}
- /* Make sure the torrc we want to use really exists. */ -/** Create and bind actions to events. Setup for initial - * tray menu configuration. */ -void -MainWindow::createActions() -{ - _actionStartStopTor = new QAction(tr("Start Tor"), this); - connect(_actionStartStopTor, SIGNAL(triggered()), this, SLOT(start())); + QString torrc = settings.getTorrc(); + - _actionExit = new QAction(tr("Exit"), this); - connect(_actionExit, SIGNAL(triggered()), this, SLOT(close())); ++ if(settings.bootstrap()) { ++ QString boottorrc = settings.bootstrapFrom(); ++ vNotice(tr("Bootstrapping torrc from %1 to %2") ++ .arg(boottorrc).arg(torrc)); ++ if(QFileInfo(boottorrc).exists()) { ++ if(QFile::copy(boottorrc, torrc)) { ++ settings.setBootstrap(false); ++ } ++ } ++ } + - _actionShowBandwidth = new QAction(tr("Bandwidth Graph"), this); - connect(_actionShowBandwidth, SIGNAL(triggered()), - _bandwidthGraph, SLOT(showWindow())); - connect(ui.lblBandwidthGraph, SIGNAL(clicked()), - _bandwidthGraph, SLOT(showWindow())); - - _actionShowMessageLog = new QAction(tr("Message Log"), this); - connect(_actionShowMessageLog, SIGNAL(triggered()), - _messageLog, SLOT(showWindow())); - connect(ui.lblMessageLog, SIGNAL(clicked()), - _messageLog, SLOT(showWindow())); - - _actionShowNetworkMap = new QAction(tr("Network Map"), this); - connect(_actionShowNetworkMap, SIGNAL(triggered()), - _netViewer, SLOT(showWindow())); - connect(ui.lblViewNetwork, SIGNAL(clicked()), - _netViewer, SLOT(showWindow())); - - _actionShowControlPanel = new QAction(tr("Control Panel"), this); - connect(_actionShowControlPanel, SIGNAL(triggered()), this, SLOT(show())); ++ /* Make sure the torrc we want to use really exists. */ + if (!torrc.isEmpty()) { + if (!QFileInfo(torrc).exists()) + touch_file(torrc, true); + args << "-f" << torrc; + }
- _actionShowConfig = new QAction(tr("Settings"), this); - connect(_actionShowConfig, SIGNAL(triggered()), this, SLOT(showConfigDialog())); + /* Specify Tor's data directory, if different from the default */ + QString dataDirectory = settings.getDataDirectory(); + if (!dataDirectory.isEmpty()) + args << "DataDirectory" << expand_filename(dataDirectory);
- _actionShowAbout = new QAction(tr("About"), this); - connect(_actionShowAbout, SIGNAL(triggered()), this, SLOT(showAboutDialog())); - - _actionShowHelp = new QAction(tr("Help"), this); - connect(_actionShowHelp, SIGNAL(triggered()), this, SLOT(showHelpDialog())); - connect(ui.lblHelpBrowser, SIGNAL(clicked()), this, SLOT(showHelpDialog())); - - _actionNewIdentity = new QAction(tr("New Identity"), this); - _actionNewIdentity->setEnabled(false); - connect(_actionNewIdentity, SIGNAL(triggered()), this, SLOT(newIdentity())); + if(settings.getControlMethod() == ControlMethod::Port) { + /* Add the intended control port value */ + quint16 controlPort = settings.getControlPort(); + if (controlPort) + args << "ControlPort" << QString::number(controlPort); + } else { + QString path = settings.getSocketPath(); + args << "ControlSocket" << path; + } + + /* Add the control port authentication arguments */ + switch (settings.getAuthenticationMethod()) { + case TorSettings::PasswordAuth: + if (! vApp->readPasswordFromStdin()) { + if (settings.useRandomPassword()) { + _controlPassword = TorSettings::randomPassword(); + _useSavedPassword = false; + } else { + _controlPassword = settings.getControlPassword(); + _useSavedPassword = true; + } + } + args << "HashedControlPassword" + << TorSettings::hashPassword(_controlPassword); + break; + case TorSettings::CookieAuth: + args << "CookieAuthentication" << "1"; + break; + default: + args << "CookieAuthentication" << "0"; + }
-#if !defined(Q_WS_MAC) - /* Don't give the menu items icons on OS X, since they end up in the - * application menu bar. Menu bar items on OS X typically do not have - * icons. */ - _actionStartStopTor->setIcon(QIcon(IMG_START_TOR_16)); - _actionExit->setIcon(QIcon(IMG_EXIT)); - _actionShowBandwidth->setIcon(QIcon(IMG_BWGRAPH)); - _actionShowMessageLog->setIcon(QIcon(IMG_MESSAGELOG)); - _actionShowNetworkMap->setIcon(QIcon(IMG_NETWORK)); - _actionShowControlPanel->setIcon(QIcon(IMG_CONTROL_PANEL)); - _actionShowConfig->setIcon(QIcon(IMG_CONFIG)); - _actionShowAbout->setIcon(QIcon(IMG_ABOUT)); - _actionShowHelp->setIcon(QIcon(IMG_HELP)); - _actionNewIdentity->setIcon(QIcon(IMG_IDENTITY)); -#endif + /* This doesn't get set to false until Tor is actually up and running, so we + * don't yell at users twice if their Tor doesn't even start, due to the fact + * that QProcess::stopped() is emitted even if the process didn't even + * start. */ + _isIntentionalExit = true; + /* Kick off the Tor process */ + _torControl->start(settings.getExecutable(), args); }
-/** Creates a tray icon with a context menu and adds it to the system - * notification area. On Mac, we also set up an application menubar. */ -void -MainWindow::createTrayIcon() +/** Slot: Called when the Tor process is started. It will connect the control + * socket and set the icons and tooltips accordingly. */ +void +MainWindow::started() { - QMenu *menu = createTrayMenu(); - - /* Add the menu it to the tray icon */ - _trayIcon.setContextMenu(menu); + TorSettings settings;
- connect(&_trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), - this, SLOT(trayIconActivated(QSystemTrayIcon::ActivationReason))); + updateTorStatus(Started);
-#if defined(Q_WS_MAC) - createMenuBar(); - qt_mac_set_dock_menu(menu); -#endif + /* Now that Tor is running, we want to know if it dies when we didn't want + * it to. */ + _isIntentionalExit = false; + /* We haven't started a delayed shutdown yet. */ + _delayedShutdownStarted = false; + /* Remember whether we started Tor or not */ + _isVidaliaRunningTor = _torControl->isVidaliaRunningTor(); + /* Try to connect to Tor's control port */ + if(settings.getControlMethod() == ControlMethod::Port) + _torControl->connect(settings.getControlAddress(), + settings.getControlPort()); + else + _torControl->connect(settings.getSocketPath()); + setStartupProgress(STARTUP_PROGRESS_CONNECTING, tr("Connecting to Tor")); }
-/** Creates a QMenu object that contains QActions which compose the system - * tray menu. */ -QMenu* -MainWindow::createTrayMenu() +/** Disconnects the control socket and stops the Tor process. */ +bool +MainWindow::stop() { - QMenu *menu = new QMenu(this); - menu->addAction(_actionStartStopTor); - menu->addSeparator(); - menu->addAction(_actionShowBandwidth); - menu->addAction(_actionShowMessageLog); - menu->addAction(_actionShowNetworkMap); - menu->addAction(_actionNewIdentity); - menu->addSeparator(); - menu->addAction(_actionShowControlPanel); - -#if !defined(Q_WS_MAC) - /* These aren't added to the dock menu on Mac, since they are in the - * standard Mac locations in the menu bar. */ - menu->addAction(_actionShowConfig); - menu->addAction(_actionShowHelp); - menu->addAction(_actionShowAbout); - menu->addSeparator(); - menu->addAction(_actionExit); -#endif - return menu; -} + ServerSettings server(_torControl); + QString errmsg; + TorStatus prevStatus; + bool rc;
-/** Creates a new menubar with no parent, so Qt will use this as the "default - * menubar" on Mac. This adds on to the existing actions from the createMens() - * method. */ -void -MainWindow::createMenuBar() -{ -#if defined(Q_WS_MAC) - /* Mac users sure like their shortcuts. Actions NOT mentioned below - * don't explicitly need shortcuts, since they are merged to the default - * menubar and get the default shortcuts anyway. */ - _actionStartStopTor->setShortcut(tr("Ctrl+T")); - _actionShowBandwidth->setShortcut(tr("Ctrl+B")); - _actionShowMessageLog->setShortcut(tr("Ctrl+L")); - _actionShowNetworkMap->setShortcut(tr("Ctrl+N")); - _actionShowHelp->setShortcut(tr("Ctrl+?")); - _actionNewIdentity->setShortcut(tr("Ctrl+I")); - _actionShowControlPanel->setShortcut(tr("Ctrl+P")); - - /* Force Qt to put merge the Exit, Configure, and About menubar options into - * the default menu, even if Vidalia is currently not speaking English. */ - _actionShowConfig->setText("config"); - _actionShowConfig->setMenuRole(QAction::PreferencesRole); - _actionShowAbout->setText("about"); - _actionShowAbout->setMenuRole(QAction::AboutRole); - _actionExit->setText("quit"); - _actionExit->setMenuRole(QAction::QuitRole); - - /* The File, Help, and Configure menus will get merged into the application - * menu by Qt. */ - if (_menuBar) - delete _menuBar; - _menuBar = new QMenuBar(0); - QMenu *fileMenu = _menuBar->addMenu("File"); - fileMenu->addAction(_actionExit); - fileMenu->addAction(_actionShowConfig); - - QMenu *torMenu = _menuBar->addMenu(tr("Tor")); - torMenu->addAction(_actionStartStopTor); - torMenu->addSeparator(); - torMenu->addAction(_actionNewIdentity); - - QMenu *viewMenu = _menuBar->addMenu(tr("View")); - viewMenu->addAction(_actionShowControlPanel); - viewMenu->addSeparator(); - viewMenu->addAction(_actionShowBandwidth); - viewMenu->addAction(_actionShowMessageLog); - viewMenu->addAction(_actionShowNetworkMap); + /* If we're running a server, give users the option of terminating + * gracefully so clients have time to find new servers. */ + if (server.isServerEnabled() && !_delayedShutdownStarted) { + /* Ask the user if they want to shutdown nicely. */ + int response = VMessageBox::question(this, tr("Relaying is Enabled"), + tr("You are currently running a relay. " + "Terminating your relay will interrupt any " + "open connections from clients.\n\n" + "Would you like to shutdown gracefully and " + "give clients time to find a new relay?"), + VMessageBox::Yes|VMessageBox::Default, + VMessageBox::No, + VMessageBox::Cancel|VMessageBox::Escape); + if (response == VMessageBox::Yes) + _delayedShutdownStarted = true; + else if (response == VMessageBox::Cancel) + return false; + }
- QMenu *helpMenu = _menuBar->addMenu(tr("Help")); - _actionShowHelp->setText(tr("Vidalia Help")); - helpMenu->addAction(_actionShowHelp); - helpMenu->addAction(_actionShowAbout); -#endif + prevStatus = updateTorStatus(Stopping); + if (_delayedShutdownStarted) { + /* Start a delayed shutdown */ + rc = _torControl->signal(TorSignal::Shutdown, &errmsg); + } else { + /* We want Tor to stop now, regardless of whether we're a server. */ + _isIntentionalExit = true; + rc = _torControl->stop(&errmsg); + } + + if (!rc) { + /* We couldn't tell Tor to stop, for some reason. */ + int response = VMessageBox::warning(this, tr("Error Shutting Down"), + p(tr("Vidalia was unable to stop the Tor software.")) + + p(errmsg), + VMessageBox::Ok|VMessageBox::Default|VMessageBox::Escape, + VMessageBox::Help); + + if (response == VMessageBox::Help) { + /* Show some troubleshooting help */ + showHelpDialog("troubleshooting.stop"); + } + /* Tor is still running since stopping failed */ + _isIntentionalExit = false; + _delayedShutdownStarted = false; + updateTorStatus(prevStatus); + } + return rc; }
-/** Sets the current tray or dock icon image to <b>iconFile</b>. */ -void -MainWindow::setTrayIcon(const QString &iconFile) +/** Slot: Called when the Tor process has exited. It will adjust the tray + * icons and tooltips accordingly. */ +void +MainWindow::stopped(int exitCode, QProcess::ExitStatus exitStatus) { -#if defined(Q_WS_MAC) - QApplication::setWindowIcon(QPixmap(iconFile)); -#endif - _trayIcon.setIcon(QIcon(iconFile)); -} + updateTorStatus(Stopped);
-/** Respond to a double-click on the tray icon by opening the Control Panel - * window. */ -void -MainWindow::trayIconActivated(QSystemTrayIcon::ActivationReason reason) -{ - if (reason == QSystemTrayIcon::DoubleClick) - setVisible(true); + /* If we didn't intentionally close Tor, then check to see if it crashed or + * if it closed itself and returned an error code. */ + if (!_isIntentionalExit) { + /* A quick overview of Tor's code tells me that if it catches a SIGTERM or + * SIGINT, Tor will exit(0). We might need to change this warning message + * if this turns out to not be the case. */ + if (exitStatus == QProcess::CrashExit || exitCode != 0) { + int ret = VMessageBox::warning(this, tr("Unexpected Error"), + tr("Vidalia detected that the Tor software exited " + "unexpectedly.\n\n" + "Please check the message log for recent " + "warning or error messages."), + VMessageBox::Ok|VMessageBox::Escape, + VMessageBox::ShowLog|VMessageBox::Default, + VMessageBox::Help); + if (ret == VMessageBox::ShowLog) + showMessageLogTab(); + else if (ret == VMessageBox::Help) + showHelpDialog("troubleshooting.torexited"); + } + } }
-/** Start a web browser when given the directory containing the executable and profile */ +/** Called when the Tor process fails to start, for example, because the path + * specified to the Tor executable didn't lead to an executable. */ void -MainWindow::launchBrowserFromDirectory() +MainWindow::startFailed(QString errmsg) { - VidaliaSettings settings; - - QString browserDirectory = settings.getBrowserDirectory(); - QString browserDirectoryFilename = settings.getBrowserExecutable(); + /* We don't display the error message for now, because the error message + * that Qt gives us in this instance is almost always "Unknown Error". That + * will make users sad. */ + Q_UNUSED(errmsg); + + updateTorStatus(Stopped);
- /* Set TZ=UTC (to stop leaking timezone information) and - * MOZ_NO_REMOTE=1 (to allow multiple instances of Firefox */ - QStringList env = QProcess::systemEnvironment(); - env << "TZ=UTC"; - env << "MOZ_NO_REMOTE=1"; - _browserProcess->setEnvironment(env); - - /* The browser is in <browserDirectory>/App/Firefox/<browserDirectoryFilename> */ - QString browserExecutable = - QDir::toNativeSeparators(browserDirectory + "/App/Firefox/" + browserDirectoryFilename); - /* The profile is in <browserDirectory>/Data/profile */ - QString profileDir = - QDir::toNativeSeparators(browserDirectory + "/Data/profile"); - - /* Copy the profile directory if it's not already there */ - QDir browserDirObj = QDir(browserDirectory); - - /* Copy the profile directory if it's not already there */ - if (!browserDirObj.exists("Data/profile")) { - browserDirObj.mkdir("Data/profile"); - copy_dir(browserDirectory + "/App/DefaultData/profile", browserDirectory + "/Data/profile"); - } + /* Display an error message and see if the user wants some help */ + int response = VMessageBox::warning(this, tr("Error Starting Tor"), + tr("Vidalia was unable to start Tor. Check your settings " + "to ensure the correct name and location of your Tor " + "executable is specified."), + VMessageBox::ShowSettings|VMessageBox::Default, + VMessageBox::Cancel|VMessageBox::Escape, + VMessageBox::Help);
- /* Copy the plugins directory if it's not already there */ - if (!browserDirObj.exists("Data/plugins")) { - browserDirObj.mkdir("Data/plugins"); - copy_dir(browserDirectory + "/App/DefaultData/plugins", browserDirectory + "/Data/plugins"); + if (response == VMessageBox::ShowSettings) { + /* Show the settings dialog so the user can make sure they're pointing to + * the correct Tor. */ + showConfigDialog(); + } else if (response == VMessageBox::Help) { + /* Show troubleshooting information about starting Tor */ + showHelpDialog("troubleshooting.start"); } - - /* Build the command line arguments */ - QStringList commandLine; - // Is this better or worse than MOZ_NO_REMOTE? - //commandLine << "-no-remote"; - commandLine << "-profile"; - commandLine << profileDir; - - /* Launch the browser */ - _browserProcess->start(browserExecutable, commandLine); }
-/** Starts the web browser and IM client, if appropriately configured */ +/** Called when the control socket has successfully connected to Tor. */ void -MainWindow::startSubprocesses() +MainWindow::connected() { - VidaliaSettings settings; - QString subprocess; + authenticate(); +}
- /* Launch the web browser */ - if (!(subprocess = settings.getBrowserDirectory()).isEmpty()) { - /* The user has set BrowserDirectory; use this */ - launchBrowserFromDirectory(); - } else if (!(subprocess = settings.getBrowserExecutable()).isEmpty()) { - /* BrowserDirectory is not set, but BrowserExecutable is; use this */ - _browserProcess->setEnvironment(QProcess::systemEnvironment() << "TZ=UTC"); - _browserProcess->start(subprocess, QStringList()); - } +/** Called when the connection to the control socket fails. The reason will be + * given in the errmsg parameter. */ +void +MainWindow::connectFailed(QString errmsg) +{ + /* Ok, ok. It really isn't going to connect. I give up. */ + int response = VMessageBox::warning(this, + tr("Connection Error"), p(errmsg), + VMessageBox::Ok|VMessageBox::Default|VMessageBox::Escape, + VMessageBox::Retry, VMessageBox::Help);
- /* Launch the IM client */ - subprocess = settings.getIMExecutable();
- if (!subprocess.isEmpty()) - _imProcess->start(subprocess, QStringList()); + if (response == VMessageBox::Retry) { + /* Let's give it another try. */ + TorSettings settings; + _torControl->connect(settings.getControlAddress(), + settings.getControlPort()); + } else { + /* Show the help browser (if requested) */ + if (response == VMessageBox::Help) + showHelpDialog("troubleshooting.connect"); + /* Since Vidalia can't connect, we can't really do much, so stop Tor. */ + _torControl->stop(); + } }
-/** Called when browser or IM client have exited */ +/** Called when Vidalia has successfully authenticated to Tor. */ void -MainWindow::onSubprocessFinished(int exitCode, QProcess::ExitStatus exitStatus) +MainWindow::authenticated() { - Q_UNUSED(exitCode) - Q_UNUSED(exitStatus) + ServerSettings serverSettings(_torControl); + QString errmsg;
- /* Get path to browser and IM client */ - VidaliaSettings settings; - QString browserExecutable = settings.getBrowserExecutable(); - QString browserDirectory = settings.getBrowserDirectory(); - QString imExecutable = settings.getIMExecutable(); + updateTorStatus(Authenticated); + + /* If Tor doesn't have bootstrapping events, then update the current + * status string and bump the progress bar along a bit. */ + if (_torControl->getTorVersion() < 0x020101) { + setStartupProgress(STARTUP_PROGRESS_CIRCUITBUILD, + tr("Connecting to the Tor network")); + } + + /* Let people click on their beloved "New Identity" button */ + _actionNewIdentity->setEnabled(true);
- /* A subprocess is finished if it successfully exited or was never asked to start */ - bool browserDone = (browserExecutable.isEmpty() - && browserDirectory.isEmpty()) - || _browserProcess->isDone(); - bool imDone = imExecutable.isEmpty() || _imProcess->isDone(); + /* Register for any pertinent asynchronous events. */ + if (!_torControl->setEvents(&errmsg)) { + VMessageBox::warning(this, tr("Error Registering for Events"), + p(tr("Vidalia was unable to register for some events. " + "Many of Vidalia's features may be unavailable.")) + + p(errmsg), + VMessageBox::Ok); + } else { + /* Stop reading from Tor's stdout immediately, since we successfully + * registered for Tor events, including any desired log events. */ + _torControl->closeTorStdout(); + }
- /* Exit if both subprocesses are finished */ - if (browserDone && imDone) { - if (browserDirectory.isEmpty()) { - /* We are using the standard launcher, exit immediately */ - vApp->quit(); - } else { - /* We are using the alternate launcher, wait until the browser has really died */ - QTimer *browserWatcher = new QTimer(this); - connect(browserWatcher, SIGNAL(timeout()), this, SLOT(onCheckForBrowser())); - browserWatcher->start(2000); - } + /* Configure UPnP port forwarding if needed */ + serverSettings.configurePortForwarding(); + + /* Check if Tor has a circuit established */ + if (_torControl->isCircuitEstablished()) { + circuitEstablished(); + } + /* Check the status of Tor's version */ + if (_torControl->getTorVersion() >= 0x020001) + checkTorVersion(); + if (_torControl->getTorVersion() >= 0x020102) { + BootstrapStatus status = _torControl->bootstrapStatus(); + if (status.isValid()) + bootstrapStatusChanged(status); } }