Started code reorganizing (Moved libtorrent specific files and webui files to subfolders)

This commit is contained in:
Christophe Dumez
2010-10-09 14:06:35 +00:00
parent 593fdad426
commit 6e7309316c
49 changed files with 478 additions and 465 deletions

476
src/webui/eventmanager.cpp Normal file
View File

@@ -0,0 +1,476 @@
/*
* Bittorrent Client using Qt4 and libtorrent.
* Copyright (C) 2006 Ishan Arora and Christophe Dumez
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* In addition, as a special exception, the copyright holders give permission to
* link this program with the OpenSSL project's "OpenSSL" library (or with
* modified versions of it that use the same license as the "OpenSSL" library),
* and distribute the linked executables. You must obey the GNU General Public
* License in all respects for all of the code used other than "OpenSSL". If you
* modify file(s), you may extend this exception to your version of the file(s),
* but you are not obligated to do so. If you do not wish to do so, delete this
* exception statement from your version.
*
* Contact : chris@qbittorrent.org
*/
#include <libtorrent/version.hpp>
#include "eventmanager.h"
#include "qbtsession.h"
#include "scannedfoldersmodel.h"
#include "misc.h"
#include "preferences.h"
//#include "proplistdelegate.h"
#include "torrentpersistentdata.h"
#include <QDebug>
#include <QTranslator>
EventManager::EventManager(QObject *parent, Bittorrent *BTSession)
: QObject(parent), BTSession(BTSession)
{
}
QList<QVariantMap> EventManager::getEventList() const {
return event_list.values();
}
QList<QVariantMap> EventManager::getPropTrackersInfo(QString hash) const {
QList<QVariantMap> trackersInfo;
QTorrentHandle h = BTSession->getTorrentHandle(hash);
if(h.is_valid()) {
QHash<QString, TrackerInfos> trackers_data = BTSession->getTrackersInfo(hash);
std::vector<announce_entry> vect_trackers = h.trackers();
std::vector<announce_entry>::iterator it;
for(it = vect_trackers.begin(); it != vect_trackers.end(); it++) {
QVariantMap tracker;
QString tracker_url = misc::toQString(it->url);
tracker["url"] = tracker_url;
TrackerInfos data = trackers_data.value(tracker_url, TrackerInfos(tracker_url));
QString error_message = data.last_message.trimmed();
#if LIBTORRENT_VERSION_MINOR > 14
if(it->verified) {
tracker["status"] = tr("Working");
} else {
if(it->updating && it->fails == 0) {
tracker["status"] = tr("Updating...");
} else {
if(it->fails > 0) {
tracker["status"] = tr("Not working");
} else {
tracker["status"] = tr("Not contacted yet");
}
}
}
#else
if(data.verified) {
tracker["status"] = tr("Working");
} else {
if(data.fail_count > 0)
tracker["status"] = tr("Not working");
else
tracker["status"] = tr("Not contacted yet");
}
#endif
tracker["num_peers"] = QString::number(trackers_data.value(tracker_url, TrackerInfos(tracker_url)).num_peers);
tracker["msg"] = error_message;
trackersInfo << tracker;
}
}
return trackersInfo;
}
QList<QVariantMap> EventManager::getPropFilesInfo(QString hash) const {
QList<QVariantMap> files;
QTorrentHandle h = BTSession->getTorrentHandle(hash);
if(!h.is_valid() || !h.has_metadata()) return files;
std::vector<int> priorities = h.file_priorities();
std::vector<size_type> fp;
h.file_progress(fp);
torrent_info t = h.get_torrent_info();
torrent_info::file_iterator fi;
int i=0;
for(fi=t.begin_files(); fi != t.end_files(); fi++) {
QVariantMap file;
QString path = QDir::cleanPath(misc::toQStringU(fi->path.string()));
QString name = path.split('/').last();
file["name"] = name;
file["size"] = misc::friendlyUnit((double)fi->size);
if(fi->size > 0)
file["progress"] = fp[i]/(double)fi->size;
else
file["progress"] = 1.; // Empty file...
file["priority"] = priorities[i];
if(i == 0)
file["is_seed"] = h.is_seed();
files << file;
++i;
}
return files;
}
void EventManager::setGlobalPreferences(QVariantMap m) const {
// UI
if(m.contains("locale")) {
QString locale = m["locale"].toString();
if(Preferences::getLocale() != locale) {
QTranslator *translator = new QTranslator;
if(translator->load(QString::fromUtf8(":/lang/qbittorrent_") + locale)){
qDebug("%s locale recognized, using translation.", qPrintable(locale));
}else{
qDebug("%s locale unrecognized, using default (en_GB).", qPrintable(locale));
}
qApp->installTranslator(translator);
}
Preferences::setLocale(locale);
}
// Downloads
if(m.contains("save_path"))
Preferences::setSavePath(m["save_path"].toString());
if(m.contains("temp_path_enabled"))
Preferences::setTempPathEnabled(m["temp_path_enabled"].toBool());
if(m.contains("temp_path"))
Preferences::setTempPath(m["temp_path"].toString());
if(m.contains("scan_dirs") && m.contains("download_in_scan_dirs")) {
QVariantList download_at_path_tmp = m["download_in_scan_dirs"].toList();
QList<bool> download_at_path;
foreach(QVariant var, download_at_path_tmp) {
download_at_path << var.toBool();
}
QStringList old_folders = Preferences::getScanDirs();
QStringList new_folders = m["scan_dirs"].toStringList();
if(download_at_path.size() == new_folders.size()) {
Preferences::setScanDirs(new_folders);
Preferences::setDownloadInScanDirs(download_at_path);
foreach(const QString &old_folder, old_folders) {
// Update deleted folders
if(!new_folders.contains(old_folder)) {
BTSession->getScanFoldersModel()->removePath(old_folder);
}
}
int i = 0;
foreach(const QString &new_folder, new_folders) {
qDebug("New watched folder: %s", qPrintable(new_folder));
// Update new folders
if(!old_folders.contains(new_folder)) {
BTSession->getScanFoldersModel()->addPath(new_folder, download_at_path.at(i));
}
++i;
}
}
}
if(m.contains("export_dir"))
Preferences::setExportDir(m["export_dir"].toString());
if(m.contains("mail_notification_enabled"))
Preferences::setMailNotificationEnabled(m["mail_notification_enabled"].toBool());
if(m.contains("mail_notification_email"))
Preferences::setMailNotificationEmail(m["mail_notification_email"].toString());
if(m.contains("mail_notification_smtp"))
Preferences::setMailNotificationSMTP(m["mail_notification_smtp"].toString());
if(m.contains("autorun_enabled"))
Preferences::setAutoRunEnabled(m["autorun_enabled"].toBool());
if(m.contains("autorun_program"))
Preferences::setAutoRunProgram(m["autorun_program"].toString());
if(m.contains("preallocate_all"))
Preferences::preAllocateAllFiles(m["preallocate_all"].toBool());
if(m.contains("queueing_enabled"))
Preferences::setQueueingSystemEnabled(m["queueing_enabled"].toBool());
if(m.contains("max_active_downloads"))
Preferences::setMaxActiveDownloads(m["max_active_downloads"].toInt());
if(m.contains("max_active_torrents"))
Preferences::setMaxActiveTorrents(m["max_active_torrents"].toInt());
if(m.contains("max_active_uploads"))
Preferences::setMaxActiveUploads(m["max_active_uploads"].toInt());
#if LIBTORRENT_VERSION_MINOR > 14
if(m.contains("incomplete_files_ext"))
Preferences::useIncompleteFilesExtension(m["incomplete_files_ext"].toBool());
#endif
// Connection
if(m.contains("listen_port"))
Preferences::setSessionPort(m["listen_port"].toInt());
if(m.contains("upnp"))
Preferences::setUPnPEnabled(m["upnp"].toBool());
if(m.contains("natpmp"))
Preferences::setNATPMPEnabled(m["natpmp"].toBool());
if(m.contains("dl_limit"))
Preferences::setGlobalDownloadLimit(m["dl_limit"].toInt());
if(m.contains("up_limit"))
Preferences::setGlobalUploadLimit(m["up_limit"].toInt());
if(m.contains("max_connec"))
Preferences::setMaxConnecs(m["max_connec"].toInt());
if(m.contains("max_connec_per_torrent"))
Preferences::setMaxConnecsPerTorrent(m["max_connec_per_torrent"].toInt());
if(m.contains("max_uploads_per_torrent"))
Preferences::setMaxUploadsPerTorrent(m["max_uploads_per_torrent"].toInt());
// Bittorrent
if(m.contains("dht"))
Preferences::setDHTEnabled(m["dht"].toBool());
if(m.contains("dhtSameAsBT"))
Preferences::setDHTPortSameAsBT(m["dhtSameAsBT"].toBool());
if(m.contains("dht_port"))
Preferences::setDHTPort(m["dht_port"].toInt());
if(m.contains("pex"))
Preferences::setPeXEnabled(m["pex"].toBool());
qDebug("Pex support: %d", (int)m["pex"].toBool());
if(m.contains("lsd"))
Preferences::setLSDEnabled(m["lsd"].toBool());
if(m.contains("encryption"))
Preferences::setEncryptionSetting(m["encryption"].toInt());
// Proxy
if(m.contains("proxy_type"))
Preferences::setPeerProxyType(m["proxy_type"].toInt());
if(m.contains("proxy_ip"))
Preferences::setPeerProxyIp(m["proxy_ip"].toString());
if(m.contains("proxy_port"))
Preferences::setPeerProxyPort(m["proxy_port"].toUInt());
if(m.contains("proxy_auth_enabled"))
Preferences::setPeerProxyAuthEnabled(m["proxy_auth_enabled"].toBool());
if(m.contains("proxy_username"))
Preferences::setPeerProxyUsername(m["proxy_username"].toString());
if(m.contains("proxy_password"))
Preferences::setPeerProxyPassword(m["proxy_password"].toString());
if(m.contains("http_proxy_type"))
Preferences::setHTTPProxyType(m["http_proxy_type"].toInt());
if(m.contains("http_proxy_ip"))
Preferences::setHTTPProxyIp(m["http_proxy_ip"].toString());
if(m.contains("http_proxy_port"))
Preferences::setHTTPProxyPort(m["http_proxy_port"].toUInt());
if(m.contains("http_proxy_auth_enabled"))
Preferences::setHTTPProxyAuthEnabled(m["http_proxy_auth_enabled"].toBool());
if(m.contains("http_proxy_username"))
Preferences::setHTTPProxyUsername(m["http_proxy_username"].toString());
if(m.contains("http_proxy_password"))
Preferences::setHTTPProxyPassword(m["http_proxy_password"].toString());
// IP Filter
if(m.contains("ip_filter_enabled"))
Preferences::setFilteringEnabled(m["ip_filter_enabled"].toBool());
if(m.contains("ip_filter_path"))
Preferences::setFilter(m["ip_filter_path"].toString());
// Web UI
if(m.contains("web_ui_port"))
Preferences::setWebUiPort(m["web_ui_port"].toUInt());
if(m.contains("web_ui_username"))
Preferences::setWebUiUsername(m["web_ui_username"].toString());
if(m.contains("web_ui_password"))
Preferences::setWebUiPassword(m["web_ui_password"].toString());
// Reload preferences
BTSession->configureSession();
}
QVariantMap EventManager::getGlobalPreferences() const {
QVariantMap data;
// UI
data["locale"] = Preferences::getLocale();
// Downloads
data["save_path"] = Preferences::getSavePath();
data["temp_path_enabled"] = Preferences::isTempPathEnabled();
data["temp_path"] = Preferences::getTempPath();
data["scan_dirs"] = Preferences::getScanDirs();
QVariantList var_list;
foreach(bool b, Preferences::getDownloadInScanDirs()) {
var_list << b;
}
data["download_in_scan_dirs"] = var_list;
data["export_dir_enabled"] = Preferences::isTorrentExportEnabled();
data["export_dir"] = Preferences::getExportDir();
data["mail_notification_enabled"] = Preferences::isMailNotificationEnabled();
data["mail_notification_email"] = Preferences::getMailNotificationEmail();
data["mail_notification_smtp"] = Preferences::getMailNotificationSMTP();
data["autorun_enabled"] = Preferences::isAutoRunEnabled();
data["autorun_program"] = Preferences::getAutoRunProgram();
data["preallocate_all"] = Preferences::preAllocateAllFiles();
data["queueing_enabled"] = Preferences::isQueueingSystemEnabled();
data["max_active_downloads"] = Preferences::getMaxActiveDownloads();
data["max_active_torrents"] = Preferences::getMaxActiveTorrents();
data["max_active_uploads"] = Preferences::getMaxActiveUploads();
#if LIBTORRENT_VERSION_MINOR > 14
data["incomplete_files_ext"] = Preferences::useIncompleteFilesExtension();
#endif
// Connection
data["listen_port"] = Preferences::getSessionPort();
data["upnp"] = Preferences::isUPnPEnabled();
data["natpmp"] = Preferences::isNATPMPEnabled();
data["dl_limit"] = Preferences::getGlobalDownloadLimit();
data["up_limit"] = Preferences::getGlobalUploadLimit();
data["max_connec"] = Preferences::getMaxConnecs();
data["max_connec_per_torrent"] = Preferences::getMaxConnecsPerTorrent();
data["max_uploads_per_torrent"] = Preferences::getMaxUploadsPerTorrent();
// Bittorrent
data["dht"] = Preferences::isDHTEnabled();
data["dhtSameAsBT"] = Preferences::isDHTPortSameAsBT();
data["dht_port"] = Preferences::getDHTPort();
data["pex"] = Preferences::isPeXEnabled();
data["lsd"] = Preferences::isLSDEnabled();
data["encryption"] = Preferences::getEncryptionSetting();
// Proxy
data["proxy_type"] = Preferences::getPeerProxyType();
data["proxy_ip"] = Preferences::getPeerProxyIp();
data["proxy_port"] = Preferences::getPeerProxyPort();
data["proxy_auth_enabled"] = Preferences::isPeerProxyAuthEnabled();
data["proxy_username"] = Preferences::getPeerProxyUsername();
data["proxy_password"] = Preferences::getPeerProxyPassword();
data["http_proxy_type"] = Preferences::getHTTPProxyType();
data["http_proxy_ip"] = Preferences::getHTTPProxyIp();
data["http_proxy_port"] = Preferences::getHTTPProxyPort();
data["http_proxy_auth_enabled"] = Preferences::isHTTPProxyAuthEnabled();
data["http_proxy_username"] = Preferences::getHTTPProxyUsername();
data["http_proxy_password"] = Preferences::getHTTPProxyPassword();
// IP Filter
data["ip_filter_enabled"] = Preferences::isFilteringEnabled();
data["ip_filter_path"] = Preferences::getFilter();
// Web UI
data["web_ui_port"] = Preferences::getWebUiPort();
data["web_ui_username"] = Preferences::getWebUiUsername();
data["web_ui_password"] = Preferences::getWebUiPassword();
return data;
}
QVariantMap EventManager::getPropGeneralInfo(QString hash) const {
QVariantMap data;
QTorrentHandle h = BTSession->getTorrentHandle(hash);
if(h.is_valid() && h.has_metadata()) {
// Save path
QString p = TorrentPersistentData::getSavePath(hash);
if(p.isEmpty()) p = h.save_path();
data["save_path"] = p;
// Creation date
data["creation_date"] = h.creation_date();
// Comment
data["comment"] = h.comment();
data["total_wasted"] = QVariant(misc::friendlyUnit(h.total_failed_bytes()+h.total_redundant_bytes()));
data["total_uploaded"] = QVariant(misc::friendlyUnit(h.all_time_upload()) + " ("+misc::friendlyUnit(h.total_payload_upload())+" "+tr("this session")+")");
data["total_downloaded"] = QVariant(misc::friendlyUnit(h.all_time_download()) + " ("+misc::friendlyUnit(h.total_payload_download())+" "+tr("this session")+")");
if(h.upload_limit() <= 0)
data["up_limit"] = QString::fromUtf8("");
else
data["up_limit"] = QVariant(misc::friendlyUnit(h.upload_limit())+tr("/s", "/second (i.e. per second)"));
if(h.download_limit() <= 0)
data["dl_limit"] = QString::fromUtf8("");
else
data["dl_limit"] = QVariant(misc::friendlyUnit(h.download_limit())+tr("/s", "/second (i.e. per second)"));
QString elapsed_txt = misc::userFriendlyDuration(h.active_time());
if(h.is_seed()) {
elapsed_txt += " ("+tr("Seeded for %1", "e.g. Seeded for 3m10s").arg(misc::userFriendlyDuration(h.seeding_time()))+")";
}
data["time_elapsed"] = elapsed_txt;
data["nb_connections"] = QVariant(QString::number(h.num_connections())+" ("+tr("%1 max", "e.g. 10 max").arg(QString::number(h.connections_limit()))+")");
// Update ratio info
double ratio = BTSession->getRealRatio(h.hash());
if(ratio > 100.)
data["share_ratio"] = QString::fromUtf8("");
else
data["share_ratio"] = QString(QByteArray::number(ratio, 'f', 1));
}
return data;
}
void EventManager::addedTorrent(QTorrentHandle& h)
{
modifiedTorrent(h);
}
void EventManager::deletedTorrent(QString hash)
{
event_list.remove(hash);
}
void EventManager::modifiedTorrent(QTorrentHandle h)
{
QString hash = h.hash();
QVariantMap event;
event["eta"] = QVariant(QString::fromUtf8(""));
if(h.is_paused()) {
if(h.has_error()) {
event["state"] = QVariant("error");
} else {
if(h.is_seed())
event["state"] = QVariant("pausedUP");
else
event["state"] = QVariant("pausedDL");
}
} else {
if(BTSession->isQueueingEnabled() && h.is_queued()) {
if(h.is_seed())
event["state"] = QVariant("queuedUP");
else
event["state"] = QVariant("queuedDL");
} else {
switch(h.state())
{
case torrent_status::finished:
case torrent_status::seeding:
if(h.upload_payload_rate() > 0) {
event["state"] = QVariant("uploading");
} else {
event["state"] = QVariant("stalledUP");
}
break;
case torrent_status::allocating:
case torrent_status::checking_files:
case torrent_status::queued_for_checking:
case torrent_status::checking_resume_data:
if(h.is_seed()) {
event["state"] = QVariant("checkingUP");
} else {
event["state"] = QVariant("checkingDL");
}
break;
case torrent_status::downloading:
case torrent_status::downloading_metadata:
if(h.download_payload_rate() > 0)
event["state"] = QVariant("downloading");
else
event["state"] = QVariant("stalledDL");
event["eta"] = misc::userFriendlyDuration(BTSession->getETA(hash));
break;
default:
qDebug("No status, should not happen!!! status is %d", h.state());
event["state"] = QVariant();
}
}
}
event["name"] = QVariant(h.name());
event["size"] = QVariant(misc::friendlyUnit(h.actual_size()));
event["progress"] = QVariant((double)h.progress());
event["dlspeed"] = QVariant(tr("%1/s", "e.g. 120 KiB/s").arg(misc::friendlyUnit(h.download_payload_rate())));
if(BTSession->isQueueingEnabled()) {
if(h.queue_position() >= 0)
event["priority"] = QVariant(QString::number(h.queue_position()));
else
event["priority"] = "*";
} else {
event["priority"] = "*";
}
event["upspeed"] = QVariant(tr("%1/s", "e.g. 120 KiB/s").arg(misc::friendlyUnit(h.upload_payload_rate())));
QString seeds = QString::number(h.num_seeds());
if(h.num_complete() > 0)
seeds += " ("+QString::number(h.num_complete())+")";
event["num_seeds"] = QVariant(seeds);
QString leechs = QString::number(h.num_peers()-h.num_seeds());
if(h.num_incomplete() > 0)
leechs += " ("+QString::number(h.num_incomplete())+")";
event["num_leechs"] = QVariant(leechs);
event["seed"] = QVariant(h.is_seed());
double ratio = BTSession->getRealRatio(hash);
if(ratio > 100.)
event["ratio"] = QString::fromUtf8("");
else
event["ratio"] = QVariant(QString::number(ratio, 'f', 1));
event["hash"] = QVariant(hash);
event_list[hash] = event;
}

