00001
00002
00003
00004
00005
00006
00007
00008
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
00026 ServicePage::ServicePage(QWidget *parent)
00027 : ConfigPage(parent, tr("Services"))
00028 {
00029
00030 ui.setupUi(this);
00031
00032 _torControl = Vidalia::torControl();
00033
00034 _torSettings = new TorSettings;
00035
00036
00037 _serviceSettings = new ServiceSettings(_torControl);
00038
00039
00040 _services = new QMap<int, Service>();
00041
00042
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
00067 ServicePage::~ServicePage()
00068 {
00069 delete _serviceSettings;
00070 }
00071
00072
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
00122
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
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
00158 void
00159 ServicePage::load()
00160 {
00161 ui.removeButton->setEnabled(false);
00162 ui.copyButton->setEnabled(false);
00163 ui.browseButton->setEnabled(false);
00164
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
00173 ServiceList serviceList = _serviceSettings->getServices();
00174 QList<Service> serviceSettingsList = serviceList.services();
00175 QListIterator<Service> it(serviceSettingsList);
00176
00177 while(it.hasNext()) {
00178 Service temp = it.next();
00179 if(isServicePublished(temp, torServiceList) == false) {
00180 completeList.push_back(temp);
00181 }
00182 }
00183
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
00195
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
00204 while(it.hasNext()) {
00205 QString temp = it.next();
00206 list.push_back(generateService(temp));
00207 }
00208 return list;
00209 }
00210
00211
00212
00213 Service
00214 ServicePage::generateService(QString s)
00215 {
00216 QString additionalOptions = s;
00217
00218 int index = additionalOptions.indexOf("250",1);
00219 additionalOptions.remove(0, index+4);
00220
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
00227 while(additionalOptions.contains("250")) {
00228 int i = additionalOptions.indexOf("250", 0);
00229 additionalOptions.remove(i, 4);
00230 }
00231
00232 if (!additionalOptions.endsWith('\n')) {
00233 additionalOptions.append("\n");
00234 }
00235
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
00244 additionalOptions.replace(QString("\n"), QString(" "));
00245 } else {
00246 additionalOptions = "";
00247 }
00248
00249 QString address, virtualPort, physAddressPort, serviceDir;
00250
00251 QStringList strList = s.split("\n");
00252 QString tempServiceDir = strList.first().trimmed();
00253 serviceDir = tempServiceDir.remove(0, 1);
00254
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
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
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
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
00307
00308 void
00309 ServicePage::initServiceTable(QMap<int, Service>* _services)
00310 {
00311
00312 int rows = ui.serviceWidget->rowCount();
00313 for(int i = 0; i < rows; i++) {
00314 ui.serviceWidget->removeRow(0);
00315 }
00316
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
00371
00372 void
00373 ServicePage::addService()
00374 {
00375 int rows = ui.serviceWidget->rowCount();
00376 ui.serviceWidget->insertRow(rows);
00377 QTableWidgetItem *address = new QTableWidgetItem("["+tr("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
00396
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
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
00419
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
00439
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
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
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
00518
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
00529 return;
00530 }
00531 QString text = item->text();
00532 switch (item->column()) {
00533 case 1:
00534 if(portValidator->validate(text, pos) == QValidator::Acceptable) {
00535
00536 item->setData(32, text);
00537 } else {
00538
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:
00546 if(text.contains(":")) {
00547
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 {
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:
00579
00580 for (int index = 0; index < ui.serviceWidget->rowCount(); index++) {
00581
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
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
00599 item->setData(32, text);
00600 break;
00601 }
00602 }
00603