mirror of
https://github.com/qbittorrent/qBittorrent.git
synced 2025-12-23 08:48:07 -06:00
Fix prefjson::setPreferences() doesn't actually save.
This commit is contained in:
@@ -3,7 +3,6 @@ INCLUDEPATH += $$PWD
|
||||
unix:!macx:dbus: include(qtnotify/qtnotify.pri)
|
||||
|
||||
include(qtlibtorrent/qtlibtorrent.pri)
|
||||
include(tracker/tracker.pri)
|
||||
|
||||
HEADERS += \
|
||||
$$PWD/misc.h \
|
||||
@@ -16,10 +15,15 @@ HEADERS += \
|
||||
$$PWD/smtp.h \
|
||||
$$PWD/dnsupdater.h \
|
||||
$$PWD/logger.h \
|
||||
$$PWD/httptypes.h \
|
||||
$$PWD/httprequestparser.h \
|
||||
$$PWD/httpresponsegenerator.h \
|
||||
$$PWD/preferences.h
|
||||
$$PWD/preferences.h \
|
||||
$$PWD/qtracker.h \
|
||||
$$PWD/http/irequesthandler.h \
|
||||
$$PWD/http/connection.h \
|
||||
$$PWD/http/requestparser.h \
|
||||
$$PWD/http/responsegenerator.h \
|
||||
$$PWD/http/server.h \
|
||||
$$PWD/http/types.h \
|
||||
$$PWD/http/responsebuilder.h
|
||||
|
||||
SOURCES += \
|
||||
$$PWD/downloadthread.cpp \
|
||||
@@ -30,6 +34,10 @@ SOURCES += \
|
||||
$$PWD/smtp.cpp \
|
||||
$$PWD/dnsupdater.cpp \
|
||||
$$PWD/logger.cpp \
|
||||
$$PWD/httprequestparser.cpp \
|
||||
$$PWD/httpresponsegenerator.cpp \
|
||||
$$PWD/preferences.cpp
|
||||
$$PWD/preferences.cpp \
|
||||
$$PWD/qtracker.cpp \
|
||||
$$PWD/http/connection.cpp \
|
||||
$$PWD/http/requestparser.cpp \
|
||||
$$PWD/http/responsegenerator.cpp \
|
||||
$$PWD/http/server.cpp \
|
||||
$$PWD/http/responsebuilder.cpp
|
||||
|
||||
111
src/core/http/connection.cpp
Normal file
111
src/core/http/connection.cpp
Normal file
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2014 Vladimir Golovnev <glassez@yandex.ru>
|
||||
* Copyright (C) 2006 Ishan Arora and Christophe Dumez
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*
|
||||
* Contact : chris@qbittorrent.org
|
||||
*/
|
||||
|
||||
#include <QTcpSocket>
|
||||
#include <QDebug>
|
||||
#include "types.h"
|
||||
#include "requestparser.h"
|
||||
#include "responsegenerator.h"
|
||||
#include "irequesthandler.h"
|
||||
#include "connection.h"
|
||||
|
||||
using namespace Http;
|
||||
|
||||
Connection::Connection(QTcpSocket *socket, IRequestHandler *requestHandler, QObject *parent)
|
||||
: QObject(parent)
|
||||
, m_socket(socket)
|
||||
, m_requestHandler(requestHandler)
|
||||
{
|
||||
m_socket->setParent(this);
|
||||
connect(m_socket, SIGNAL(readyRead()), SLOT(read()));
|
||||
connect(m_socket, SIGNAL(disconnected()), SLOT(deleteLater()));
|
||||
}
|
||||
|
||||
Connection::~Connection()
|
||||
{
|
||||
}
|
||||
|
||||
void Connection::read()
|
||||
{
|
||||
m_receivedData.append(m_socket->readAll());
|
||||
|
||||
Request request;
|
||||
RequestParser::ErrorCode err = RequestParser::parse(m_receivedData, request);
|
||||
switch (err)
|
||||
{
|
||||
case RequestParser::IncompleteRequest:
|
||||
// Partial request waiting for the rest
|
||||
break;
|
||||
case RequestParser::BadRequest:
|
||||
sendResponse(Response(400, "Bad Request"));
|
||||
break;
|
||||
case RequestParser::NoError:
|
||||
Environment env;
|
||||
env.clientAddress = m_socket->peerAddress();
|
||||
Response response = m_requestHandler->processRequest(request, env);
|
||||
if (acceptsGzipEncoding(request.headers["accept-encoding"]))
|
||||
response.headers[HEADER_CONTENT_ENCODING] = "gzip";
|
||||
sendResponse(response);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Connection::sendResponse(const Response &response)
|
||||
{
|
||||
m_socket->write(ResponseGenerator::generate(response));
|
||||
m_socket->disconnectFromHost();
|
||||
}
|
||||
|
||||
bool Connection::acceptsGzipEncoding(const QString &encoding)
|
||||
{
|
||||
int pos = encoding.indexOf("gzip", 0, Qt::CaseInsensitive);
|
||||
if (pos == -1)
|
||||
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 value;
|
||||
if (comma_pos == -1)
|
||||
value = encoding.mid(pos + 1, comma_pos);
|
||||
else
|
||||
value = encoding.mid(pos + 1, comma_pos - (pos + 1));
|
||||
|
||||
if (value.toDouble() == 0.0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
71
src/core/http/connection.h
Normal file
71
src/core/http/connection.h
Normal file
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2014 Vladimir Golovnev <glassez@yandex.ru>
|
||||
* Copyright (C) 2006 Ishan Arora and Christophe Dumez
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*
|
||||
* Contact : chris@qbittorrent.org
|
||||
*/
|
||||
|
||||
|
||||
#ifndef HTTP_CONNECTION_H
|
||||
#define HTTP_CONNECTION_H
|
||||
|
||||
#include <QObject>
|
||||
#include "types.h"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QTcpSocket;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
namespace Http
|
||||
{
|
||||
|
||||
class IRequestHandler;
|
||||
|
||||
class Connection : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_DISABLE_COPY(Connection)
|
||||
|
||||
public:
|
||||
Connection(QTcpSocket *socket, IRequestHandler *requestHandler, QObject *parent = 0);
|
||||
~Connection();
|
||||
|
||||
private slots:
|
||||
void read();
|
||||
|
||||
private:
|
||||
static bool acceptsGzipEncoding(const QString &encoding);
|
||||
void sendResponse(const Response &response);
|
||||
|
||||
QTcpSocket *m_socket;
|
||||
IRequestHandler *m_requestHandler;
|
||||
QByteArray m_receivedData;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // HTTP_CONNECTION_H
|
||||
47
src/core/http/irequesthandler.h
Normal file
47
src/core/http/irequesthandler.h
Normal file
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
|
||||
*
|
||||
* 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 HTTP_IREQUESTHANDLER_H
|
||||
#define HTTP_IREQUESTHANDLER_H
|
||||
|
||||
#include "types.h"
|
||||
|
||||
namespace Http
|
||||
{
|
||||
|
||||
class IRequestHandler
|
||||
{
|
||||
public:
|
||||
virtual ~IRequestHandler() {}
|
||||
virtual Response processRequest(const Request &request, const Environment &env) = 0;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // HTTP_IREQUESTHANDLER_H
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
#include <QDir>
|
||||
#include <QTemporaryFile>
|
||||
#include <QDebug>
|
||||
#include "httprequestparser.h"
|
||||
#include "requestparser.h"
|
||||
|
||||
const QByteArray EOL("\r\n");
|
||||
const QByteArray EOH("\r\n\r\n");
|
||||
@@ -51,19 +51,21 @@ inline QString unquoted(const QString& str)
|
||||
return str;
|
||||
}
|
||||
|
||||
HttpRequestParser::ErrorCode HttpRequestParser::parse(const QByteArray& data, HttpRequest& request, uint maxContentLength)
|
||||
using namespace Http;
|
||||
|
||||
RequestParser::ErrorCode RequestParser::parse(const QByteArray& data, Request& request, uint maxContentLength)
|
||||
{
|
||||
return HttpRequestParser(maxContentLength).parseHttpRequest(data, request);
|
||||
return RequestParser(maxContentLength).parseHttpRequest(data, request);
|
||||
}
|
||||
|
||||
HttpRequestParser::HttpRequestParser(uint maxContentLength)
|
||||
: maxContentLength_(maxContentLength)
|
||||
RequestParser::RequestParser(uint maxContentLength)
|
||||
: m_maxContentLength(maxContentLength)
|
||||
{
|
||||
}
|
||||
|
||||
HttpRequestParser::ErrorCode HttpRequestParser::parseHttpRequest(const QByteArray& data, HttpRequest& request)
|
||||
RequestParser::ErrorCode RequestParser::parseHttpRequest(const QByteArray& data, Request& request)
|
||||
{
|
||||
request_ = HttpRequest();
|
||||
m_request = Request();
|
||||
|
||||
// Parse HTTP request header
|
||||
const int header_end = data.indexOf(EOH);
|
||||
@@ -81,10 +83,10 @@ HttpRequestParser::ErrorCode HttpRequestParser::parseHttpRequest(const QByteArra
|
||||
|
||||
// Parse HTTP request message
|
||||
int content_length = 0;
|
||||
if (request_.headers.contains("content-length"))
|
||||
if (m_request.headers.contains("content-length"))
|
||||
{
|
||||
content_length = request_.headers["content-length"].toInt();
|
||||
if (content_length > static_cast<int>(maxContentLength_))
|
||||
content_length = m_request.headers["content-length"].toInt();
|
||||
if (content_length > static_cast<int>(m_maxContentLength))
|
||||
{
|
||||
qWarning() << Q_FUNC_INFO << "bad request: message too long";
|
||||
return BadRequest;
|
||||
@@ -108,20 +110,20 @@ HttpRequestParser::ErrorCode HttpRequestParser::parseHttpRequest(const QByteArra
|
||||
// qDebug() << "HTTP Request header:";
|
||||
// qDebug() << data.left(header_end) << "\n";
|
||||
|
||||
request = request_;
|
||||
request = m_request;
|
||||
return NoError;
|
||||
}
|
||||
|
||||
bool HttpRequestParser::parseStartingLine(const QString &line)
|
||||
bool RequestParser::parseStartingLine(const QString &line)
|
||||
{
|
||||
const QRegExp rx("^([A-Z]+)\\s+(\\S+)\\s+HTTP/\\d\\.\\d$");
|
||||
|
||||
if (rx.indexIn(line.trimmed()) >= 0)
|
||||
{
|
||||
request_.method = rx.cap(1);
|
||||
m_request.method = rx.cap(1);
|
||||
|
||||
QUrl url = QUrl::fromEncoded(rx.cap(2).toLatin1());
|
||||
request_.path = url.path(); // Path
|
||||
m_request.path = url.path(); // Path
|
||||
|
||||
// Parse GET parameters
|
||||
#if (QT_VERSION < QT_VERSION_CHECK(5, 0, 0))
|
||||
@@ -132,7 +134,7 @@ bool HttpRequestParser::parseStartingLine(const QString &line)
|
||||
while (i.hasNext())
|
||||
{
|
||||
QPair<QString, QString> pair = i.next();
|
||||
request_.gets[pair.first] = pair.second;
|
||||
m_request.gets[pair.first] = pair.second;
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -142,7 +144,7 @@ bool HttpRequestParser::parseStartingLine(const QString &line)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool HttpRequestParser::parseHeaderLine(const QString &line, QPair<QString, QString>& out)
|
||||
bool RequestParser::parseHeaderLine(const QString &line, QPair<QString, QString>& out)
|
||||
{
|
||||
int i = line.indexOf(QLatin1Char(':'));
|
||||
if (i == -1)
|
||||
@@ -155,7 +157,7 @@ bool HttpRequestParser::parseHeaderLine(const QString &line, QPair<QString, QStr
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HttpRequestParser::parseHttpHeader(const QByteArray &data)
|
||||
bool RequestParser::parseHttpHeader(const QByteArray &data)
|
||||
{
|
||||
QString str = QString::fromUtf8(data);
|
||||
QStringList lines = str.trimmed().split(EOL);
|
||||
@@ -191,13 +193,13 @@ bool HttpRequestParser::parseHttpHeader(const QByteArray &data)
|
||||
if (!parseHeaderLine(*it, header))
|
||||
return false;
|
||||
|
||||
request_.headers[header.first] = header.second;
|
||||
m_request.headers[header.first] = header.second;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
QList<QByteArray> HttpRequestParser::splitMultipartData(const QByteArray& data, const QByteArray& boundary)
|
||||
QList<QByteArray> RequestParser::splitMultipartData(const QByteArray& data, const QByteArray& boundary)
|
||||
{
|
||||
QList<QByteArray> ret;
|
||||
QByteArray sep = boundary + EOL;
|
||||
@@ -223,14 +225,14 @@ QList<QByteArray> HttpRequestParser::splitMultipartData(const QByteArray& data,
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool HttpRequestParser::parseContent(const QByteArray& data)
|
||||
bool RequestParser::parseContent(const QByteArray& data)
|
||||
{
|
||||
// Parse message content
|
||||
qDebug() << Q_FUNC_INFO << "Content-Length: " << request_.headers["content-length"];
|
||||
qDebug() << Q_FUNC_INFO << "Content-Length: " << m_request.headers["content-length"];
|
||||
qDebug() << Q_FUNC_INFO << "data.size(): " << data.size();
|
||||
|
||||
// Parse url-encoded POST data
|
||||
if (request_.headers["content-type"].startsWith("application/x-www-form-urlencoded"))
|
||||
if (m_request.headers["content-type"].startsWith("application/x-www-form-urlencoded"))
|
||||
{
|
||||
QUrl url;
|
||||
#if (QT_VERSION < QT_VERSION_CHECK(5, 0, 0))
|
||||
@@ -243,7 +245,7 @@ bool HttpRequestParser::parseContent(const QByteArray& data)
|
||||
while (i.hasNext())
|
||||
{
|
||||
QPair<QString, QString> pair = i.next();
|
||||
request_.posts[pair.first.toLower()] = pair.second;
|
||||
m_request.posts[pair.first.toLower()] = pair.second;
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -268,7 +270,7 @@ Content-Disposition: form-data; name=\"Upload\"
|
||||
Submit Query
|
||||
--cH2ae0GI3KM7GI3Ij5ae0ei4Ij5Ij5--
|
||||
**/
|
||||
QString content_type = request_.headers["content-type"];
|
||||
QString content_type = m_request.headers["content-type"];
|
||||
if (content_type.startsWith("multipart/form-data"))
|
||||
{
|
||||
const QRegExp boundaryRegexQuoted("boundary=\"([ \\w'()+,-\\./:=\\?]+)\"");
|
||||
@@ -309,7 +311,7 @@ Submit Query
|
||||
return false;
|
||||
}
|
||||
|
||||
bool HttpRequestParser::parseFormData(const QByteArray& data)
|
||||
bool RequestParser::parseFormData(const QByteArray& data)
|
||||
{
|
||||
// Parse form data header
|
||||
const int header_end = data.indexOf(EOH);
|
||||
@@ -347,17 +349,17 @@ bool HttpRequestParser::parseFormData(const QByteArray& data)
|
||||
ufile.type = disposition["content-type"];
|
||||
ufile.data = data.mid(header_end + EOH.length());
|
||||
|
||||
request_.files[disposition["name"]] = ufile;
|
||||
m_request.files[disposition["name"]] = ufile;
|
||||
}
|
||||
else
|
||||
{
|
||||
request_.posts[disposition["name"]] = QString::fromUtf8(data.mid(header_end + EOH.length()));
|
||||
m_request.posts[disposition["name"]] = QString::fromUtf8(data.mid(header_end + EOH.length()));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HttpRequestParser::parseHeaderValue(const QString& value, QStringMap& out)
|
||||
bool RequestParser::parseHeaderValue(const QString& value, QStringMap& out)
|
||||
{
|
||||
QStringList items = value.split(QLatin1Char(';'));
|
||||
out[""] = items[0];
|
||||
@@ -29,24 +29,27 @@
|
||||
* Contact : chris@qbittorrent.org
|
||||
*/
|
||||
|
||||
#ifndef HTTPREQUESTPARSER_H
|
||||
#define HTTPREQUESTPARSER_H
|
||||
#ifndef HTTP_REQUESTPARSER_H
|
||||
#define HTTP_REQUESTPARSER_H
|
||||
|
||||
#include "httptypes.h"
|
||||
#include "types.h"
|
||||
|
||||
class HttpRequestParser
|
||||
namespace Http
|
||||
{
|
||||
|
||||
class RequestParser
|
||||
{
|
||||
public:
|
||||
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 */);
|
||||
static ErrorCode parse(const QByteArray& data, Request& request, uint maxContentLength = 10000000 /* ~10MB */);
|
||||
|
||||
private:
|
||||
HttpRequestParser(uint maxContentLength);
|
||||
RequestParser(uint maxContentLength);
|
||||
|
||||
ErrorCode parseHttpRequest(const QByteArray& data, HttpRequest& request);
|
||||
ErrorCode parseHttpRequest(const QByteArray& data, Request& request);
|
||||
|
||||
bool parseHttpHeader(const QByteArray& data);
|
||||
bool parseStartingLine(const QString &line);
|
||||
@@ -57,8 +60,10 @@ private:
|
||||
static bool parseHeaderLine(const QString& line, QPair<QString, QString>& out);
|
||||
static bool parseHeaderValue(const QString& value, QStringMap& out);
|
||||
|
||||
const uint maxContentLength_;
|
||||
HttpRequest request_;
|
||||
const uint m_maxContentLength;
|
||||
Request m_request;
|
||||
};
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif // HTTP_REQUESTPARSER_H
|
||||
74
src/core/http/responsebuilder.cpp
Normal file
74
src/core/http/responsebuilder.cpp
Normal file
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
|
||||
*
|
||||
* 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 "responsebuilder.h"
|
||||
|
||||
using namespace Http;
|
||||
|
||||
ResponseBuilder::ResponseBuilder(QObject *parent)
|
||||
: QObject(parent)
|
||||
{
|
||||
}
|
||||
|
||||
void ResponseBuilder::status(uint code, const QString &text)
|
||||
{
|
||||
m_response.status = ResponseStatus(code, text);
|
||||
}
|
||||
|
||||
void ResponseBuilder::header(const QString &name, const QString &value)
|
||||
{
|
||||
m_response.headers[name] = value;
|
||||
}
|
||||
|
||||
void ResponseBuilder::print(const QString &text, const QString &type)
|
||||
{
|
||||
print_impl(text.toUtf8(), type);
|
||||
}
|
||||
|
||||
void ResponseBuilder::print(const QByteArray &data, const QString &type)
|
||||
{
|
||||
print_impl(data, type);
|
||||
}
|
||||
|
||||
void ResponseBuilder::clear()
|
||||
{
|
||||
m_response = Response();
|
||||
}
|
||||
|
||||
Response ResponseBuilder::response() const
|
||||
{
|
||||
return m_response;
|
||||
}
|
||||
|
||||
void ResponseBuilder::print_impl(const QByteArray &data, const QString &type)
|
||||
{
|
||||
if (!m_response.headers.contains(HEADER_CONTENT_TYPE))
|
||||
m_response.headers[HEADER_CONTENT_TYPE] = type;
|
||||
|
||||
m_response.content += data;
|
||||
}
|
||||
60
src/core/http/responsebuilder.h
Normal file
60
src/core/http/responsebuilder.h
Normal file
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
|
||||
*
|
||||
* 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 HTTP_RESPONSEBUILDER_H
|
||||
#define HTTP_RESPONSEBUILDER_H
|
||||
|
||||
#include <QObject>
|
||||
#include "types.h"
|
||||
|
||||
namespace Http
|
||||
{
|
||||
|
||||
class ResponseBuilder : public QObject
|
||||
{
|
||||
public:
|
||||
explicit ResponseBuilder(QObject *parent = 0);
|
||||
|
||||
protected:
|
||||
void status(uint code = 200, const QString &text = QLatin1String("OK"));
|
||||
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 clear();
|
||||
|
||||
Response response() const;
|
||||
|
||||
private:
|
||||
void print_impl(const QByteArray &data, const QString &type);
|
||||
|
||||
Response m_response;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // HTTP_RESPONSEBUILDER_H
|
||||
@@ -30,11 +30,13 @@
|
||||
*/
|
||||
|
||||
#include <zlib.h>
|
||||
#include "httpresponsegenerator.h"
|
||||
#include "responsegenerator.h"
|
||||
|
||||
bool gCompress(QByteArray data, QByteArray& dest_buffer);
|
||||
|
||||
QByteArray HttpResponseGenerator::generate(HttpResponse response)
|
||||
using namespace Http;
|
||||
|
||||
QByteArray ResponseGenerator::generate(Response response)
|
||||
{
|
||||
if (response.headers[HEADER_CONTENT_ENCODING] == "gzip")
|
||||
{
|
||||
@@ -30,15 +30,20 @@
|
||||
*/
|
||||
|
||||
|
||||
#ifndef HTTPRESPONSEGENERATOR_H
|
||||
#define HTTPRESPONSEGENERATOR_H
|
||||
#ifndef HTTP_RESPONSEGENERATOR_H
|
||||
#define HTTP_RESPONSEGENERATOR_H
|
||||
|
||||
#include "httptypes.h"
|
||||
#include "types.h"
|
||||
|
||||
class HttpResponseGenerator
|
||||
namespace Http
|
||||
{
|
||||
|
||||
class ResponseGenerator
|
||||
{
|
||||
public:
|
||||
static QByteArray generate(HttpResponse response);
|
||||
static QByteArray generate(Response response);
|
||||
};
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif // HTTP_RESPONSEGENERATOR_H
|
||||
100
src/core/http/server.cpp
Normal file
100
src/core/http/server.cpp
Normal file
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2014 Vladimir Golovnev <glassez@yandex.ru>
|
||||
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
|
||||
* Copyright (C) 2006 Ishan Arora <ishan@qbittorrent.org>
|
||||
*
|
||||
* 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 QT_NO_OPENSSL
|
||||
#include <QSslSocket>
|
||||
#else
|
||||
#include <QTcpSocket>
|
||||
#endif
|
||||
#include "connection.h"
|
||||
#include "server.h"
|
||||
|
||||
using namespace Http;
|
||||
|
||||
Server::Server(IRequestHandler *requestHandler, QObject* parent)
|
||||
: QTcpServer(parent)
|
||||
, m_requestHandler(requestHandler)
|
||||
#ifndef QT_NO_OPENSSL
|
||||
, m_https(false)
|
||||
#endif
|
||||
{
|
||||
}
|
||||
|
||||
Server::~Server()
|
||||
{
|
||||
}
|
||||
|
||||
#ifndef QT_NO_OPENSSL
|
||||
void Server::enableHttps(const QSslCertificate &certificate, const QSslKey &key)
|
||||
{
|
||||
m_certificate = certificate;
|
||||
m_key = key;
|
||||
m_https = true;
|
||||
}
|
||||
|
||||
void Server::disableHttps()
|
||||
{
|
||||
m_https = false;
|
||||
m_certificate.clear();
|
||||
m_key.clear();
|
||||
}
|
||||
#endif
|
||||
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
|
||||
void Server::incomingConnection(qintptr socketDescriptor)
|
||||
#else
|
||||
void Server::incomingConnection(int socketDescriptor)
|
||||
#endif
|
||||
{
|
||||
QTcpSocket *serverSocket;
|
||||
#ifndef QT_NO_OPENSSL
|
||||
if (m_https)
|
||||
serverSocket = new QSslSocket(this);
|
||||
else
|
||||
#endif
|
||||
serverSocket = new QTcpSocket(this);
|
||||
if (serverSocket->setSocketDescriptor(socketDescriptor))
|
||||
{
|
||||
#ifndef QT_NO_OPENSSL
|
||||
if (m_https)
|
||||
{
|
||||
static_cast<QSslSocket*>(serverSocket)->setProtocol(QSsl::AnyProtocol);
|
||||
static_cast<QSslSocket*>(serverSocket)->setPrivateKey(m_key);
|
||||
static_cast<QSslSocket*>(serverSocket)->setLocalCertificate(m_certificate);
|
||||
static_cast<QSslSocket*>(serverSocket)->startServerEncryption();
|
||||
}
|
||||
#endif
|
||||
new Connection(serverSocket, m_requestHandler, this);
|
||||
}
|
||||
else
|
||||
{
|
||||
serverSocket->deleteLater();
|
||||
}
|
||||
}
|
||||
80
src/core/http/server.h
Normal file
80
src/core/http/server.h
Normal file
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2014 Vladimir Golovnev <glassez@yandex.ru>
|
||||
* Copyright (C) 2006 Ishan Arora and Christophe Dumez <chris@qbittorrent.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*
|
||||
* Contact : chris@qbittorrent.org
|
||||
*/
|
||||
|
||||
|
||||
#ifndef HTTP_SERVER_H
|
||||
#define HTTP_SERVER_H
|
||||
|
||||
#include <QTcpServer>
|
||||
#ifndef QT_NO_OPENSSL
|
||||
#include <QSslCertificate>
|
||||
#include <QSslKey>
|
||||
#endif
|
||||
|
||||
namespace Http
|
||||
{
|
||||
|
||||
class IRequestHandler;
|
||||
class Connection;
|
||||
|
||||
class Server : public QTcpServer
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_DISABLE_COPY(Server)
|
||||
|
||||
public:
|
||||
Server(IRequestHandler *requestHandler, QObject *parent = 0);
|
||||
~Server();
|
||||
|
||||
#ifndef QT_NO_OPENSSL
|
||||
void enableHttps(const QSslCertificate &certificate, const QSslKey &key);
|
||||
void disableHttps();
|
||||
#endif
|
||||
|
||||
private:
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
|
||||
void incomingConnection(qintptr socketDescriptor);
|
||||
#else
|
||||
void incomingConnection(int socketDescriptor);
|
||||
#endif
|
||||
|
||||
private:
|
||||
IRequestHandler *m_requestHandler;
|
||||
#ifndef QT_NO_OPENSSL
|
||||
bool m_https;
|
||||
QSslCertificate m_certificate;
|
||||
QSslKey m_key;
|
||||
#endif
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // HTTP_SERVER_H
|
||||
@@ -26,8 +26,8 @@
|
||||
* exception statement from your version.
|
||||
*/
|
||||
|
||||
#ifndef HTTPTYPES_H
|
||||
#define HTTPTYPES_H
|
||||
#ifndef HTTP_TYPES_H
|
||||
#define HTTP_TYPES_H
|
||||
|
||||
#include <QString>
|
||||
#include <QMap>
|
||||
@@ -35,6 +35,9 @@
|
||||
|
||||
typedef QMap<QString, QString> QStringMap;
|
||||
|
||||
namespace Http
|
||||
{
|
||||
|
||||
const QString HEADER_SET_COOKIE = "Set-Cookie";
|
||||
const QString HEADER_CONTENT_TYPE = "Content-Type";
|
||||
const QString HEADER_CONTENT_ENCODING = "Content-Encoding";
|
||||
@@ -48,43 +51,45 @@ const QString CONTENT_TYPE_JS = "text/javascript; charset=UTF-8";
|
||||
const QString CONTENT_TYPE_PNG = "image/png";
|
||||
const QString CONTENT_TYPE_TXT = "text/plain; charset=UTF-8";
|
||||
|
||||
struct HttpEnvironment
|
||||
struct Environment
|
||||
{
|
||||
QHostAddress clientAddress;
|
||||
QHostAddress clientAddress;
|
||||
};
|
||||
|
||||
struct UploadedFile
|
||||
{
|
||||
QString filename; // original filename
|
||||
QString type; // MIME type
|
||||
QByteArray data; // File data
|
||||
QString filename; // original filename
|
||||
QString type; // MIME type
|
||||
QByteArray data; // File data
|
||||
};
|
||||
|
||||
struct HttpRequest
|
||||
struct Request
|
||||
{
|
||||
QString method;
|
||||
QString path;
|
||||
QStringMap headers;
|
||||
QStringMap gets;
|
||||
QStringMap posts;
|
||||
QMap<QString, UploadedFile> files;
|
||||
QString method;
|
||||
QString path;
|
||||
QStringMap headers;
|
||||
QStringMap gets;
|
||||
QStringMap posts;
|
||||
QMap<QString, UploadedFile> files;
|
||||
};
|
||||
|
||||
struct HttpResponseStatus
|
||||
struct ResponseStatus
|
||||
{
|
||||
uint code;
|
||||
QString text;
|
||||
uint code;
|
||||
QString text;
|
||||
|
||||
HttpResponseStatus(uint code = 200, const QString& text = "OK"): code(code), text(text) {}
|
||||
ResponseStatus(uint code = 200, const QString& text = "OK"): code(code), text(text) {}
|
||||
};
|
||||
|
||||
struct HttpResponse
|
||||
struct Response
|
||||
{
|
||||
HttpResponseStatus status;
|
||||
QStringMap headers;
|
||||
QByteArray content;
|
||||
ResponseStatus status;
|
||||
QStringMap headers;
|
||||
QByteArray content;
|
||||
|
||||
HttpResponse(uint code = 200, const QString& text = "OK"): status(code, text) {}
|
||||
Response(uint code = 200, const QString& text = "OK"): status(code, text) {}
|
||||
};
|
||||
|
||||
#endif // HTTPTYPES_H
|
||||
}
|
||||
|
||||
#endif // HTTP_TYPES_H
|
||||
@@ -57,7 +57,6 @@
|
||||
#include "geoipmanager.h"
|
||||
#endif
|
||||
#include "torrentpersistentdata.h"
|
||||
#include "httpserver.h"
|
||||
#include "bandwidthscheduler.h"
|
||||
#include <libtorrent/version.hpp>
|
||||
#include <libtorrent/extensions/ut_metadata.hpp>
|
||||
@@ -79,7 +78,6 @@
|
||||
#include <libtorrent/magnet_uri.hpp>
|
||||
#include <queue>
|
||||
#include <string.h>
|
||||
#include "dnsupdater.h"
|
||||
|
||||
#if LIBTORRENT_VERSION_NUM < 10000
|
||||
#include <libtorrent/upnp.hpp>
|
||||
@@ -117,7 +115,6 @@ QBtSession::QBtSession()
|
||||
#if LIBTORRENT_VERSION_NUM < 10000
|
||||
, m_upnp(0), m_natpmp(0)
|
||||
#endif
|
||||
, m_dynDNSUpdater(0)
|
||||
, m_alertDispatcher(0)
|
||||
{
|
||||
BigRatioTimer = new QTimer(this);
|
||||
@@ -159,6 +156,7 @@ QBtSession::QBtSession()
|
||||
connect(m_scanFolders, SIGNAL(torrentsAdded(QStringList&)), SLOT(addTorrentsFromScanFolder(QStringList&)));
|
||||
// Apply user settings to Bittorrent session
|
||||
configureSession();
|
||||
connect(pref, SIGNAL(changed()), SLOT(configureSession()));
|
||||
// Torrent speed monitor
|
||||
m_speedMonitor = new TorrentSpeedMonitor(this);
|
||||
m_torrentStatistics = new TorrentStatistics(this, this);
|
||||
@@ -191,9 +189,6 @@ QBtSession::~QBtSession() {
|
||||
delete downloader;
|
||||
if (bd_scheduler)
|
||||
delete bd_scheduler;
|
||||
// HTTP Server
|
||||
if (httpServer)
|
||||
delete httpServer;
|
||||
delete m_alertDispatcher;
|
||||
delete m_torrentStatistics;
|
||||
qDebug("Deleting the session");
|
||||
@@ -583,9 +578,6 @@ void QBtSession::configureSession() {
|
||||
}else{
|
||||
disableIPFilter();
|
||||
}
|
||||
// Update Web UI
|
||||
// Use a QTimer because the function can be called from qBtSession constructor
|
||||
QTimer::singleShot(0, this, SLOT(initWebUi()));
|
||||
// * Proxy settings
|
||||
proxy_settings proxySettings;
|
||||
if (pref->isProxyEnabled()) {
|
||||
@@ -655,64 +647,6 @@ void QBtSession::configureSession() {
|
||||
qDebug("Session configured");
|
||||
}
|
||||
|
||||
void QBtSession::initWebUi() {
|
||||
Preferences* const pref = Preferences::instance();
|
||||
if (pref->isWebUiEnabled()) {
|
||||
const quint16 port = pref->getWebUiPort();
|
||||
|
||||
if (httpServer) {
|
||||
if (httpServer->serverPort() != port) {
|
||||
httpServer->close();
|
||||
}
|
||||
} else {
|
||||
httpServer = new HttpServer(this);
|
||||
}
|
||||
|
||||
#ifndef QT_NO_OPENSSL
|
||||
if (pref->isWebUiHttpsEnabled()) {
|
||||
QSslCertificate cert(pref->getWebUiHttpsCertificate());
|
||||
QSslKey key;
|
||||
const QByteArray raw_key = pref->getWebUiHttpsKey();
|
||||
key = QSslKey(raw_key, QSsl::Rsa);
|
||||
if (!cert.isNull() && !key.isNull())
|
||||
httpServer->enableHttps(cert, key);
|
||||
else
|
||||
httpServer->disableHttps();
|
||||
} else {
|
||||
httpServer->disableHttps();
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!httpServer->isListening()) {
|
||||
Logger* const logger = Logger::instance();
|
||||
bool success = httpServer->listen(QHostAddress::Any, port);
|
||||
if (success)
|
||||
logger->addMessage(tr("The Web UI is listening on port %1").arg(port));
|
||||
else
|
||||
logger->addMessage(tr("Web User Interface Error - Unable to bind Web UI to port %1").arg(port), Log::CRITICAL);
|
||||
}
|
||||
// DynDNS
|
||||
if (pref->isDynDNSEnabled()) {
|
||||
if (!m_dynDNSUpdater)
|
||||
m_dynDNSUpdater = new DNSUpdater(this);
|
||||
else
|
||||
m_dynDNSUpdater->updateCredentials();
|
||||
} else {
|
||||
if (m_dynDNSUpdater) {
|
||||
delete m_dynDNSUpdater;
|
||||
m_dynDNSUpdater = 0;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (httpServer)
|
||||
delete httpServer;
|
||||
if (m_dynDNSUpdater) {
|
||||
delete m_dynDNSUpdater;
|
||||
m_dynDNSUpdater = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void QBtSession::useAlternativeSpeedsLimit(bool alternative) {
|
||||
qDebug() << Q_FUNC_INFO << alternative;
|
||||
// Save new state to remember it on startup
|
||||
@@ -1495,6 +1429,7 @@ void QBtSession::enableUPnP(bool b) {
|
||||
s->start_upnp();
|
||||
s->start_natpmp();
|
||||
#endif
|
||||
// TODO: Remove dependency from WebUI
|
||||
// Use UPnP/NAT-PMP for Web UI too
|
||||
if (pref->isWebUiEnabled() && pref->useUPnPForWebUIPort()) {
|
||||
const qint16 port = pref->getWebUiPort();
|
||||
|
||||
@@ -90,12 +90,10 @@ namespace libtorrent {
|
||||
|
||||
class DownloadThread;
|
||||
class FilterParserThread;
|
||||
class HttpServer;
|
||||
class BandwidthScheduler;
|
||||
class ScanFoldersModel;
|
||||
class TorrentSpeedMonitor;
|
||||
class TorrentStatistics;
|
||||
class DNSUpdater;
|
||||
class QAlertDispatcher;
|
||||
|
||||
enum TorrentExportFolder {
|
||||
@@ -209,7 +207,6 @@ public slots:
|
||||
#endif
|
||||
void addMagnetInteractive(const QString& uri);
|
||||
void downloadFromURLList(const QStringList& urls);
|
||||
void configureSession();
|
||||
void banIP(QString ip);
|
||||
void recursiveTorrentDownload(const QTorrentHandle &h);
|
||||
void unhideMagnet(const QString &hash);
|
||||
@@ -264,9 +261,9 @@ private slots:
|
||||
void mergeTorrents(QTorrentHandle& h_ex, boost::intrusive_ptr<libtorrent::torrent_info> t);
|
||||
void mergeTorrents(QTorrentHandle& h_ex, const QString& magnet_uri);
|
||||
void exportTorrentFile(const QTorrentHandle &h, TorrentExportFolder folder = RegularTorrentExportFolder);
|
||||
void initWebUi();
|
||||
void handleIPFilterParsed(int ruleCount);
|
||||
void handleIPFilterError();
|
||||
void configureSession();
|
||||
|
||||
signals:
|
||||
void addedTorrent(const QTorrentHandle& h);
|
||||
@@ -327,8 +324,6 @@ private:
|
||||
// IP filtering
|
||||
QPointer<FilterParserThread> filterParser;
|
||||
QString filterPath;
|
||||
// Web UI
|
||||
QPointer<HttpServer> httpServer;
|
||||
QList<QUrl> url_skippingDlg;
|
||||
// GeoIP
|
||||
#ifndef DISABLE_GUI
|
||||
@@ -344,8 +339,6 @@ private:
|
||||
libtorrent::upnp *m_upnp;
|
||||
libtorrent::natpmp *m_natpmp;
|
||||
#endif
|
||||
// DynDNS
|
||||
DNSUpdater *m_dynDNSUpdater;
|
||||
QAlertDispatcher* m_alertDispatcher;
|
||||
TorrentStatistics* m_torrentStatistics;
|
||||
};
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt4 and libtorrent.
|
||||
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
|
||||
* Copyright (C) 2006 Christophe Dumez
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
@@ -28,163 +29,171 @@
|
||||
* Contact : chris@qbittorrent.org
|
||||
*/
|
||||
|
||||
#include <QTcpSocket>
|
||||
|
||||
#include <libtorrent/bencode.hpp>
|
||||
#include <libtorrent/entry.hpp>
|
||||
|
||||
#include "qtracker.h"
|
||||
#include "preferences.h"
|
||||
#include "httpresponsegenerator.h"
|
||||
#include "httprequestparser.h"
|
||||
|
||||
#include <vector>
|
||||
#include <libtorrent/bencode.hpp>
|
||||
|
||||
using namespace libtorrent;
|
||||
#include "preferences.h"
|
||||
#include "http/server.h"
|
||||
#include "qtracker.h"
|
||||
|
||||
QTracker::QTracker(QObject *parent) :
|
||||
QTcpServer(parent)
|
||||
// QPeer
|
||||
bool QPeer::operator!=(const QPeer &other) const
|
||||
{
|
||||
Q_ASSERT(Preferences::instance()->isTrackerEnabled());
|
||||
connect(this, SIGNAL(newConnection()), this, SLOT(handlePeerConnection()));
|
||||
return qhash() != other.qhash();
|
||||
}
|
||||
|
||||
QTracker::~QTracker() {
|
||||
if (isListening()) {
|
||||
bool QPeer::operator==(const QPeer &other) const
|
||||
{
|
||||
return qhash() == other.qhash();
|
||||
}
|
||||
|
||||
QString QPeer::qhash() const
|
||||
{
|
||||
return ip + ":" + QString::number(port);
|
||||
}
|
||||
|
||||
libtorrent::entry QPeer::toEntry(bool no_peer_id) const
|
||||
{
|
||||
libtorrent::entry::dictionary_type peer_map;
|
||||
if (!no_peer_id)
|
||||
peer_map["id"] = libtorrent::entry(peer_id.toStdString());
|
||||
peer_map["ip"] = libtorrent::entry(ip.toStdString());
|
||||
peer_map["port"] = libtorrent::entry(port);
|
||||
|
||||
return libtorrent::entry(peer_map);
|
||||
}
|
||||
|
||||
// QTracker
|
||||
|
||||
QTracker::QTracker(QObject *parent)
|
||||
: Http::ResponseBuilder(parent)
|
||||
, m_server(new Http::Server(this, this))
|
||||
{
|
||||
}
|
||||
|
||||
QTracker::~QTracker()
|
||||
{
|
||||
if (m_server->isListening())
|
||||
qDebug("Shutting down the embedded tracker...");
|
||||
close();
|
||||
}
|
||||
// TODO: Store the torrent list
|
||||
}
|
||||
|
||||
void QTracker::handlePeerConnection()
|
||||
{
|
||||
QTcpSocket *socket;
|
||||
while((socket = nextPendingConnection()))
|
||||
{
|
||||
qDebug("QTracker: New peer connection");
|
||||
connect(socket, SIGNAL(readyRead()), SLOT(readRequest()));
|
||||
}
|
||||
}
|
||||
|
||||
bool QTracker::start()
|
||||
{
|
||||
const int listen_port = Preferences::instance()->getTrackerPort();
|
||||
//
|
||||
if (isListening()) {
|
||||
if (serverPort() == listen_port) {
|
||||
|
||||
if (m_server->isListening()) {
|
||||
if (m_server->serverPort() == listen_port) {
|
||||
// Already listening on the right port, just return
|
||||
return true;
|
||||
}
|
||||
// Wrong port, closing the server
|
||||
close();
|
||||
m_server->close();
|
||||
}
|
||||
|
||||
qDebug("Starting the embedded tracker...");
|
||||
// Listen on the predefined port
|
||||
return listen(QHostAddress::Any, listen_port);
|
||||
return m_server->listen(QHostAddress::Any, listen_port);
|
||||
}
|
||||
|
||||
void QTracker::readRequest()
|
||||
Http::Response QTracker::processRequest(const Http::Request &request, const Http::Environment &env)
|
||||
{
|
||||
QTcpSocket *socket = static_cast<QTcpSocket*>(sender());
|
||||
QByteArray input = socket->readAll();
|
||||
//qDebug("QTracker: Raw request:\n%s", input.data());
|
||||
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 (request.method != "GET") {
|
||||
qDebug("QTracker: Unsupported HTTP request: %s", qPrintable(request.method));
|
||||
respondInvalidRequest(socket, 100, "Invalid request type");
|
||||
return;
|
||||
}
|
||||
if (!request.path.startsWith("/announce", Qt::CaseInsensitive)) {
|
||||
qDebug("QTracker: Unrecognized path: %s", qPrintable(request.path));
|
||||
respondInvalidRequest(socket, 100, "Invalid request type");
|
||||
return;
|
||||
}
|
||||
clear(); // clear response
|
||||
|
||||
// OK, this is a GET request
|
||||
respondToAnnounceRequest(socket, request.gets);
|
||||
//qDebug("QTracker received the following request:\n%s", qPrintable(parser.toString()));
|
||||
// Is request a GET request?
|
||||
if (request.method != "GET") {
|
||||
qDebug("QTracker: Unsupported HTTP request: %s", qPrintable(request.method));
|
||||
status(100, "Invalid request type");
|
||||
}
|
||||
else if (!request.path.startsWith("/announce", Qt::CaseInsensitive)) {
|
||||
qDebug("QTracker: Unrecognized path: %s", qPrintable(request.path));
|
||||
status(100, "Invalid request type");
|
||||
}
|
||||
else {
|
||||
// OK, this is a GET request
|
||||
m_request = request;
|
||||
m_env = env;
|
||||
respondToAnnounceRequest();
|
||||
}
|
||||
|
||||
return response();
|
||||
}
|
||||
|
||||
void QTracker::respondInvalidRequest(QTcpSocket *socket, int code, QString msg)
|
||||
{
|
||||
HttpResponse response(code, msg);
|
||||
socket->write(HttpResponseGenerator::generate(response));
|
||||
socket->disconnectFromHost();
|
||||
}
|
||||
|
||||
void QTracker::respondToAnnounceRequest(QTcpSocket *socket,
|
||||
const QMap<QString, QString>& get_parameters)
|
||||
void QTracker::respondToAnnounceRequest()
|
||||
{
|
||||
const QStringMap &gets = m_request.gets;
|
||||
TrackerAnnounceRequest annonce_req;
|
||||
|
||||
// IP
|
||||
annonce_req.peer.ip = socket->peerAddress().toString();
|
||||
annonce_req.peer.ip = m_env.clientAddress.toString();
|
||||
|
||||
// 1. Get info_hash
|
||||
if (!get_parameters.contains("info_hash")) {
|
||||
if (!gets.contains("info_hash")) {
|
||||
qDebug("QTracker: Missing info_hash");
|
||||
respondInvalidRequest(socket, 101, "Missing info_hash");
|
||||
status(101, "Missing info_hash");
|
||||
return;
|
||||
}
|
||||
annonce_req.info_hash = get_parameters.value("info_hash");
|
||||
annonce_req.info_hash = gets.value("info_hash");
|
||||
// info_hash cannot be longer than 20 bytes
|
||||
/*if (annonce_req.info_hash.toLatin1().length() > 20) {
|
||||
qDebug("QTracker: Info_hash is not 20 byte long: %s (%d)", qPrintable(annonce_req.info_hash), annonce_req.info_hash.toLatin1().length());
|
||||
respondInvalidRequest(socket, 150, "Invalid infohash");
|
||||
status(150, "Invalid infohash");
|
||||
return;
|
||||
}*/
|
||||
|
||||
// 2. Get peer ID
|
||||
if (!get_parameters.contains("peer_id")) {
|
||||
if (!gets.contains("peer_id")) {
|
||||
qDebug("QTracker: Missing peer_id");
|
||||
respondInvalidRequest(socket, 102, "Missing peer_id");
|
||||
status(102, "Missing peer_id");
|
||||
return;
|
||||
}
|
||||
annonce_req.peer.peer_id = get_parameters.value("peer_id");
|
||||
annonce_req.peer.peer_id = gets.value("peer_id");
|
||||
// peer_id cannot be longer than 20 bytes
|
||||
/*if (annonce_req.peer.peer_id.length() > 20) {
|
||||
qDebug("QTracker: peer_id is not 20 byte long: %s", qPrintable(annonce_req.peer.peer_id));
|
||||
respondInvalidRequest(socket, 151, "Invalid peerid");
|
||||
status(151, "Invalid peerid");
|
||||
return;
|
||||
}*/
|
||||
|
||||
// 3. Get port
|
||||
if (!get_parameters.contains("port")) {
|
||||
if (!gets.contains("port")) {
|
||||
qDebug("QTracker: Missing port");
|
||||
respondInvalidRequest(socket, 103, "Missing port");
|
||||
status(103, "Missing port");
|
||||
return;
|
||||
}
|
||||
bool ok = false;
|
||||
annonce_req.peer.port = get_parameters.value("port").toInt(&ok);
|
||||
annonce_req.peer.port = gets.value("port").toInt(&ok);
|
||||
if (!ok || annonce_req.peer.port < 1 || annonce_req.peer.port > 65535) {
|
||||
qDebug("QTracker: Invalid port number (%d)", annonce_req.peer.port);
|
||||
respondInvalidRequest(socket, 103, "Missing port");
|
||||
status(103, "Missing port");
|
||||
return;
|
||||
}
|
||||
|
||||
// 4. Get event
|
||||
annonce_req.event = "";
|
||||
if (get_parameters.contains("event")) {
|
||||
annonce_req.event = get_parameters.value("event");
|
||||
if (gets.contains("event")) {
|
||||
annonce_req.event = gets.value("event");
|
||||
qDebug("QTracker: event is %s", qPrintable(annonce_req.event));
|
||||
}
|
||||
|
||||
// 5. Get numwant
|
||||
annonce_req.numwant = 50;
|
||||
if (get_parameters.contains("numwant")) {
|
||||
int tmp = get_parameters.value("numwant").toInt();
|
||||
if (gets.contains("numwant")) {
|
||||
int tmp = gets.value("numwant").toInt();
|
||||
if (tmp > 0) {
|
||||
qDebug("QTracker: numwant=%d", tmp);
|
||||
qDebug("QTracker: numwant = %d", tmp);
|
||||
annonce_req.numwant = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
// 6. no_peer_id (extension)
|
||||
annonce_req.no_peer_id = false;
|
||||
if (get_parameters.contains("no_peer_id")) {
|
||||
if (gets.contains("no_peer_id"))
|
||||
annonce_req.no_peer_id = true;
|
||||
}
|
||||
|
||||
// 7. TODO: support "compact" extension
|
||||
|
||||
// Done parsing, now let's reply
|
||||
if (m_torrents.contains(annonce_req.info_hash)) {
|
||||
if (annonce_req.event == "stopped") {
|
||||
@@ -207,33 +216,32 @@ void QTracker::respondToAnnounceRequest(QTcpSocket *socket,
|
||||
}
|
||||
peers[annonce_req.peer.qhash()] = annonce_req.peer;
|
||||
m_torrents[annonce_req.info_hash] = peers;
|
||||
|
||||
// Reply
|
||||
ReplyWithPeerList(socket, annonce_req);
|
||||
replyWithPeerList(annonce_req);
|
||||
}
|
||||
|
||||
void QTracker::ReplyWithPeerList(QTcpSocket *socket, const TrackerAnnounceRequest &annonce_req)
|
||||
void QTracker::replyWithPeerList(const TrackerAnnounceRequest &annonce_req)
|
||||
{
|
||||
// Prepare the entry for bencoding
|
||||
entry::dictionary_type reply_dict;
|
||||
reply_dict["interval"] = entry(ANNOUNCE_INTERVAL);
|
||||
libtorrent::entry::dictionary_type reply_dict;
|
||||
reply_dict["interval"] = libtorrent::entry(ANNOUNCE_INTERVAL);
|
||||
QList<QPeer> peers = m_torrents.value(annonce_req.info_hash).values();
|
||||
entry::list_type peer_list;
|
||||
libtorrent::entry::list_type peer_list;
|
||||
foreach (const QPeer & p, peers) {
|
||||
//if (p != annonce_req.peer)
|
||||
peer_list.push_back(p.toEntry(annonce_req.no_peer_id));
|
||||
}
|
||||
reply_dict["peers"] = entry(peer_list);
|
||||
entry reply_entry(reply_dict);
|
||||
reply_dict["peers"] = libtorrent::entry(peer_list);
|
||||
libtorrent::entry reply_entry(reply_dict);
|
||||
// bencode
|
||||
std::vector<char> buf;
|
||||
bencode(std::back_inserter(buf), reply_entry);
|
||||
libtorrent::bencode(std::back_inserter(buf), reply_entry);
|
||||
QByteArray reply(&buf[0], static_cast<int>(buf.size()));
|
||||
qDebug("QTracker: reply with the following bencoded data:\n %s", reply.constData());
|
||||
|
||||
// HTTP reply
|
||||
HttpResponse response(200, "OK");
|
||||
response.content = reply;
|
||||
socket->write(HttpResponseGenerator::generate(response));
|
||||
socket->disconnectFromHost();
|
||||
print(reply, Http::CONTENT_TYPE_TXT);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt4 and libtorrent.
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
|
||||
* Copyright (C) 2010 Christophe Dumez
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
@@ -31,11 +32,33 @@
|
||||
#ifndef QTRACKER_H
|
||||
#define QTRACKER_H
|
||||
|
||||
#include <QTcpServer>
|
||||
#include <libtorrent/entry.hpp>
|
||||
#include <QHash>
|
||||
#include "http/types.h"
|
||||
#include "http/responsebuilder.h"
|
||||
#include "http/irequesthandler.h"
|
||||
|
||||
#include "trackerannouncerequest.h"
|
||||
#include "qpeer.h"
|
||||
struct QPeer
|
||||
{
|
||||
QString ip;
|
||||
QString peer_id;
|
||||
int port;
|
||||
|
||||
bool operator!=(const QPeer &other) const;
|
||||
bool operator==(const QPeer &other) const;
|
||||
QString qhash() const;
|
||||
libtorrent::entry toEntry(bool no_peer_id) const;
|
||||
};
|
||||
|
||||
struct TrackerAnnounceRequest
|
||||
{
|
||||
QString info_hash;
|
||||
QString event;
|
||||
int numwant;
|
||||
QPeer peer;
|
||||
// Extensions
|
||||
bool no_peer_id;
|
||||
};
|
||||
|
||||
// static limits
|
||||
const int MAX_TORRENTS = 100;
|
||||
@@ -45,9 +68,11 @@ const int ANNOUNCE_INTERVAL = 1800; // 30min
|
||||
typedef QHash<QString, QPeer> PeerList;
|
||||
typedef QHash<QString, PeerList> TorrentList;
|
||||
|
||||
/* Basic Bittorrent tracker implementation in Qt4 */
|
||||
namespace Http { class Server; }
|
||||
|
||||
/* Basic Bittorrent tracker implementation in Qt */
|
||||
/* Following http://wiki.theory.org/BitTorrent_Tracker_Protocol */
|
||||
class QTracker : public QTcpServer
|
||||
class QTracker : public Http::ResponseBuilder, public Http::IRequestHandler
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_DISABLE_COPY(QTracker)
|
||||
@@ -55,18 +80,19 @@ class QTracker : public QTcpServer
|
||||
public:
|
||||
explicit QTracker(QObject *parent = 0);
|
||||
~QTracker();
|
||||
bool start();
|
||||
|
||||
protected slots:
|
||||
void readRequest();
|
||||
void handlePeerConnection();
|
||||
void respondInvalidRequest(QTcpSocket *socket, int code, QString msg);
|
||||
void respondToAnnounceRequest(QTcpSocket *socket, const QMap<QString, QString>& get_parameters);
|
||||
void ReplyWithPeerList(QTcpSocket *socket, const TrackerAnnounceRequest &annonce_req);
|
||||
bool start();
|
||||
Http::Response processRequest(const Http::Request &request, const Http::Environment &env);
|
||||
|
||||
private:
|
||||
void respondToAnnounceRequest();
|
||||
void replyWithPeerList(const TrackerAnnounceRequest &annonce_req);
|
||||
|
||||
Http::Server *m_server;
|
||||
TorrentList m_torrents;
|
||||
|
||||
Http::Request m_request;
|
||||
Http::Environment m_env;
|
||||
};
|
||||
|
||||
#endif // QTRACKER_H
|
||||
@@ -1,35 +0,0 @@
|
||||
#ifndef QPEER_H
|
||||
#define QPEER_H
|
||||
|
||||
#include <libtorrent/entry.hpp>
|
||||
#include <QString>
|
||||
|
||||
struct QPeer {
|
||||
|
||||
bool operator!=(const QPeer &other) const {
|
||||
return qhash() != other.qhash();
|
||||
}
|
||||
|
||||
bool operator==(const QPeer &other) const {
|
||||
return qhash() == other.qhash();
|
||||
}
|
||||
|
||||
QString qhash() const {
|
||||
return ip+":"+QString::number(port);
|
||||
}
|
||||
|
||||
libtorrent::entry toEntry(bool no_peer_id) const {
|
||||
libtorrent::entry::dictionary_type peer_map;
|
||||
if (!no_peer_id)
|
||||
peer_map["id"] = libtorrent::entry(peer_id.toStdString());
|
||||
peer_map["ip"] = libtorrent::entry(ip.toStdString());
|
||||
peer_map["port"] = libtorrent::entry(port);
|
||||
return libtorrent::entry(peer_map);
|
||||
}
|
||||
|
||||
QString ip;
|
||||
QString peer_id;
|
||||
int port;
|
||||
};
|
||||
|
||||
#endif // QPEER_H
|
||||
@@ -1,9 +0,0 @@
|
||||
INCLUDEPATH += $$PWD
|
||||
|
||||
HEADERS += \
|
||||
$$PWD/qtracker.h \
|
||||
$$PWD/trackerannouncerequest.h \
|
||||
$$PWD/qpeer.h
|
||||
|
||||
SOURCES += \
|
||||
$$PWD/qtracker.cpp
|
||||
@@ -1,15 +0,0 @@
|
||||
#ifndef TRACKERANNOUNCEREQUEST_H
|
||||
#define TRACKERANNOUNCEREQUEST_H
|
||||
|
||||
#include <qpeer.h>
|
||||
|
||||
struct TrackerAnnounceRequest {
|
||||
QString info_hash;
|
||||
QString event;
|
||||
int numwant;
|
||||
QPeer peer;
|
||||
// Extensions
|
||||
bool no_peer_id;
|
||||
};
|
||||
|
||||
#endif // TRACKERANNOUNCEREQUEST_H
|
||||
Reference in New Issue
Block a user