66
src/webui/eventmanager.h Normal file
View File

@@ -0,0 +1,66 @@
/*
* Bittorrent Client using Qt4 and libtorrent.
* Copyright (C) 2006 Ishan Arora and Christophe Dumez
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* In addition, as a special exception, the copyright holders give permission to
* link this program with the OpenSSL project's "OpenSSL" library (or with
* modified versions of it that use the same license as the "OpenSSL" library),
* and distribute the linked executables. You must obey the GNU General Public
* License in all respects for all of the code used other than "OpenSSL". If you
* modify file(s), you may extend this exception to your version of the file(s),
* but you are not obligated to do so. If you do not wish to do so, delete this
* exception statement from your version.
*
* Contact : chris@qbittorrent.org
*/
#ifndef EVENTMANAGER_H
#define EVENTMANAGER_H
#include "qtorrenthandle.h"
#include <QHash>
#include <QVariant>
class Bittorrent;
class EventManager : public QObject
{
Q_OBJECT
private:
QHash<QString, QVariantMap> event_list;
Bittorrent* BTSession;
protected:
void update(QVariantMap event);
public:
EventManager(QObject *parent, Bittorrent* BTSession);
QList<QVariantMap> getEventList() const;
QVariantMap getPropGeneralInfo(QString hash) const;
QList<QVariantMap> getPropTrackersInfo(QString hash) const;
QList<QVariantMap> getPropFilesInfo(QString hash) const;
QVariantMap getGlobalPreferences() const;
void setGlobalPreferences(QVariantMap m) const;
public slots:
void addedTorrent(QTorrentHandle& h);
void deletedTorrent(QString hash);
void modifiedTorrent(QTorrentHandle h);
};
#endif

