Vidalia 0.2.15
AdvancedPage.cpp
Go to the documentation of this file.
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 AdvancedPage.cpp
00013 ** \brief Advanced Tor and Vidalia configuration options
00014 */
00015 
00016 #include "AdvancedPage.h"
00017 #include "TorrcDialog.h"
00018 #include "Vidalia.h"
00019 #include "VMessageBox.h"
00020 #include "IpValidator.h"
00021 #include "Local8BitStringValidator.h"
00022 
00023 #include "file.h"
00024 
00025 #if defined(Q_WS_WIN)
00026 #include "TorService.h"
00027 #endif
00028 
00029 #include <QFile>
00030 #include <QFileInfo>
00031 #include <QHostAddress>
00032 #include <QTextCodec>
00033 
00034 
00035 /** Constructor */
00036 AdvancedPage::AdvancedPage(QWidget *parent)
00037   : ConfigPage(parent, "Advanced")
00038 {
00039   /* Invoke the Qt Designer generated object setup routine */
00040   ui.setupUi(this);
00041 
00042   /* Create TorSettings object */
00043   _settings = new TorSettings(Vidalia::torControl());
00044 
00045   /* Set validators for the control port and IP address fields */
00046   ui.lineControlAddress->setValidator(new IpValidator(this));
00047   ui.lineControlPort->setValidator(new QIntValidator(1, 65535, this));
00048 
00049   /* Set encoding validators for text boxes containing values that may be
00050    * passed to Tor via the control port. */
00051   ui.lineTorConfig->setValidator(new Local8BitStringValidator(this));
00052   ui.lineTorDataDirectory->setValidator(new Local8BitStringValidator(this));
00053   ui.linePassword->setValidator(new Local8BitStringValidator(this));
00054 
00055   /* Bind event to actions */
00056   connect(ui.btnBrowseTorConfig, SIGNAL(clicked()), this, SLOT(browseTorConfig()));
00057   connect(ui.btnBrowseTorDataDirectory, SIGNAL(clicked()),
00058           this, SLOT(browseTorDataDirectory()));
00059   connect(ui.cmbAuthMethod, SIGNAL(currentIndexChanged(int)),
00060           this, SLOT(authMethodChanged(int)));
00061   connect(ui.chkRandomPassword, SIGNAL(toggled(bool)),
00062           ui.linePassword, SLOT(setDisabled(bool)));
00063   connect(ui.btnEditTorConfig, SIGNAL(clicked()),
00064           this, SLOT(displayTorrcDialog()));
00065   connect(ui.rdoControlPort, SIGNAL(toggled(bool)), this, SLOT(toggleControl(bool)));
00066   connect(ui.btnBrowseSocketPath, SIGNAL(clicked()), this, SLOT(browseSocketPath()));
00067   connect(ui.chkAuto, SIGNAL(toggled(bool)), this, SLOT(toggleAuto(bool)));
00068 
00069   /* Hide platform specific features */
00070 #if defined(Q_WS_WIN)
00071 #if 0
00072   ui.grpService->setVisible(TorService::isSupported());
00073 #endif
00074   /* Disable ControlSocket */
00075   ui.rdoControlSocket->setEnabled(false);
00076 #endif
00077 }
00078 
00079 /** Destructor */
00080 AdvancedPage::~AdvancedPage()
00081 {
00082   delete _settings;
00083 }
00084 
00085 /** Called when the user changes the UI translation. */
00086 void
00087 AdvancedPage::retranslateUi()
00088 {
00089   ui.retranslateUi(this);
00090 }
00091 
00092 /** Applies the network configuration settings to Tor. Returns true if the
00093  * settings were applied successfully. Otherwise, <b>errmsg</b> is set
00094  * and false is returned. */
00095 bool
00096 AdvancedPage::apply(QString &errmsg)
00097 {
00098   return _settings->apply(&errmsg);
00099 }
00100 
00101 /** Reverts the Tor configuration settings to their values at the last
00102  * time they were successfully applied to Tor. */
00103 bool
00104 AdvancedPage::changedSinceLastApply()
00105 {
00106   return _settings->changedSinceLastApply();
00107 }
00108 
00109 /** Returns true if the user has changed their advanced Tor settings since
00110  * the last time they were applied to Tor. */
00111 void
00112 AdvancedPage::revert()
00113 {
00114   return _settings->revert();
00115 }
00116 
00117 /** Saves all settings for this page. */
00118 bool
00119 AdvancedPage::save(QString &errmsg)
00120 {
00121   QHostAddress controlAddress(ui.lineControlAddress->text());
00122   QString path(ui.lineSocketPath->text());
00123 
00124   if(ui.chkAuto->isChecked()) {
00125     if(ui.lineTorDataDirectory->text().isEmpty()) {
00126       errmsg = tr("You've checked the autoconfiguration option for the ControlPort, but"
00127                   " provided no Data Directory. Please add one, or uncheck the"
00128                   " \"Configure ControlPort automatically\" option.");
00129       return false;
00130     }
00131     _settings->setAutoControlPort(true);
00132   }
00133 
00134   /* Validate the control settings */
00135   if(ui.rdoControlPort->isChecked()) {
00136     if (controlAddress.isNull()) {
00137       errmsg = tr("'%1' is not a valid IP address.")
00138                 .arg(ui.lineControlAddress->text());
00139       return false; 
00140     }
00141     _settings->setControlMethod(ControlMethod::Port);
00142   } else {
00143     QFileInfo finfo(path);
00144     if(!finfo.exists()) {
00145       errmsg = tr("ControlSocket path doesn't exist.");
00146       return false;
00147     }
00148     _settings->setControlMethod(ControlMethod::Socket);
00149   }
00150   
00151   /* Validate the selected authentication options */
00152   TorSettings::AuthenticationMethod authMethod = 
00153     indexToAuthMethod(ui.cmbAuthMethod->currentIndex());
00154   if (authMethod == TorSettings::PasswordAuth
00155         && ui.linePassword->text().isEmpty()
00156         && !ui.chkRandomPassword->isChecked()) {
00157     errmsg = tr("You selected 'Password' authentication, but did not "
00158                 "specify a password.");
00159     return false;
00160   }
00161 
00162   /* Ensure that the DataDirectory and torrc options only contain characters
00163    * that are valid in the local 8-bit encoding. */
00164   if (! Local8BitStringValidator::canEncode(ui.lineTorConfig->text())) {
00165     errmsg = tr("The specified Tor configuration file location contains "
00166                 "characters that cannot be represented in your system's "
00167                 "current 8-bit character encoding.");
00168     return false;
00169   }
00170   if (! Local8BitStringValidator::canEncode(ui.lineTorDataDirectory->text())) {
00171     errmsg = tr("The specified Tor data directory location contains "
00172                 "characters that cannot be represented in your system's "
00173                 "current 8-bit character encoding.");
00174     return false;
00175   }
00176 
00177   /* Only remember the torrc and datadir values if Vidalia started Tor, or
00178    * if the user changed the displayed values. */
00179   if (Vidalia::torControl()->isVidaliaRunningTor() or 
00180       !Vidalia::torControl()->isConnected()) {
00181     QString torrc = ui.lineTorConfig->text();
00182     if (torrc != _settings->getTorrc()) {
00183       _settings->setTorrc(torrc);
00184       if(Vidalia::torControl()->isConnected()) {
00185         QMessageBox::StandardButtons res = QMessageBox::question(this, tr("Warning"), 
00186             tr("You changed torrc path, would you like to restart Tor?"),
00187             QMessageBox::Yes | QMessageBox::No);
00188         if(res == QMessageBox::Yes)
00189           emit restartTor();
00190       }
00191     }
00192 
00193     QString dataDir = ui.lineTorDataDirectory->text();
00194     if (dataDir != _settings->getDataDirectory())
00195       _settings->setDataDirectory(dataDir);
00196   }
00197 
00198   if(!ui.chkAuto->isChecked()) {
00199     _settings->setControlAddress(controlAddress);
00200     _settings->setControlPort(ui.lineControlPort->text().toUShort());
00201   }
00202   _settings->setSocketPath(ui.lineSocketPath->text());
00203 
00204   _settings->setAuthenticationMethod(authMethod);
00205   _settings->setUseRandomPassword(ui.chkRandomPassword->isChecked());
00206   if (authMethod == TorSettings::PasswordAuth
00207         && !ui.chkRandomPassword->isChecked())
00208     _settings->setControlPassword(ui.linePassword->text());
00209 
00210 #if 0
00211 #if defined(Q_WS_WIN)
00212   /* Install or uninstall the Tor service as necessary */
00213   setupService(ui.chkUseService->isChecked());
00214 #endif
00215 #endif
00216 
00217   return true;
00218 }
00219 
00220 /** Loads previously saved settings. */
00221 void
00222 AdvancedPage::load()
00223 {
00224   ui.lineControlAddress->setText(_settings->getControlAddress().toString());
00225   ui.lineControlPort->setText(QString::number(_settings->getControlPort()));
00226   ui.lineTorConfig->setText(_settings->getTorrc());
00227   ui.lineTorDataDirectory->setText(_settings->getDataDirectory());
00228   ui.chkAuto->setChecked(_settings->autoControlPort());
00229 
00230   ui.cmbAuthMethod->setCurrentIndex(
00231     authMethodToIndex(_settings->getAuthenticationMethod()));
00232   ui.chkRandomPassword->setChecked(_settings->useRandomPassword());
00233   if (!ui.chkRandomPassword->isChecked())
00234     ui.linePassword->setText(_settings->getControlPassword());
00235   ui.rdoControlPort->setChecked(_settings->getControlMethod() == ControlMethod::Port);
00236   ui.rdoControlSocket->setChecked(_settings->getControlMethod() == ControlMethod::Socket);
00237   ui.lineSocketPath->setText(_settings->getSocketPath());
00238 
00239 #if 0
00240 #if defined(Q_WS_WIN)
00241   TorService s;
00242   ui.chkUseService->setChecked(s.isInstalled());
00243 #endif
00244 #endif
00245 
00246   if(Vidalia::torControl()->getTorVersion() < 0x2021a) { // 0x2021a == 0.2.2.26
00247     ui.chkAuto->setChecked(false);
00248     ui.chkAuto->setVisible(false);
00249   }
00250 }
00251 
00252 /** Called when the user selects a different authentication method from the
00253  * combo box. */
00254 void
00255 AdvancedPage::authMethodChanged(int index)
00256 {
00257   bool usePassword = (indexToAuthMethod(index) == TorSettings::PasswordAuth);
00258   ui.linePassword->setEnabled(usePassword && !ui.chkRandomPassword->isChecked());
00259   ui.chkRandomPassword->setEnabled(usePassword);
00260 }
00261 
00262 /** Returns the authentication method for the given <b>index</b>. */
00263 TorSettings::AuthenticationMethod
00264 AdvancedPage::indexToAuthMethod(int index)
00265 {
00266   switch (index) {
00267     case 0: return TorSettings::NullAuth;
00268     case 1: return TorSettings::CookieAuth;
00269     case 2: return TorSettings::PasswordAuth;
00270     default: break;
00271   }
00272   return TorSettings::UnknownAuth;
00273 }
00274 
00275 /** Returns the index in the authentication methods combo box for the given
00276  * authentication <b>method</b>. */
00277 int
00278 AdvancedPage::authMethodToIndex(TorSettings::AuthenticationMethod method)
00279 {
00280   switch (method) {
00281     case TorSettings::NullAuth: return 0;
00282     case TorSettings::CookieAuth: return 1;
00283     default: break;
00284   }
00285   return 2;
00286 }
00287 
00288 /** Open a QFileDialog to browse for Tor config file. */
00289 void
00290 AdvancedPage::browseTorConfig()
00291 {
00292   /* Prompt the user to select a file or create a new one */
00293   QString filename = QFileDialog::getOpenFileName(this, 
00294                        tr("Select Tor Configuration File"),
00295                        QFileInfo(ui.lineTorConfig->text()).filePath(),
00296                        tr("Tor Configuration File (torrc);;All Files (*)"));
00297 
00298   /* Make sure a filename was selected */
00299   if (filename.isEmpty()) {
00300     return;
00301   }
00302 
00303   /* Check if the file exists */
00304   QFile torrcFile(filename);
00305   if (!QFileInfo(filename).exists()) {
00306     /* The given file does not exist. Should we create it? */
00307     int response = VMessageBox::question(this,
00308                      tr("File Not Found"),
00309                      tr("%1 does not exist. Would you like to create it?")
00310                                                             .arg(filename),
00311                      VMessageBox::Yes, VMessageBox::No);
00312     
00313     if (response == VMessageBox::No) {
00314       /* Don't create it. Just bail. */
00315       return;
00316     }
00317     /* Attempt to create the specified file */
00318     QString errmsg;
00319     if (!touch_file(filename, false, &errmsg)) {
00320       VMessageBox::warning(this,
00321         tr("Failed to Create File"),
00322         tr("Unable to create %1 [%2]").arg(filename)
00323                                       .arg(errmsg),
00324         VMessageBox::Ok);
00325       return;
00326     }
00327   }
00328   ui.lineTorConfig->setText(filename);
00329 }
00330 
00331 /** Opens a QFileDialog for the user to browse to or create a directory for
00332  * Tor's DataDirectory. */
00333 void
00334 AdvancedPage::browseTorDataDirectory()
00335 {
00336   QString dataDir = QFileDialog::getExistingDirectory(this,
00337                       tr("Select a Directory to Use for Tor Data"),
00338                       ui.lineTorDataDirectory->text());
00339 
00340   if (!dataDir.isEmpty()) 
00341     ui.lineTorDataDirectory->setText(dataDir);
00342 }
00343 
00344 /** Opens a QFileDialog for the user to browse to or create a socket path to
00345  * communicate to Tor */
00346 void
00347 AdvancedPage::browseSocketPath()
00348 {
00349   QString start = QDir::currentPath();
00350   if(!ui.lineSocketPath->text().isEmpty())
00351     start = ui.lineSocketPath->text();
00352   QString socketPath = QFileDialog::getOpenFileName(this,
00353                       tr("Select a file to use for Tor socket path"),
00354                       start);
00355 
00356   if (!socketPath.isEmpty()) 
00357     ui.lineSocketPath->setText(socketPath);
00358 }
00359 
00360 #if 0
00361 #if defined(Q_WS_WIN)
00362 /** Installs or removes the Tor service as necessary. */
00363 void
00364 AdvancedPage::setupService(bool useService)
00365 {
00366   TorService service;
00367   bool isInstalled = service.isInstalled();
00368 
00369   if (!useService && isInstalled) {
00370     /* Uninstall if we don't want to use it anymore */
00371     Vidalia::torControl()->stop();
00372     
00373     if (!service.remove()) {
00374       VMessageBox::critical(this,
00375                             tr("Unable to remove Tor Service"),
00376                             tr("Vidalia was unable to remove the Tor service.\n\n"
00377                                "You may need to remove it manually."), 
00378                             VMessageBox::Ok, VMessageBox::Cancel);
00379     }
00380   } else if (useService && !isInstalled) {
00381     /* Install if we want to start using a service */
00382     if (!service.install(_settings->getExecutable(),
00383                          _settings->getTorrc(),
00384                          _settings->getControlPort())) {
00385       VMessageBox::critical(this,
00386                             tr("Unable to install Tor Service"),
00387                             tr("Vidalia was unable to install the Tor service."),
00388                             VMessageBox::Ok, VMessageBox::Cancel);
00389     }
00390   }
00391 }
00392 #endif
00393 #endif
00394 
00395 /** Called when the user presses the Edit current torrc button */
00396 void 
00397 AdvancedPage::displayTorrcDialog()
00398 {
00399   TorrcDialog rcdialog(this);
00400   rcdialog.exec();
00401 }
00402 
00403 void 
00404 AdvancedPage::toggleControl(bool)
00405 {
00406   if(ui.rdoControlPort->isChecked()) {
00407     ui.lblAddress->setEnabled(true);
00408     ui.lineControlAddress->setEnabled(true);
00409     ui.lineControlPort->setEnabled(true);
00410     ui.lblPath->setEnabled(false);
00411     ui.lineSocketPath->setEnabled(false);
00412     ui.btnBrowseSocketPath->setEnabled(false);
00413     ui.chkAuto->setEnabled(true);
00414   } else {
00415 #if !defined(Q_OS_WIN32)
00416     ui.lblAddress->setEnabled(false);
00417     ui.lineControlAddress->setEnabled(false);
00418     ui.lineControlPort->setEnabled(false);
00419     ui.lblPath->setEnabled(true);
00420     ui.lineSocketPath->setEnabled(true);
00421     ui.btnBrowseSocketPath->setEnabled(true);
00422     ui.chkAuto->setEnabled(false);
00423 #endif
00424   }
00425 }
00426 
00427 void
00428 AdvancedPage::toggleAuto(bool)
00429 {
00430   ui.lblAddress->setVisible(!ui.chkAuto->isChecked());
00431   ui.lineControlAddress->setVisible(!ui.chkAuto->isChecked());
00432   ui.label->setVisible(!ui.chkAuto->isChecked());
00433   ui.lineControlPort->setVisible(!ui.chkAuto->isChecked());
00434 }