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