diff --git a/src/FinishedTorrents.cpp b/src/FinishedTorrents.cpp index 518d4c8b0..95d746354 100644 --- a/src/FinishedTorrents.cpp +++ b/src/FinishedTorrents.cpp @@ -280,7 +280,8 @@ int FinishedTorrents::getRowFromHash(QString hash) const{ // Note: does not actually pause the torrent in BT Session void FinishedTorrents::pauseTorrent(QString hash) { int row = getRowFromHash(hash); - Q_ASSERT(row != -1); + if(row == -1) + return; finishedListModel->setData(finishedListModel->index(row, F_UPSPEED), QVariant((double)0.0)); finishedListModel->setData(finishedListModel->index(row, F_NAME), QIcon(QString::fromUtf8(":/Icons/skin/paused.png")), Qt::DecorationRole); finishedListModel->setData(finishedListModel->index(row, F_LEECH), QVariant(QString::fromUtf8("0"))); diff --git a/src/GUI.cpp b/src/GUI.cpp index 7ff5da992..d71bf5553 100644 --- a/src/GUI.cpp +++ b/src/GUI.cpp @@ -31,6 +31,7 @@ #include #include "GUI.h" +#include "httpserver.h" #include "downloadingTorrents.h" #include "misc.h" #include "createtorrent_imp.h" @@ -119,7 +120,9 @@ GUI::GUI(QWidget *parent, QStringList torrentCmdLine) : QMainWindow(parent), dis connect(BTSession, SIGNAL(scanDirFoundTorrents(const QStringList&)), this, SLOT(processScannedFiles(const QStringList&))); connect(BTSession, SIGNAL(newDownloadedTorrent(QString, QString)), this, SLOT(processDownloadedFiles(QString, QString))); connect(BTSession, SIGNAL(downloadFromUrlFailure(QString, QString)), this, SLOT(handleDownloadFromUrlFailure(QString, QString))); - connect(BTSession, SIGNAL(torrent_deleted(QString, QString, bool)), this, SLOT(deleteTorrent(QString, QString, bool))); + connect(BTSession, SIGNAL(deletedTorrent(QString)), this, SLOT(deleteTorrent(QString))); + connect(BTSession, SIGNAL(torrent_ratio_deleted(QString)), this, SLOT(deleteRatioTorrent(QString))); + connect(BTSession, SIGNAL(pausedTorrent(QString)), this, SLOT(pauseTorrent(QString))); qDebug("create tabWidget"); tabs = new QTabWidget(); // Download torrents tab @@ -158,6 +161,15 @@ GUI::GUI(QWidget *parent, QStringList torrentCmdLine) : QMainWindow(parent), dis readSettings(); // Add torrent given on command line processParams(torrentCmdLine); + // Initialize Web UI + httpServer = 0; + if(settings.value("Preferences/WebUI/Enabled", false).toBool()) + { + quint16 port = settings.value("Preferences/WebUI/Port", 8080).toUInt(); + QString username = settings.value("Preferences/WebUI/Username", "").toString(); + QString password = settings.value("Preferences/WebUI/Password", "").toString(); + initWebUi(username, password, port); + } // Use a tcp server to allow only one instance of qBittorrent tcpServer = new QTcpServer(); if (!tcpServer->listen(QHostAddress::LocalHost, 1666)) { @@ -196,6 +208,9 @@ GUI::~GUI() { delete tcpServer; delete connecStatusLblIcon; delete tabs; + // HTTP Server + if(httpServer) + delete httpServer; // Keyboard shortcuts delete switchSearchShortcut; delete switchSearchShortcut2; @@ -687,27 +702,22 @@ void GUI::on_actionDelete_Permanently_triggered() { QString fileName = h.name(); // Remove the torrent BTSession->deleteTorrent(hash, true); - // Delete item from list - if(inDownloadList) { - downloadingTorrentTab->deleteTorrent(hash); - } else { - finishedTorrentTab->deleteTorrent(hash); - } // Update info bar downloadingTorrentTab->setInfoBar(tr("'%1' was removed permanently.", "'xxx.avi' was removed permanently.").arg(fileName)); } } -void GUI::deleteTorrent(QString hash, QString fileName, bool finished) { - if(finished) { - finishedTorrentTab->deleteTorrent(hash); - } else { - downloadingTorrentTab->deleteTorrent(hash); - } +void GUI::deleteRatioTorrent(QString fileName) { // Update info bar downloadingTorrentTab->setInfoBar(tr("'%1' was removed because its ratio reached the maximum value you set.", "%1 is a file name").arg(fileName)); } +void GUI::deleteTorrent(QString hash) { + // Delete item from list + downloadingTorrentTab->deleteTorrent(hash); + finishedTorrentTab->deleteTorrent(hash); +} + // delete selected items in the list void GUI::on_actionDelete_triggered() { QStringList hashes; @@ -752,12 +762,6 @@ void GUI::on_actionDelete_triggered() { QString fileName = h.name(); // Remove the torrent BTSession->deleteTorrent(hash, false); - // Delete item from list - if(inDownloadList) { - downloadingTorrentTab->deleteTorrent(hash); - } else { - finishedTorrentTab->deleteTorrent(hash); - } // Update info bar downloadingTorrentTab->setInfoBar(tr("'%1' was removed.", "'xxx.avi' was removed.").arg(fileName)); } @@ -1127,6 +1131,11 @@ void GUI::on_actionPause_triggered() { } } +void GUI::pauseTorrent(QString hash) { + downloadingTorrentTab->pauseTorrent(hash); + finishedTorrentTab->pauseTorrent(hash); +} + // Resume All Downloads in DL list void GUI::on_actionStart_All_triggered() { bool change = false; @@ -1344,10 +1353,40 @@ void GUI::OptionsSaved(QString info, bool deleteOptions) { systrayIntegration = newSystrayIntegration; // Update info bar downloadingTorrentTab->setInfoBar(info); + // Update Web UI + if (options->isWebUiEnabled()) + { + quint16 port = options->webUiPort(); + QString username = options->webUiUsername(); + QString password = options->webUiPassword(); + initWebUi(username, password, port); + } + else if(httpServer) + { + delete httpServer; + httpServer = 0; + } // Update session configureSession(deleteOptions); } +bool GUI::initWebUi(QString username, QString password, int port) +{ + if(httpServer) + { + httpServer->close(); + } + else + httpServer = new HttpServer(BTSession, 500, this); + httpServer->setAuthorization(username, password); + bool success = httpServer->listen(QHostAddress::Any, port); + if (success) + qDebug()<<"Web UI listening on port "< max_ratio) { QString fileName = h.name(); deleteTorrent(hash); - emit torrent_deleted(hash, fileName, true); + emit torrent_ratio_deleted(fileName); } } } @@ -249,6 +249,7 @@ void bittorrent::deleteTorrent(QString hash, bool permanent) { // Deleting in a thread to avoid GUI freeze deleter->deleteTorrent(savePath, files_arb); } + emit deletedTorrent(hash); } // Return a list of hashes for the finished torrents @@ -312,6 +313,7 @@ bool bittorrent::pauseTorrent(QString hash) { // Save fast resume data saveFastResumeAndRatioData(hash); qDebug("Torrent paused successfully"); + emit pausedTorrent(hash); }else{ if(!h.is_valid()) { qDebug("Could not pause torrent %s, reason: invalid", hash.toUtf8().data()); @@ -341,6 +343,7 @@ bool bittorrent::resumeTorrent(QString hash) { TorrentsStartTime[hash] = QDateTime::currentDateTime(); h.resume(); success = true; + emit resumedTorrent(hash); } // Delete .paused file if(QFile::exists(misc::qBittorrentPath()+"BT_backup"+QDir::separator()+hash+".paused")) @@ -353,6 +356,18 @@ bool bittorrent::resumeTorrent(QString hash) { return success; } +void bittorrent::pauseAllTorrents() { + QStringList list = getUnfinishedTorrents() + getFinishedTorrents(); + foreach(QString hash, list) + pauseTorrent(hash); +} + +void bittorrent::resumeAllTorrents() { + QStringList list = getUnfinishedTorrents() + getFinishedTorrents(); + foreach(QString hash, list) + resumeTorrent(hash); +} + void bittorrent::loadWebSeeds(QString hash) { QFile urlseeds_file(misc::qBittorrentPath()+"BT_backup"+QDir::separator()+hash+".urlseeds"); if(!urlseeds_file.open(QIODevice::ReadOnly | QIODevice::Text)) return; @@ -698,12 +713,12 @@ bool bittorrent::enableDHT(bool b) { dht_state = bdecode(std::istream_iterator(dht_state_file), std::istream_iterator()); }catch (std::exception&) {} try { - s->start_dht(dht_state); - s->add_dht_router(std::make_pair(std::string("router.bittorrent.com"), 6881)); - s->add_dht_router(std::make_pair(std::string("router.utorrent.com"), 6881)); - s->add_dht_router(std::make_pair(std::string("router.bitcomet.com"), 6881)); - DHTEnabled = true; - qDebug("DHT enabled"); + s->start_dht(dht_state); + s->add_dht_router(std::make_pair(std::string("router.bittorrent.com"), 6881)); + s->add_dht_router(std::make_pair(std::string("router.utorrent.com"), 6881)); + s->add_dht_router(std::make_pair(std::string("router.bitcomet.com"), 6881)); + DHTEnabled = true; + qDebug("DHT enabled"); }catch(std::exception e) { qDebug("Could not enable DHT, reason: %s", e.what()); return false; @@ -1166,7 +1181,7 @@ void bittorrent::readAlerts() { // Authentication if(p->status_code != 401) { QString hash = h.hash(); - qDebug("Received a tracker error for %s", (const char*)h.current_tracker().toUtf8()); + qDebug("Received a tracker error for %s", (const char*)misc::toQString(p->url).toUtf8()); QHash errors = trackersErrors.value(hash, QHash()); // p->url requires at least libtorrent v0.13.1 errors[misc::toQString(p->url)] = QString::fromUtf8(a->msg().c_str()); diff --git a/src/bittorrent.h b/src/bittorrent.h index 3a10560b5..9a68d9857 100644 --- a/src/bittorrent.h +++ b/src/bittorrent.h @@ -99,6 +99,8 @@ class bittorrent : public QObject{ void deleteTorrent(QString hash, bool permanent = false); bool pauseTorrent(QString hash); bool resumeTorrent(QString hash); + void pauseAllTorrents(); + void resumeAllTorrents(); void saveDHTEntry(); void preAllocateAllFiles(bool b); void saveFastResumeAndRatioData(); @@ -153,6 +155,9 @@ class bittorrent : public QObject{ void invalidTorrent(QString path); void duplicateTorrent(QString path); void addedTorrent(QString path, QTorrentHandle& h, bool fastResume); + void deletedTorrent(QString hash); + void pausedTorrent(QString hash); + void resumedTorrent(QString hash); void finishedTorrent(QTorrentHandle& h); void fullDiskError(QTorrentHandle& h); void trackerError(QString hash, QString time, QString msg); @@ -167,7 +172,7 @@ class bittorrent : public QObject{ void fastResumeDataRejected(QString name); void urlSeedProblem(QString url, QString msg); void torrentFinishedChecking(QString hash); - void torrent_deleted(QString hash, QString fileName, bool finished); + void torrent_ratio_deleted(QString fileName); void UPnPError(QString msg); void UPnPSuccess(QString msg); }; diff --git a/src/downloadingTorrents.cpp b/src/downloadingTorrents.cpp index 4ae26de43..3dc8b6176 100644 --- a/src/downloadingTorrents.cpp +++ b/src/downloadingTorrents.cpp @@ -147,7 +147,8 @@ unsigned int DownloadingTorrents::getNbTorrentsInList() const { // Note: do not actually pause the torrent in BT session void DownloadingTorrents::pauseTorrent(QString hash) { int row = getRowFromHash(hash); - Q_ASSERT(row != -1); + if(row == -1) + return; DLListModel->setData(DLListModel->index(row, DLSPEED), QVariant((double)0.0)); DLListModel->setData(DLListModel->index(row, UPSPEED), QVariant((double)0.0)); DLListModel->setData(DLListModel->index(row, ETA), QVariant((qlonglong)-1)); diff --git a/src/options.ui b/src/options.ui index 2929e8b1a..1c26fc2df 100644 --- a/src/options.ui +++ b/src/options.ui @@ -1383,132 +1383,131 @@ Share ratio settings - - - - 11 - 30 - 535 - 31 - - - - - - - Desired ratio: - - - - - - - false - - - - 8 - - - - Qt::AlignHCenter - - - 1 - - - 1.000000000000000 - - - 10.000000000000000 - - - 0.100000000000000 - - - 1.000000000000000 - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - 11 - 67 - 535 - 31 - - - - - - - Remove finished torrents when their ratio reaches: - - - - - - - false - - - - 8 - - - - Qt::AlignHCenter - - - 1 - - - 1.000000000000000 - - - 10.000000000000000 - - - 0.100000000000000 - - - 1.000000000000000 - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - + + + + + + + Desired ratio: + + + + + + + false + + + + 8 + + + + Qt::AlignHCenter + + + 1 + + + 1.000000000000000 + + + 10.000000000000000 + + + 0.100000000000000 + + + 1.000000000000000 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + Remove finished torrents when their ratio reaches: + + + + + + + false + + + + 8 + + + + Qt::AlignHCenter + + + 1 + + + 1.000000000000000 + + + 10.000000000000000 + + + 0.100000000000000 + + + 1.000000000000000 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + @@ -1845,6 +1844,175 @@ + + + Web UI + + + :/Icons/password.png + + + + + + true + + + Enable Web User Interface + + + true + + + + + + + true + + + HTTP Server + + + + + + true + + + Port: + + + + + + + true + + + 65525 + + + 80 + + + + + + + Qt::Horizontal + + + + 21 + 29 + + + + + + + + + + + true + + + Authentication + + + + + + + + true + + + Username: + + + + + + + true + + + Password: + + + + + + + + + + + true + + + + + + 1000 + + + QLineEdit::Normal + + + + + + + true + + + + + + 1000 + + + QLineEdit::Password + + + + + + + + + Qt::Horizontal + + + + 198 + 57 + + + + + + + + + + + Qt::Vertical + + + + 623 + 41 + + + + + + @@ -1881,6 +2049,74 @@ + + tabOptions + comboI18n + comboStyle + checkConfirmExit + checkSpeedInTitle + spinRefreshInterval + checkNoSystray + checkCloseToSystray + checkMinimizeToSysTray + checkStartMinimized + checkSystrayBalloons + textSavePath + browseSaveDirButton + checkPreallocateAll + checkAdditionDialog + checkStartPaused + checkScanDir + textScanDir + browseScanDirButton + actionTorrentDlOnDblClBox + actionTorrentFnOnDblClBox + spinPortMin + spinPortMax + checkUPnP + checkNATPMP + checkUploadLimit + checkDownloadLimit + spinUploadLimit + spinDownloadLimit + comboProxyType + textProxyIP + spinProxyPort + checkProxyAuth + textProxyUsername + textProxyPassword + checkProxyTrackers + checkProxyPeers + checkProxyDHT + checkProxyWebseeds + checkMaxConnecs + spinMaxConnec + checkMaxConnecsPerTorrent + spinMaxConnecPerTorrent + checkMaxUploadsPerTorrent + spinMaxUploadsPerTorrent + checkDHT + checkPeX + checkLSD + comboEncryption + checkRatioLimit + spinRatio + checkRatioRemove + spinMaxRatio + checkIPFilter + filtersList + addFilterRangeButton + delFilterRangeButton + textFilterPath + browseFilterButton + spinRSSRefresh + spinRSSMaxArticlesPerFeed + checkWebUi + spinWebUiPort + textWebUiUsername + textWebUiPassword + buttonBox + diff --git a/src/options_imp.cpp b/src/options_imp.cpp index e4635053d..e27516473 100644 --- a/src/options_imp.cpp +++ b/src/options_imp.cpp @@ -142,6 +142,8 @@ options_imp::options_imp(QWidget *parent):QDialog(parent){ connect(checkRatioRemove, SIGNAL(stateChanged(int)), this, SLOT(enableDeleteRatio(int))); // IP Filter tab connect(checkIPFilter, SIGNAL(stateChanged(int)), this, SLOT(enableFilter(int))); + // Web UI tab + connect(checkWebUi, SIGNAL(toggled(bool)), this, SLOT(enableWebUi(bool))); // Apply button is activated when a value is changed // General tab @@ -203,6 +205,11 @@ options_imp::options_imp(QWidget *parent):QDialog(parent){ connect(textFilterPath, SIGNAL(textChanged(QString)), this, SLOT(enableApplyButton())); connect(spinRSSRefresh, SIGNAL(valueChanged(QString)), this, SLOT(enableApplyButton())); connect(spinRSSMaxArticlesPerFeed, SIGNAL(valueChanged(QString)), this, SLOT(enableApplyButton())); + // Web UI tab + connect(checkWebUi, SIGNAL(toggled(bool)), this, SLOT(enableApplyButton())); + connect(spinWebUiPort, SIGNAL(valueChanged(int)), this, SLOT(enableApplyButton())); + connect(textWebUiUsername, SIGNAL(textChanged(QString)), this, SLOT(enableApplyButton())); + connect(textWebUiPassword, SIGNAL(textChanged(QString)), this, SLOT(enableApplyButton())); // Disable apply Button applyButton->setEnabled(false); if(!QSystemTrayIcon::supportsMessages()){ @@ -335,6 +342,17 @@ void options_imp::saveOptions(){ settings.setValue(QString::fromUtf8("RSSMaxArticlesPerFeed"), spinRSSMaxArticlesPerFeed->value()); // End RSS preferences settings.endGroup(); + // Web UI + settings.beginGroup("WebUI"); + settings.setValue("Enabled", isWebUiEnabled()); + if(isWebUiEnabled()) + { + settings.setValue("Port", webUiPort()); + settings.setValue("Username", webUiUsername()); + settings.setValue("Password", webUiPassword()); + } + // End Web UI + settings.endGroup(); // End preferences settings.endGroup(); } @@ -586,6 +604,15 @@ void options_imp::loadOptions(){ spinRSSMaxArticlesPerFeed->setValue(settings.value(QString::fromUtf8("RSSMaxArticlesPerFeed"), 50).toInt()); // End RSS preferences settings.endGroup(); + // Web UI + settings.beginGroup("WebUI"); + checkWebUi->setChecked(settings.value("Enabled", false).toBool()); + enableWebUi(isWebUiEnabled()); + spinWebUiPort->setValue(settings.value("Port", 8080).toInt()); + textWebUiUsername->setText(settings.value("Username", "user").toString()); + textWebUiPassword->setText(settings.value("Password", "").toString()); + // End Web UI + settings.endGroup(); } // return min & max ports @@ -1242,3 +1269,30 @@ void options_imp::on_delFilterRangeButton_clicked(){ } ipfilter.close(); } + +// Web UI + +void options_imp::enableWebUi(bool checkBoxValue){ + groupWebUiServer->setEnabled(checkBoxValue); + groupWebUiAuth->setEnabled(checkBoxValue); +} + +bool options_imp::isWebUiEnabled() const +{ + return checkWebUi->isChecked(); +} + +quint16 options_imp::webUiPort() const +{ + return spinWebUiPort->value(); +} + +QString options_imp::webUiUsername() const +{ + return textWebUiUsername->text(); +} + +QString options_imp::webUiPassword() const +{ + return textWebUiPassword->text(); +} diff --git a/src/options_imp.h b/src/options_imp.h index 842c5f45a..d2b0cf837 100644 --- a/src/options_imp.h +++ b/src/options_imp.h @@ -111,6 +111,10 @@ class options_imp : public QDialog, private Ui::Dialog { // IP Filter bool isFilteringEnabled() const; ip_filter getFilter() const; + bool isWebUiEnabled() const; + quint16 webUiPort() const; + QString webUiUsername() const; + QString webUiPassword() const; protected slots: void enableUploadLimit(int checkBoxValue); @@ -140,6 +144,7 @@ class options_imp : public QDialog, private Ui::Dialog { void enableSystrayOptions(); void disableSystrayOptions(); void setSystrayOptionsState(int checkBoxValue); + void enableWebUi(bool checkBoxValue); public slots: void setLocale(QString locale); diff --git a/src/src.pro b/src/src.pro index 79cce7a1c..4e6510aee 100644 --- a/src/src.pro +++ b/src/src.pro @@ -100,7 +100,8 @@ win32 { RESOURCES = icons.qrc \ lang.qrc \ - search.qrc + search.qrc \ + webui.qrc # Translations TRANSLATIONS = $$LANG_PATH/qbittorrent_fr.ts \ @@ -133,7 +134,7 @@ TRANSLATIONS = $$LANG_PATH/qbittorrent_fr.ts \ HEADERS += GUI.h misc.h options_imp.h about_imp.h \ properties_imp.h createtorrent_imp.h \ DLListDelegate.h SearchListDelegate.h \ - PropListDelegate.h previewSelect.h \ + PropListDelegate.h previewSelect.h \ PreviewListDelegate.h trackerLogin.h \ downloadThread.h downloadFromURLImp.h \ torrentAddition.h deleteThread.h \ @@ -143,8 +144,10 @@ HEADERS += GUI.h misc.h options_imp.h about_imp.h \ qtorrenthandle.h downloadingTorrents.h \ engineSelectDlg.h pluginSource.h \ arborescence.h qgnomelook.h realprogressbar.h \ - realprogressbarthread.h \ - qrealarray.h + realprogressbarthread.h qrealarray.h \ + httpserver.h httpconnection.h \ + httprequestparser.h httpresponsegenerator.h \ + json.h eventmanager.h FORMS += MainWindow.ui options.ui about.ui \ properties.ui createtorrent.ui preview.ui \ login.ui downloadFromURL.ui addTorrentDialog.ui \ @@ -153,19 +156,25 @@ FORMS += MainWindow.ui options.ui about.ui \ SOURCES += GUI.cpp \ main.cpp \ options_imp.cpp \ - properties_imp.cpp \ - createtorrent_imp.cpp \ - bittorrent.cpp \ - searchEngine.cpp \ - rss_imp.cpp \ - FinishedTorrents.cpp \ + properties_imp.cpp \ + createtorrent_imp.cpp \ + bittorrent.cpp \ + searchEngine.cpp \ + rss_imp.cpp \ + FinishedTorrents.cpp \ qtorrenthandle.cpp \ downloadingTorrents.cpp \ engineSelectDlg.cpp \ downloadThread.cpp \ realprogressbar.cpp \ - realprogressbarthread.cpp \ - qrealarray.cpp + realprogressbarthread.cpp \ + qrealarray.cpp \ + httpserver.cpp \ + httpconnection.cpp \ + httprequestparser.cpp \ + httpresponsegenerator.cpp \ + json.cpp \ + eventmanager.cpp DESTDIR = .