View File

@@ -0,0 +1,541 @@
/*
* Bittorrent Client using Qt4 and libtorrent.
* Copyright (C) 2006 Ishan Arora and Christophe Dumez
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* In addition, as a special exception, the copyright holders give permission to
* link this program with the OpenSSL project's "OpenSSL" library (or with
* modified versions of it that use the same license as the "OpenSSL" library),
* and distribute the linked executables. You must obey the GNU General Public
* License in all respects for all of the code used other than "OpenSSL". If you
* modify file(s), you may extend this exception to your version of the file(s),
* but you are not obligated to do so. If you do not wish to do so, delete this
* exception statement from your version.
*
* Contact : chris@qbittorrent.org
*/
#include "httpconnection.h"
#include "httpserver.h"
#include "eventmanager.h"
#include "preferences.h"
#include "json.h"
#include "qbtsession.h"
#include "misc.h"
#include <QTcpSocket>
#include <QDateTime>
#include <QStringList>
#include <QHttpRequestHeader>
#include <QHttpResponseHeader>
#include <QFile>
#include <QDebug>
#include <QRegExp>
#include <QTemporaryFile>
HttpConnection::HttpConnection(QTcpSocket *socket, Bittorrent *BTSession, HttpServer *parent)
: QObject(parent), socket(socket), parent(parent), BTSession(BTSession)
{
socket->setParent(this);
connect(socket, SIGNAL(readyRead()), this, SLOT(read()));
connect(socket, SIGNAL(disconnected()), this, SLOT(deleteLater()));
}
HttpConnection::~HttpConnection()
{
delete socket;
}
void HttpConnection::processDownloadedFile(QString url, QString file_path) {
qDebug("URL %s successfully downloaded !", (const char*)url.toLocal8Bit());
emit torrentReadyToBeDownloaded(file_path, false, url, false);
}
void HttpConnection::handleDownloadFailure(QString url, QString reason) {
std::cerr << "Could not download " << (const char*)url.toLocal8Bit() << ", reason: " << (const char*)reason.toLocal8Bit() << "\n";
}
void HttpConnection::read()
{
QByteArray input = socket->readAll();
/*qDebug(" -------");
qDebug("|REQUEST|");
qDebug(" -------"); */
//qDebug("%s", input.toAscii().constData());
if(input.size() > 100000) {
qDebug("Request too big");
generator.setStatusLine(400, "Bad Request");
write();
return;
}
parser.write(input);
if(parser.isError())
{
generator.setStatusLine(400, "Bad Request");
write();
}
else
if (parser.isParsable())
respond();
}
void HttpConnection::write()
{
QByteArray output = generator.toByteArray();
/*qDebug(" --------");
qDebug("|RESPONSE|");
qDebug(" --------");
qDebug()<<output;*/
socket->write(output);
socket->disconnectFromHost();
}
QString HttpConnection::translateDocument(QString data) {
std::string contexts[] = {"TransferListFiltersWidget", "TransferListWidget", "PropertiesWidget", "GUI", "MainWindow", "HttpServer", "confirmDeletionDlg", "TrackerList", "TorrentFilesModel", "options_imp", "Preferences", "TrackersAdditionDlg", "ScanFoldersModel"};
int i=0;
bool found = false;
do {
found = false;
QRegExp regex(QString::fromUtf8("_\\(([\\w\\s?!:\\/\\(\\),%µ&\\-\\.]+)\\)"));
i = regex.indexIn(data, i);
if(i >= 0) {
//qDebug("Found translatable string: %s", regex.cap(1).toUtf8().data());
QString word = regex.cap(1);
QString translation = word;
int context_index= 0;
do {
translation = qApp->translate(contexts[context_index].c_str(), word.toLocal8Bit().constData(), 0, QCoreApplication::UnicodeUTF8, 1);
++context_index;
}while(translation == word && context_index < 13);
// Remove keyboard shortcuts
translation = translation.replace("&", "");
//qDebug("Translation is %s", translation.toUtf8().data());
data = data.replace(i, regex.matchedLength(), translation);
i += translation.length();
found = true;
}
}while(found && i < data.size());
return data;
}
void HttpConnection::respond() {
//qDebug("Respond called");
const QString peer_ip = socket->peerAddress().toString();
const int nb_fail = parent->NbFailedAttemptsForIp(peer_ip);
if(nb_fail >= MAX_AUTH_FAILED_ATTEMPTS) {
generator.setStatusLine(403, "Forbidden");
generator.setMessage(tr("Your IP address has been banned after too many failed authentication attempts."));
write();
return;
}
QString auth = parser.value("Authorization");
if(auth.isEmpty()) {
// Return unauthorized header
qDebug("Auth is Empty...");
generator.setStatusLine(401, "Unauthorized");
generator.setValue("WWW-Authenticate", "Digest realm=\""+QString(QBT_REALM)+"\", nonce=\""+parent->generateNonce()+"\", opaque=\""+parent->generateNonce()+"\", stale=\"false\", algorithm=\"MD5\", qop=\"auth\"");
write();
return;
}
//qDebug("Auth: %s", qPrintable(auth.split(" ").first()));
if (QString::compare(auth.split(" ").first(), "Digest", Qt::CaseInsensitive) != 0 || !parent->isAuthorized(auth.toLocal8Bit(), parser.method())) {
// Update failed attempt counter
parent->increaseNbFailedAttemptsForIp(peer_ip);
qDebug("client IP: %s (%d failed attempts)", qPrintable(peer_ip), nb_fail+1);
// Return unauthorized header
generator.setStatusLine(401, "Unauthorized");
generator.setValue("WWW-Authenticate", "Digest realm=\""+QString(QBT_REALM)+"\", nonce=\""+parent->generateNonce()+"\", opaque=\""+parent->generateNonce()+"\", stale=\"false\", algorithm=\"MD5\", qop=\"auth\"");
write();
return;
}
// Client successfully authenticated, reset number of failed attempts
parent->resetNbFailedAttemptsForIp(peer_ip);
QString url = parser.url();
// Favicon
if(url.endsWith("favicon.ico")) {
qDebug("Returning favicon");
QFile favicon(":/Icons/skin/qbittorrent16.png");
if(favicon.open(QIODevice::ReadOnly)) {
QByteArray data = favicon.readAll();
generator.setStatusLine(200, "OK");
generator.setContentTypeByExt("png");
generator.setMessage(data);
write();
} else {
respondNotFound();
}
return;
}
QStringList list = url.split('/', QString::SkipEmptyParts);
if (list.contains(".") || list.contains(".."))
{
respondNotFound();
return;
}
if (list.size() == 0)
list.append("index.html");
if (list.size() >= 2)
{
if (list[0] == "json")
{
if (list[1] == "events")
{
respondJson();
return;
}
if(list.size() > 2) {
if(list[1] == "propertiesGeneral") {
QString hash = list[2];
respondGenPropertiesJson(hash);
return;
}
if(list[1] == "propertiesTrackers") {
QString hash = list[2];
respondTrackersPropertiesJson(hash);
return;
}
if(list[1] == "propertiesFiles") {
QString hash = list[2];
respondFilesPropertiesJson(hash);
return;
}
} else {
if(list[1] == "preferences") {
respondPreferencesJson();
} else {
if(list[1] == "transferInfo") {
respondGlobalTransferInfoJson();
}
}
}
}
if (list[0] == "command")
{
QString command = list[1];
respondCommand(command);
generator.setStatusLine(200, "OK");
write();
return;
}
}
if (list[0] == "images") {
list[0] = "Icons";
} else {
if(list.last().endsWith(".html"))
list.prepend("html");
list.prepend("webui");
}
url = ":/" + list.join("/");
QFile file(url);
if(!file.open(QIODevice::ReadOnly))
{
qDebug("File %s was not found!", qPrintable(url));
respondNotFound();
return;
}
QString ext = list.last();
int index = ext.lastIndexOf('.') + 1;
if (index > 0)
ext.remove(0, index);
else
ext.clear();
QByteArray data = file.readAll();
// Translate the page
if(ext == "html" || (ext == "js" && !list.last().startsWith("excanvas"))) {
data = translateDocument(QString::fromUtf8(data.data())).toUtf8();
}
generator.setStatusLine(200, "OK");
generator.setContentTypeByExt(ext);
generator.setMessage(data);
write();
}
void HttpConnection::respondNotFound()
{
generator.setStatusLine(404, "File not found");
write();
}
void HttpConnection::respondJson()
{
EventManager* manager = parent->eventManager();
QString string = json::toJson(manager->getEventList());
generator.setStatusLine(200, "OK");
generator.setContentTypeByExt("js");
generator.setMessage(string);
write();
}
void HttpConnection::respondGenPropertiesJson(QString hash) {
EventManager* manager = parent->eventManager();
QString string = json::toJson(manager->getPropGeneralInfo(hash));
generator.setStatusLine(200, "OK");
generator.setContentTypeByExt("js");
generator.setMessage(string);
write();
}
void HttpConnection::respondTrackersPropertiesJson(QString hash) {
EventManager* manager = parent->eventManager();
QString string = json::toJson(manager->getPropTrackersInfo(hash));
generator.setStatusLine(200, "OK");
generator.setContentTypeByExt("js");
generator.setMessage(string);
write();
}
void HttpConnection::respondFilesPropertiesJson(QString hash) {
EventManager* manager = parent->eventManager();
QString string = json::toJson(manager->getPropFilesInfo(hash));
generator.setStatusLine(200, "OK");
generator.setContentTypeByExt("js");
generator.setMessage(string);
write();
}
void HttpConnection::respondPreferencesJson() {
EventManager* manager = parent->eventManager();
QString string = json::toJson(manager->getGlobalPreferences());
generator.setStatusLine(200, "OK");
generator.setContentTypeByExt("js");
generator.setMessage(string);
write();
}
void HttpConnection::respondGlobalTransferInfoJson() {
QVariantMap info;
session_status sessionStatus = BTSession->getSessionStatus();
info["DlInfos"] = tr("D: %1/s - T: %2", "Download speed: x KiB/s - Transferred: x MiB").arg(misc::friendlyUnit(sessionStatus.payload_download_rate)).arg(misc::friendlyUnit(sessionStatus.total_payload_download));
info["UpInfos"] = tr("U: %1/s - T: %2", "Upload speed: x KiB/s - Transferred: x MiB").arg(misc::friendlyUnit(sessionStatus.payload_upload_rate)).arg(misc::friendlyUnit(sessionStatus.total_payload_upload));
QString string = json::toJson(info);
generator.setStatusLine(200, "OK");
generator.setContentTypeByExt("js");
generator.setMessage(string);
write();
}
void HttpConnection::respondCommand(QString command)
{
if(command == "download")
{
QString urls = parser.post("urls");
QStringList list = urls.split('\n');
foreach(QString url, list){
url = url.trimmed();
if(!url.isEmpty()){
if(url.startsWith("bc://bt/", Qt::CaseInsensitive)) {
qDebug("Converting bc link to magnet link");
url = misc::bcLinkToMagnet(url);
}
if(url.startsWith("magnet:", Qt::CaseInsensitive)) {
emit MagnetReadyToBeDownloaded(url);
} else {
qDebug("Downloading url: %s", (const char*)url.toLocal8Bit());
emit UrlReadyToBeDownloaded(url);
}
}
}
return;
}
if(command == "addTrackers") {
QString hash = parser.post("hash");
if(!hash.isEmpty()) {
QTorrentHandle h = BTSession->getTorrentHandle(hash);
if(h.is_valid() && h.has_metadata()) {
QString urls = parser.post("urls");
QStringList list = urls.split('\n');
foreach(QString url, list) {
announce_entry e(url.toStdString());
h.add_tracker(e);
}
}
}
}
if(command == "upload")
{
QByteArray torrentfile = parser.torrent();
// Get a unique filename
QString filePath;
QTemporaryFile tmpfile;
tmpfile.setAutoRemove(false);
if (tmpfile.open()) {
filePath = tmpfile.fileName();
} else {
std::cerr << "I/O Error: Could not create temporary file" << std::endl;
return;
}
tmpfile.close();
// Now temporary file is created but closed so that it can be used.
// write torrent to temporary file
QFile torrent(filePath);
if(torrent.open(QIODevice::WriteOnly)) {
torrent.write(torrentfile);
torrent.close();
}
emit torrentReadyToBeDownloaded(filePath, false, QString(), false);
// Prepare response
generator.setStatusLine(200, "OK");
generator.setContentTypeByExt("html");
generator.setMessage(QString("<script type=\"text/javascript\">window.parent.hideAll();</script>"));
write();
return;
}
if(command == "resumeall") {
emit resumeAllTorrents();
return;
}
if(command == "pauseall") {
emit pauseAllTorrents();
return;
}
if(command == "resume") {
emit resumeTorrent(parser.post("hash"));
return;
}
if(command == "setPreferences") {
QString json_str = parser.post("json");
EventManager* manager = parent->eventManager();
manager->setGlobalPreferences(json::fromJson(json_str));
}
if(command == "setFilePrio") {
QString hash = parser.post("hash");
int file_id = parser.post("id").toInt();
int priority = parser.post("priority").toInt();
QTorrentHandle h = BTSession->getTorrentHandle(hash);
if(h.is_valid() && h.has_metadata()) {
h.file_priority(file_id, priority);
}
}
if(command == "getGlobalUpLimit") {
generator.setStatusLine(200, "OK");
generator.setContentTypeByExt("html");
generator.setMessage(QString::number(BTSession->getSession()->upload_rate_limit()));
write();
}
if(command == "getGlobalDlLimit") {
generator.setStatusLine(200, "OK");
generator.setContentTypeByExt("html");
generator.setMessage(QString::number(BTSession->getSession()->download_rate_limit()));
write();
}
if(command == "getTorrentUpLimit") {
QString hash = parser.post("hash");
QTorrentHandle h = BTSession->getTorrentHandle(hash);
if(h.is_valid()) {
generator.setStatusLine(200, "OK");
generator.setContentTypeByExt("html");
generator.setMessage(QString::number(h.upload_limit()));
write();
}
}
if(command == "getTorrentDlLimit") {
QString hash = parser.post("hash");
QTorrentHandle h = BTSession->getTorrentHandle(hash);
if(h.is_valid()) {
generator.setStatusLine(200, "OK");
generator.setContentTypeByExt("html");
generator.setMessage(QString::number(h.download_limit()));
write();
}
}
if(command == "setTorrentUpLimit") {
QString hash = parser.post("hash");
qlonglong limit = parser.post("limit").toLongLong();
if(limit == 0) limit = -1;
QTorrentHandle h = BTSession->getTorrentHandle(hash);
if(h.is_valid()) {
h.set_upload_limit(limit);
}
}
if(command == "setTorrentDlLimit") {
QString hash = parser.post("hash");
qlonglong limit = parser.post("limit").toLongLong();
if(limit == 0) limit = -1;
QTorrentHandle h = BTSession->getTorrentHandle(hash);
if(h.is_valid()) {
h.set_download_limit(limit);
}
}
if(command == "setGlobalUpLimit") {
qlonglong limit = parser.post("limit").toLongLong();
if(limit == 0) limit = -1;
BTSession->getSession()->set_upload_rate_limit(limit);
Preferences::setGlobalUploadLimit(limit/1024.);
}
if(command == "setGlobalDlLimit") {
qlonglong limit = parser.post("limit").toLongLong();
if(limit == 0) limit = -1;
BTSession->getSession()->set_download_rate_limit(limit);
Preferences::setGlobalDownloadLimit(limit/1024.);
}
if(command == "pause") {
emit pauseTorrent(parser.post("hash"));
return;
}
if(command == "delete") {
emit deleteTorrent(parser.post("hash"), false);
return;
}
if(command == "deletePerm") {
emit deleteTorrent(parser.post("hash"), true);
return;
}
if(command == "increasePrio") {
QTorrentHandle h = BTSession->getTorrentHandle(parser.post("hash"));
if(h.is_valid()) h.queue_position_up();
return;
}
if(command == "decreasePrio") {
QTorrentHandle h = BTSession->getTorrentHandle(parser.post("hash"));
if(h.is_valid()) h.queue_position_down();
return;
}
if(command == "topPrio") {
QTorrentHandle h = BTSession->getTorrentHandle(parser.post("hash"));
if(h.is_valid()) h.queue_position_top();
return;
}
if(command == "bottomPrio") {
QTorrentHandle h = BTSession->getTorrentHandle(parser.post("hash"));
if(h.is_valid()) h.queue_position_bottom();
return;
}
if(command == "recheck"){
recheckTorrent(parser.post("hash"));
return;
}
if(command == "recheckall"){
recheckAllTorrents();
return;
}
}
void HttpConnection::recheckTorrent(QString hash) {
QTorrentHandle h = BTSession->getTorrentHandle(hash);
if(h.is_valid()){
BTSession->recheckTorrent(h.hash());
}
}
void HttpConnection::recheckAllTorrents() {
std::vector<torrent_handle> torrents = BTSession->getTorrents();
std::vector<torrent_handle>::iterator torrentIT;
for(torrentIT = torrents.begin(); torrentIT != torrents.end(); torrentIT++) {
QTorrentHandle h = QTorrentHandle(*torrentIT);
if(h.is_valid())
BTSession->recheckTorrent(h.hash());
}
}

