servicepage.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.vidalia-project.net/. No part of Vidalia, including this file,
00007 **  may be copied, modified, propagated, or distributed except according to the
00008 **  terms described in the LICENSE file.
00009 */
00010 
00011 #include <stringutil.h>
00012 #include <vmessagebox.h>
00013 #include <qheaderview.h>
00014 #include <qclipboard.h>
00015 #include <QFile>
00016 #include <QTextStream>
00017 #include <file.h>
00018 #include "configdialog.h"
00019 #include "ipvalidator.h"
00020 #include "service.h"
00021 #include "servicelist.h"
00022 #include "domainvalidator.h"
00023 #include "ipvalidator.h"
00024 
00025 /** Constructor */
00026 ServicePage::ServicePage(QWidget *parent)
00027 : ConfigPage(parent, tr("Services"))
00028 {
00029   /* Invoke the Qt Designer generated object setup routine */
00030   ui.setupUi(this);
00031   /* Keep a pointer to the TorControl object used to talk to Tor */
00032   _torControl = Vidalia::torControl();
00033   /* Create Tor settings objects */
00034   _torSettings = new TorSettings;
00035   /* Keep a pointer to the ServiceSettings object used to store
00036    * the configuration */
00037   _serviceSettings = new ServiceSettings(_torControl);
00038   /* A QMap, mapping from the row number to the Entity for
00039    * all services */
00040   _services = new QMap<int, Service>();
00041   /* A QMap, mapping from the directory path to the Entity for
00042    * all Tor services */
00043   _torServices = new QMap<QString, Service>();
00044 
00045   ui.serviceWidget->horizontalHeader()->resizeSection(0, 150);
00046   ui.serviceWidget->horizontalHeader()->resizeSection(1, 89);
00047   ui.serviceWidget->horizontalHeader()->resizeSection(2, 100);
00048   ui.serviceWidget->horizontalHeader()->resizeSection(3, 120);
00049   ui.serviceWidget->horizontalHeader()->resizeSection(4, 60);
00050   ui.serviceWidget->horizontalHeader()->setResizeMode(0, QHeaderView::Stretch);
00051   ui.serviceWidget->horizontalHeader()->setResizeMode(1, QHeaderView::Stretch);
00052   ui.serviceWidget->horizontalHeader()->setResizeMode(2, QHeaderView::Stretch);
00053   ui.serviceWidget->horizontalHeader()->setResizeMode(3, QHeaderView::Stretch);
00054   ui.serviceWidget->verticalHeader()->hide();
00055 
00056   connect(ui.addButton, SIGNAL(clicked()), this, SLOT(addService()));
00057   connect(ui.removeButton, SIGNAL(clicked()), this, SLOT(removeService()));
00058   connect(ui.copyButton, SIGNAL(clicked()), this, SLOT(copyToClipboard()));
00059   connect(ui.browseButton, SIGNAL(clicked()), this, SLOT(browseDirectory()));
00060   connect(ui.serviceWidget, SIGNAL(itemClicked(QTableWidgetItem*)),
00061           this, SLOT(serviceSelectionChanged()));
00062   connect(ui.serviceWidget, SIGNAL(itemChanged(QTableWidgetItem*)),
00063           this, SLOT(valueChanged()));
00064 }
00065 
00066 /** Destructor */
00067 ServicePage::~ServicePage()
00068 {
00069   delete _serviceSettings;
00070 }
00071 
00072 /** Saves changes made to settings on the Server settings page. */
00073 bool
00074 ServicePage::save(QString &errmsg)
00075 {
00076   QList<Service> serviceList;
00077   QList<Service> publishedServices;
00078   int index = 0;
00079   while(index < ui.serviceWidget->rowCount()) {
00080     QString address = ui.serviceWidget->item(index,0)->text();
00081     QString virtualPort = ui.serviceWidget->item(index,1)->text();
00082     QString physicalAddress = ui.serviceWidget->item(index,2)->text();
00083     QString directoryPath = ui.serviceWidget->item(index,3)->text();
00084     bool enabled = _services->value(index).enabled();
00085     Service temp(address, virtualPort, physicalAddress, directoryPath,
00086                  enabled);
00087     temp.setAdditionalServiceOptions(
00088       _services->value(ui.serviceWidget->currentRow()).additionalServiceOptions());
00089     serviceList.push_back(temp);
00090     if(enabled) {
00091       publishedServices.push_back(temp);
00092     }
00093     index++;
00094   }
00095 
00096   bool save = checkBeforeSaving(serviceList);
00097   if(save) {
00098     ServiceList sList;
00099     if(serviceList.size() > 0) {
00100       sList.setServices(serviceList);
00101     } else {
00102       _services = new QMap<int, Service>();
00103       sList.setServices(_services->values());
00104     }
00105     _serviceSettings->setServices(sList);
00106     if(publishedServices.size() > 0) {
00107       startServicesInTor(publishedServices);
00108     } else {
00109       QString errmsg1 = tr("Error while trying to unpublish all services");
00110       QString &errmsg = errmsg1;
00111       _serviceSettings->unpublishAllServices(&errmsg);
00112     }
00113     return true;
00114   } else {
00115     errmsg = tr("Please configure at least a service directory and a virtual "
00116                 "port for each service you want to save. Remove the other ones.");
00117     return false;
00118   }
00119 }
00120 
00121 /** this method checks if either all services have minimal
00122  *  configuration or not */
00123 bool
00124 ServicePage::checkBeforeSaving(QList<Service> serviceList)
00125 {
00126   bool result = true;
00127   foreach(Service s, serviceList) {
00128     if(s.serviceDirectory().isEmpty() || s.virtualPort().isEmpty()) {
00129       result = false;
00130       break;
00131     }
00132   }
00133   return result;
00134 }
00135 
00136 /** this method generates the configuration string for a list of services */
00137 void
00138 ServicePage::startServicesInTor(QList<Service> services)
00139 {
00140   QString serviceConfString;
00141   QString errmsg = "Error while trying to publish services.";
00142   QListIterator<Service> it(services);
00143 
00144   while(it.hasNext()) {
00145     Service temp = it.next();
00146     serviceConfString.append("hiddenservicedir=" +
00147                              string_escape(temp.serviceDirectory()) + " ");
00148     serviceConfString.append("hiddenserviceport=" +
00149      string_escape(temp.virtualPort() +
00150      (temp.physicalAddressPort().isEmpty() ? "" : " " +
00151       temp.physicalAddressPort())));
00152     serviceConfString.append(" " + temp.additionalServiceOptions());
00153   }
00154   _serviceSettings->applyServices(serviceConfString, &errmsg);
00155 }
00156 
00157 /** Loads previously saved settings */
00158 void
00159 ServicePage::load()
00160 {
00161   ui.removeButton->setEnabled(false);
00162   ui.copyButton->setEnabled(false);
00163   ui.browseButton->setEnabled(false);
00164   // get all services
00165   _services = new QMap<int, Service>();
00166   _torServices = new QMap<QString, Service>();
00167   QList<Service> torServiceList;
00168 
00169   QString torConfigurationString = _serviceSettings->getHiddenServiceDirectories();
00170   torServiceList = extractSingleServices(torConfigurationString);
00171   QList<Service> completeList = torServiceList;
00172   // the services stored with vidalia
00173   ServiceList serviceList = _serviceSettings->getServices();
00174   QList<Service> serviceSettingsList = serviceList.services();
00175   QListIterator<Service> it(serviceSettingsList);
00176   // check whether a service is already in the list because he is published
00177   while(it.hasNext()) {
00178     Service temp = it.next();
00179     if(isServicePublished(temp, torServiceList) == false) {
00180       completeList.push_back(temp);
00181     }
00182   }
00183   // generate the _services data structure used during vidalia session
00184   QListIterator<Service> it2(completeList);
00185   int index = 0;
00186   while (it2.hasNext()) {
00187     Service tempService = it2.next();
00188     _services->insert(index, tempService);
00189     index++;
00190   }
00191   initServiceTable(_services);
00192 }
00193 
00194 /** this method returns a list of services by parsing the configuration
00195  *  string given by the tor controller */
00196 QList<Service>
00197 ServicePage::extractSingleServices(QString conf)
00198 {
00199   QList<Service> list;
00200   QStringList strList = conf.split("250 HiddenServiceDir");
00201   strList.removeFirst();
00202   QListIterator<QString> it(strList);
00203   //for each service directory splitted string = service
00204   while(it.hasNext()) {
00205     QString temp = it.next();
00206     list.push_back(generateService(temp));
00207   }
00208   return list;
00209 }
00210 
00211 /** this return a Service by parseing the configuration string
00212  *  of Tor and storeing its values into the object */
00213 Service
00214 ServicePage::generateService(QString s)
00215 {
00216   QString additionalOptions = s;
00217   // remove directory
00218   int index = additionalOptions.indexOf("250",1);
00219   additionalOptions.remove(0, index+4);
00220   // remove the first appearance of the port
00221   int startindex = additionalOptions.indexOf("hiddenserviceport", 0,
00222                                              Qt::CaseInsensitive);
00223   int endindex = additionalOptions.indexOf("250", startindex);
00224   if(endindex != -1) {
00225     additionalOptions.remove(startindex, (endindex-startindex)+4);
00226     //remove all appearances of "250"
00227     while(additionalOptions.contains("250")) {
00228       int i = additionalOptions.indexOf("250", 0);
00229       additionalOptions.remove(i, 4);
00230     }
00231     // prepare for correct quotation
00232     if (!additionalOptions.endsWith('\n')) {
00233       additionalOptions.append("\n");
00234     }
00235     //quote the values
00236     int j = additionalOptions.indexOf("=", 0);
00237     while(j != -1) {
00238       additionalOptions.insert(j+1, "\"");
00239       int end = additionalOptions.indexOf("\n", j);
00240       additionalOptions.insert(end, "\"");
00241       j = additionalOptions.indexOf("=", end);
00242     }
00243     //replace the line brakes with a space and create one single line
00244     additionalOptions.replace(QString("\n"), QString(" "));
00245   } else {
00246       additionalOptions = "";
00247   }
00248 
00249   QString address, virtualPort, physAddressPort, serviceDir;
00250   // service directory
00251   QStringList strList = s.split("\n");
00252   QString tempServiceDir = strList.first().trimmed();
00253   serviceDir = tempServiceDir.remove(0, 1);
00254   //virtual port
00255   QStringList strList2 = s.split("HiddenServicePort");
00256   strList2.removeFirst();
00257   QStringList strList3 = strList2.first().split("\n");
00258   QStringList strList4 = strList3.first().split(" ");
00259   if(strList4.size() > 0) {
00260     QString tempVirtualPort = strList4.first();
00261     virtualPort = tempVirtualPort.remove(0, 1);
00262     strList4.removeFirst();
00263     //physical address:port
00264     if(!strList4.isEmpty()) {
00265       physAddressPort = strList4.first().trimmed();
00266     }
00267   } else {
00268     QString tempVirtualPort = strList3.first();
00269     virtualPort = tempVirtualPort.remove(0, 1);
00270   }
00271   //get .onion address
00272   QString serviceHostnameDir = serviceDir;
00273   serviceHostnameDir.append("/");
00274   serviceHostnameDir.append("hostname");
00275   QFile file(serviceHostnameDir);
00276   if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
00277     address = "[Directory not found]";
00278   } else {
00279     QTextStream in(&file);
00280     QString hostname;
00281     while (!in.atEnd()) {
00282       hostname.append(in.readLine());
00283     }
00284     address = hostname;
00285   }
00286   Service service(address, virtualPort, physAddressPort, serviceDir, true);
00287   service.setAdditionalServiceOptions(additionalOptions);
00288   _torServices->insert(serviceDir, service);
00289   return service;
00290 }
00291 
00292 /** this method checks either a service is published or not */
00293 bool
00294 ServicePage::isServicePublished(Service service, QList<Service> torServices)
00295 {
00296   QListIterator<Service> it(torServices);
00297   while(it.hasNext()) {
00298     Service temp = it.next();
00299     if(temp.serviceDirectory().compare(service.serviceDirectory()) == 0) {
00300       return true;
00301     }
00302   }
00303   return false;
00304 }
00305 
00306 /** this method creates/displays the values for each service
00307  *  shown in the service listing */
00308 void
00309 ServicePage::initServiceTable(QMap<int, Service>* _services)
00310 {
00311   // clean the widget
00312   int rows = ui.serviceWidget->rowCount();
00313   for(int i = 0; i < rows; i++) {
00314     ui.serviceWidget->removeRow(0);
00315   }
00316   //for each service
00317   int index = 0;
00318   while(index < _services->size()) {
00319     Service tempService = _services->value(index);
00320     ui.serviceWidget->insertRow(index);
00321     QTableWidgetItem *cboxitem = new QTableWidgetItem();
00322     cboxitem->setFlags(Qt::ItemIsSelectable);
00323     QTableWidgetItem *addressitem = new QTableWidgetItem();
00324     addressitem->setFlags(Qt::ItemIsSelectable);
00325     if(tempService.serviceAddress().length() < 0) {
00326       addressitem->setText(tempService.serviceAddress());
00327     } else {
00328       QString serviceHostnameDir = tempService.serviceDirectory();
00329       serviceHostnameDir.append("/");
00330       serviceHostnameDir.append("hostname");
00331       QFile file(serviceHostnameDir);
00332       if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
00333         addressitem->setText("[Directory not found]");
00334       } else {
00335         QTextStream in(&file);
00336         QString hostname;
00337         while (!in.atEnd()) {
00338           hostname.append(in.readLine());
00339         }
00340         addressitem->setText(hostname);
00341         tempService.setServiceAddress(hostname);
00342       }
00343     }
00344     addressitem->setData(32, addressitem->text());
00345     QTableWidgetItem *serviceDir =
00346         new QTableWidgetItem(tempService.serviceDirectory(), 0);
00347     serviceDir->setData(32, tempService.serviceDirectory());
00348     QTableWidgetItem* virtualportitem =
00349         new QTableWidgetItem(tempService.virtualPort(), 0);
00350     virtualportitem->setData(32, tempService.virtualPort());
00351     QTableWidgetItem* targetitem =
00352         new QTableWidgetItem(tempService.physicalAddressPort(),0);
00353     targetitem->setData(32, tempService.physicalAddressPort());
00354     if(tempService.enabled()) {
00355       cboxitem->setCheckState(Qt::Checked);
00356       serviceDir->setFlags(Qt::ItemIsSelectable);
00357     } else {
00358       cboxitem->setCheckState(Qt::Unchecked);
00359     }
00360     cboxitem->setTextAlignment(Qt::AlignCenter);
00361     ui.serviceWidget->setItem(index, 0, addressitem);
00362     ui.serviceWidget->setItem(index, 1, virtualportitem);
00363     ui.serviceWidget->setItem(index, 2, targetitem);
00364     ui.serviceWidget->setItem(index, 3, serviceDir);
00365     ui.serviceWidget->setItem(index, 4, cboxitem);
00366     index++;
00367   }
00368 }
00369 
00370 /** this method is called when the user clicks the "Add"-Button
00371  *  it generates a new empty table entrie(row) */
00372 void
00373 ServicePage::addService()
00374 {
00375   int rows = ui.serviceWidget->rowCount();
00376   ui.serviceWidget->insertRow(rows);
00377   QTableWidgetItem *address = new QTableWidgetItem("[Created by Tor]");
00378   address->setFlags(Qt::ItemIsSelectable);
00379   QTableWidgetItem *dummy = new QTableWidgetItem();
00380   QTableWidgetItem *dummy2 = new QTableWidgetItem();
00381   QTableWidgetItem *dummy3 = new QTableWidgetItem();
00382   QTableWidgetItem *cboxitem = new QTableWidgetItem();
00383   cboxitem->setFlags(Qt::ItemIsSelectable);
00384   cboxitem->setCheckState(Qt::Checked);
00385   ui.serviceWidget->setItem(rows, 0, address);
00386   ui.serviceWidget->setItem(rows, 1, dummy);
00387   ui.serviceWidget->setItem(rows, 2, dummy2);
00388   ui.serviceWidget->setItem(rows, 3, dummy3);
00389   ui.serviceWidget->setItem(rows, 4, cboxitem);
00390   Service s;
00391   s.setEnabled(true);
00392   _services->insert(rows, s);
00393 }
00394 
00395 /** this method is called when the user clicks the "Remove"-Button
00396  *  it removes a service/row of the service listing */
00397 void
00398 ServicePage::removeService()
00399 {
00400   int rows = ui.serviceWidget->rowCount();
00401   int selrow = ui.serviceWidget->currentRow();
00402   if(selrow < 0 || selrow >= _services->size()) {
00403     VMessageBox::warning(this, tr("Error"), tr("Please select a Service."),
00404                          VMessageBox::Ok);
00405     return;
00406   } else {
00407     ui.serviceWidget->removeRow(selrow);
00408     //decrease all other service keys
00409     for(int i = 0; i < (rows-selrow-1); i++) {
00410       int index = i+selrow;
00411       Service s = _services->take(index+1);
00412       _services->insert(index, s);
00413     }
00414   }
00415   serviceSelectionChanged();
00416 }
00417 
00418 /** this method is called when the user clicks on the "Copy"-Button, it
00419  *  copies the .onion-Address of the selected service into the clipboard */
00420 void
00421 ServicePage::copyToClipboard()
00422 {
00423   int selrow = ui.serviceWidget->currentRow();
00424   if(selrow < 0 || selrow >= _services->size()) {
00425     VMessageBox::warning(this, tr("Error"), tr("Please select a Service."),
00426                          VMessageBox::Ok);
00427     return;
00428   } else {
00429     QString onionAddress = ui.serviceWidget->item(selrow,0)->text();
00430     QClipboard *clipboard = QApplication::clipboard();
00431     QString clipboardText;
00432     QTableWidgetItem* selectedItem = ui.serviceWidget->item(selrow,0);
00433     clipboardText.append(selectedItem->text());
00434     clipboard->setText(clipboardText);
00435   }
00436 }
00437 
00438 /** this method is called when the user clicks on the "Brows"-Button it opens
00439  *  a QFileDialog to choose a service directory */
00440 void
00441 ServicePage::browseDirectory()
00442 {
00443   int selrow = ui.serviceWidget->currentRow();
00444   if(selrow < 0 || selrow >= _services->size()) {
00445     VMessageBox::warning(this, tr("Error"), tr("Please select a Service."),
00446                          VMessageBox::Ok);
00447     return;
00448   } else {
00449     QString dirname =
00450       QFileDialog::getExistingDirectory(this,
00451                                         tr("Select Service Directory"), "",
00452                                         QFileDialog::ShowDirsOnly
00453                                           | QFileDialog::DontResolveSymlinks);
00454 
00455     if (dirname.isEmpty()) {
00456       return;
00457     }
00458     ui.serviceWidget->item(selrow,3)->setText(dirname);
00459     Service s = _services->take(selrow);
00460     s.setServiceDirectory(dirname);
00461     _services->insert(selrow, s);
00462   }
00463 }
00464 
00465 /** this method is called when the selects an other tablewidgetitem */
00466 void
00467 ServicePage::serviceSelectionChanged()
00468 {
00469   bool emptyTable = false;
00470   if(ui.serviceWidget->rowCount() > 0) {
00471     ui.removeButton->setEnabled(true);
00472     ui.copyButton->setEnabled(true);
00473     ui.browseButton->setEnabled(true);
00474   } else {
00475     ui.removeButton->setEnabled(false);
00476     ui.copyButton->setEnabled(false);
00477     ui.browseButton->setEnabled(false);
00478     emptyTable = true;
00479   }
00480   int currentRow = ui.serviceWidget->currentRow();
00481   if(emptyTable == false) {
00482     QTableWidgetItem* item = ui.serviceWidget->item(currentRow, 0);
00483     if(item != NULL) {
00484       bool b = item->text().contains(".onion");
00485       ui.copyButton->setEnabled(b);
00486     }
00487   }
00488   
00489   QString selDir = _services->value(ui.serviceWidget->currentRow()).
00490                                     serviceDirectory();
00491   QList<QString> strList =  _torServices->keys();
00492   if(selDir.length() > 0) {
00493     QListIterator<QString> it(strList);
00494     while(it.hasNext()) {
00495       QString temp = it.next();
00496       if(selDir.compare(temp) == 0) {
00497         ui.browseButton->setEnabled(false);
00498         break;
00499       }
00500     }
00501   }
00502   // if the user has clicked on the checkbox cell
00503   if(ui.serviceWidget->currentColumn() == 4) {
00504     Service service = _services->take(currentRow);
00505     QTableWidgetItem* item = ui.serviceWidget->item(currentRow,4);
00506     if(service.enabled()) {
00507       item->setCheckState(Qt::Unchecked);
00508       service.setEnabled(false);
00509     } else {
00510       item->setCheckState(Qt::Checked);
00511       service.setEnabled(true);
00512     }
00513     _services->insert(currentRow, service);
00514   }
00515 }
00516 
00517 /** this method is called when the user finished editing a cell and it provides
00518  *  that only valid values are set */
00519 void
00520 ServicePage::valueChanged()
00521 {
00522   int pos = 0;
00523   QIntValidator* portValidator = new QIntValidator(1, 65535, this);
00524   DomainValidator* domainValidator = new DomainValidator(this);
00525   IPValidator* ipValidator = new IPValidator(this);
00526   QTableWidgetItem* item = ui.serviceWidget->currentItem();
00527   if (item == NULL || item->text() == NULL || item->text().length() == 0) {
00528     // nothing to validate here
00529     return;
00530   }
00531   QString text = item->text();
00532   switch (item->column()) {
00533     case 1: // virtual port
00534       if(portValidator->validate(text, pos) == QValidator::Acceptable) {
00535         // correct data; buffer value in user role 32
00536         item->setData(32, text);
00537       } else {
00538         //incorrect data; restore value from user role 32
00539         VMessageBox::warning(this, tr("Error"),
00540             tr("Virtual Port may only contain valid port numbers [1..65535]."),
00541             VMessageBox::Ok);
00542         item->setText(item->data(32).toString());
00543       }
00544       break;
00545     case 2: // target
00546       if(text.contains(":")) {
00547         // check for <address>:<port>
00548         QStringList strList = text.split(":");
00549         if (strList.size() != 2) {
00550           goto invalid;
00551         }
00552         QString address = strList.at(0);
00553         QString port = strList.at(1);
00554         if((address.compare("localhost") != 0 &&
00555           ipValidator->validate(address, pos) != QValidator::Acceptable &&
00556           domainValidator->validate(address, pos) != QValidator::Acceptable) ||
00557           portValidator->validate(port, pos) != QValidator::Acceptable) {
00558           goto invalid;
00559         }
00560       } else { // either <address> or <port>
00561         if (text.compare("localhost") != 0 &&
00562           ipValidator->validate(text, pos) != QValidator::Acceptable &&
00563           domainValidator->validate(text, pos) != QValidator::Acceptable &&
00564           portValidator->validate(text, pos) != QValidator::Acceptable) {
00565           goto invalid;
00566         }
00567       }
00568       goto valid;
00569  invalid:
00570       VMessageBox::warning(this, tr("Error"),
00571           tr("Target may only contain address:port, address, or port."),
00572           VMessageBox::Ok);
00573       item->setText(item->data(32).toString());
00574       break;
00575  valid:
00576       item->setData(32, text);
00577       break;
00578     case 3: // service directory
00579       // compare with directories of other enabled services
00580       for (int index = 0; index < ui.serviceWidget->rowCount(); index++) {
00581         // skip own row
00582         if(index == item->row()) {
00583           continue;
00584         }
00585         QTableWidgetItem* compareWith = ui.serviceWidget->item(index, 3);
00586         if(compareWith != NULL) {
00587           QString actualDir = compareWith->text();
00588           if(actualDir.length() > 0 && text.compare(actualDir) == 0) {
00589             // service directory already in use
00590             VMessageBox::warning(this, tr("Error"),
00591                 tr("Directory already in use by another service."),
00592                 VMessageBox::Ok);
00593             item->setText(item->data(32).toString());
00594             return;
00595           }
00596         }
00597       }
00598       // correct data; buffer value in user role 32
00599       item->setData(32, text);
00600       break;
00601   }
00602 }
00603 

Generated on Sat Aug 16 17:31:48 2008 for Vidalia by  doxygen 1.5.6