diff --git a/src/Icons/oxygen/system-log-out.png b/src/Icons/oxygen/system-log-out.png
new file mode 100644
index 000000000..d8817b7b5
Binary files /dev/null and b/src/Icons/oxygen/system-log-out.png differ
diff --git a/src/icons.qrc b/src/icons.qrc
index 5a138af3b..e338bd2f7 100644
--- a/src/icons.qrc
+++ b/src/icons.qrc
@@ -1,5 +1,5 @@
-
-
+
+
Icons/3-state-checkbox.gif
Icons/L.gif
Icons/loading.png
@@ -349,5 +349,6 @@
Icons/skin/tabs.gif
Icons/skin/toolbox-divider.gif
Icons/skin/uploading.png
+ Icons/oxygen/system-log-out.png
-
\ No newline at end of file
+
diff --git a/src/preferences/preferences.cpp b/src/preferences/preferences.cpp
index 2fcbe4cf2..542c9d312 100644
--- a/src/preferences/preferences.cpp
+++ b/src/preferences/preferences.cpp
@@ -951,7 +951,6 @@ QString Preferences::getWebUiPassword() const {
QString pass_ha1 = value("Preferences/WebUI/Password_ha1").toString();
if (pass_ha1.isEmpty()) {
QCryptographicHash md5(QCryptographicHash::Md5);
- md5.addData(getWebUiUsername().toLocal8Bit()+":"+QBT_REALM+":");
md5.addData("adminadmin");
pass_ha1 = md5.result().toHex();
}
@@ -959,13 +958,8 @@ QString Preferences::getWebUiPassword() const {
}
void Preferences::setWebUiPassword(const QString &new_password) {
- // Get current password md5
- QString current_pass_md5 = getWebUiPassword();
- // Check if password did not change
- if (current_pass_md5 == new_password) return;
// Encode to md5 and save
QCryptographicHash md5(QCryptographicHash::Md5);
- md5.addData(getWebUiUsername().toLocal8Bit()+":"+QBT_REALM+":");
md5.addData(new_password.toLocal8Bit());
setValue("Preferences/WebUI/Password_ha1", md5.result().toHex());
diff --git a/src/preferences/preferences.h b/src/preferences/preferences.h
index 272288890..2d8718201 100755
--- a/src/preferences/preferences.h
+++ b/src/preferences/preferences.h
@@ -44,7 +44,6 @@
#include
-#define QBT_REALM "Web UI Access"
enum scheduler_days { EVERY_DAY, WEEK_DAYS, WEEK_ENDS, MON, TUE, WED, THU, FRI, SAT, SUN };
enum maxRatioAction {PAUSE_ACTION, REMOVE_ACTION};
namespace Proxy {
diff --git a/src/qtlibtorrent/qbtsession.cpp b/src/qtlibtorrent/qbtsession.cpp
index 502c310bb..00021832c 100755
--- a/src/qtlibtorrent/qbtsession.cpp
+++ b/src/qtlibtorrent/qbtsession.cpp
@@ -671,8 +671,6 @@ void QBtSession::initWebUi() {
}
#endif
- httpServer->setAuthorization(username, password);
- httpServer->setlocalAuthEnabled(pref->isWebUiLocalAuthEnabled());
if (!httpServer->isListening()) {
bool success = httpServer->listen(QHostAddress::Any, port);
if (success)
diff --git a/src/tracker/qtracker.cpp b/src/tracker/qtracker.cpp
index cfab904f3..0e334ccda 100644
--- a/src/tracker/qtracker.cpp
+++ b/src/tracker/qtracker.cpp
@@ -29,18 +29,14 @@
*/
#include
-#include
-#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
-#include
-#endif
#include
#include
-#include "httprequestheader.h"
-#include "httpresponseheader.h"
#include "qtracker.h"
#include "preferences.h"
+#include "httpresponsegenerator.h"
+#include "httprequestparser.h"
#include
@@ -93,53 +89,38 @@ void QTracker::readRequest()
QTcpSocket *socket = static_cast(sender());
QByteArray input = socket->readAll();
//qDebug("QTracker: Raw request:\n%s", input.data());
- HttpRequestHeader http_request(input);
- if (!http_request.isValid()) {
- qDebug("QTracker: Invalid HTTP Request:\n %s", qPrintable(http_request.toString()));
+ HttpRequest request;
+ if (HttpRequestParser::parse(input, request) != HttpRequestParser::NoError) {
+ qDebug("QTracker: Invalid HTTP Request:\n %s", qPrintable(input));
respondInvalidRequest(socket, 100, "Invalid request type");
return;
}
//qDebug("QTracker received the following request:\n%s", qPrintable(parser.toString()));
// Request is correct, is it a GET request?
- if (http_request.method() != "GET") {
- qDebug("QTracker: Unsupported HTTP request: %s", qPrintable(http_request.method()));
+ if (request.method != "GET") {
+ qDebug("QTracker: Unsupported HTTP request: %s", qPrintable(request.method));
respondInvalidRequest(socket, 100, "Invalid request type");
return;
}
- if (!http_request.path().startsWith("/announce", Qt::CaseInsensitive)) {
- qDebug("QTracker: Unrecognized path: %s", qPrintable(http_request.path()));
+ if (!request.path.startsWith("/announce", Qt::CaseInsensitive)) {
+ qDebug("QTracker: Unrecognized path: %s", qPrintable(request.path));
respondInvalidRequest(socket, 100, "Invalid request type");
return;
}
// OK, this is a GET request
- // Parse GET parameters
- QHash get_parameters;
- QUrl url = QUrl::fromEncoded(http_request.path().toLatin1());
-#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
- QUrlQuery query(url);
- QListIterator > i(query.queryItems());
-#else
- QListIterator > i(url.queryItems());
-#endif
- while (i.hasNext()) {
- QPair pair = i.next();
- get_parameters[pair.first] = pair.second;
- }
-
- respondToAnnounceRequest(socket, get_parameters);
+ respondToAnnounceRequest(socket, request.gets);
}
void QTracker::respondInvalidRequest(QTcpSocket *socket, int code, QString msg)
{
- HttpResponseHeader response;
- response.setStatusLine(code, msg);
- socket->write(response.toString().toLocal8Bit());
+ HttpResponse response(code, msg);
+ socket->write(HttpResponseGenerator::generate(response));
socket->disconnectFromHost();
}
void QTracker::respondToAnnounceRequest(QTcpSocket *socket,
- const QHash& get_parameters)
+ const QMap& get_parameters)
{
TrackerAnnounceRequest annonce_req;
// IP
@@ -246,12 +227,12 @@ void QTracker::ReplyWithPeerList(QTcpSocket *socket, const TrackerAnnounceReques
// bencode
std::vector buf;
bencode(std::back_inserter(buf), reply_entry);
- QByteArray reply(&buf[0], buf.size());
+ QByteArray reply(&buf[0], static_cast(buf.size()));
qDebug("QTracker: reply with the following bencoded data:\n %s", reply.constData());
// HTTP reply
- HttpResponseHeader response;
- response.setStatusLine(200, "OK");
- socket->write(response.toString().toLocal8Bit() + reply);
+ HttpResponse response(200, "OK");
+ response.content = reply;
+ socket->write(HttpResponseGenerator::generate(response));
socket->disconnectFromHost();
}
diff --git a/src/tracker/qtracker.h b/src/tracker/qtracker.h
index 4d76bc91e..bf313ba18 100644
--- a/src/tracker/qtracker.h
+++ b/src/tracker/qtracker.h
@@ -61,7 +61,7 @@ protected slots:
void readRequest();
void handlePeerConnection();
void respondInvalidRequest(QTcpSocket *socket, int code, QString msg);
- void respondToAnnounceRequest(QTcpSocket *socket, const QHash& get_parameters);
+ void respondToAnnounceRequest(QTcpSocket *socket, const QMap& get_parameters);
void ReplyWithPeerList(QTcpSocket *socket, const TrackerAnnounceRequest &annonce_req);
private:
diff --git a/src/webui/abstractrequesthandler.cpp b/src/webui/abstractrequesthandler.cpp
new file mode 100644
index 000000000..8e6d97d19
--- /dev/null
+++ b/src/webui/abstractrequesthandler.cpp
@@ -0,0 +1,174 @@
+/*
+ * Bittorrent Client using Qt and libtorrent.
+ * Copyright (C) 2014 Vladimir Golovnev
+ * Copyright (C) 2012, 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.
+ */
+
+#include
+#include
+#include
+#include
+#include "preferences.h"
+#include "webapplication.h"
+#include "abstractrequesthandler.h"
+
+AbstractRequestHandler::AbstractRequestHandler(const HttpRequest &request, const HttpEnvironment &env, WebApplication *app)
+ : app_(app), session_(0), request_(request), env_(env)
+{
+ if (isBanned())
+ {
+ status(403, "Forbidden");
+ print(QObject::tr("Your IP address has been banned after too many failed authentication attempts."));
+ return;
+ }
+
+ sessionInitialize();
+
+ if (!sessionActive() && !isAuthNeeded()) sessionStart();
+}
+
+HttpResponse AbstractRequestHandler::run()
+{
+ response_ = HttpResponse();
+ processRequest();
+ return response_;
+}
+
+bool AbstractRequestHandler::isAuthNeeded()
+{
+ return
+ (
+ env_.clientAddress != QHostAddress::LocalHost &&
+ env_.clientAddress != QHostAddress::LocalHostIPv6
+ ) || Preferences::instance()->isWebUiLocalAuthEnabled();
+}
+
+void AbstractRequestHandler::status(uint code, const QString& text)
+{
+ response_.status = HttpResponseStatus(code, text);
+}
+
+void AbstractRequestHandler::header(const QString& name, const QString& value)
+{
+ response_.headers[name] = value;
+}
+
+void AbstractRequestHandler::print(const QString& text, const QString& type)
+{
+ print_impl(text.toUtf8(), type);
+}
+
+void AbstractRequestHandler::print(const QByteArray& data, const QString& type)
+{
+ print_impl(data, type);
+}
+
+void AbstractRequestHandler::print_impl(const QByteArray& data, const QString& type)
+{
+ if (!response_.headers.contains(HEADER_CONTENT_TYPE))
+ response_.headers[HEADER_CONTENT_TYPE] = type;
+
+ response_.content += data;
+}
+
+void AbstractRequestHandler::printFile(const QString& path)
+{
+ QByteArray data;
+ QString type;
+
+ if (!app_->readFile(path, data, type))
+ {
+ status(404, "Not Found");
+ return;
+ }
+
+ print(data, type);
+}
+
+void AbstractRequestHandler::sessionInitialize()
+{
+ app_->sessionInitialize(this);
+}
+
+void AbstractRequestHandler::sessionStart()
+{
+ if (app_->sessionStart(this))
+ {
+ QNetworkCookie cookie(C_SID.toUtf8(), session_->id.toUtf8());
+ cookie.setPath("/");
+ header(HEADER_SET_COOKIE, cookie.toRawForm());
+ }
+}
+
+void AbstractRequestHandler::sessionEnd()
+{
+ if (sessionActive())
+ {
+ QNetworkCookie cookie(C_SID.toUtf8(), session_->id.toUtf8());
+ cookie.setPath("/");
+ cookie.setExpirationDate(QDateTime::currentDateTime());
+
+ if (app_->sessionEnd(this))
+ {
+ header(HEADER_SET_COOKIE, cookie.toRawForm());
+ }
+ }
+}
+
+bool AbstractRequestHandler::isBanned() const
+{
+ return app_->isBanned(this);
+}
+
+int AbstractRequestHandler::failedAttempts() const
+{
+ return app_->failedAttempts(this);
+}
+
+void AbstractRequestHandler::resetFailedAttempts()
+{
+ app_->resetFailedAttempts(this);
+}
+
+void AbstractRequestHandler::increaseFailedAttempts()
+{
+ app_->increaseFailedAttempts(this);
+}
+
+QString AbstractRequestHandler::saveTmpFile(const QByteArray &data)
+{
+ QTemporaryFile tmpfile(QDir::temp().absoluteFilePath("qBT-XXXXXX.torrent"));
+ tmpfile.setAutoRemove(false);
+ if (tmpfile.open())
+ {
+ tmpfile.write(data);
+ tmpfile.close();
+ return tmpfile.fileName();
+ }
+
+ qWarning() << "I/O Error: Could not create temporary file";
+ return QString();
+}
diff --git a/src/webui/abstractrequesthandler.h b/src/webui/abstractrequesthandler.h
new file mode 100644
index 000000000..9f2c3b605
--- /dev/null
+++ b/src/webui/abstractrequesthandler.h
@@ -0,0 +1,89 @@
+/*
+ * Bittorrent Client using Qt and libtorrent.
+ * Copyright (C) 2014 Vladimir Golovnev
+ *
+ * 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.
+ */
+
+#ifndef ABSTRACTREQUESTHANDLER_H
+#define ABSTRACTREQUESTHANDLER_H
+
+#include
+#include "httptypes.h"
+
+class WebApplication;
+struct WebSession;
+
+class AbstractRequestHandler
+{
+ friend class WebApplication;
+
+public:
+ AbstractRequestHandler(
+ const HttpRequest& request, const HttpEnvironment& env,
+ WebApplication* app);
+
+ HttpResponse run();
+
+protected:
+ virtual void processRequest() = 0;
+
+ void status(uint code, const QString& text);
+ void header(const QString& name, const QString& value);
+ void print(const QString& text, const QString& type = CONTENT_TYPE_HTML);
+ void print(const QByteArray& data, const QString& type = CONTENT_TYPE_HTML);
+ void printFile(const QString& path);
+
+ // Session management
+ bool sessionActive() const { return session_ != 0; }
+ void sessionInitialize();
+ void sessionStart();
+ void sessionEnd();
+
+ // Ban management
+ bool isBanned() const;
+ int failedAttempts() const;
+ void resetFailedAttempts();
+ void increaseFailedAttempts();
+
+ bool isAuthNeeded();
+
+ // save data to temporary file on disk and return its name (or empty string if fails)
+ static QString saveTmpFile(const QByteArray& data);
+
+ inline WebSession* session() { return session_; }
+ inline HttpRequest request() const { return request_; }
+ inline HttpEnvironment env() const { return env_; }
+
+private:
+ WebApplication* app_;
+ WebSession* session_;
+ const HttpRequest request_;
+ const HttpEnvironment env_;
+ HttpResponse response_;
+
+ void print_impl(const QByteArray& data, const QString& type);
+};
+
+#endif // ABSTRACTREQUESTHANDLER_H
diff --git a/src/webui/extra_translations.h b/src/webui/extra_translations.h
new file mode 100644
index 000000000..bd9018ca4
--- /dev/null
+++ b/src/webui/extra_translations.h
@@ -0,0 +1,75 @@
+/*
+ * Bittorrent Client using Qt and libtorrent.
+ * Copyright (C) 2014 Vladimir Golovnev
+ *
+ * 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.
+ */
+
+#ifndef EXTRA_TRANSLATIONS_H
+#define EXTRA_TRANSLATIONS_H
+
+#include
+
+// Additional translations for Web UI
+static const char *__TRANSLATIONS__[] = {
+ QT_TRANSLATE_NOOP("HttpServer", "File"),
+ QT_TRANSLATE_NOOP("HttpServer", "Edit"),
+ QT_TRANSLATE_NOOP("HttpServer", "Help"),
+ QT_TRANSLATE_NOOP("HttpServer", "Download Torrents from their URL or Magnet link"),
+ QT_TRANSLATE_NOOP("HttpServer", "Only one link per line"),
+ QT_TRANSLATE_NOOP("HttpServer", "Download local torrent"),
+ QT_TRANSLATE_NOOP("HttpServer", "Torrent files were correctly added to download list."),
+ QT_TRANSLATE_NOOP("HttpServer", "Point to torrent file"),
+ QT_TRANSLATE_NOOP("HttpServer", "Download"),
+ QT_TRANSLATE_NOOP("HttpServer", "Are you sure you want to delete the selected torrents from the transfer list and hard disk?"),
+ QT_TRANSLATE_NOOP("HttpServer", "Download rate limit must be greater than 0 or disabled."),
+ QT_TRANSLATE_NOOP("HttpServer", "Upload rate limit must be greater than 0 or disabled."),
+ QT_TRANSLATE_NOOP("HttpServer", "Maximum number of connections limit must be greater than 0 or disabled."),
+ QT_TRANSLATE_NOOP("HttpServer", "Maximum number of connections per torrent limit must be greater than 0 or disabled."),
+ QT_TRANSLATE_NOOP("HttpServer", "Maximum number of upload slots per torrent limit must be greater than 0 or disabled."),
+ QT_TRANSLATE_NOOP("HttpServer", "Unable to save program preferences, qBittorrent is probably unreachable."),
+ QT_TRANSLATE_NOOP("HttpServer", "Language"),
+ QT_TRANSLATE_NOOP("HttpServer", "The port used for incoming connections must be greater than 1024 and less than 65535."),
+ QT_TRANSLATE_NOOP("HttpServer", "The port used for the Web UI must be greater than 1024 and less than 65535."),
+ QT_TRANSLATE_NOOP("HttpServer", "The Web UI username must be at least 3 characters long."),
+ QT_TRANSLATE_NOOP("HttpServer", "The Web UI password must be at least 3 characters long."),
+ QT_TRANSLATE_NOOP("HttpServer", "Save"),
+ QT_TRANSLATE_NOOP("HttpServer", "qBittorrent client is not reachable"),
+ QT_TRANSLATE_NOOP("HttpServer", "HTTP Server"),
+ QT_TRANSLATE_NOOP("HttpServer", "The following parameters are supported:"),
+ QT_TRANSLATE_NOOP("HttpServer", "Torrent path"),
+ QT_TRANSLATE_NOOP("HttpServer", "Torrent name"),
+ QT_TRANSLATE_NOOP("HttpServer", "qBittorrent has been shutdown."),
+ QT_TRANSLATE_NOOP("HttpServer", "Unable to log in, qBittorrent is probably unreachable."),
+ QT_TRANSLATE_NOOP("HttpServer", "Invalid Username or Password."),
+ QT_TRANSLATE_NOOP("HttpServer", "Password"),
+ QT_TRANSLATE_NOOP("HttpServer", "Login"),
+ QT_TRANSLATE_NOOP("HttpServer", "qBittorrent web User Interface")
+};
+
+static const struct { const char *source; const char *comment; } __COMMENTED_TRANSLATIONS__[] = {
+ QT_TRANSLATE_NOOP3("HttpServer", "Downloaded", "Is the file downloaded or not?")
+};
+
+#endif // EXTRA_TRANSLATIONS_H
diff --git a/src/webui/httpconnection.cpp b/src/webui/httpconnection.cpp
index 00ac1c636..196b1f5e7 100644
--- a/src/webui/httpconnection.cpp
+++ b/src/webui/httpconnection.cpp
@@ -1,5 +1,6 @@
/*
- * Bittorrent Client using Qt4 and libtorrent.
+ * Bittorrent Client using Qt and libtorrent.
+ * Copyright (C) 2014 Vladimir Golovnev
* Copyright (C) 2006 Ishan Arora and Christophe Dumez
*
* This program is free software; you can redistribute it and/or
@@ -24,661 +25,84 @@
* 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 "httprequestheader.h"
-#include "httpresponseheader.h"
-#include "preferences.h"
-#include "btjson.h"
-#include "prefjson.h"
-#include "qbtsession.h"
-#include "misc.h"
-#include "fs_utils.h"
-#ifndef DISABLE_GUI
-#include "iconprovider.h"
-#endif
#include
-#include
-#include
-#include
#include
-#include
-#include
-#include
-#include
-
-#include
-
-using namespace libtorrent;
+#include "httptypes.h"
+#include "httpserver.h"
+#include "httprequestparser.h"
+#include "httpresponsegenerator.h"
+#include "webapplication.h"
+#include "requesthandler.h"
+#include "httpconnection.h"
-HttpConnection::HttpConnection(QTcpSocket *socket, HttpServer *parent)
- : QObject(parent), m_socket(socket), m_httpserver(parent)
+HttpConnection::HttpConnection(QTcpSocket *socket, HttpServer *httpserver)
+ : QObject(httpserver), m_socket(socket)
{
m_socket->setParent(this);
connect(m_socket, SIGNAL(readyRead()), SLOT(read()));
connect(m_socket, SIGNAL(disconnected()), SLOT(deleteLater()));
}
-HttpConnection::~HttpConnection() {
+HttpConnection::~HttpConnection()
+{
delete m_socket;
}
-void HttpConnection::processDownloadedFile(const QString &url,
- const QString &file_path) {
- qDebug("URL %s successfully downloaded !", qPrintable(url));
- emit torrentReadyToBeDownloaded(file_path, false, url, false);
-}
-
-void HttpConnection::handleDownloadFailure(const QString& url,
- const QString& reason) {
- std::cerr << "Could not download " << qPrintable(url) << ", reason: "
- << qPrintable(reason) << std::endl;
-}
-
void HttpConnection::read()
{
m_receivedData.append(m_socket->readAll());
- // Parse HTTP request header
- const int header_end = m_receivedData.indexOf("\r\n\r\n");
- if (header_end < 0) {
- qDebug() << "Partial request: \n" << m_receivedData;
+ HttpRequest request;
+ HttpRequestParser::ErrorCode err = HttpRequestParser::parse(m_receivedData, request);
+ switch (err)
+ {
+ case HttpRequestParser::IncompleteRequest:
// Partial request waiting for the rest
- return;
- }
-
- const QByteArray header = m_receivedData.left(header_end);
- m_parser.writeHeader(header);
- if (m_parser.isError()) {
- qWarning() << Q_FUNC_INFO << "header parsing error";
- m_receivedData.clear();
- m_generator.setStatusLine(400, "Bad Request");
- m_generator.setContentEncoding(m_parser.acceptsEncoding());
- write();
- return;
- }
-
- // Parse HTTP request message
- if (m_parser.header().hasContentLength()) {
- const int expected_length = m_parser.header().contentLength();
- QByteArray message = m_receivedData.mid(header_end + 4, expected_length);
-
- if (expected_length > 10000000 /* ~10MB */) {
- qWarning() << "Bad request: message too long";
- m_generator.setStatusLine(400, "Bad Request");
- m_generator.setContentEncoding(m_parser.acceptsEncoding());
- m_receivedData.clear();
- write();
- return;
- }
-
- if (message.length() < expected_length) {
- // Message too short, waiting for the rest
- qDebug() << "Partial message:\n" << message;
- return;
- }
-
- m_parser.writeMessage(message);
- m_receivedData = m_receivedData.mid(header_end + 4 + expected_length);
- } else {
- m_receivedData.clear();
- }
-
- if (m_parser.isError()) {
- qWarning() << Q_FUNC_INFO << "message parsing error";
- m_generator.setStatusLine(400, "Bad Request");
- m_generator.setContentEncoding(m_parser.acceptsEncoding());
- write();
- } else {
- respond();
- }
-}
-
-void HttpConnection::write()
+ break;
+ case HttpRequestParser::BadRequest:
+ write(HttpResponse(400, "Bad Request"));
+ break;
+ case HttpRequestParser::NoError:
+ HttpEnvironment env;
+ env.clientAddress = m_socket->peerAddress();
+ HttpResponse response = RequestHandler(request, env, WebApplication::instance()).run();
+ if (acceptsGzipEncoding(request.headers["accept-encoding"]))
+ response.headers[HEADER_CONTENT_ENCODING] = "gzip";
+ write(response);
+ break;
+ }
+}
+
+void HttpConnection::write(const HttpResponse& response)
{
- m_socket->write(m_generator.toByteArray());
+ m_socket->write(HttpResponseGenerator::generate(response));
m_socket->disconnectFromHost();
}
-void HttpConnection::translateDocument(QString& data) {
- static QRegExp regex(QString::fromUtf8("_\\(([\\w\\s?!:\\/\\(\\),%µ&\\-\\.]+)\\)"));
- static QRegExp mnemonic("\\(?&([a-zA-Z]?\\))?");
- const std::string contexts[] = {"TransferListFiltersWidget", "TransferListWidget",
- "PropertiesWidget", "MainWindow", "HttpServer",
- "confirmDeletionDlg", "TrackerList", "TorrentFilesModel",
- "options_imp", "Preferences", "TrackersAdditionDlg",
- "ScanFoldersModel", "PropTabBar", "TorrentModel",
- "downloadFromURL", "misc"};
- const size_t context_count = sizeof(contexts)/sizeof(contexts[0]);
- int i = 0;
- bool found = true;
-
- const QString locale = Preferences::instance()->getLocale();
- bool isTranslationNeeded = !locale.startsWith("en") || locale.startsWith("en_AU") || locale.startsWith("en_GB");
-
- while(i < data.size() && found) {
- i = regex.indexIn(data, i);
- if (i >= 0) {
- //qDebug("Found translatable string: %s", regex.cap(1).toUtf8().data());
- QByteArray word = regex.cap(1).toUtf8();
-
- QString translation = word;
- if (isTranslationNeeded) {
- size_t context_index = 0;
- while(context_index < context_count && translation == word) {
-#if (QT_VERSION < QT_VERSION_CHECK(5, 0, 0))
- translation = qApp->translate(contexts[context_index].c_str(), word.constData(), 0, QCoreApplication::UnicodeUTF8, 1);
-#else
- translation = qApp->translate(contexts[context_index].c_str(), word.constData(), 0, 1);
-#endif
- ++context_index;
- }
- }
- // Remove keyboard shortcuts
- translation.replace(mnemonic, "");
-
- data.replace(i, regex.matchedLength(), translation);
- i += translation.length();
- } else {
- found = false; // no more translatable strings
- }
- }
-}
-
-void HttpConnection::respond() {
- if ((m_socket->peerAddress() != QHostAddress::LocalHost
- && m_socket->peerAddress() != QHostAddress::LocalHostIPv6)
- || m_httpserver->isLocalAuthEnabled()) {
- // Authentication
- const QString peer_ip = m_socket->peerAddress().toString();
- const int nb_fail = m_httpserver->NbFailedAttemptsForIp(peer_ip);
- if (nb_fail >= MAX_AUTH_FAILED_ATTEMPTS) {
- m_generator.setStatusLine(403, "Forbidden");
- m_generator.setMessage(tr("Your IP address has been banned after too many failed authentication attempts."));
- m_generator.setContentEncoding(m_parser.acceptsEncoding());
- write();
- return;
- }
- QString auth = m_parser.header().value("Authorization");
- if (auth.isEmpty()) {
- // Return unauthorized header
- qDebug("Auth is Empty...");
- m_generator.setStatusLine(401, "Unauthorized");
- m_generator.setValue("WWW-Authenticate", "Digest realm=\""+QString(QBT_REALM)+"\", nonce=\""+m_httpserver->generateNonce()+"\", opaque=\""+m_httpserver->generateNonce()+"\", stale=\"false\", algorithm=\"MD5\", qop=\"auth\"");
- m_generator.setContentEncoding(m_parser.acceptsEncoding());
- write();
- return;
- }
- //qDebug("Auth: %s", qPrintable(auth.split(" ").first()));
- if (QString::compare(auth.split(" ").first(), "Digest", Qt::CaseInsensitive) != 0
- || !m_httpserver->isAuthorized(auth.toUtf8(), m_parser.header().method())) {
- // Update failed attempt counter
- m_httpserver->increaseNbFailedAttemptsForIp(peer_ip);
- qDebug("client IP: %s (%d failed attempts)", qPrintable(peer_ip), nb_fail);
- // Return unauthorized header
- m_generator.setStatusLine(401, "Unauthorized");
- m_generator.setValue("WWW-Authenticate", "Digest realm=\""+QString(QBT_REALM)+"\", nonce=\""+m_httpserver->generateNonce()+"\", opaque=\""+m_httpserver->generateNonce()+"\", stale=\"false\", algorithm=\"MD5\", qop=\"auth\"");
- m_generator.setContentEncoding(m_parser.acceptsEncoding());
- write();
- return;
- }
- // Client successfully authenticated, reset number of failed attempts
- m_httpserver->resetNbFailedAttemptsForIp(peer_ip);
- }
- QString url = m_parser.url();
- // Favicon
- if (url.endsWith("favicon.ico")) {
- qDebug("Returning favicon");
- QFile favicon(":/Icons/skin/qbittorrent16.png");
- if (favicon.open(QIODevice::ReadOnly)) {
- const QByteArray data = favicon.readAll();
- favicon.close();
- m_generator.setStatusLine(200, "OK");
- m_generator.setContentTypeByExt("png");
- m_generator.setMessage(data);
- m_generator.setContentEncoding(m_parser.acceptsEncoding());
- write();
- } else {
- respondNotFound();
- }
- return;
- }
-
- QStringList list = url.split('/', QString::SkipEmptyParts);
- if (list.contains(".") || list.contains("..")) {
- respondNotFound();
- return;
- }
+bool HttpConnection::acceptsGzipEncoding(const QString& encoding)
+{
+ int pos = encoding.indexOf("gzip", 0, Qt::CaseInsensitive);
+ if (pos == -1)
+ return false;
- if (list.isEmpty())
- list.append("index.html");
+ // Let's see if there's a qvalue of 0.0 following
+ if (encoding[pos + 4] != ';') //there isn't, so it accepts gzip anyway
+ return true;
- if (list.size() >= 2) {
- if (list[0] == "json") {
- if (list[1] == "torrents") {
- respondTorrentsJson();
- return;
- }
- if (list.size() > 2) {
- if (list[1] == "propertiesGeneral") {
- const QString& hash = list[2];
- respondGenPropertiesJson(hash);
- return;
- }
- if (list[1] == "propertiesTrackers") {
- const QString& hash = list[2];
- respondTrackersPropertiesJson(hash);
- return;
- }
- if (list[1] == "propertiesFiles") {
- const QString& hash = list[2];
- respondFilesPropertiesJson(hash);
- return;
- }
- } else {
- if (list[1] == "preferences") {
- respondPreferencesJson();
- return;
- } else {
- if (list[1] == "transferInfo") {
- respondGlobalTransferInfoJson();
- return;
- }
- }
- }
- }
- if (list[0] == "command") {
- const QString& command = list[1];
- if (command == "shutdown") {
- qDebug() << "Shutdown request from Web UI";
- // Special case handling for shutdown, we
- // need to reply to the Web UI before
- // actually shutting down.
- m_generator.setStatusLine(200, "OK");
- m_generator.setContentEncoding(m_parser.acceptsEncoding());
- write();
- qApp->processEvents();
- // Exit application
- qApp->exit();
- } else {
- respondCommand(command);
- m_generator.setStatusLine(200, "OK");
- m_generator.setContentEncoding(m_parser.acceptsEncoding());
- write();
- }
- return;
- }
- }
+ //So let's find = and the next comma
+ pos = encoding.indexOf("=", pos + 4, Qt::CaseInsensitive);
+ int comma_pos = encoding.indexOf(",", pos, Qt::CaseInsensitive);
- // Icons from theme
- //qDebug() << "list[0]" << list[0];
- if (list[0] == "theme" && list.size() == 2) {
-#ifdef DISABLE_GUI
- url = ":/Icons/oxygen/"+list[1]+".png";
-#else
- url = IconProvider::instance()->getIconPath(list[1]);
-#endif
- qDebug() << "There icon:" << url;
- } else {
- 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);
+ QString value;
+ if (comma_pos == -1)
+ value = encoding.mid(pos + 1, comma_pos);
else
- ext.clear();
- QByteArray data = file.readAll();
- file.close();
-
- // Translate the page
- if (ext == "html" || (ext == "js" && !list.last().startsWith("excanvas"))) {
- QString dataStr = QString::fromUtf8(data.constData());
- translateDocument(dataStr);
- if (url.endsWith("about.html")) {
- dataStr.replace("${VERSION}", VERSION);
- }
- data = dataStr.toUtf8();
- }
- m_generator.setStatusLine(200, "OK");
- m_generator.setContentTypeByExt(ext);
- m_generator.setMessage(data);
- m_generator.setContentEncoding(m_parser.acceptsEncoding());
- write();
-}
-
-void HttpConnection::respondNotFound() {
- m_generator.setStatusLine(404, "File not found");
- m_generator.setContentEncoding(m_parser.acceptsEncoding());
- write();
-}
+ value = encoding.mid(pos + 1, comma_pos - (pos + 1));
-void HttpConnection::respondTorrentsJson() {
- m_generator.setStatusLine(200, "OK");
- m_generator.setContentTypeByExt("js");
- m_generator.setMessage(btjson::getTorrents());
- m_generator.setContentEncoding(m_parser.acceptsEncoding());
- write();
-}
-
-void HttpConnection::respondGenPropertiesJson(const QString& hash) {
- m_generator.setStatusLine(200, "OK");
- m_generator.setContentTypeByExt("js");
- m_generator.setMessage(btjson::getPropertiesForTorrent(hash));
- m_generator.setContentEncoding(m_parser.acceptsEncoding());
- write();
-}
-
-void HttpConnection::respondTrackersPropertiesJson(const QString& hash) {
- m_generator.setStatusLine(200, "OK");
- m_generator.setContentTypeByExt("js");
- m_generator.setMessage(btjson::getTrackersForTorrent(hash));
- m_generator.setContentEncoding(m_parser.acceptsEncoding());
- write();
-}
-
-void HttpConnection::respondFilesPropertiesJson(const QString& hash) {
- m_generator.setStatusLine(200, "OK");
- m_generator.setContentTypeByExt("js");
- m_generator.setMessage(btjson::getFilesForTorrent(hash));
- m_generator.setContentEncoding(m_parser.acceptsEncoding());
- write();
-}
-
-void HttpConnection::respondPreferencesJson() {
- m_generator.setStatusLine(200, "OK");
- m_generator.setContentTypeByExt("js");
- m_generator.setMessage(prefjson::getPreferences());
- m_generator.setContentEncoding(m_parser.acceptsEncoding());
- write();
-}
+ if (value.toDouble() == 0.0)
+ return false;
-void HttpConnection::respondGlobalTransferInfoJson() {
- m_generator.setStatusLine(200, "OK");
- m_generator.setContentTypeByExt("js");
- m_generator.setMessage(btjson::getTransferInfo());
- m_generator.setContentEncoding(m_parser.acceptsEncoding());
- write();
-}
-
-void HttpConnection::respondCommand(const QString& command) {
- qDebug() << Q_FUNC_INFO << command;
- if (command == "download") {
- QString urls = m_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", qPrintable(url));
- emit UrlReadyToBeDownloaded(url);
- }
- }
- }
- return;
- }
-
- if (command == "addTrackers") {
- QString hash = m_parser.post("hash");
- if (!hash.isEmpty()) {
- QTorrentHandle h = QBtSession::instance()->getTorrentHandle(hash);
- if (h.is_valid() && h.has_metadata()) {
- QString urls = m_parser.post("urls");
- QStringList list = urls.split('\n');
- foreach (const QString& url, list) {
- announce_entry e(url.toStdString());
- h.add_tracker(e);
- }
- }
- }
- return;
- }
- if (command == "upload") {
- qDebug() << Q_FUNC_INFO << "upload";
- const QList& torrents = m_parser.torrents();
- foreach(const QByteArray& torrentContent, torrents) {
- // Get a unique filename
- QTemporaryFile *tmpfile = new QTemporaryFile(QDir::temp().absoluteFilePath("qBT-XXXXXX.torrent"));
- tmpfile->setAutoRemove(false);
- if (tmpfile->open()) {
- QString filePath = tmpfile->fileName();
- tmpfile->write(torrentContent);
- tmpfile->close();
- // XXX: tmpfile needs to be deleted on Windows before using the file
- // or it will complain that the file is used by another process.
- delete tmpfile;
- emit torrentReadyToBeDownloaded(filePath, false, QString(), false);
- // Clean up
- fsutils::forceRemove(filePath);
- } else {
- std::cerr << "I/O Error: Could not create temporary file" << std::endl;
- delete tmpfile;
- return;
- }
- }
- // Prepare response
- m_generator.setStatusLine(200, "OK");
- m_generator.setContentTypeByExt("html");
- m_generator.setMessage(QString(""));
- m_generator.setContentEncoding(m_parser.acceptsEncoding());
- write();
- return;
- }
- if (command == "resumeall") {
- emit resumeAllTorrents();
- return;
- }
- if (command == "pauseall") {
- emit pauseAllTorrents();
- return;
- }
- if (command == "resume") {
- emit resumeTorrent(m_parser.post("hash"));
- return;
- }
- if (command == "setPreferences") {
- prefjson::setPreferences(m_parser.post("json"));
- return;
- }
- if (command == "setFilePrio") {
- QString hash = m_parser.post("hash");
- int file_id = m_parser.post("id").toInt();
- int priority = m_parser.post("priority").toInt();
- QTorrentHandle h = QBtSession::instance()->getTorrentHandle(hash);
- if (h.is_valid() && h.has_metadata()) {
- h.file_priority(file_id, priority);
- }
- return;
- }
- if (command == "getGlobalUpLimit") {
- m_generator.setStatusLine(200, "OK");
- m_generator.setContentTypeByExt("html");
- m_generator.setMessage(QByteArray::number(QBtSession::instance()->getSession()->settings().upload_rate_limit));
- m_generator.setContentEncoding(m_parser.acceptsEncoding());
- write();
- return;
- }
- if (command == "getGlobalDlLimit") {
- m_generator.setStatusLine(200, "OK");
- m_generator.setContentTypeByExt("html");
- m_generator.setMessage(QByteArray::number(QBtSession::instance()->getSession()->settings().download_rate_limit));
- m_generator.setContentEncoding(m_parser.acceptsEncoding());
- write();
- return;
- }
- if (command == "getTorrentUpLimit") {
- QString hash = m_parser.post("hash");
- QTorrentHandle h = QBtSession::instance()->getTorrentHandle(hash);
- if (h.is_valid()) {
- m_generator.setStatusLine(200, "OK");
- m_generator.setContentTypeByExt("html");
- m_generator.setMessage(QByteArray::number(h.upload_limit()));
- m_generator.setContentEncoding(m_parser.acceptsEncoding());
- write();
- }
- return;
- }
- if (command == "getTorrentDlLimit") {
- QString hash = m_parser.post("hash");
- QTorrentHandle h = QBtSession::instance()->getTorrentHandle(hash);
- if (h.is_valid()) {
- m_generator.setStatusLine(200, "OK");
- m_generator.setContentTypeByExt("html");
- m_generator.setMessage(QByteArray::number(h.download_limit()));
- m_generator.setContentEncoding(m_parser.acceptsEncoding());
- write();
- }
- return;
- }
- if (command == "setTorrentUpLimit") {
- QString hash = m_parser.post("hash");
- qlonglong limit = m_parser.post("limit").toLongLong();
- if (limit == 0) limit = -1;
- QTorrentHandle h = QBtSession::instance()->getTorrentHandle(hash);
- if (h.is_valid()) {
- h.set_upload_limit(limit);
- }
- return;
- }
- if (command == "setTorrentDlLimit") {
- QString hash = m_parser.post("hash");
- qlonglong limit = m_parser.post("limit").toLongLong();
- if (limit == 0) limit = -1;
- QTorrentHandle h = QBtSession::instance()->getTorrentHandle(hash);
- if (h.is_valid()) {
- h.set_download_limit(limit);
- }
- return;
- }
- if (command == "setGlobalUpLimit") {
- qlonglong limit = m_parser.post("limit").toLongLong();
- if (limit == 0) limit = -1;
- QBtSession::instance()->setUploadRateLimit(limit);
- Preferences::instance()->setGlobalUploadLimit(limit/1024.);
- return;
- }
- if (command == "setGlobalDlLimit") {
- qlonglong limit = m_parser.post("limit").toLongLong();
- if (limit == 0) limit = -1;
- QBtSession::instance()->setDownloadRateLimit(limit);
- Preferences::instance()->setGlobalDownloadLimit(limit/1024.);
- return;
- }
- if (command == "pause") {
- emit pauseTorrent(m_parser.post("hash"));
- return;
- }
- if (command == "delete") {
- QStringList hashes = m_parser.post("hashes").split("|");
- foreach (const QString &hash, hashes) {
- emit deleteTorrent(hash, false);
- }
- return;
- }
- if (command == "deletePerm") {
- QStringList hashes = m_parser.post("hashes").split("|");
- foreach (const QString &hash, hashes) {
- emit deleteTorrent(hash, true);
- }
- return;
- }
- if (command == "increasePrio") {
- increaseTorrentsPriority(m_parser.post("hashes").split("|"));
- return;
- }
- if (command == "decreasePrio") {
- decreaseTorrentsPriority(m_parser.post("hashes").split("|"));
- return;
- }
- if (command == "topPrio") {
- foreach (const QString &hash, m_parser.post("hashes").split("|")) {
- QTorrentHandle h = QBtSession::instance()->getTorrentHandle(hash);
- if (h.is_valid()) h.queue_position_top();
- }
- return;
- }
- if (command == "bottomPrio") {
- foreach (const QString &hash, m_parser.post("hashes").split("|")) {
- QTorrentHandle h = QBtSession::instance()->getTorrentHandle(hash);
- if (h.is_valid()) h.queue_position_bottom();
- }
- return;
- }
- if (command == "recheck") {
- QBtSession::instance()->recheckTorrent(m_parser.post("hash"));
- return;
- }
-}
-
-void HttpConnection::decreaseTorrentsPriority(const QStringList &hashes) {
- qDebug() << Q_FUNC_INFO << hashes;
- std::priority_queue,
- std::vector >,
- std::less > > torrent_queue;
- // Sort torrents by priority
- foreach (const QString &hash, hashes) {
- try {
- QTorrentHandle h = QBtSession::instance()->getTorrentHandle(hash);
- if (!h.is_seed()) {
- torrent_queue.push(qMakePair(h.queue_position(), h));
- }
- }catch(invalid_handle&) {}
- }
- // Decrease torrents priority (starting with the ones with lowest priority)
- while(!torrent_queue.empty()) {
- QTorrentHandle h = torrent_queue.top().second;
- try {
- h.queue_position_down();
- } catch(invalid_handle& h) {}
- torrent_queue.pop();
- }
-}
-
-void HttpConnection::increaseTorrentsPriority(const QStringList &hashes)
-{
- qDebug() << Q_FUNC_INFO << hashes;
- std::priority_queue,
- std::vector >,
- std::greater > > torrent_queue;
- // Sort torrents by priority
- foreach (const QString &hash, hashes) {
- try {
- QTorrentHandle h = QBtSession::instance()->getTorrentHandle(hash);
- if (!h.is_seed()) {
- torrent_queue.push(qMakePair(h.queue_position(), h));
- }
- }catch(invalid_handle&) {}
- }
- // Increase torrents priority (starting with the ones with highest priority)
- while(!torrent_queue.empty()) {
- QTorrentHandle h = torrent_queue.top().second;
- try {
- h.queue_position_up();
- } catch(invalid_handle& h) {}
- torrent_queue.pop();
- }
+ return true;
}
diff --git a/src/webui/httpconnection.h b/src/webui/httpconnection.h
index 1af5fcc04..e1a9ed82f 100644
--- a/src/webui/httpconnection.h
+++ b/src/webui/httpconnection.h
@@ -1,5 +1,6 @@
/*
- * Bittorrent Client using Qt4 and libtorrent.
+ * Bittorrent Client using Qt and libtorrent.
+ * Copyright (C) 2014 Vladimir Golovnev
* Copyright (C) 2006 Ishan Arora and Christophe Dumez
*
* This program is free software; you can redistribute it and/or
@@ -24,17 +25,14 @@
* 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
+#include "httptypes.h"
class HttpServer;
@@ -48,46 +46,18 @@ class HttpConnection : public QObject
Q_DISABLE_COPY(HttpConnection)
public:
- HttpConnection(QTcpSocket *m_socket, HttpServer *m_httpserver);
+ HttpConnection(QTcpSocket* socket, HttpServer* httpserver);
~HttpConnection();
- void translateDocument(QString& data);
-
-protected slots:
- void write();
- void respond();
- void respondTorrentsJson();
- void respondGenPropertiesJson(const QString& hash);
- void respondTrackersPropertiesJson(const QString& hash);
- void respondFilesPropertiesJson(const QString& hash);
- void respondPreferencesJson();
- void respondGlobalTransferInfoJson();
- void respondCommand(const QString& command);
- void respondNotFound();
- void processDownloadedFile(const QString& url, const QString& file_path);
- void handleDownloadFailure(const QString& url, const QString& reason);
- void decreaseTorrentsPriority(const QStringList& hashes);
- void increaseTorrentsPriority(const QStringList& hashes);
private slots:
void read();
-signals:
- void UrlReadyToBeDownloaded(const QString& url);
- void MagnetReadyToBeDownloaded(const QString& uri);
- void torrentReadyToBeDownloaded(const QString&, bool, const QString&, bool);
- void deleteTorrent(const QString& hash, bool permanently);
- void resumeTorrent(const QString& hash);
- void pauseTorrent(const QString& hash);
- void increasePrioTorrent(const QString& hash);
- void decreasePrioTorrent(const QString& hash);
- void resumeAllTorrents();
- void pauseAllTorrents();
-
private:
+ void write(const HttpResponse& response);
+
+ static bool acceptsGzipEncoding(const QString& encoding);
+
QTcpSocket *m_socket;
- HttpServer *m_httpserver;
- HttpRequestParser m_parser;
- HttpResponseGenerator m_generator;
QByteArray m_receivedData;
};
diff --git a/src/webui/httpheader.cpp b/src/webui/httpheader.cpp
deleted file mode 100644
index e681c25b1..000000000
--- a/src/webui/httpheader.cpp
+++ /dev/null
@@ -1,161 +0,0 @@
-/*
- * Bittorrent Client using Qt4 and libtorrent.
- * Copyright (C) 2014 sledgehammer999
- *
- * 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 : hammered999@gmail.com
- */
-
-#include "httpheader.h"
-
-HttpHeader::HttpHeader() : m_valid(true) {}
-
-HttpHeader::HttpHeader(const QString &str) : m_valid(true) {
- parse(str);
-}
-
-HttpHeader::~HttpHeader() {}
-
-void HttpHeader::addValue(const QString &key, const QString &value) {
- m_headers.insert(key.toLower(), value.trimmed());
-}
-
-QStringList HttpHeader::allValues(const QString &key) const {
- QStringList list = m_headers.values(key);
- return list;
-}
-
-uint HttpHeader::contentLength() const {
- QString lenVal = m_headers.value("content-length");
- return lenVal.toUInt();
-}
-
-QString HttpHeader::contentType() const {
- QString str = m_headers.value("content-type");
- // content-type might have a parameter so we need to strip it.
- // eg. application/x-www-form-urlencoded; charset=utf-8
- int index = str.indexOf(';');
- if (index == -1)
- return str;
- else
- return str.left(index);
-}
-
-bool HttpHeader::hasContentLength() const {
- return m_headers.contains("content-length");
-}
-
-bool HttpHeader::hasContentType() const {
- return m_headers.contains("content-type");
-}
-
-bool HttpHeader::hasKey(const QString &key) const {
- return m_headers.contains(key.toLower());
-}
-
-bool HttpHeader::isValid() const {
- return m_valid;
-}
-
-QStringList HttpHeader::keys() const {
- QStringList list = m_headers.keys();
- return list;
-}
-
-void HttpHeader::removeAllValues(const QString &key) {
- m_headers.remove(key);
-}
-
-void HttpHeader::removeValue(const QString &key) {
- m_headers.remove(key);
-}
-
-void HttpHeader::setContentLength(int len) {
- m_headers.replace("content-length", QString::number(len));
-}
-
-void HttpHeader::setContentType(const QString &type) {
- m_headers.replace("content-type", type.trimmed());
-}
-
-void HttpHeader::setValue(const QString &key, const QString &value) {
- m_headers.replace(key.toLower(), value.trimmed());
-}
-
-void HttpHeader::setValues(const QList > &values) {
- for (int i=0; i < values.size(); ++i) {
- setValue(values[i].first, values[i].second);
- }
-}
-
-QString HttpHeader::toString() const {
- QString str;
- typedef QMultiHash::const_iterator h_it;
-
- for (h_it it = m_headers.begin(), itend = m_headers.end();
- it != itend; ++it) {
- str = str + it.key() + ": " + it.value() + "\r\n";
- }
-
- str += "\r\n";
- return str;
-}
-
-QString HttpHeader::value(const QString &key) const {
- return m_headers.value(key.toLower());
-}
-
-QList > HttpHeader::values() const {
- QList > list;
- typedef QMultiHash::const_iterator h_it;
-
- for (h_it it = m_headers.begin(), itend = m_headers.end();
- it != itend; ++it) {
- list.append(qMakePair(it.key(), it.value()));
- }
-
- return list;
-}
-
-void HttpHeader::parse(const QString &str) {
- QStringList headers = str.split("\r\n", QString::SkipEmptyParts, Qt::CaseInsensitive);
- for (int i=0; i < headers.size(); ++i) {
- int index = headers[i].indexOf(':', Qt::CaseInsensitive);
- if (index == -1) {
- setValid(false);
- break;
- }
-
- QString key = headers[i].left(index);
- QString value = headers[i].right(headers[i].size() - index - 1);
- m_headers.insert(key.toLower(), value.trimmed());
- }
-}
-
-void HttpHeader::setValid(bool valid) {
- m_valid = valid;
- if (!m_valid)
- m_headers.clear();
-}
diff --git a/src/webui/httpheader.h b/src/webui/httpheader.h
deleted file mode 100644
index 7a7ac8b2f..000000000
--- a/src/webui/httpheader.h
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Bittorrent Client using Qt4 and libtorrent.
- * Copyright (C) 2014 sledgehammer999
- *
- * 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 : hammered999@gmail.com
- */
-
-#ifndef HTTPHEADER_H
-#define HTTPHEADER_H
-
-#include
-#include
-#include
-#include
-
-class HttpHeader {
-
-public:
- HttpHeader();
- HttpHeader(const QString &str);
- virtual ~HttpHeader();
- void addValue(const QString &key, const QString &value);
- QStringList allValues(const QString &key) const;
- uint contentLength() const;
- QString contentType() const;
- bool hasContentLength() const;
- bool hasContentType() const;
- bool hasKey(const QString &key) const;
- bool isValid() const;
- QStringList keys() const;
- virtual int majorVersion() const =0;
- virtual int minorVersion() const =0;
- void removeAllValues(const QString &key);
- void removeValue(const QString &key);
- void setContentLength(int len);
- void setContentType(const QString &type);
- void setValue(const QString &key, const QString &value);
- void setValues(const QList > &values);
- virtual QString toString() const;
- QString value(const QString &key) const;
- QList > values() const;
-
-protected:
- void parse(const QString &str);
- void setValid(bool valid = true);
-
-private:
- QMultiHash m_headers;
- bool m_valid;
-};
-
-#endif // HTTPHEADER_H
diff --git a/src/webui/httprequestheader.cpp b/src/webui/httprequestheader.cpp
deleted file mode 100644
index f799ac8e3..000000000
--- a/src/webui/httprequestheader.cpp
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- * Bittorrent Client using Qt4 and libtorrent.
- * Copyright (C) 2014 sledgehammer999
- *
- * 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 : hammered999@gmail.com
- */
-
-#include "httprequestheader.h"
-
-
-HttpRequestHeader::HttpRequestHeader() : HttpHeader(), m_majorVersion(1), m_minorVersion(1) {}
-
-HttpRequestHeader::HttpRequestHeader(const QString &method, const QString &path, int majorVer, int minorVer) :
- HttpHeader(), m_method(method), m_path(path), m_majorVersion(majorVer), m_minorVersion(minorVer) {}
-
-HttpRequestHeader::HttpRequestHeader(const QString &str): HttpHeader() {
- int line = str.indexOf("\r\n", 0, Qt::CaseInsensitive);
- QString req = str.left(line);
- QString headers = str.right(str.size() - line - 2); //"\r\n" == 2
- QStringList method = req.split(" ", QString::SkipEmptyParts, Qt::CaseInsensitive);
- if (method.size() != 3)
- setValid(false);
- else {
- m_method = method[0];
- m_path = method[1];
- if (parseVersions(method[2]))
- parse(headers);
- else
- setValid(false);
- }
-}
-
-int HttpRequestHeader::majorVersion() const {
- return m_majorVersion;
-}
-
-int HttpRequestHeader::minorVersion() const {
- return m_minorVersion;
-}
-
-QString HttpRequestHeader::method() const {
- return m_method;
-}
-
-QString HttpRequestHeader::path() const {
- return m_path;
-}
-
-void HttpRequestHeader::setRequest(const QString &method, const QString &path, int majorVer, int minorVer) {
- m_method = method;
- m_path = path;
- m_majorVersion = majorVer;
- m_minorVersion = minorVer;
-}
-
-QString HttpRequestHeader::toString() const {
- QString str = m_method + " ";
- str+= m_path + " ";
- str+= "HTTP/" + QString::number(m_majorVersion) + "." + QString::number(m_minorVersion) + "\r\n";
- str += HttpHeader::toString();
- return str;
-}
-
-bool HttpRequestHeader::parseVersions(const QString &str) {
- if (str.size() <= 5) // HTTP/ which means version missing
- return false;
-
- if (str.left(4) != "HTTP")
- return false;
-
- QString versions = str.right(str.size() - 5); // Strip "HTTP/"
-
- int decPoint = versions.indexOf('.');
- if (decPoint == -1)
- return false;
-
- bool ok;
-
- m_majorVersion = versions.left(decPoint).toInt(&ok);
- if (!ok)
- return false;
-
- m_minorVersion = versions.right(versions.size() - decPoint - 1).toInt(&ok);
- if (!ok)
- return false;
-
- return true;
-
-}
diff --git a/src/webui/httprequestheader.h b/src/webui/httprequestheader.h
deleted file mode 100644
index 0723eca60..000000000
--- a/src/webui/httprequestheader.h
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Bittorrent Client using Qt4 and libtorrent.
- * Copyright (C) 2014 sledgehammer999
- *
- * 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 : hammered999@gmail.com
- */
-
-#ifndef HTTPREQUESTHEADER_H
-#define HTTPREQUESTHEADER_H
-
-#include "httpheader.h"
-
-class HttpRequestHeader: public HttpHeader {
-public:
- HttpRequestHeader();
- // The path argument must be properly encoded for an HTTP request.
- HttpRequestHeader(const QString &method, const QString &path, int majorVer = 1, int minorVer = 1);
- HttpRequestHeader(const QString &str);
- virtual int majorVersion() const;
- virtual int minorVersion() const;
- QString method() const;
- // This is the raw path from the header. No decoding/encoding is performed.
- QString path() const;
- // The path argument must be properly encoded for an HTTP request.
- void setRequest(const QString &method, const QString &path, int majorVer = 1, int minorVer = 1);
- virtual QString toString() const;
-
-private:
- bool parseVersions(const QString &str);
- QString m_method;
- QString m_path;
- int m_majorVersion;
- int m_minorVersion;
-};
-
-#endif // HTTPREQUESTHEADER_H
diff --git a/src/webui/httprequestparser.cpp b/src/webui/httprequestparser.cpp
index 4f185c7b3..262e93123 100644
--- a/src/webui/httprequestparser.cpp
+++ b/src/webui/httprequestparser.cpp
@@ -1,5 +1,6 @@
/*
- * Bittorrent Client using Qt4 and libtorrent.
+ * Bittorrent Client using Qt and libtorrent.
+ * Copyright (C) 2014 Vladimir Golovnev
* Copyright (C) 2006 Ishan Arora and Christophe Dumez
*
* This program is free software; you can redistribute it and/or
@@ -24,110 +25,231 @@
* 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
#include
+//#include
#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
#include
#endif
+#include
+#include
#include
+#include "httprequestparser.h"
+
+const QByteArray EOL("\r\n");
+const QByteArray EOH("\r\n\r\n");
-HttpRequestParser::HttpRequestParser(): m_error(false)
+inline QString unquoted(const QString& str)
{
+ if ((str[0] == '\"') && (str[str.length() - 1] == '\"'))
+ return str.mid(1, str.length() - 2);
+
+ return str;
}
-HttpRequestParser::~HttpRequestParser()
+HttpRequestParser::ErrorCode HttpRequestParser::parse(const QByteArray& data, HttpRequest& request, uint maxContentLength)
{
+ return HttpRequestParser(maxContentLength).parseHttpRequest(data, request);
}
-bool HttpRequestParser::isError() const {
- return m_error;
+HttpRequestParser::HttpRequestParser(uint maxContentLength)
+ : maxContentLength_(maxContentLength)
+{
}
-const QString& HttpRequestParser::url() const {
- return m_path;
-}
+HttpRequestParser::ErrorCode HttpRequestParser::parseHttpRequest(const QByteArray& data, HttpRequest& request)
+{
+ request_ = HttpRequest();
-const QByteArray& HttpRequestParser::message() const {
- return m_data;
-}
+ // Parse HTTP request header
+ const int header_end = data.indexOf(EOH);
+ if (header_end < 0)
+ {
+ qDebug() << Q_FUNC_INFO << "incomplete request";
+ return IncompleteRequest;
+ }
-QString HttpRequestParser::get(const QString& key) const {
- return m_getMap.value(key);
-}
+ if (!parseHttpHeader(data.left(header_end)))
+ {
+ qWarning() << Q_FUNC_INFO << "header parsing error";
+ return BadRequest;
+ }
-QString HttpRequestParser::post(const QString& key) const {
- return m_postMap.value(key);
-}
+ // Parse HTTP request message
+ int content_length = 0;
+ if (request_.headers.contains("content-length"))
+ {
+ content_length = request_.headers["content-length"].toInt();
+ if (content_length > static_cast(maxContentLength_))
+ {
+ qWarning() << Q_FUNC_INFO << "bad request: message too long";
+ return BadRequest;
+ }
-const QList& HttpRequestParser::torrents() const {
- return m_torrents;
+ QByteArray content = data.mid(header_end + EOH.length(), content_length);
+ if (content.length() < content_length)
+ {
+ qDebug() << Q_FUNC_INFO << "incomplete request";
+ return IncompleteRequest;
+ }
+
+ if (!parseContent(content))
+ {
+ qWarning() << Q_FUNC_INFO << "message parsing error";
+ return BadRequest;
+ }
+ }
+
+// qDebug() << Q_FUNC_INFO;
+// qDebug() << "HTTP Request header:";
+// qDebug() << data.left(header_end) << "\n";
+
+ request = request_;
+ return NoError;
}
-void HttpRequestParser::writeHeader(const QByteArray& ba) {
- m_error = false;
- // Parse header
- m_header = HttpRequestHeader(ba);
- QUrl url = QUrl::fromEncoded(m_header.path().toLatin1());
- m_path = url.path();
+bool HttpRequestParser::parseStartingLine(const QString &line)
+{
+ const QRegExp rx("^([A-Z]+)\\s+(\\S+)\\s+HTTP/\\d\\.\\d$");
- // Parse GET parameters
-#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
- QUrlQuery query(url);
- QListIterator > i(query.queryItems());
+ if (rx.indexIn(line.trimmed()) >= 0)
+ {
+ request_.method = rx.cap(1);
+
+ QUrl url = QUrl::fromEncoded(rx.cap(2).toLatin1());
+ request_.path = url.path(); // Path
+
+ // Parse GET parameters
+#if (QT_VERSION < QT_VERSION_CHECK(5, 0, 0))
+ QListIterator > i(url.queryItems());
#else
- QListIterator > i(url.queryItems());
+ QListIterator > i(QUrlQuery(url).queryItems());
#endif
- while (i.hasNext()) {
- QPair pair = i.next();
- m_getMap[pair.first] = pair.second;
+ while (i.hasNext())
+ {
+ QPair pair = i.next();
+ request_.gets[pair.first] = pair.second;
+ }
+
+ return true;
+ }
+
+ qWarning() << Q_FUNC_INFO << "invalid http header:" << line;
+ return false;
+}
+
+bool HttpRequestParser::parseHeaderLine(const QString &line, QPair& out)
+{
+ int i = line.indexOf(QLatin1Char(':'));
+ if (i == -1)
+ {
+ qWarning() << Q_FUNC_INFO << "invalid http header:" << line;
+ return false;
+ }
+
+ out = qMakePair(line.left(i).trimmed().toLower(), line.mid(i + 1).trimmed());
+ return true;
+}
+
+bool HttpRequestParser::parseHttpHeader(const QByteArray &data)
+{
+ QString str = QString::fromUtf8(data);
+ QStringList lines = str.trimmed().split(EOL);
+
+ QStringList headerLines;
+ foreach (const QString& line, lines)
+ {
+ if (line[0].isSpace()) // header line continuation
+ {
+ if (!headerLines.isEmpty()) // really continuation
+ {
+ headerLines.last() += QLatin1Char(' ');
+ headerLines.last() += line.trimmed();
+ }
+ }
+ else
+ {
+ headerLines.append(line);
+ }
+ }
+
+ if (headerLines.isEmpty())
+ return false; // Empty header
+
+ QStringList::Iterator it = headerLines.begin();
+ if (!parseStartingLine(*it))
+ return false;
+
+ ++it;
+ for (; it != headerLines.end(); ++it)
+ {
+ QPair header;
+ if (!parseHeaderLine(*it, header))
+ return false;
+
+ request_.headers[header.first] = header.second;
}
+
+ return true;
}
-static QList splitRawData(QByteArray rawData, const QByteArray& sep)
+QList HttpRequestParser::splitMultipartData(const QByteArray& data, const QByteArray& boundary)
{
QList ret;
+ QByteArray sep = boundary + EOL;
const int sepLength = sep.size();
- int index = 0;
- while ((index = rawData.indexOf(sep)) >= 0) {
- ret << rawData.left(index);
- rawData = rawData.mid(index + sepLength);
+
+ int start = 0, end = 0;
+ if ((end = data.indexOf(sep, start)) >= 0)
+ {
+ start = end + sepLength; // skip first boundary
+
+ while ((end = data.indexOf(sep, start)) >= 0)
+ {
+ ret << data.mid(start, end - start);
+ start = end + sepLength;
+ }
+
+ // last or single part
+ sep = boundary + "--" + EOL;
+ if ((end = data.indexOf(sep, start)) >= 0)
+ ret << data.mid(start, end - start);
}
+
return ret;
}
-void HttpRequestParser::writeMessage(const QByteArray& ba) {
+bool HttpRequestParser::parseContent(const QByteArray& data)
+{
// Parse message content
- Q_ASSERT (m_header.hasContentLength());
- m_error = false;
- m_data = ba;
- qDebug() << Q_FUNC_INFO << "m_data.size(): " << m_data.size();
+ qDebug() << Q_FUNC_INFO << "Content-Length: " << request_.headers["content-length"];
+ qDebug() << Q_FUNC_INFO << "data.size(): " << data.size();
- // Parse POST data
- if (m_header.contentType() == "application/x-www-form-urlencoded") {
+ // Parse url-encoded POST data
+ if (request_.headers["content-type"].startsWith("application/x-www-form-urlencoded"))
+ {
QUrl url;
-#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
- QString tmp(m_data);
- QUrlQuery query(tmp);
- QListIterator > i(query.queryItems(QUrl::FullyDecoded));
-#else
- url.setEncodedQuery(m_data);
+#if (QT_VERSION < QT_VERSION_CHECK(5, 0, 0))
+ url.setEncodedQuery(data);
QListIterator > i(url.queryItems());
+#else
+ url.setQuery(data);
+ QListIterator > i(QUrlQuery(url).queryItems(QUrl::FullyDecoded));
#endif
- while (i.hasNext()) {
+ while (i.hasNext())
+ {
QPair pair = i.next();
- m_postMap[pair.first] = pair.second;
+ request_.posts[pair.first] = pair.second;
}
- return;
+
+ return true;
}
// Parse multipart/form data (torrent file)
/**
- m_data has the following format (if boundary is "cH2ae0GI3KM7GI3Ij5ae0ei4Ij5Ij5")
+ data has the following format (if boundary is "cH2ae0GI3KM7GI3Ij5ae0ei4Ij5Ij5")
--cH2ae0GI3KM7GI3Ij5ae0ei4Ij5Ij5
Content-Disposition: form-data; name=\"Filename\"
@@ -144,58 +266,108 @@ Content-Disposition: form-data; name=\"Upload\"
Submit Query
--cH2ae0GI3KM7GI3Ij5ae0ei4Ij5Ij5--
**/
- if (m_header.contentType().startsWith("multipart/form-data")) {
- qDebug() << Q_FUNC_INFO << "header is: " << m_header.toString();
- static QRegExp boundaryRegexQuoted("boundary=\"([ \\w'()+,-\\./:=\\?]+)\"");
- static QRegExp boundaryRegexNotQuoted("boundary=([\\w'()+,-\\./:=\\?]+)");
+ QString content_type = request_.headers["content-type"];
+ if (content_type.startsWith("multipart/form-data"))
+ {
+ const QRegExp boundaryRegexQuoted("boundary=\"([ \\w'()+,-\\./:=\\?]+)\"");
+ const QRegExp boundaryRegexNotQuoted("boundary=([\\w'()+,-\\./:=\\?]+)");
+
QByteArray boundary;
- if (boundaryRegexQuoted.indexIn(m_header.toString()) < 0) {
- if (boundaryRegexNotQuoted.indexIn(m_header.toString()) < 0) {
+ if (boundaryRegexQuoted.indexIn(content_type) < 0)
+ {
+ if (boundaryRegexNotQuoted.indexIn(content_type) < 0)
+ {
qWarning() << "Could not find boundary in multipart/form-data header!";
- m_error = true;
- return;
- } else {
+ return false;
+ }
+ else
+ {
boundary = "--" + boundaryRegexNotQuoted.cap(1).toLatin1();
}
- } else {
+ }
+ else
+ {
boundary = "--" + boundaryRegexQuoted.cap(1).toLatin1();
}
+
qDebug() << "Boundary is " << boundary;
- QList parts = splitRawData(m_data, boundary);
+ QList parts = splitMultipartData(data, boundary);
qDebug() << parts.size() << "parts in data";
- foreach (const QByteArray& part, parts) {
- const int filenameIndex = part.indexOf("filename=");
- if (filenameIndex < 0)
- continue;
- qDebug() << "Found a torrent";
- m_torrents << part.mid(part.indexOf("\r\n\r\n", filenameIndex + 9) + 4);
+
+ foreach (const QByteArray& part, parts)
+ {
+ if (!parseFormData(part))
+ return false;
}
+
+ return true;
}
-}
-bool HttpRequestParser::acceptsEncoding() {
- QString encoding = m_header.value("Accept-Encoding");
+ qWarning() << Q_FUNC_INFO << "unknown content type:" << qPrintable(content_type);
+ return false;
+}
- int pos = encoding.indexOf("gzip", 0, Qt::CaseInsensitive);
- if (pos == -1)
+bool HttpRequestParser::parseFormData(const QByteArray& data)
+{
+ // Parse form data header
+ const int header_end = data.indexOf(EOH);
+ if (header_end < 0)
+ {
+ qDebug() << "Invalid form data: \n" << data;
return false;
+ }
- // Let's see if there's a qvalue of 0.0 following
- if (encoding[pos+4] != ';') //there isn't, so it accepts gzip anyway
- return true;
-
- //So let's find = and the next comma
- pos = encoding.indexOf("=", pos+4, Qt::CaseInsensitive);
- int comma_pos = encoding.indexOf(",", pos, Qt::CaseInsensitive);
+ QString header_str = QString::fromUtf8(data.left(header_end));
+ QStringList lines = header_str.trimmed().split(EOL);
+ QStringMap headers;
+ foreach (const QString& line, lines)
+ {
+ QPair header;
+ if (!parseHeaderLine(line, header))
+ return false;
- QString value;
- if (comma_pos == -1)
- value = encoding.mid(pos+1, comma_pos);
- else
- value = encoding.mid(pos+1, comma_pos-(pos+1));
+ headers[header.first] = header.second;
+ }
- if (value.toDouble() == 0.0)
+ QStringMap disposition;
+ if (!headers.contains("content-disposition") ||
+ !parseHeaderValue(headers["content-disposition"], disposition) ||
+ !disposition.contains("name"))
+ {
+ qDebug() << "Invalid form data header: \n" << header_str;
return false;
+ }
+
+ if (disposition.contains("filename"))
+ {
+ UploadedFile ufile;
+ ufile.filename = disposition["filename"];
+ ufile.type = disposition["content-type"];
+ ufile.data = data.mid(header_end + EOH.length());
+
+ request_.files[disposition["name"]] = ufile;
+ }
else
- return true;
+ {
+ request_.posts[disposition["name"]] = QString::fromUtf8(data.mid(header_end + EOH.length()));
+ }
+
+ return true;
+}
+
+bool HttpRequestParser::parseHeaderValue(const QString& value, QStringMap& out)
+{
+ QStringList items = value.split(QLatin1Char(';'));
+ out[""] = items[0];
+
+ for (QStringList::size_type i = 1; i < items.size(); ++i)
+ {
+ int pos = items[i].indexOf("=");
+ if (pos < 0)
+ return false;
+
+ out[items[i].left(pos).trimmed()] = unquoted(items[i].mid(pos + 1).trimmed());
+ }
+
+ return true;
}
diff --git a/src/webui/httprequestparser.h b/src/webui/httprequestparser.h
index 180f3c1cf..55a21be36 100644
--- a/src/webui/httprequestparser.h
+++ b/src/webui/httprequestparser.h
@@ -1,5 +1,6 @@
/*
- * Bittorrent Client using Qt4 and libtorrent.
+ * Bittorrent Client using Qt and libtorrent.
+ * Copyright (C) 2014 Vladimir Golovnev
* Copyright (C) 2006 Ishan Arora and Christophe Dumez
*
* This program is free software; you can redistribute it and/or
@@ -24,41 +25,38 @@
* 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
-#include "httprequestheader.h"
-
-class HttpRequestParser {
+#include "httptypes.h"
+class HttpRequestParser
+{
public:
- HttpRequestParser();
- ~HttpRequestParser();
- bool isError() const;
- const QString& url() const;
- const QByteArray& message() const;
- QString get(const QString& key) const;
- QString post(const QString& key) const;
- const QList& torrents() const;
- void writeHeader(const QByteArray& ba);
- void writeMessage(const QByteArray& ba);
- bool acceptsEncoding();
- inline const HttpRequestHeader& header() const { return m_header; }
+ enum ErrorCode { NoError = 0, IncompleteRequest, BadRequest };
+
+ // when result != NoError parsed request is undefined
+ // Warning! Header names are converted to lower-case.
+ static ErrorCode parse(const QByteArray& data, HttpRequest& request, uint maxContentLength = 10000000 /* ~10MB */);
private:
- HttpRequestHeader m_header;
- bool m_error;
- QByteArray m_data;
- QString m_path;
- QHash m_postMap;
- QHash m_getMap;
- QList m_torrents;
+ HttpRequestParser(uint maxContentLength);
+
+ ErrorCode parseHttpRequest(const QByteArray& data, HttpRequest& request);
+
+ bool parseHttpHeader(const QByteArray& data);
+ bool parseStartingLine(const QString &line);
+ bool parseContent(const QByteArray& data);
+ bool parseFormData(const QByteArray& data);
+ QList splitMultipartData(const QByteArray& data, const QByteArray& boundary);
+
+ static bool parseHeaderLine(const QString& line, QPair& out);
+ static bool parseHeaderValue(const QString& value, QStringMap& out);
+
+ const uint maxContentLength_;
+ HttpRequest request_;
};
#endif
diff --git a/src/webui/httpresponsegenerator.cpp b/src/webui/httpresponsegenerator.cpp
index f5ace37fe..9ebb14d41 100644
--- a/src/webui/httpresponsegenerator.cpp
+++ b/src/webui/httpresponsegenerator.cpp
@@ -1,5 +1,6 @@
/*
- * Bittorrent Client using Qt4 and libtorrent.
+ * Bittorrent Client using Qt and libtorrent.
+ * Copyright (C) 2014 Vladimir Golovnev
* Copyright (C) 2006 Ishan Arora and Christophe Dumez
*
* This program is free software; you can redistribute it and/or
@@ -24,48 +25,52 @@
* 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"
#include
+#include "httpresponsegenerator.h"
-void HttpResponseGenerator::setMessage(const QByteArray& message)
-{
- m_message = message;
-}
+bool gCompress(QByteArray data, QByteArray& dest_buffer);
-void HttpResponseGenerator::setMessage(const QString& message)
+QByteArray HttpResponseGenerator::generate(HttpResponse response)
{
- setMessage(message.toUtf8());
-}
+ if (response.headers[HEADER_CONTENT_ENCODING] == "gzip")
+ {
+ // A gzip seems to have 23 bytes overhead.
+ // Also "Content-Encoding: gzip\r\n" is 26 bytes long
+ // So we only benefit from gzip if the message is bigger than 23+26 = 49
+ // If the message is smaller than 49 bytes we actually send MORE data if we gzip
+ QByteArray dest_buf;
+ if ((response.content.size() > 49) && (gCompress(response.content, dest_buf)))
+ {
+ response.content = dest_buf;
+ }
+ else
+ {
+ response.headers.remove(HEADER_CONTENT_ENCODING);
+ }
+ }
+
+ if (response.content.length() > 0)
+ response.headers[HEADER_CONTENT_LENGTH] = QString::number(response.content.length());
+
+ QString ret(QLatin1String("HTTP/1.1 %1 %2\r\n%3\r\n"));
+
+ QString header;
+ foreach (const QString& key, response.headers.keys())
+ header += QString("%1: %2\r\n").arg(key).arg(response.headers[key]);
-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/png");
- return;
- }
+ ret = ret.arg(response.status.code).arg(response.status.text).arg(header);
+
+// qDebug() << Q_FUNC_INFO;
+// qDebug() << "HTTP Response header:";
+// qDebug() << ret;
+
+ return ret.toUtf8() + response.content;
}
-bool HttpResponseGenerator::gCompress(QByteArray &dest_buffer) {
+bool gCompress(QByteArray data, QByteArray& dest_buffer)
+{
static const int BUFSIZE = 128 * 1024;
char tmp_buf[BUFSIZE];
int ret;
@@ -74,8 +79,8 @@ bool HttpResponseGenerator::gCompress(QByteArray &dest_buffer) {
strm.zalloc = Z_NULL;
strm.zfree = Z_NULL;
strm.opaque = Z_NULL;
- strm.next_in = reinterpret_cast(m_message.data());
- strm.avail_in = m_message.length();
+ strm.next_in = reinterpret_cast(data.data());
+ strm.avail_in = data.length();
strm.next_out = reinterpret_cast(tmp_buf);
strm.avail_out = BUFSIZE;
@@ -88,53 +93,37 @@ bool HttpResponseGenerator::gCompress(QByteArray &dest_buffer) {
return false;
while (strm.avail_in != 0)
- {
+ {
ret = deflate(&strm, Z_NO_FLUSH);
if (ret != Z_OK)
return false;
+
if (strm.avail_out == 0)
{
- dest_buffer.append(tmp_buf, BUFSIZE);
- strm.next_out = reinterpret_cast(tmp_buf);
- strm.avail_out = BUFSIZE;
+ dest_buffer.append(tmp_buf, BUFSIZE);
+ strm.next_out = reinterpret_cast(tmp_buf);
+ strm.avail_out = BUFSIZE;
}
- }
+ }
int deflate_res = Z_OK;
- while (deflate_res == Z_OK) {
- if (strm.avail_out == 0) {
+ while (deflate_res == Z_OK)
+ {
+ if (strm.avail_out == 0)
+ {
dest_buffer.append(tmp_buf, BUFSIZE);
strm.next_out = reinterpret_cast(tmp_buf);
strm.avail_out = BUFSIZE;
}
+
deflate_res = deflate(&strm, Z_FINISH);
}
if (deflate_res != Z_STREAM_END)
return false;
+
dest_buffer.append(tmp_buf, BUFSIZE - strm.avail_out);
deflateEnd(&strm);
return true;
}
-
-QByteArray HttpResponseGenerator::toByteArray() {
- // A gzip seems to have 23 bytes overhead.
- // Also "content-encoding: gzip\r\n" is 26 bytes long
- // So we only benefit from gzip if the message is bigger than 23+26 = 49
- // If the message is smaller than 49 bytes we actually send MORE data if we gzip
- if (m_gzip && m_message.size() > 49) {
- QByteArray dest_buf;
- if (gCompress(dest_buf)) {
- setValue("content-encoding", "gzip");
-#if QT_VERSION < 0x040800
- m_message = dest_buf;
-#else
- m_message.swap(dest_buf);
-#endif
- }
- }
-
- setContentLength(m_message.size());
- return HttpResponseHeader::toString().toUtf8() + m_message;
-}
diff --git a/src/webui/httpresponsegenerator.h b/src/webui/httpresponsegenerator.h
index 4d8525a26..057016589 100644
--- a/src/webui/httpresponsegenerator.h
+++ b/src/webui/httpresponsegenerator.h
@@ -1,5 +1,6 @@
/*
- * Bittorrent Client using Qt4 and libtorrent.
+ * Bittorrent Client using Qt and libtorrent.
+ * Copyright (C) 2014 Vladimir Golovnev
* Copyright (C) 2006 Ishan Arora and Christophe Dumez
*
* This program is free software; you can redistribute it and/or
@@ -24,32 +25,18 @@
* 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 "httpresponseheader.h"
+#include "httptypes.h"
-class HttpResponseGenerator : public HttpResponseHeader
+class HttpResponseGenerator
{
-
public:
- HttpResponseGenerator(): m_gzip(false) {}
- void setMessage(const QByteArray& message);
- void setMessage(const QString& message);
- void setContentTypeByExt(const QString& ext);
- void setContentEncoding(bool gzip) { m_gzip = gzip; }
- QByteArray toByteArray();
-
-private:
- bool gCompress(QByteArray &dest_buffer);
- QByteArray m_message;
- bool m_gzip;
-
+ static QByteArray generate(HttpResponse response);
};
#endif
diff --git a/src/webui/httpresponseheader.cpp b/src/webui/httpresponseheader.cpp
deleted file mode 100644
index f95abc20d..000000000
--- a/src/webui/httpresponseheader.cpp
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- * Bittorrent Client using Qt4 and libtorrent.
- * Copyright (C) 2014 sledgehammer999
- *
- * 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 : hammered999@gmail.com
- */
-
-#include "httpresponseheader.h"
-
-HttpResponseHeader::HttpResponseHeader(): HttpHeader(), m_code(200), m_majorVersion(1), m_minorVersion(1) {}
-
-HttpResponseHeader::HttpResponseHeader(int code, const QString &text, int majorVer, int minorVer):
- HttpHeader(), m_code(code), m_text(text), m_majorVersion(majorVer), m_minorVersion(minorVer) {}
-
-HttpResponseHeader::HttpResponseHeader(const QString &str): HttpHeader() {
- int line = str.indexOf("\r\n", 0, Qt::CaseInsensitive);
- QString res = str.left(line);
- QString headers = str.right(str.size() - line - 2); //"\r\n" == 2
- QStringList status = res.split(" ", QString::SkipEmptyParts, Qt::CaseInsensitive);
- if (status.size() != 3 || status.size() != 2) //reason-phrase could be empty
- setValid(false);
- else {
- if (parseVersions(status[0])) {
- bool ok;
- m_code = status[1].toInt(&ok);
- if (ok) {
- m_text = status[2];
- parse(headers);
- }
- else
- setValid(false);
- }
- else
- setValid(false);
- }
-}
-
-int HttpResponseHeader::majorVersion() const {
- return m_majorVersion;
-}
-
-int HttpResponseHeader::minorVersion() const {
- return m_minorVersion;
-}
-
-QString HttpResponseHeader::reasonPhrase() const {
- return m_text;
-}
-
-void HttpResponseHeader::setStatusLine(int code, const QString &text, int majorVer, int minorVer) {
- m_code = code;
- m_text = text;
- m_majorVersion = majorVer;
- m_minorVersion = minorVer;
-}
-
-int HttpResponseHeader::statusCode() const {
- return m_code;
-}
-
-QString HttpResponseHeader::toString() const {
- QString str = "HTTP/" + QString::number(m_majorVersion) + "." + QString::number(m_minorVersion) + " ";
-
- QString code = QString::number(m_code);
- if (code.size() > 3) {
- str+= code.left(3) + " ";
- }
- else if (code.size() < 3) {
- int padding = 3 - code.size();
- for (int i = 0; i < padding; ++i)
- code.push_back("0");
- str += code + " ";
- }
- else {
- str += code + " ";
- }
-
- str += m_text + "\r\n";
- str += HttpHeader::toString();
- return str;
-}
-
-bool HttpResponseHeader::parseVersions(const QString &str) {
- if (str.size() <= 5) // HTTP/ which means version missing
- return false;
-
- if (str.left(4) != "HTTP")
- return false;
-
- QString versions = str.right(str.size() - 5); // Strip "HTTP/"
-
- int decPoint = versions.indexOf('.');
- if (decPoint == -1)
- return false;
-
- bool ok;
-
- m_majorVersion = versions.left(decPoint).toInt(&ok);
- if (!ok)
- return false;
-
- m_minorVersion = versions.right(versions.size() - decPoint - 1).toInt(&ok);
- if (!ok)
- return false;
-
- return true;
-
-}
diff --git a/src/webui/httpresponseheader.h b/src/webui/httpresponseheader.h
deleted file mode 100644
index 4a54ef5a9..000000000
--- a/src/webui/httpresponseheader.h
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Bittorrent Client using Qt4 and libtorrent.
- * Copyright (C) 2014 sledgehammer999
- *
- * 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 : hammered999@gmail.com
- */
-
-#ifndef HTTPRESPONSEHEADER_H
-#define HTTPRESPONSEHEADER_H
-
-#include "httpheader.h"
-
-class HttpResponseHeader: public HttpHeader {
-public:
- HttpResponseHeader();
- HttpResponseHeader(int code, const QString &text = QString(), int majorVer = 1, int minorVer = 1);
- HttpResponseHeader(const QString &str);
- virtual int majorVersion() const;
- virtual int minorVersion() const;
- QString reasonPhrase() const;
- void setStatusLine(int code, const QString &text = QString(), int majorVer = 1, int minorVer = 1);
- int statusCode() const;
- virtual QString toString() const;
-
-private:
- bool parseVersions(const QString &str);
- int m_code;
- QString m_text;
- int m_majorVersion;
- int m_minorVersion;
-};
-
-#endif // HTTPRESPONSEHEADER_H
diff --git a/src/webui/httpserver.cpp b/src/webui/httpserver.cpp
index 7aadb8905..c37c5301c 100644
--- a/src/webui/httpserver.cpp
+++ b/src/webui/httpserver.cpp
@@ -1,6 +1,8 @@
/*
- * Bittorrent Client using Qt4 and libtorrent.
- * Copyright (C) 2006 Ishan Arora and Christophe Dumez
+ * Bittorrent Client using Qt and libtorrent.
+ * Copyright (C) 2014 Vladimir Golovnev
+ * Copyright (C) 2006 Christophe Dumez
+ * Copyright (C) 2006 Ishan Arora
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -24,132 +26,38 @@
* 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 "qbtsession.h"
-#include
-#include
-#include
-#include
-
#ifndef QT_NO_OPENSSL
#include
#else
#include
#endif
+#include "httpconnection.h"
+#include "httpserver.h"
-using namespace libtorrent;
-
-const int BAN_TIME = 3600000; // 1 hour
-
-class UnbanTimer: public QTimer {
-public:
- UnbanTimer(const QString& peer_ip, QObject *parent): QTimer(parent),
- m_peerIp(peer_ip) {
- setSingleShot(true);
- setInterval(BAN_TIME);
- }
-
- inline const QString& peerIp() const { return m_peerIp; }
-
-private:
- QString m_peerIp;
-};
-
-void HttpServer::UnbanTimerEvent() {
- UnbanTimer* ubantimer = static_cast(sender());
- qDebug("Ban period has expired for %s", qPrintable(ubantimer->peerIp()));
- m_clientFailedAttempts.remove(ubantimer->peerIp());
- ubantimer->deleteLater();
-}
-
-int HttpServer::NbFailedAttemptsForIp(const QString& ip) const {
- return m_clientFailedAttempts.value(ip, 0);
-}
-
-void HttpServer::increaseNbFailedAttemptsForIp(const QString& ip) {
- const int nb_fail = m_clientFailedAttempts.value(ip, 0) + 1;
- m_clientFailedAttempts.insert(ip, nb_fail);
- if (nb_fail == MAX_AUTH_FAILED_ATTEMPTS) {
- // Max number of failed attempts reached
- // Start ban period
- UnbanTimer* ubantimer = new UnbanTimer(ip, this);
- connect(ubantimer, SIGNAL(timeout()), SLOT(UnbanTimerEvent()));
- ubantimer->start();
- }
-}
-
-void HttpServer::resetNbFailedAttemptsForIp(const QString& ip) {
- m_clientFailedAttempts.remove(ip);
-}
-
-HttpServer::HttpServer(QObject* parent) : QTcpServer(parent)
-{
-
- const Preferences* const pref = Preferences::instance();
-
- m_username = pref->getWebUiUsername().toUtf8();
- m_passwordSha1 = pref->getWebUiPassword().toUtf8();
- m_localAuthEnabled = pref->isWebUiLocalAuthEnabled();
-
- // HTTPS-related
+HttpServer::HttpServer(QObject* parent)
+ : QTcpServer(parent)
#ifndef QT_NO_OPENSSL
- m_https = pref->isWebUiHttpsEnabled();
- if (m_https) {
- m_certificate = QSslCertificate(pref->getWebUiHttpsCertificate());
- m_key = QSslKey(pref->getWebUiHttpsKey(), QSsl::Rsa);
- }
+ , m_https(false)
#endif
-
- // Additional translations for Web UI
- QString a = tr("File");
- a = tr("Edit");
- a = tr("Help");
- 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.");
- a = tr("Save");
- a = tr("qBittorrent client is not reachable");
- a = tr("HTTP Server");
- a = tr("The following parameters are supported:");
- a = tr("Torrent path");
- a = tr("Torrent name");
- a = tr("qBittorrent has been shutdown.");
+{
}
-HttpServer::~HttpServer() {
+HttpServer::~HttpServer()
+{
}
#ifndef QT_NO_OPENSSL
-void HttpServer::enableHttps(const QSslCertificate &certificate,
- const QSslKey &key) {
+void HttpServer::enableHttps(const QSslCertificate &certificate, const QSslKey &key)
+{
m_certificate = certificate;
m_key = key;
m_https = true;
}
-void HttpServer::disableHttps() {
+void HttpServer::disableHttps()
+{
m_https = false;
m_certificate.clear();
m_key.clear();
@@ -169,143 +77,21 @@ void HttpServer::incomingConnection(int socketDescriptor)
else
#endif
serverSocket = new QTcpSocket(this);
- if (serverSocket->setSocketDescriptor(socketDescriptor)) {
+ if (serverSocket->setSocketDescriptor(socketDescriptor))
+ {
#ifndef QT_NO_OPENSSL
- if (m_https) {
+ if (m_https)
+ {
static_cast(serverSocket)->setProtocol(QSsl::AnyProtocol);
static_cast(serverSocket)->setPrivateKey(m_key);
static_cast(serverSocket)->setLocalCertificate(m_certificate);
static_cast(serverSocket)->startServerEncryption();
}
#endif
- handleNewConnection(serverSocket);
- } else {
- serverSocket->deleteLater();
- }
-}
-
-void HttpServer::handleNewConnection(QTcpSocket *socket)
-{
- HttpConnection *connection = new HttpConnection(socket, this);
- //connect connection to QBtSession::instance()
- connect(connection, SIGNAL(UrlReadyToBeDownloaded(QString)), QBtSession::instance(), SLOT(downloadUrlAndSkipDialog(QString)));
- connect(connection, SIGNAL(MagnetReadyToBeDownloaded(QString)), QBtSession::instance(), SLOT(addMagnetSkipAddDlg(QString)));
- connect(connection, SIGNAL(torrentReadyToBeDownloaded(QString, bool, QString, bool)), QBtSession::instance(), SLOT(addTorrent(QString, bool, QString, bool)));
- connect(connection, SIGNAL(deleteTorrent(QString, bool)), QBtSession::instance(), SLOT(deleteTorrent(QString, bool)));
- connect(connection, SIGNAL(pauseTorrent(QString)), QBtSession::instance(), SLOT(pauseTorrent(QString)));
- connect(connection, SIGNAL(resumeTorrent(QString)), QBtSession::instance(), SLOT(resumeTorrent(QString)));
- connect(connection, SIGNAL(pauseAllTorrents()), QBtSession::instance(), SLOT(pauseAllTorrents()));
- connect(connection, SIGNAL(resumeAllTorrents()), QBtSession::instance(), SLOT(resumeAllTorrents()));
-}
-
-QString HttpServer::generateNonce() const {
- QCryptographicHash md5(QCryptographicHash::Md5);
- md5.addData(QTime::currentTime().toString("hhmmsszzz").toUtf8());
- md5.addData(":");
- md5.addData(QBT_REALM);
- return md5.result().toHex();
-}
-
-void HttpServer::setAuthorization(const QString& username,
- const QString& password_sha1) {
- m_username = username.toUtf8();
- m_passwordSha1 = password_sha1.toUtf8();
-}
-
-// Parse HTTP AUTH string
-// http://tools.ietf.org/html/rfc2617
-bool HttpServer::isAuthorized(const QByteArray& auth,
- const 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", qPrintable(prop_user), username.data());
- if (prop_user != m_username) {
- // User name is invalid, we can reject already
- qDebug("AUTH-PROB: Username is invalid");
- return false;
+ new HttpConnection(serverSocket, this);
}
- // 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).toUtf8();
- 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).toUtf8();
- //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).toUtf8();
- //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).toUtf8();
- //qDebug("prop response is: %s", prop_response.data());
- // Compute correct reponse
- QCryptographicHash md5_ha2(QCryptographicHash::Md5);
- md5_ha2.addData(method.toUtf8() + ":" + 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).toUtf8();
- //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).toUtf8();
- //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).toUtf8();
- //qDebug("prop qop is: %s", prop_qop.data());
- md5_ha.addData(m_passwordSha1+":"+prop_nonce+":"+prop_nc+":"+prop_cnonce+":"+prop_qop+":"+ha2);
- response = md5_ha.result().toHex();
- } else {
- QCryptographicHash md5_ha(QCryptographicHash::Md5);
- md5_ha.addData(m_passwordSha1+":"+prop_nonce+":"+ha2);
- response = md5_ha.result().toHex();
+ else
+ {
+ serverSocket->deleteLater();
}
- //qDebug("AUTH: comparing reponses: (%d)", static_cast(prop_response == response));
- return prop_response == response;
-}
-
-void HttpServer::setlocalAuthEnabled(bool enabled) {
- m_localAuthEnabled = enabled;
-}
-
-bool HttpServer::isLocalAuthEnabled() const {
- return m_localAuthEnabled;
}
diff --git a/src/webui/httpserver.h b/src/webui/httpserver.h
index d596d090f..170cceec8 100644
--- a/src/webui/httpserver.h
+++ b/src/webui/httpserver.h
@@ -1,6 +1,7 @@
/*
- * Bittorrent Client using Qt4 and libtorrent.
- * Copyright (C) 2006 Ishan Arora and Christophe Dumez
+ * Bittorrent Client using Qt and libtorrent.
+ * Copyright (C) 2014 Vladimir Golovnev
+ * 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
@@ -24,50 +25,26 @@
* 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
#include
-#include
-#include
-#include
-
#ifndef QT_NO_OPENSSL
#include
#include
#endif
-#include "preferences.h"
-
-class EventManager;
-
-QT_BEGIN_NAMESPACE
-class QTimer;
-QT_END_NAMESPACE
-
-const int MAX_AUTH_FAILED_ATTEMPTS = 5;
-
-class HttpServer : public QTcpServer {
+class HttpServer : public QTcpServer
+{
Q_OBJECT
Q_DISABLE_COPY(HttpServer)
public:
HttpServer(QObject* parent = 0);
~HttpServer();
- void setAuthorization(const QString& username, const QString& password_sha1);
- bool isAuthorized(const QByteArray& auth, const QString& method) const;
- void setlocalAuthEnabled(bool enabled);
- bool isLocalAuthEnabled() const;
- QString generateNonce() const;
- int NbFailedAttemptsForIp(const QString& ip) const;
- void increaseNbFailedAttemptsForIp(const QString& ip);
- void resetNbFailedAttemptsForIp(const QString& ip);
#ifndef QT_NO_OPENSSL
void enableHttps(const QSslCertificate &certificate, const QSslKey &key);
@@ -81,17 +58,7 @@ private:
void incomingConnection(int socketDescriptor);
#endif
-private slots:
- void UnbanTimerEvent();
-
-private:
- void handleNewConnection(QTcpSocket *socket);
-
private:
- QByteArray m_username;
- QByteArray m_passwordSha1;
- QHash m_clientFailedAttempts;
- bool m_localAuthEnabled;
#ifndef QT_NO_OPENSSL
bool m_https;
QSslCertificate m_certificate;
diff --git a/src/webui/httptypes.h b/src/webui/httptypes.h
new file mode 100644
index 000000000..614e9a4d2
--- /dev/null
+++ b/src/webui/httptypes.h
@@ -0,0 +1,89 @@
+/*
+ * Bittorrent Client using Qt and libtorrent.
+ * Copyright (C) 2014 Vladimir Golovnev
+ *
+ * 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.
+ */
+
+#ifndef HTTPTYPES_H
+#define HTTPTYPES_H
+
+#include
+#include
+#include
+
+typedef QMap QStringMap;
+
+const QString HEADER_SET_COOKIE = "Set-Cookie";
+const QString HEADER_CONTENT_TYPE = "Content-Type";
+const QString HEADER_CONTENT_ENCODING = "Content-Encoding";
+const QString HEADER_CONTENT_LENGTH = "Content-Length";
+
+const QString CONTENT_TYPE_CSS = "text/css";
+const QString CONTENT_TYPE_GIF = "image/gif";
+const QString CONTENT_TYPE_HTML = "text/html";
+const QString CONTENT_TYPE_JS = "text/javascript";
+const QString CONTENT_TYPE_PNG = "image/png";
+const QString CONTENT_TYPE_TXT = "text/plain";
+
+struct HttpEnvironment
+{
+ QHostAddress clientAddress;
+};
+
+struct UploadedFile
+{
+ QString filename; // original filename
+ QString type; // MIME type
+ QByteArray data; // File data
+};
+
+struct HttpRequest
+{
+ QString method;
+ QString path;
+ QStringMap headers;
+ QStringMap gets;
+ QStringMap posts;
+ QMap files;
+};
+
+struct HttpResponseStatus
+{
+ uint code;
+ QString text;
+
+ HttpResponseStatus(uint code = 200, const QString& text = "OK"): code(code), text(text) {}
+};
+
+struct HttpResponse
+{
+ HttpResponseStatus status;
+ QStringMap headers;
+ QByteArray content;
+
+ HttpResponse(uint code = 200, const QString& text = "OK"): status(code, text) {}
+};
+
+#endif // HTTPTYPES_H
diff --git a/src/webui/jsonutils.h b/src/webui/jsonutils.h
index b08cfb0d8..5668b356b 100644
--- a/src/webui/jsonutils.h
+++ b/src/webui/jsonutils.h
@@ -1,6 +1,6 @@
/*
* Bittorrent Client using Qt and libtorrent.
- * Copyright (C) 2014 Vladimir Golovnev
+ * Copyright (C) 2014 Vladimir Golovnev
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -24,8 +24,6 @@
* 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 : glassez@yandex.ru
*/
#ifndef JSONUTILS_H
diff --git a/src/webui/requesthandler.cpp b/src/webui/requesthandler.cpp
new file mode 100644
index 000000000..26633bd4d
--- /dev/null
+++ b/src/webui/requesthandler.cpp
@@ -0,0 +1,588 @@
+/*
+ * Bittorrent Client using Qt and libtorrent.
+ * Copyright (C) 2014 Vladimir Golovnev
+ * Copyright (C) 2012, 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.
+ */
+
+#include
+#ifdef DISABLE_GUI
+#include
+#else
+#include
+#endif
+#include
+#include
+#include
+#include
+#include
+#ifndef DISABLE_GUI
+#include "iconprovider.h"
+#endif
+#include "misc.h"
+#include "fs_utils.h"
+#include "preferences.h"
+#include "btjson.h"
+#include "prefjson.h"
+#include "qbtsession.h"
+#include "requesthandler.h"
+
+using namespace libtorrent;
+
+const QString WWW_FOLDER = ":/www/public/";
+const QString PRIVATE_FOLDER = ":/www/private/";
+const QString DEFAULT_SCOPE = "public";
+const QString SCOPE_IMAGES = "images";
+const QString SCOPE_THEME = "theme";
+const QString DEFAULT_ACTION = "index";
+const QString WEBUI_ACTION = "webui";
+
+#define ADD_ACTION(scope, action) actions[#scope][#action] = &RequestHandler::action_##scope##_##action
+
+QMap > RequestHandler::initializeActions()
+{
+ QMap > actions;
+
+ ADD_ACTION(public, webui);
+ ADD_ACTION(public, index);
+ ADD_ACTION(public, login);
+ ADD_ACTION(public, logout);
+ ADD_ACTION(public, theme);
+ ADD_ACTION(public, images);
+ ADD_ACTION(json, torrents);
+ ADD_ACTION(json, preferences);
+ ADD_ACTION(json, transferInfo);
+ ADD_ACTION(json, propertiesGeneral);
+ ADD_ACTION(json, propertiesTrackers);
+ ADD_ACTION(json, propertiesFiles);
+ ADD_ACTION(command, shutdown);
+ ADD_ACTION(command, download);
+ ADD_ACTION(command, upload);
+ ADD_ACTION(command, addTrackers);
+ ADD_ACTION(command, resumeAll);
+ ADD_ACTION(command, pauseAll);
+ ADD_ACTION(command, resume);
+ ADD_ACTION(command, pause);
+ ADD_ACTION(command, setPreferences);
+ ADD_ACTION(command, setFilePrio);
+ ADD_ACTION(command, getGlobalUpLimit);
+ ADD_ACTION(command, getGlobalDlLimit);
+ ADD_ACTION(command, setGlobalUpLimit);
+ ADD_ACTION(command, setGlobalDlLimit);
+ ADD_ACTION(command, getTorrentUpLimit);
+ ADD_ACTION(command, getTorrentDlLimit);
+ ADD_ACTION(command, setTorrentUpLimit);
+ ADD_ACTION(command, setTorrentDlLimit);
+ ADD_ACTION(command, delete);
+ ADD_ACTION(command, deletePerm);
+ ADD_ACTION(command, increasePrio);
+ ADD_ACTION(command, decreasePrio);
+ ADD_ACTION(command, topPrio);
+ ADD_ACTION(command, bottomPrio);
+ ADD_ACTION(command, recheck);
+
+ return actions;
+}
+
+void RequestHandler::action_public_index()
+{
+ QString path;
+ if (!args_.isEmpty())
+ {
+ if (args_.back() == "favicon.ico")
+ path = ":/Icons/skin/qbittorrent16.png";
+ else
+ path = WWW_FOLDER + args_.join("/");
+ }
+
+ printFile(path);
+}
+
+void RequestHandler::action_public_webui()
+{
+ if (!sessionActive())
+ printFile(PRIVATE_FOLDER + "login.html");
+ else
+ printFile(PRIVATE_FOLDER + "index.html");
+}
+
+void RequestHandler::action_public_login()
+{
+ const Preferences* const pref = Preferences::instance();
+ QCryptographicHash md5(QCryptographicHash::Md5);
+
+ md5.addData(request().posts["password"].toLocal8Bit());
+ QString pass = md5.result().toHex();
+
+ if ((request().posts["username"] == pref->getWebUiUsername()) && (pass == pref->getWebUiPassword()))
+ {
+ sessionStart();
+ print(QByteArray("Ok."), CONTENT_TYPE_TXT);
+ }
+ else
+ {
+ QString addr = env().clientAddress.toString();
+ increaseFailedAttempts();
+ qDebug("client IP: %s (%d failed attempts)", qPrintable(addr), failedAttempts());
+ print(QByteArray("Fails."), CONTENT_TYPE_TXT);
+ }
+}
+
+void RequestHandler::action_public_logout()
+{
+ sessionEnd();
+}
+
+void RequestHandler::action_public_theme()
+{
+ if (args_.size() != 1)
+ {
+ status(404, "Not Found");
+ return;
+ }
+
+#ifdef DISABLE_GUI
+ QString url = ":/Icons/oxygen/" + args_.front() + ".png";
+#else
+ QString url = IconProvider::instance()->getIconPath(args_.front());
+#endif
+ qDebug() << Q_FUNC_INFO << "There icon:" << url;
+
+ printFile(url);
+}
+
+void RequestHandler::action_public_images()
+{
+ const QString path = ":/Icons/" + args_.join("/");
+ printFile(path);
+}
+
+void RequestHandler::action_json_torrents()
+{
+ print(btjson::getTorrents(), CONTENT_TYPE_JS);
+}
+
+void RequestHandler::action_json_preferences()
+{
+ print(prefjson::getPreferences(), CONTENT_TYPE_JS);
+}
+
+void RequestHandler::action_json_transferInfo()
+{
+ print(btjson::getTransferInfo(), CONTENT_TYPE_JS);
+}
+
+void RequestHandler::action_json_propertiesGeneral()
+{
+ print(btjson::getPropertiesForTorrent(args_.front()), CONTENT_TYPE_JS);
+}
+
+void RequestHandler::action_json_propertiesTrackers()
+{
+ print(btjson::getTrackersForTorrent(args_.front()), CONTENT_TYPE_JS);
+}
+
+void RequestHandler::action_json_propertiesFiles()
+{
+ print(btjson::getFilesForTorrent(args_.front()), CONTENT_TYPE_JS);
+}
+
+void RequestHandler::action_command_shutdown()
+{
+ qDebug() << "Shutdown request from Web UI";
+ // Special case handling for shutdown, we
+ // need to reply to the Web UI before
+ // actually shutting down.
+
+ QTimer::singleShot(0, qApp, SLOT(quit()));
+}
+
+void RequestHandler::action_command_download()
+{
+ QString urls = request().posts["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);
+ }
+ else if (url.startsWith("magnet:", Qt::CaseInsensitive))
+ {
+ QBtSession::instance()->addMagnetSkipAddDlg(url);
+ }
+ else
+ {
+ qDebug("Downloading url: %s", qPrintable(url));
+ QBtSession::instance()->downloadUrlAndSkipDialog(url);
+ }
+ }
+ }
+}
+
+void RequestHandler::action_command_upload()
+{
+ qDebug() << Q_FUNC_INFO;
+
+ foreach(const UploadedFile& torrent, request().files)
+ {
+ QString filePath = saveTmpFile(torrent.data);
+
+ if (!filePath.isEmpty())
+ {
+ QBtSession::instance()->addTorrent(filePath);
+ // Clean up
+ fsutils::forceRemove(filePath);
+ print(QLatin1String(""));
+ }
+ else
+ {
+ qWarning() << "I/O Error: Could not create temporary file";
+ }
+ }
+}
+
+void RequestHandler::action_command_addTrackers()
+{
+ QString hash = request().posts["hash"];
+
+ if (!hash.isEmpty())
+ {
+ QTorrentHandle h = QBtSession::instance()->getTorrentHandle(hash);
+
+ if (h.is_valid() && h.has_metadata())
+ {
+ QString urls = request().posts["urls"];
+ QStringList list = urls.split('\n');
+
+ foreach (const QString& url, list)
+ {
+ announce_entry e(url.toStdString());
+ h.add_tracker(e);
+ }
+ }
+ }
+}
+
+void RequestHandler::action_command_resumeAll()
+{
+ QBtSession::instance()->resumeAllTorrents();
+}
+
+void RequestHandler::action_command_pauseAll()
+{
+ QBtSession::instance()->pauseAllTorrents();
+}
+
+void RequestHandler::action_command_resume()
+{
+ QBtSession::instance()->resumeTorrent(request().posts["hash"]);
+}
+
+void RequestHandler::action_command_pause()
+{
+ QBtSession::instance()->pauseTorrent(request().posts["hash"]);
+}
+
+void RequestHandler::action_command_setPreferences()
+{
+ prefjson::setPreferences(request().posts["json"]);
+}
+
+void RequestHandler::action_command_setFilePrio()
+{
+ QString hash = request().posts["hash"];
+ int file_id = request().posts["id"].toInt();
+ int priority = request().posts["priority"].toInt();
+ QTorrentHandle h = QBtSession::instance()->getTorrentHandle(hash);
+
+ if (h.is_valid() && h.has_metadata())
+ {
+ h.file_priority(file_id, priority);
+ }
+}
+
+void RequestHandler::action_command_getGlobalUpLimit()
+{
+ print(QByteArray::number(QBtSession::instance()->getSession()->settings().upload_rate_limit));
+}
+
+void RequestHandler::action_command_getGlobalDlLimit()
+{
+ print(QByteArray::number(QBtSession::instance()->getSession()->settings().download_rate_limit));
+}
+
+void RequestHandler::action_command_setGlobalUpLimit()
+{
+ qlonglong limit = request().posts["limit"].toLongLong();
+ if (limit == 0) limit = -1;
+
+ QBtSession::instance()->setUploadRateLimit(limit);
+ Preferences::instance()->setGlobalUploadLimit(limit/1024.);
+}
+
+void RequestHandler::action_command_setGlobalDlLimit()
+{
+ qlonglong limit = request().posts["limit"].toLongLong();
+ if (limit == 0) limit = -1;
+
+ QBtSession::instance()->setDownloadRateLimit(limit);
+ Preferences::instance()->setGlobalDownloadLimit(limit/1024.);
+}
+
+void RequestHandler::action_command_getTorrentUpLimit()
+{
+ QString hash = request().posts["hash"];
+ QTorrentHandle h = QBtSession::instance()->getTorrentHandle(hash);
+
+ if (h.is_valid())
+ {
+ print(QByteArray::number(h.upload_limit()));
+ }
+}
+
+void RequestHandler::action_command_getTorrentDlLimit()
+{
+ QString hash = request().posts["hash"];
+ QTorrentHandle h = QBtSession::instance()->getTorrentHandle(hash);
+
+ if (h.is_valid())
+ {
+ print(QByteArray::number(h.download_limit()));
+ }
+}
+
+void RequestHandler::action_command_setTorrentUpLimit()
+{
+ QString hash = request().posts["hash"];
+ qlonglong limit = request().posts["limit"].toLongLong();
+ if (limit == 0) limit = -1;
+ QTorrentHandle h = QBtSession::instance()->getTorrentHandle(hash);
+
+ if (h.is_valid())
+ {
+ h.set_upload_limit(limit);
+ }
+}
+
+void RequestHandler::action_command_setTorrentDlLimit()
+{
+ QString hash = request().posts["hash"];
+ qlonglong limit = request().posts["limit"].toLongLong();
+ if (limit == 0) limit = -1;
+ QTorrentHandle h = QBtSession::instance()->getTorrentHandle(hash);
+
+ if (h.is_valid())
+ {
+ h.set_download_limit(limit);
+ }
+}
+
+void RequestHandler::action_command_delete()
+{
+ QStringList hashes = request().posts["hashes"].split("|");
+
+ foreach (const QString &hash, hashes)
+ {
+ QBtSession::instance()->deleteTorrent(hash, false);
+ }
+}
+
+void RequestHandler::action_command_deletePerm()
+{
+ QStringList hashes = request().posts["hashes"].split("|");
+
+ foreach (const QString &hash, hashes)
+ {
+ QBtSession::instance()->deleteTorrent(hash, true);
+ }
+}
+
+void RequestHandler::action_command_increasePrio()
+{
+ QStringList hashes = request().posts["hashes"].split("|");
+ std::priority_queue,
+ std::vector >,
+ std::greater > > torrent_queue;
+
+ // Sort torrents by priority
+ foreach (const QString &hash, hashes)
+ {
+ try
+ {
+ QTorrentHandle h = QBtSession::instance()->getTorrentHandle(hash);
+ if (!h.is_seed())
+ {
+ torrent_queue.push(qMakePair(h.queue_position(), h));
+ }
+ }
+ catch(invalid_handle&) {}
+ }
+
+ // Increase torrents priority (starting with the ones with highest priority)
+ while(!torrent_queue.empty())
+ {
+ QTorrentHandle h = torrent_queue.top().second;
+
+ try
+ {
+ h.queue_position_up();
+ }
+ catch(invalid_handle&) {}
+
+ torrent_queue.pop();
+ }
+}
+
+void RequestHandler::action_command_decreasePrio()
+{
+ QStringList hashes = request().posts["hashes"].split("|");
+ std::priority_queue,
+ std::vector >,
+ std::less > > torrent_queue;
+
+ // Sort torrents by priority
+ foreach (const QString &hash, hashes)
+ {
+ try
+ {
+ QTorrentHandle h = QBtSession::instance()->getTorrentHandle(hash);
+
+ if (!h.is_seed())
+ {
+ torrent_queue.push(qMakePair(h.queue_position(), h));
+ }
+ }
+ catch(invalid_handle&) {}
+ }
+
+ // Decrease torrents priority (starting with the ones with lowest priority)
+ while(!torrent_queue.empty())
+ {
+ QTorrentHandle h = torrent_queue.top().second;
+
+ try
+ {
+ h.queue_position_down();
+ }
+ catch(invalid_handle&) {}
+
+ torrent_queue.pop();
+ }
+}
+
+void RequestHandler::action_command_topPrio()
+{
+ foreach (const QString &hash, request().posts["hashes"].split("|"))
+ {
+ QTorrentHandle h = QBtSession::instance()->getTorrentHandle(hash);
+ if (h.is_valid()) h.queue_position_top();
+ }
+}
+
+void RequestHandler::action_command_bottomPrio()
+{
+ foreach (const QString &hash, request().posts["hashes"].split("|"))
+ {
+ QTorrentHandle h = QBtSession::instance()->getTorrentHandle(hash);
+ if (h.is_valid()) h.queue_position_bottom();
+ }
+}
+
+void RequestHandler::action_command_recheck()
+{
+ QBtSession::instance()->recheckTorrent(request().posts["hash"]);
+}
+
+bool RequestHandler::isPublicScope()
+{
+ return (scope_ == DEFAULT_SCOPE);
+}
+
+void RequestHandler::processRequest()
+{
+ if (args_.contains(".") || args_.contains(".."))
+ {
+ qDebug() << Q_FUNC_INFO << "Invalid path:" << request().path;
+ status(404, "Not Found");
+ return;
+ }
+
+ if (!isPublicScope() && !sessionActive())
+ {
+ status(403, "Forbidden");
+ return;
+ }
+
+ if (actions_.value(scope_).value(action_) != 0)
+ {
+ (this->*(actions_[scope_][action_]))();
+ }
+ else
+ {
+ status(404, "Not Found");
+ qDebug() << Q_FUNC_INFO << "Resource not found:" << request().path;
+ }
+}
+
+void RequestHandler::parsePath()
+{
+ if(request().path == "/") action_ = WEBUI_ACTION;
+
+ // check action for requested path
+ QStringList pathItems = request().path.split('/', QString::SkipEmptyParts);
+ if (!pathItems.empty())
+ {
+ if (actions_.contains(pathItems.front()))
+ {
+ scope_ = pathItems.front();
+ pathItems.pop_front();
+ }
+ }
+
+ if (!pathItems.empty())
+ {
+ if (actions_[scope_].contains(pathItems.front()))
+ {
+ action_ = pathItems.front();
+ pathItems.pop_front();
+ }
+ }
+
+ args_ = pathItems;
+}
+
+RequestHandler::RequestHandler(const HttpRequest &request, const HttpEnvironment &env, WebApplication *app)
+ : AbstractRequestHandler(request, env, app), scope_(DEFAULT_SCOPE), action_(DEFAULT_ACTION)
+{
+ parsePath();
+}
+
+QMap > RequestHandler::actions_ = RequestHandler::initializeActions();
diff --git a/src/webui/requesthandler.h b/src/webui/requesthandler.h
new file mode 100644
index 000000000..a92f85269
--- /dev/null
+++ b/src/webui/requesthandler.h
@@ -0,0 +1,100 @@
+/*
+ * Bittorrent Client using Qt and libtorrent.
+ * Copyright (C) 2014 Vladimir Golovnev
+ *
+ * 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.
+ */
+
+#ifndef REQUESTHANDLER_H
+#define REQUESTHANDLER_H
+
+#include
+#include "httptypes.h"
+#include "abstractrequesthandler.h"
+
+class WebApplication;
+
+class RequestHandler: public AbstractRequestHandler
+{
+public:
+ RequestHandler(
+ const HttpRequest& request, const HttpEnvironment& env,
+ WebApplication* app);
+
+private:
+ // Actions
+ void action_public_webui();
+ void action_public_index();
+ void action_public_login();
+ void action_public_logout();
+ void action_public_theme();
+ void action_public_images();
+ void action_json_torrents();
+ void action_json_preferences();
+ void action_json_transferInfo();
+ void action_json_propertiesGeneral();
+ void action_json_propertiesTrackers();
+ void action_json_propertiesFiles();
+ void action_command_shutdown();
+ void action_command_download();
+ void action_command_upload();
+ void action_command_addTrackers();
+ void action_command_resumeAll();
+ void action_command_pauseAll();
+ void action_command_resume();
+ void action_command_pause();
+ void action_command_setPreferences();
+ void action_command_setFilePrio();
+ void action_command_getGlobalUpLimit();
+ void action_command_getGlobalDlLimit();
+ void action_command_setGlobalUpLimit();
+ void action_command_setGlobalDlLimit();
+ void action_command_getTorrentUpLimit();
+ void action_command_getTorrentDlLimit();
+ void action_command_setTorrentUpLimit();
+ void action_command_setTorrentDlLimit();
+ void action_command_delete();
+ void action_command_deletePerm();
+ void action_command_increasePrio();
+ void action_command_decreasePrio();
+ void action_command_topPrio();
+ void action_command_bottomPrio();
+ void action_command_recheck();
+
+ typedef void (RequestHandler::*Action)();
+
+ QString scope_;
+ QString action_;
+ QStringList args_;
+
+ void processRequest();
+
+ bool isPublicScope();
+ void parsePath();
+
+ static QMap > initializeActions();
+ static QMap > actions_;
+};
+
+#endif // REQUESTHANDLER_H
diff --git a/src/webui/webapplication.cpp b/src/webui/webapplication.cpp
new file mode 100644
index 000000000..2ada7817c
--- /dev/null
+++ b/src/webui/webapplication.cpp
@@ -0,0 +1,309 @@
+/*
+ * Bittorrent Client using Qt and libtorrent.
+ * Copyright (C) 2014 Vladimir Golovnev
+ *
+ * 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.
+ */
+
+#ifdef DISABLE_GUI
+#include
+#else
+#include
+#endif
+#include
+#include
+#include
+#include
+#include "preferences.h"
+#include "requesthandler.h"
+#include "webapplication.h"
+
+// UnbanTimer
+
+class UnbanTimer: public QTimer
+{
+public:
+ UnbanTimer(const QHostAddress& peer_ip, QObject *parent)
+ : QTimer(parent), m_peerIp(peer_ip)
+ {
+ setSingleShot(true);
+ setInterval(BAN_TIME);
+ }
+
+ inline const QHostAddress& peerIp() const { return m_peerIp; }
+
+private:
+ QHostAddress m_peerIp;
+};
+
+// WebApplication
+
+WebApplication::WebApplication(QObject *parent)
+ : QObject(parent)
+{
+}
+
+WebApplication::~WebApplication()
+{
+ // cleanup sessions data
+ foreach (WebSession* session, sessions_.values())
+ delete session;
+}
+
+WebApplication *WebApplication::instance()
+{
+ static WebApplication inst;
+ return &inst;
+}
+
+void WebApplication::UnbanTimerEvent()
+{
+ UnbanTimer* ubantimer = static_cast(sender());
+ qDebug("Ban period has expired for %s", qPrintable(ubantimer->peerIp().toString()));
+ clientFailedAttempts_.remove(ubantimer->peerIp());
+ ubantimer->deleteLater();
+}
+
+bool WebApplication::sessionInitialize(AbstractRequestHandler* _this)
+{
+ if (_this->session_ == 0)
+ {
+ QString cookie = _this->request_.headers.value("cookie");
+ //qDebug() << Q_FUNC_INFO << "cookie: " << cookie;
+
+ QString sessionId;
+ const QString SID_START = C_SID + "=";
+ int pos = cookie.indexOf(SID_START);
+ if (pos >= 0)
+ {
+ pos += SID_START.length();
+ int end = cookie.indexOf(QRegExp("[,;]"), pos);
+ sessionId = cookie.mid(pos, end >= 0 ? end - pos : end);
+ }
+
+ // TODO: Additional session check
+
+ if (!sessionId.isNull())
+ {
+ if (sessions_.contains(sessionId))
+ {
+ _this->session_ = sessions_[sessionId];
+ return true;
+ }
+ else
+ {
+ qDebug() << Q_FUNC_INFO << "session does not exist!";
+ }
+ }
+ }
+
+ return false;
+}
+
+bool WebApplication::readFile(const QString& path, QByteArray &data, QString &type)
+{
+ QString ext = "";
+ int index = path.lastIndexOf('.') + 1;
+ if (index > 0)
+ ext = path.mid(index);
+
+ // find translated file in cache
+ if (translatedFiles_.contains(path))
+ {
+ data = translatedFiles_[path];
+ }
+ else
+ {
+ QFile file(path);
+ if (!file.open(QIODevice::ReadOnly))
+ {
+ qDebug("File %s was not found!", qPrintable(path));
+ return false;
+ }
+
+ data = file.readAll();
+ file.close();
+
+ // Translate the file
+ if ((ext == "html") || ((ext == "js") && !path.endsWith("excanvas-compressed.js")))
+ {
+ QString dataStr = QString::fromUtf8(data.constData());
+ translateDocument(dataStr);
+
+ if (path.endsWith("about.html"))
+ {
+ dataStr.replace("${VERSION}", VERSION);
+ }
+
+ data = dataStr.toUtf8();
+ translatedFiles_[path] = data; // cashing translated file
+ }
+ }
+
+ type = CONTENT_TYPE_BY_EXT[ext];
+ return true;
+}
+
+QString WebApplication::generateSid()
+{
+ QString sid;
+
+ qsrand(QDateTime::currentDateTime().toTime_t());
+ do
+ {
+ const size_t size = 6;
+ quint32 tmp[size];
+
+ for (size_t i = 0; i < size; ++i)
+ tmp[i] = qrand();
+
+ sid = QByteArray::fromRawData(reinterpret_cast(tmp), sizeof(quint32) * size).toBase64();
+ }
+ while (sessions_.contains(sid));
+
+ return sid;
+}
+
+void WebApplication::translateDocument(QString& data)
+{
+ const QRegExp regex(QString::fromUtf8("_\\(([\\w\\s?!:\\/\\(\\),%µ&\\-\\.]+)\\)"));
+ const QRegExp mnemonic("\\(?&([a-zA-Z]?\\))?");
+ const std::string contexts[] = {
+ "TransferListFiltersWidget", "TransferListWidget", "PropertiesWidget",
+ "HttpServer", "confirmDeletionDlg", "TrackerList", "TorrentFilesModel",
+ "options_imp", "Preferences", "TrackersAdditionDlg", "ScanFoldersModel",
+ "PropTabBar", "TorrentModel", "downloadFromURL", "MainWindow", "misc"
+ };
+ const size_t context_count = sizeof(contexts) / sizeof(contexts[0]);
+ int i = 0;
+ bool found = true;
+
+ const QString locale = Preferences::instance()->getLocale();
+ bool isTranslationNeeded = !locale.startsWith("en") || locale.startsWith("en_AU") || locale.startsWith("en_GB");
+
+ while(i < data.size() && found)
+ {
+ i = regex.indexIn(data, i);
+ if (i >= 0)
+ {
+ //qDebug("Found translatable string: %s", regex.cap(1).toUtf8().data());
+ QByteArray word = regex.cap(1).toUtf8();
+
+ QString translation = word;
+ if (isTranslationNeeded)
+ {
+ size_t context_index = 0;
+ while ((context_index < context_count) && (translation == word))
+ {
+#if (QT_VERSION < QT_VERSION_CHECK(5, 0, 0))
+ translation = qApp->translate(contexts[context_index].c_str(), word.constData(), 0, QCoreApplication::UnicodeUTF8, 1);
+#else
+ translation = qApp->translate(contexts[context_index].c_str(), word.constData(), 0, 1);
+#endif
+ ++context_index;
+ }
+ }
+ // Remove keyboard shortcuts
+ translation.replace(mnemonic, "");
+
+ data.replace(i, regex.matchedLength(), translation);
+ i += translation.length();
+ }
+ else
+ {
+ found = false; // no more translatable strings
+ }
+ }
+}
+
+bool WebApplication::isBanned(const AbstractRequestHandler *_this) const
+{
+ return clientFailedAttempts_.value(_this->env_.clientAddress, 0) >= MAX_AUTH_FAILED_ATTEMPTS;
+}
+
+int WebApplication::failedAttempts(const AbstractRequestHandler* _this) const
+{
+ return clientFailedAttempts_.value(_this->env_.clientAddress, 0);
+}
+
+void WebApplication::resetFailedAttempts(AbstractRequestHandler* _this)
+{
+ clientFailedAttempts_.remove(_this->env_.clientAddress);
+}
+
+void WebApplication::increaseFailedAttempts(AbstractRequestHandler* _this)
+{
+ const int nb_fail = clientFailedAttempts_.value(_this->env_.clientAddress, 0) + 1;
+
+ clientFailedAttempts_[_this->env_.clientAddress] = nb_fail;
+ if (nb_fail == MAX_AUTH_FAILED_ATTEMPTS)
+ {
+ // Max number of failed attempts reached
+ // Start ban period
+ UnbanTimer* ubantimer = new UnbanTimer(_this->env_.clientAddress, this);
+ connect(ubantimer, SIGNAL(timeout()), SLOT(UnbanTimerEvent()));
+ ubantimer->start();
+ }
+}
+
+bool WebApplication::sessionStart(AbstractRequestHandler *_this)
+{
+ if (_this->session_ == 0)
+ {
+ _this->session_ = new WebSession(generateSid());
+ sessions_[_this->session_->id] = _this->session_;
+ return true;
+ }
+
+ return false;
+}
+
+bool WebApplication::sessionEnd(AbstractRequestHandler *_this)
+{
+ if ((_this->session_ != 0) && (sessions_.contains(_this->session_->id)))
+ {
+ sessions_.remove(_this->session_->id);
+ delete _this->session_;
+ _this->session_ = 0;
+ return true;
+ }
+
+ return false;
+}
+
+QStringMap WebApplication::initializeContentTypeByExtMap()
+{
+ QStringMap map;
+
+ map["htm"] = CONTENT_TYPE_HTML;
+ map["html"] = CONTENT_TYPE_HTML;
+ map["css"] = CONTENT_TYPE_CSS;
+ map["gif"] = CONTENT_TYPE_GIF;
+ map["png"] = CONTENT_TYPE_PNG;
+ map["js"] = CONTENT_TYPE_JS;
+
+ return map;
+}
+
+const QStringMap WebApplication::CONTENT_TYPE_BY_EXT = WebApplication::initializeContentTypeByExtMap();
diff --git a/src/webui/webapplication.h b/src/webui/webapplication.h
new file mode 100644
index 000000000..0e005f0b3
--- /dev/null
+++ b/src/webui/webapplication.h
@@ -0,0 +1,87 @@
+/*
+ * Bittorrent Client using Qt and libtorrent.
+ * Copyright (C) 2014 Vladimir Golovnev
+ *
+ * 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.
+ */
+
+#ifndef WEBAPPLICATION_H
+#define WEBAPPLICATION_H
+
+#include
+#include
+#include
+#include "httptypes.h"
+
+struct WebSession
+{
+ const QString id;
+
+ WebSession(const QString& id): id(id) {}
+};
+
+const QString C_SID = "SID"; // name of session id cookie
+const int BAN_TIME = 3600000; // 1 hour
+const int MAX_AUTH_FAILED_ATTEMPTS = 5;
+
+class AbstractRequestHandler;
+
+class WebApplication: public QObject
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(WebApplication)
+
+public:
+ WebApplication(QObject* parent = 0);
+ virtual ~WebApplication();
+
+ static WebApplication* instance();
+
+ bool isBanned(const AbstractRequestHandler* _this) const;
+ int failedAttempts(const AbstractRequestHandler *_this) const;
+ void resetFailedAttempts(AbstractRequestHandler* _this);
+ void increaseFailedAttempts(AbstractRequestHandler* _this);
+
+ bool sessionStart(AbstractRequestHandler* _this);
+ bool sessionEnd(AbstractRequestHandler* _this);
+ bool sessionInitialize(AbstractRequestHandler* _this);
+
+ bool readFile(const QString &path, QByteArray& data, QString& type);
+
+private slots:
+ void UnbanTimerEvent();
+
+private:
+ QMap sessions_;
+ QHash clientFailedAttempts_;
+ QMap translatedFiles_;
+
+ QString generateSid();
+ static void translateDocument(QString& data);
+
+ static const QStringMap CONTENT_TYPE_BY_EXT;
+ static QStringMap initializeContentTypeByExtMap();
+};
+
+#endif // WEBAPPLICATION_H
diff --git a/src/webui/webui.pri b/src/webui/webui.pri
index d5153d7d5..be4989a22 100644
--- a/src/webui/webui.pri
+++ b/src/webui/webui.pri
@@ -6,10 +6,12 @@ HEADERS += $$PWD/httpserver.h \
$$PWD/httpresponsegenerator.h \
$$PWD/btjson.h \
$$PWD/prefjson.h \
- $$PWD/httpheader.h \
- $$PWD/httprequestheader.h \
- $$PWD/httpresponseheader.h \
- $$PWD/jsonutils.h
+ $$PWD/jsonutils.h \
+ $$PWD/httptypes.h \
+ $$PWD/extra_translations.h \
+ $$PWD/webapplication.h \
+ $$PWD/abstractrequesthandler.h \
+ $$PWD/requesthandler.h
SOURCES += $$PWD/httpserver.cpp \
$$PWD/httpconnection.cpp \
@@ -17,9 +19,9 @@ SOURCES += $$PWD/httpserver.cpp \
$$PWD/httpresponsegenerator.cpp \
$$PWD/btjson.cpp \
$$PWD/prefjson.cpp \
- $$PWD/httpheader.cpp \
- $$PWD/httprequestheader.cpp \
- $$PWD/httpresponseheader.cpp
+ $$PWD/webapplication.cpp \
+ $$PWD/abstractrequesthandler.cpp \
+ $$PWD/requesthandler.cpp
# QJson JSON parser/serializer for using with Qt4
lessThan(QT_MAJOR_VERSION, 5) {
diff --git a/src/webui/webui.qrc b/src/webui/webui.qrc
index 08d58de79..035301d86 100644
--- a/src/webui/webui.qrc
+++ b/src/webui/webui.qrc
@@ -1,37 +1,39 @@
-
-
- html/index.html
- html/download.html
- html/addtrackers.html
- html/upload.html
- html/about.html
- html/filters.html
- html/transferlist.html
- html/prop-general.html
- html/prop-trackers.html
- html/prop-files.html
- html/properties.html
- html/uploadlimit.html
- html/downloadlimit.html
- html/preferences.html
- html/preferences_content.html
- html/confirmdeletion.html
- css/Core.css
- css/Layout.css
- css/Window.css
- css/Tabs.css
- css/dynamicTable.css
- css/style.css
- scripts/excanvas-compressed.js
- scripts/mocha-yc.js
- scripts/mocha-init.js
- scripts/mootools-1.2-core-yc.js
- scripts/mootools-1.2-more.js
- scripts/dynamicTable.js
- scripts/client.js
- scripts/download.js
- scripts/progressbar.js
- scripts/contextmenu.js
- scripts/parametrics.js
-
+
+
+ www/private/index.html
+ www/private/login.html
+ www/public/css/Core.css
+ www/public/css/dynamicTable.css
+ www/public/css/Layout.css
+ www/public/css/style.css
+ www/public/css/Tabs.css
+ www/public/css/Window.css
+ www/public/scripts/client.js
+ www/public/scripts/contextmenu.js
+ www/public/scripts/download.js
+ www/public/scripts/dynamicTable.js
+ www/public/scripts/excanvas-compressed.js
+ www/public/scripts/mocha.js
+ www/public/scripts/mocha-init.js
+ www/public/scripts/mocha-yc.js
+ www/public/scripts/mootools-1.2-core-yc.js
+ www/public/scripts/mootools-1.2-more.js
+ www/public/scripts/parametrics.js
+ www/public/scripts/progressbar.js
+ www/public/about.html
+ www/public/addtrackers.html
+ www/public/confirmdeletion.html
+ www/public/download.html
+ www/public/downloadlimit.html
+ www/public/filters.html
+ www/public/preferences.html
+ www/public/preferences_content.html
+ www/public/properties.html
+ www/public/prop-files.html
+ www/public/prop-general.html
+ www/public/prop-trackers.html
+ www/public/transferlist.html
+ www/public/upload.html
+ www/public/uploadlimit.html
+
diff --git a/src/webui/html/index.html b/src/webui/www/private/index.html
similarity index 67%
rename from src/webui/html/index.html
rename to src/webui/www/private/index.html
index 44f0cae9c..33f5fded8 100644
--- a/src/webui/html/index.html
+++ b/src/webui/www/private/index.html
@@ -8,10 +8,10 @@
-
-
-
-
+
+
+
+