View File

@@ -0,0 +1,92 @@
/*
* Bittorrent Client using Qt4 and libtorrent.
* Copyright (C) 2006 Ishan Arora and Christophe Dumez
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* In addition, as a special exception, the copyright holders give permission to
* link this program with the OpenSSL project's "OpenSSL" library (or with
* modified versions of it that use the same license as the "OpenSSL" library),
* and distribute the linked executables. You must obey the GNU General Public
* License in all respects for all of the code used other than "OpenSSL". If you
* modify file(s), you may extend this exception to your version of the file(s),
* but you are not obligated to do so. If you do not wish to do so, delete this
* exception statement from your version.
*
* Contact : chris@qbittorrent.org
*/
#ifndef HTTPCONNECTION_H
#define HTTPCONNECTION_H
#include "httprequestparser.h"
#include "httpresponsegenerator.h"
#include <QObject>
class QTcpSocket;
class HttpServer;
class Bittorrent;
class HttpConnection : public QObject
{
Q_OBJECT
private:
QTcpSocket *socket;
HttpServer *parent;
Bittorrent *BTSession;
protected:
HttpRequestParser parser;
HttpResponseGenerator generator;
protected slots:
void write();
virtual void respond();
void respondJson();
void respondGenPropertiesJson(QString hash);
void respondTrackersPropertiesJson(QString hash);
void respondFilesPropertiesJson(QString hash);
void respondPreferencesJson();
void respondGlobalTransferInfoJson();
void respondCommand(QString command);
void respondNotFound();
void processDownloadedFile(QString, QString);
void handleDownloadFailure(QString, QString);
void recheckTorrent(QString hash);
void recheckAllTorrents();
public:
HttpConnection(QTcpSocket *socket, Bittorrent* BTSession, HttpServer *parent);
~HttpConnection();
QString translateDocument(QString data);
private slots:
void read();
signals:
void UrlReadyToBeDownloaded(QString url);
void MagnetReadyToBeDownloaded(QString uri);
void torrentReadyToBeDownloaded(QString, bool, QString, bool);
void deleteTorrent(QString hash, bool permanently);
void resumeTorrent(QString hash);
void pauseTorrent(QString hash);
void increasePrioTorrent(QString hash);
void decreasePrioTorrent(QString hash);
void resumeAllTorrents();
void pauseAllTorrents();
};
#endif

