Vidalia  0.2.17
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     _settings->setAutoControlPort(false);
00202   }
00203   _settings->setSocketPath(ui.lineSocketPath->text());
00204 
00205   _settings->setAuthenticationMethod(authMethod);
00206   _settings->setUseRandomPassword(ui.chkRandomPassword->isChecked());
00207   if (authMethod == TorSettings::PasswordAuth
00208         && !ui.chkRandomPassword->isChecked())
00209     _settings->setControlPassword(ui.linePassword->text());
00210 
00211 #if 0
00212 #if defined(Q_WS_WIN)
00213   /* Install or uninstall the Tor service as necessary */
00214   setupService(ui.chkUseService->isChecked());
00215 #endif
00216 #endif
00217 
00218   return true;
00219 }
00220 
00221 /** Loads previously saved settings. */
00222 void
00223 AdvancedPage::load()
00224 {
00225   ui.lineControlAddress->setText(_settings->getControlAddress().toString());
00226   ui.lineControlPort->setText(QString::number(_settings->getControlPort()));
00227   ui.lineTorConfig->setText(_settings->getTorrc());
00228   ui.lineTorDataDirectory->setText(_settings->getDataDirectory());
00229   ui.chkAuto->setChecked(_settings->autoControlPort());
00230 
00231   ui.cmbAuthMethod->setCurrentIndex(
00232     authMethodToIndex(_settings->getAuthenticationMethod()));
00233   ui.chkRandomPassword->setChecked(_settings->useRandomPassword());
00234   if (!ui.chkRandomPassword->isChecked())
00235     ui.linePassword->setText(_settings->getControlPassword());
00236   ui.rdoControlPort->setChecked(_settings->getControlMethod() == ControlMethod::Port);
00237   ui.rdoControlSocket->setChecked(_settings->getControlMethod() == ControlMethod::Socket);
00238   ui.lineSocketPath->setText(_settings->getSocketPath());
00239 
00240 #if 0
00241 #if defined(Q_WS_WIN)
00242   TorService s;
00243   ui.chkUseService->setChecked(s.isInstalled());
00244 #endif
00245 #endif
00246 
00247   if(Vidalia::torControl()->getTorVersion() < 0x2021a) { // 0x2021a == 0.2.2.26
00248     ui.chkAuto->setChecked(false);
00249     ui.chkAuto->setVisible(false);
00250   }
00251 }
00252 
00253 /** Called when the user selects a different authentication method from the
00254  * combo box. */
00255 void
00256 AdvancedPage::authMethodChanged(int index)
00257 {
00258   bool usePassword = (indexToAuthMethod(index) == TorSettings::PasswordAuth);
00259   ui.linePassword->setEnabled(usePassword && !ui.chkRandomPassword->isChecked());
00260   ui.chkRandomPassword->setEnabled(usePassword);
00261 }
00262 
00263 /** Returns the authentication method for the given <b>index</b>. */
00264 TorSettings::AuthenticationMethod
00265 AdvancedPage::indexToAuthMethod(int index)
00266 {
00267   switch (index) {
00268     case 0: return TorSettings::NullAuth;
00269     case 1: return TorSettings::CookieAuth;
00270     case 2: return TorSettings::PasswordAuth;
00271     default: break;
00272   }
00273   return TorSettings::UnknownAuth;
00274 }
00275 
00276 /** Returns the index in the authentication methods combo box for the given
00277  * authentication <b>method</b>. */
00278 int
00279 AdvancedPage::authMethodToIndex(TorSettings::AuthenticationMethod method)
00280 {
00281   switch (method) {
00282     case TorSettings::NullAuth: return 0;
00283     case TorSettings::CookieAuth: return 1;
00284     default: break;
00285   }
00286   return 2;
00287 }
00288 
00289 /** Open a QFileDialog to browse for Tor config file. */
00290 void
00291 AdvancedPage::browseTorConfig()
00292 {
00293   /* Prompt the user to select a file or create a new one */
00294   QString filename = QFileDialog::getOpenFileName(this, 
00295                        tr("Select Tor Configuration File"),
00296                        QFileInfo(ui.lineTorConfig->text()).filePath(),
00297                        tr("Tor Configuration File (torrc);;All Files (*)"));
00298 
00299   /* Make sure a filename was selected */
00300   if (filename.isEmpty()) {
00301     return;
00302   }
00303 
00304   /* Check if the file exists */
00305   QFile torrcFile(filename);
00306   if (!QFileInfo(filename).exists()) {
00307     /* The given file does not exist. Should we create it? */
00308     int response = VMessageBox::question(this,
00309                      tr("File Not Found"),
00310                      tr("%1 does not exist. Would you like to create it?")
00311                                                             .arg(filename),
00312                      VMessageBox::Yes, VMessageBox::No);
00313     
00314     if (response == VMessageBox::No) {
00315       /* Don't create it. Just bail. */
00316       return;
00317     }
00318     /* Attempt to create the specified file */
00319     QString errmsg;
00320     if (!touch_file(filename, false, &errmsg)) {
00321       VMessageBox::warning(this,
00322         tr("Failed to Create File"),
00323         tr("Unable to create %1 [%2]").arg(filename)
00324                                       .arg(errmsg),
00325         VMessageBox::Ok);
00326       return;
00327     }
00328   }
00329   ui.lineTorConfig->setText(filename);
00330 }
00331 
00332 /** Opens a QFileDialog for the user to browse to or create a directory for
00333  * Tor's DataDirectory. */
00334 void
00335 AdvancedPage::browseTorDataDirectory()
00336 {
00337   QString dataDir = QFileDialog::getExistingDirectory(this,
00338                       tr("Select a Directory to Use for Tor Data"),
00339                       ui.lineTorDataDirectory->text());
00340 
00341   if (!dataDir.isEmpty()) 
00342     ui.lineTorDataDirectory->setText(dataDir);
00343 }
00344 
00345 /** Opens a QFileDialog for the user to browse to or create a socket path to
00346  * communicate to Tor */
00347 void
00348 AdvancedPage::browseSocketPath()
00349 {
00350   QString start = QDir::currentPath();
00351   if(!ui.lineSocketPath->text().isEmpty())
00352     start = ui.lineSocketPath->text();
00353   QString socketPath = QFileDialog::getOpenFileName(this,
00354                       tr("Select a file to use for Tor socket path"),
00355                       start);
00356 
00357   if (!socketPath.isEmpty()) 
00358     ui.lineSocketPath->setText(socketPath);
00359 }
00360 
00361 #if 0
00362 #if defined(Q_WS_WIN)
00363 /** Installs or removes the Tor service as necessary. */
00364 void
00365 AdvancedPage::setupService(bool useService)
00366 {
00367   TorService service;
00368   bool isInstalled = service.isInstalled();
00369 
00370   if (!useService && isInstalled) {
00371     /* Uninstall if we don't want to use it anymore */
00372     Vidalia::torControl()->stop();
00373     
00374     if (!service.remove()) {
00375       VMessageBox::critical(this,
00376                             tr("Unable to remove Tor Service"),
00377                             tr("Vidalia was unable to remove the Tor service.\n\n"
00378                                "You may need to remove it manually."), 
00379                             VMessageBox::Ok, VMessageBox::Cancel);
00380     }
00381   } else if (useService && !isInstalled) {
00382     /* Install if we want to start using a service */
00383     if (!service.install(_settings->getExecutable(),
00384                          _settings->getTorrc(),
00385                          _settings->getControlPort())) {
00386       VMessageBox::critical(this,
00387                             tr("Unable to install Tor Service"),
00388                             tr("Vidalia was unable to install the Tor service."),
00389                             VMessageBox::Ok, VMessageBox::Cancel);
00390     }
00391   }
00392 }
00393 #endif
00394 #endif
00395 
00396 /** Called when the user presses the Edit current torrc button */
00397 void 
00398 AdvancedPage::displayTorrcDialog()
00399 {
00400   TorrcDialog rcdialog(this);
00401   rcdialog.exec();
00402 }
00403 
00404 void 
00405 AdvancedPage::toggleControl(bool)
00406 {
00407   if(ui.rdoControlPort->isChecked()) {
00408     ui.lblAddress->setEnabled(true);
00409     ui.lineControlAddress->setEnabled(true);
00410     ui.lineControlPort->setEnabled(true);
00411     ui.lblPath->setEnabled(false);
00412     ui.lineSocketPath->setEnabled(false);
00413     ui.btnBrowseSocketPath->setEnabled(false);
00414     ui.chkAuto->setEnabled(true);
00415   } else {
00416 #if !defined(Q_OS_WIN32)
00417     ui.lblAddress->setEnabled(false);
00418     ui.lineControlAddress->setEnabled(false);
00419     ui.lineControlPort->setEnabled(false);
00420     ui.lblPath->setEnabled(true);
00421     ui.lineSocketPath->setEnabled(true);
00422     ui.btnBrowseSocketPath->setEnabled(true);
00423     ui.chkAuto->setEnabled(false);
00424 #endif
00425   }
00426 }
00427 
00428 void
00429 AdvancedPage::toggleAuto(bool)
00430 {
00431   ui.lblAddress->setVisible(!ui.chkAuto->isChecked());
00432   ui.lineControlAddress->setVisible(!ui.chkAuto->isChecked());
00433   ui.label->setVisible(!ui.chkAuto->isChecked());
00434   ui.lineControlPort->setVisible(!ui.chkAuto->isChecked());
00435 }