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