View File

@@ -0,0 +1,148 @@
/*
* Bittorrent Client using Qt4 and libtorrent.
* Copyright (C) 2006 Ishan Arora and Christophe Dumez
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* In addition, as a special exception, the copyright holders give permission to
* link this program with the OpenSSL project's "OpenSSL" library (or with
* modified versions of it that use the same license as the "OpenSSL" library),
* and distribute the linked executables. You must obey the GNU General Public
* License in all respects for all of the code used other than "OpenSSL". If you
* modify file(s), you may extend this exception to your version of the file(s),
* but you are not obligated to do so. If you do not wish to do so, delete this
* exception statement from your version.
*
* Contact : chris@qbittorrent.org
*/
#include "httprequestparser.h"
#include <QUrl>
#include <QDebug>
HttpRequestParser::HttpRequestParser()
{
headerDone = false;
messageDone = false;
error = false;
}
HttpRequestParser::~HttpRequestParser()
{
}
bool HttpRequestParser::isParsable() const
{
return !error && headerDone && isValid() && (messageDone || !hasContentLength() || contentLength() == 0);
}
bool HttpRequestParser::isError() const
{
return error;
}
QString HttpRequestParser::url() const
{
return path;
}
QByteArray HttpRequestParser::message() const
{
if(isParsable())
return data;
return QByteArray();
}
QString HttpRequestParser::get(const QString key) const
{
return getMap[key];
}
QString HttpRequestParser::post(const QString key) const
{
return postMap[key];
}
QByteArray HttpRequestParser::torrent() const
{
return torrent_content;
}
void HttpRequestParser::write(QByteArray str)
{
while (!headerDone && str.size()>0)
{
int index = str.indexOf('\n') + 1;
if(index == 0)
{
data += str;
str.clear();
}
else
{
data += str.left(index);
str.remove(0, index);
if(data.right(4) == "\r\n\r\n")
{
QHttpRequestHeader::operator=(QHttpRequestHeader(data));
headerDone = true;
data.clear();
QUrl url = QUrl::fromEncoded(QHttpRequestHeader::path().toAscii());
path = url.path();
//() << path;
QListIterator<QPair<QString, QString> > i(url.queryItems());
while (i.hasNext())
{
QPair<QString, QString> pair = i.next();
getMap[pair.first] = pair.second;
//qDebug() << pair.first << "=" << get(pair.first);
}
}
}
}
if(!messageDone && str.size()>0)
{
if(hasContentLength())
{
data += str;
if(data.size() >= (int) contentLength())
{
data.resize(contentLength());
messageDone = true;
//parse POST data
if(contentType() == "application/x-www-form-urlencoded")
{
QUrl url;
url.setEncodedQuery(data);
QListIterator<QPair<QString, QString> > i(url.queryItems());
while (i.hasNext())
{
QPair<QString, QString> pair = i.next();
postMap[pair.first] = pair.second;
//qDebug() << pair.first << "=" << post(pair.first);
}
}
if(contentType() == "multipart/form-data")
{
//qDebug() << data.right(data.size()-data.indexOf("\r\n\r\n")-QByteArray("\r\n\r\n").size());
torrent_content = data.right(data.size()-data.indexOf("\r\n\r\n")-QByteArray("\r\n\r\n").size());
}
}
}
else
error = true;
}
}

