commit 3d4bbd50e6b7d8f66d04ca2c59295fd99d68c6d4
Merge: cf0296a 8d691c7
Author: Tomas Touceda <chiiph(a)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);
}
}