View File

@@ -0,0 +1,62 @@
/*
* Bittorrent Client using Qt4 and libtorrent.
* Copyright (C) 2006 Ishan Arora and Christophe Dumez
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* In addition, as a special exception, the copyright holders give permission to
* link this program with the OpenSSL project's "OpenSSL" library (or with
* modified versions of it that use the same license as the "OpenSSL" library),
* and distribute the linked executables. You must obey the GNU General Public
* License in all respects for all of the code used other than "OpenSSL". If you
* modify file(s), you may extend this exception to your version of the file(s),
* but you are not obligated to do so. If you do not wish to do so, delete this
* exception statement from your version.
*
* Contact : chris@qbittorrent.org
*/
#ifndef HTTPREQUESTPARSER_H
#define HTTPREQUESTPARSER_H
#include<QHttpRequestHeader>
class HttpRequestParser : public QHttpRequestHeader
{
private:
bool headerDone;
bool messageDone;
bool error;
QByteArray data;
QString path;
QMap<QString, QString> postMap;
QMap<QString, QString> getMap;
QByteArray torrent_content;
public:
HttpRequestParser();
~HttpRequestParser();
bool isParsable() const;
bool isError() const;
QString url() const;
QByteArray message() const;
QString get(const QString key) const;
QString post(const QString key) const;
QByteArray torrent() const;
void write(QByteArray str);
};
#endif

View File

@@ -0,0 +1,83 @@
/*
* Bittorrent Client using Qt4 and libtorrent.
* Copyright (C) 2006 Ishan Arora and Christophe Dumez
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* In addition, as a special exception, the copyright holders give permission to
* link this program with the OpenSSL project's "OpenSSL" library (or with
* modified versions of it that use the same license as the "OpenSSL" library),
* and distribute the linked executables. You must obey the GNU General Public
* License in all respects for all of the code used other than "OpenSSL". If you
* modify file(s), you may extend this exception to your version of the file(s),
* but you are not obligated to do so. If you do not wish to do so, delete this
* exception statement from your version.
*
* Contact : chris@qbittorrent.org
*/
#include "httpresponsegenerator.h"
void HttpResponseGenerator::setMessage(const QByteArray message)
{
HttpResponseGenerator::message = message;
setContentLength(message.size());
}
void HttpResponseGenerator::setMessage(const QString message)
{
// This must be UTF-8!
setMessage(message.toUtf8());
}
void HttpResponseGenerator::stripMessage()
{
message.clear();
}
QByteArray HttpResponseGenerator::toByteArray() const
{
return QHttpResponseHeader::toString().toLocal8Bit() + message;
}
void HttpResponseGenerator::setContentTypeByExt(const QString ext)
{
if(ext == "css")
{
setContentType("text/css");
return;
}
if(ext == "gif")
{
setContentType("image/gif");
return;
}
if(ext == "htm" || ext == "html")
{
setContentType("text/html");
return;
}
if(ext == "js")
{
setContentType("text/javascript");
return;
}
if(ext == "png")
{
setContentType("image/x-png");
return;
}
}

View File

@@ -0,0 +1,50 @@
/*
* Bittorrent Client using Qt4 and libtorrent.
* Copyright (C) 2006 Ishan Arora and Christophe Dumez
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* In addition, as a special exception, the copyright holders give permission to
* link this program with the OpenSSL project's "OpenSSL" library (or with
* modified versions of it that use the same license as the "OpenSSL" library),
* and distribute the linked executables. You must obey the GNU General Public
* License in all respects for all of the code used other than "OpenSSL". If you
* modify file(s), you may extend this exception to your version of the file(s),
* but you are not obligated to do so. If you do not wish to do so, delete this
* exception statement from your version.
*
* Contact : chris@qbittorrent.org
*/
#ifndef HTTPRESPONSEGENERATOR_H
#define HTTPRESPONSEGENERATOR_H
#include<QHttpResponseHeader>
class HttpResponseGenerator : public QHttpResponseHeader
{
private:
QByteArray message;
public:
void setMessage(const QByteArray message);
void setMessage(const QString message);
void stripMessage();
void setContentTypeByExt(const QString ext);
virtual QByteArray toByteArray() const;
};
#endif

269
src/webui/httpserver.cpp Normal file
View File

@@ -0,0 +1,269 @@
/*
* Bittorrent Client using Qt4 and libtorrent.
* Copyright (C) 2006 Ishan Arora and Christophe Dumez
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* In addition, as a special exception, the copyright holders give permission to
* link this program with the OpenSSL project's "OpenSSL" library (or with
* modified versions of it that use the same license as the "OpenSSL" library),
* and distribute the linked executables. You must obey the GNU General Public
* License in all respects for all of the code used other than "OpenSSL". If you
* modify file(s), you may extend this exception to your version of the file(s),
* but you are not obligated to do so. If you do not wish to do so, delete this
* exception statement from your version.
*
* Contact : chris@qbittorrent.org
*/
#include "httpserver.h"
#include "httpconnection.h"
#include "eventmanager.h"
#include "qbtsession.h"
#include <QTimer>
#include <QCryptographicHash>
#include <QTime>
#include <QRegExp>
#include <QTimer>
const int BAN_TIME = 3600000; // 1 hour
class UnbanTimer: public QTimer {
public:
UnbanTimer(QObject *parent, QString peer_ip): QTimer(parent), peer_ip(peer_ip){
setSingleShot(true);
setInterval(BAN_TIME);
}
~UnbanTimer() {
qDebug("||||||||||||Deleting ban timer|||||||||||||||");
}
QString peer_ip;
};
void HttpServer::UnbanTimerEvent() {
UnbanTimer* ubantimer = static_cast<UnbanTimer*>(sender());
qDebug("Ban period has expired for %s", qPrintable(ubantimer->peer_ip));
client_failed_attempts.remove(ubantimer->peer_ip);
ubantimer->deleteLater();
}
int HttpServer::NbFailedAttemptsForIp(QString ip) const {
return client_failed_attempts.value(ip, 0);
}
void HttpServer::increaseNbFailedAttemptsForIp(QString ip) {
const int nb_fail = client_failed_attempts.value(ip, 0);
client_failed_attempts.insert(ip, nb_fail+1);
if(nb_fail == MAX_AUTH_FAILED_ATTEMPTS-1) {
// Max number of failed attempts reached
// Start ban period
UnbanTimer* ubantimer = new UnbanTimer(this, ip);
connect(ubantimer, SIGNAL(timeout()), this, SLOT(UnbanTimerEvent()));
ubantimer->start();
}
}
void HttpServer::resetNbFailedAttemptsForIp(QString ip) {
client_failed_attempts.remove(ip);
}
HttpServer::HttpServer(Bittorrent *_BTSession, int msec, QObject* parent) : QTcpServer(parent) {
username = Preferences::getWebUiUsername().toLocal8Bit();
password_ha1 = Preferences::getWebUiPassword().toLocal8Bit();
connect(this, SIGNAL(newConnection()), this, SLOT(newHttpConnection()));
BTSession = _BTSession;
manager = new EventManager(this, BTSession);
//add torrents
std::vector<torrent_handle> torrents = BTSession->getTorrents();
std::vector<torrent_handle>::iterator torrentIT;
for(torrentIT = torrents.begin(); torrentIT != torrents.end(); torrentIT++) {
QTorrentHandle h = QTorrentHandle(*torrentIT);
if(h.is_valid())
manager->addedTorrent(h);
}
//connect BTSession to manager
connect(BTSession, SIGNAL(addedTorrent(QTorrentHandle&)), manager, SLOT(addedTorrent(QTorrentHandle&)));
connect(BTSession, SIGNAL(deletedTorrent(QString)), manager, SLOT(deletedTorrent(QString)));
//set timer
timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), this, SLOT(onTimer()));
timer->start(msec);
// Additional translations for Web UI
QString a = tr("File");
a = tr("Edit");
a = tr("Help");
a = tr("Delete from HD");
a = tr("Download Torrents from their URL or Magnet link");
a = tr("Only one link per line");
a = tr("Download local torrent");
a = tr("Torrent files were correctly added to download list.");
a = tr("Point to torrent file");
a = tr("Download");
a = tr("Are you sure you want to delete the selected torrents from the transfer list and hard disk?");
a = tr("Download rate limit must be greater than 0 or disabled.");
a = tr("Upload rate limit must be greater than 0 or disabled.");
a = tr("Maximum number of connections limit must be greater than 0 or disabled.");
a = tr("Maximum number of connections per torrent limit must be greater than 0 or disabled.");
a = tr("Maximum number of upload slots per torrent limit must be greater than 0 or disabled.");
a = tr("Unable to save program preferences, qBittorrent is probably unreachable.");
a = tr("Language");
a = tr("Downloaded", "Is the file downloaded or not?");
a = tr("The port used for incoming connections must be greater than 1024 and less than 65535.");
a = tr("The port used for the Web UI must be greater than 1024 and less than 65535.");
a = tr("The Web UI username must be at least 3 characters long.");
a = tr("The Web UI password must be at least 3 characters long.");
}
HttpServer::~HttpServer()
{
delete timer;
delete manager;
}
void HttpServer::newHttpConnection()
{
QTcpSocket *socket;
while((socket = nextPendingConnection()))
{
HttpConnection *connection = new HttpConnection(socket, BTSession, this);
//connect connection to BTSession
connect(connection, SIGNAL(UrlReadyToBeDownloaded(QString)), BTSession, SLOT(downloadUrlAndSkipDialog(QString)));
connect(connection, SIGNAL(MagnetReadyToBeDownloaded(QString)), BTSession, SLOT(addMagnetSkipAddDlg(QString)));
connect(connection, SIGNAL(torrentReadyToBeDownloaded(QString, bool, QString, bool)), BTSession, SLOT(addTorrent(QString, bool, QString, bool)));
connect(connection, SIGNAL(deleteTorrent(QString, bool)), BTSession, SLOT(deleteTorrent(QString, bool)));
connect(connection, SIGNAL(pauseTorrent(QString)), BTSession, SLOT(pauseTorrent(QString)));
connect(connection, SIGNAL(resumeTorrent(QString)), BTSession, SLOT(resumeTorrent(QString)));
connect(connection, SIGNAL(pauseAllTorrents()), BTSession, SLOT(pauseAllTorrents()));
connect(connection, SIGNAL(resumeAllTorrents()), BTSession, SLOT(resumeAllTorrents()));
}
}
void HttpServer::onTimer() {
std::vector<torrent_handle> torrents = BTSession->getTorrents();
std::vector<torrent_handle>::iterator torrentIT;
for(torrentIT = torrents.begin(); torrentIT != torrents.end(); torrentIT++) {
QTorrentHandle h = QTorrentHandle(*torrentIT);
if(h.is_valid())
manager->modifiedTorrent(h);
}
}
QString HttpServer::generateNonce() const {
QCryptographicHash md5(QCryptographicHash::Md5);
md5.addData(QTime::currentTime().toString("hhmmsszzz").toLocal8Bit());
md5.addData(":");
md5.addData(QBT_REALM);
return md5.result().toHex();
}
void HttpServer::setAuthorization(QString _username, QString _password_ha1) {
username = _username.toLocal8Bit();
password_ha1 = _password_ha1.toLocal8Bit();
}
// Parse HTTP AUTH string
// http://tools.ietf.org/html/rfc2617
bool HttpServer::isAuthorized(QByteArray auth, QString method) const {
//qDebug("AUTH string is %s", auth.data());
// Get user name
QRegExp regex_user(".*username=\"([^\"]+)\".*"); // Must be a quoted string
if(regex_user.indexIn(auth) < 0) return false;
QString prop_user = regex_user.cap(1);
//qDebug("AUTH: Proposed username is %s, real username is %s", prop_user.toLocal8Bit().data(), username.data());
if(prop_user != username) {
// User name is invalid, we can reject already
qDebug("AUTH-PROB: Username is invalid");
return false;
}
// Get realm
QRegExp regex_realm(".*realm=\"([^\"]+)\".*"); // Must be a quoted string
if(regex_realm.indexIn(auth) < 0) {
qDebug("AUTH-PROB: Missing realm");
return false;
}
QByteArray prop_realm = regex_realm.cap(1).toLocal8Bit();
if(prop_realm != QBT_REALM) {
qDebug("AUTH-PROB: Wrong realm");
return false;
}
// get nonce
QRegExp regex_nonce(".*nonce=[\"]?([\\w=]+)[\"]?.*");
if(regex_nonce.indexIn(auth) < 0) {
qDebug("AUTH-PROB: missing nonce");
return false;
}
QByteArray prop_nonce = regex_nonce.cap(1).toLocal8Bit();
qDebug("prop nonce is: %s", prop_nonce.data());
// get uri
QRegExp regex_uri(".*uri=\"([^\"]+)\".*");
if(regex_uri.indexIn(auth) < 0) {
qDebug("AUTH-PROB: Missing uri");
return false;
}
QByteArray prop_uri = regex_uri.cap(1).toLocal8Bit();
qDebug("prop uri is: %s", prop_uri.data());
// get response
QRegExp regex_response(".*response=[\"]?([\\w=]+)[\"]?.*");
if(regex_response.indexIn(auth) < 0) {
qDebug("AUTH-PROB: Missing response");
return false;
}
QByteArray prop_response = regex_response.cap(1).toLocal8Bit();
qDebug("prop response is: %s", prop_response.data());
// Compute correct reponse
QCryptographicHash md5_ha2(QCryptographicHash::Md5);
md5_ha2.addData(method.toLocal8Bit() + ":" + prop_uri);
QByteArray ha2 = md5_ha2.result().toHex();
QByteArray response = "";
if(auth.contains("qop=")) {
QCryptographicHash md5_ha(QCryptographicHash::Md5);
// Get nc
QRegExp regex_nc(".*nc=[\"]?([\\w=]+)[\"]?.*");
if(regex_nc.indexIn(auth) < 0) {
qDebug("AUTH-PROB: qop but missing nc");
return false;
}
QByteArray prop_nc = regex_nc.cap(1).toLocal8Bit();
qDebug("prop nc is: %s", prop_nc.data());
QRegExp regex_cnonce(".*cnonce=[\"]?([\\w=]+)[\"]?.*");
if(regex_cnonce.indexIn(auth) < 0) {
qDebug("AUTH-PROB: qop but missing cnonce");
return false;
}
QByteArray prop_cnonce = regex_cnonce.cap(1).toLocal8Bit();
qDebug("prop cnonce is: %s", prop_cnonce.data());
QRegExp regex_qop(".*qop=[\"]?(\\w+)[\"]?.*");
if(regex_qop.indexIn(auth) < 0) {
qDebug("AUTH-PROB: missing qop");
return false;
}
QByteArray prop_qop = regex_qop.cap(1).toLocal8Bit();
qDebug("prop qop is: %s", prop_qop.data());
md5_ha.addData(password_ha1+":"+prop_nonce+":"+prop_nc+":"+prop_cnonce+":"+prop_qop+":"+ha2);
response = md5_ha.result().toHex();
} else {
QCryptographicHash md5_ha(QCryptographicHash::Md5);
md5_ha.addData(password_ha1+":"+prop_nonce+":"+ha2);
response = md5_ha.result().toHex();
}
qDebug("AUTH: comparing reponses: (%d)", static_cast<int>(prop_response == response));
return prop_response == response;
}
EventManager* HttpServer::eventManager() const
{
return manager;
}

75
src/webui/httpserver.h Normal file
View File

@@ -0,0 +1,75 @@
/*
* Bittorrent Client using Qt4 and libtorrent.
* Copyright (C) 2006 Ishan Arora and Christophe Dumez
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* In addition, as a special exception, the copyright holders give permission to
* link this program with the OpenSSL project's "OpenSSL" library (or with
* modified versions of it that use the same license as the "OpenSSL" library),
* and distribute the linked executables. You must obey the GNU General Public
* License in all respects for all of the code used other than "OpenSSL". If you
* modify file(s), you may extend this exception to your version of the file(s),
* but you are not obligated to do so. If you do not wish to do so, delete this
* exception statement from your version.
*
* Contact : chris@qbittorrent.org
*/
#ifndef HTTPSERVER_H
#define HTTPSERVER_H
#include <QPair>
#include <QTcpServer>
#include <QByteArray>
#include <QHash>
#include "preferences.h"
class Bittorrent;
class QTimer;
class EventManager;
const int MAX_AUTH_FAILED_ATTEMPTS = 5;
class HttpServer : public QTcpServer {
Q_OBJECT
public:
HttpServer(Bittorrent *BTSession, int msec, QObject* parent = 0);
~HttpServer();
void setAuthorization(QString username, QString password_ha1);
bool isAuthorized(QByteArray auth, QString method) const;
EventManager *eventManager() const;
QString generateNonce() const;
int NbFailedAttemptsForIp(QString ip) const;
void increaseNbFailedAttemptsForIp(QString ip);
void resetNbFailedAttemptsForIp(QString ip);
private slots:
void newHttpConnection();
void onTimer();
void UnbanTimerEvent();
private:
QByteArray username;
QByteArray password_ha1;
Bittorrent *BTSession;
EventManager *manager;
QTimer *timer;
QHash<QString, int> client_failed_attempts;
};
#endif

190
src/webui/json.h Normal file
View File

@@ -0,0 +1,190 @@
/*
* Bittorrent Client using Qt4 and libtorrent.
* Copyright (C) 2006 Ishan Arora and Christophe Dumez
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* In addition, as a special exception, the copyright holders give permission to
* link this program with the OpenSSL project's "OpenSSL" library (or with
* modified versions of it that use the same license as the "OpenSSL" library),
* and distribute the linked executables. You must obey the GNU General Public
* License in all respects for all of the code used other than "OpenSSL". If you
* modify file(s), you may extend this exception to your version of the file(s),
* but you are not obligated to do so. If you do not wish to do so, delete this
* exception statement from your version.
*
* Contact : chris@qbittorrent.org
*/
#ifndef JSON_H
#define JSON_H
#include <QVariant>
namespace json {
QString toJson(QVariant v) {
if (v.isNull())
return "null";
switch(v.type())
{
case QVariant::Bool:
case QVariant::Double:
case QVariant::Int:
case QVariant::LongLong:
case QVariant::UInt:
case QVariant::ULongLong:
//case QMetaType::Float:
return v.value<QString>();
case QVariant::StringList:
case QVariant::List: {
QStringList strList;
foreach(const QVariant &var, v.toList()) {
strList << toJson(var);
}
return "["+strList.join(",")+"]";
}
case QVariant::String:
{
QString s = v.value<QString>();
QString result = "\"";
for(int i=0; i<s.size(); i++)
{
QChar ch = s[i];
switch(ch.toAscii())
{
case '\b':
result += "\\b";
break;
case '\f':
result += "\\f";
break;
case '\n':
result += "\\n";
break;
case '\r':
result += "\\r";
break;
case '\t':
result += "\\t";
break;
case '\"':
case '\'':
case '\\':
case '&':
result += '\\';
case '\0':
default:
result += ch;
}
}
result += "\"";
return result;
}
default:
qDebug("Unknown QVariantType: %d", (int)v.type());
return "undefined";
}
}
QString toJson(QVariantMap m) {
QStringList vlist;
foreach(QString key, m.keys()) {
vlist << toJson(key)+":"+toJson(m[key]);
}
return "{"+vlist.join(",")+"}";
}
QVariantMap fromJson(QString json) {
qDebug("JSON is %s", qPrintable(json));
QVariantMap m;
if(json.startsWith("{") && json.endsWith("}")) {
json.chop(1);
json = json.replace(0, 1, "");
QStringList couples;
QString tmp = "";
bool in_list = false;
foreach(QChar c, json) {
if(c == ',' && !in_list) {
couples << tmp;
tmp = "";
} else {
if(c == '[') {
in_list = true;
} else {
if(c == ']') {
in_list = false;
}
}
tmp += c;
}
}
if(!tmp.isEmpty()) couples << tmp;
foreach(QString couple, couples) {
QStringList parts = couple.split(":");
if(parts.size() != 2) continue;
QString key = parts.first();
if(key.startsWith("\"") && key.endsWith("\"")) {
key.chop(1);
key = key.replace(0, 1, "");
}
QString value_str = parts.last();
QVariant value;
if(value_str.startsWith("[") && value_str.endsWith("]")) {
value_str.chop(1);
value_str.replace(0, 1, "");
QStringList list_elems = value_str.split(",", QString::SkipEmptyParts);
QVariantList varlist;
foreach(QString list_val, list_elems) {
if(list_val.startsWith("\"") && list_val.endsWith("\"")) {
list_val.chop(1);
list_val = list_val.replace(0, 1, "");
varlist << list_val;
} else {
varlist << list_val.toInt();
}
}
value = varlist;
} else {
if(value_str.startsWith("\"") && value_str.endsWith("\"")) {
value_str.chop(1);
value_str = value_str.replace(0, 1, "");
value = value_str;
} else {
value = value_str.toInt();
}
}
m.insert(key,value);
qDebug("%s:%s", key.toLocal8Bit().data(), value_str.toLocal8Bit().data());
}
}
return m;
}
QString toJson(QList<QVariantMap> v) {
QStringList res;
foreach(QVariantMap m, v) {
QStringList vlist;
foreach(QString key, m.keys()) {
vlist << toJson(key)+":"+toJson(m[key]);
}
res << "{"+vlist.join(",")+"}";
}
return "["+res.join(",")+"]";
}
}
#endif

16
src/webui/webui.pri Normal file
View File

@@ -0,0 +1,16 @@
INCLUDEPATH += $$PWD
HEADERS += $$PWD/httpserver.h \
$$PWD/httpconnection.h \
$$PWD/httprequestparser.h \
$$PWD/httpresponsegenerator.h \
$$PWD/eventmanager.h \
$$PWD/json.h
SOURCES += $$PWD/httpserver.cpp \
$$PWD/httpconnection.cpp \
$$PWD/httprequestparser.cpp \
$$PWD/httpresponsegenerator.cpp \
$$PWD/eventmanager.cpp
RESOURCES += $$PWD/webui.qrc

37
src/webui/webui.qrc Normal file
View File

@@ -0,0 +1,37 @@
<!DOCTYPE RCC><RCC version="1.0">
<qresource prefix="webui/">
<file>html/index.html</file>
<file>html/download.html</file>
<file>html/addtrackers.html</file>
<file>html/upload.html</file>
<file>html/uploadframe.html</file>
<file>html/about.html</file>
<file>html/filters.html</file>
<file>html/transferlist.html</file>
<file>html/prop-general.html</file>
<file>html/prop-trackers.html</file>
<file>html/prop-files.html</file>
<file>html/properties.html</file>
<file>html/uploadlimit.html</file>
<file>html/downloadlimit.html</file>
<file>html/preferences.html</file>
<file>html/preferences_content.html</file>
<file>css/Core.css</file>
<file>css/Layout.css</file>
<file>css/Window.css</file>
<file>css/Tabs.css</file>
<file>css/dynamicTable.css</file>
<file>css/style.css</file>
<file>scripts/excanvas-compressed.js</file>
<file>scripts/mocha-yc.js</file>
<file>scripts/mocha-init.js</file>
<file>scripts/mootools-1.2-core-yc.js</file>
<file>scripts/mootools-1.2-more.js</file>
<file>scripts/dynamicTable.js</file>
<file>scripts/client.js</file>
<file>scripts/download.js</file>
<file>scripts/progressbar.js</file>
<file>scripts/contextmenu.js</file>
<file>scripts/parametrics.js</file>
</qresource>
</RCC>