diff --git a/src/core/http/connection.cpp b/src/core/http/connection.cpp index 435d2e73c..84985e895 100644 --- a/src/core/http/connection.cpp +++ b/src/core/http/connection.cpp @@ -40,13 +40,13 @@ using namespace Http; Connection::Connection(QTcpSocket *socket, IRequestHandler *requestHandler, QObject *parent) - : QObject(parent) - , m_socket(socket) - , m_requestHandler(requestHandler) + : 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())); + m_socket->setParent(this); + connect(m_socket, SIGNAL(readyRead()), SLOT(read())); + connect(m_socket, SIGNAL(disconnected()), SLOT(deleteLater())); } Connection::~Connection() @@ -55,57 +55,56 @@ Connection::~Connection() void Connection::read() { - m_receivedData.append(m_socket->readAll()); + 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; - } + 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(); + 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; + 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; + // 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); + //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)); + 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; + if (value.toDouble() == 0.0) + return false; - return true; + return true; } diff --git a/src/core/http/connection.h b/src/core/http/connection.h index 4d3b28243..19c8acb6a 100644 --- a/src/core/http/connection.h +++ b/src/core/http/connection.h @@ -47,23 +47,23 @@ class IRequestHandler; class Connection : public QObject { - Q_OBJECT - Q_DISABLE_COPY(Connection) + Q_OBJECT + Q_DISABLE_COPY(Connection) public: - Connection(QTcpSocket *socket, IRequestHandler *requestHandler, QObject *parent = 0); - ~Connection(); + Connection(QTcpSocket *socket, IRequestHandler *requestHandler, QObject *parent = 0); + ~Connection(); private slots: - void read(); + void read(); private: - static bool acceptsGzipEncoding(const QString &encoding); - void sendResponse(const Response &response); + static bool acceptsGzipEncoding(const QString &encoding); + void sendResponse(const Response &response); - QTcpSocket *m_socket; - IRequestHandler *m_requestHandler; - QByteArray m_receivedData; + QTcpSocket *m_socket; + IRequestHandler *m_requestHandler; + QByteArray m_receivedData; }; } diff --git a/src/core/http/requestparser.cpp b/src/core/http/requestparser.cpp index d056aa539..4a766a900 100644 --- a/src/core/http/requestparser.cpp +++ b/src/core/http/requestparser.cpp @@ -31,7 +31,6 @@ #include #include -//#include #if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) #include #endif @@ -45,214 +44,196 @@ const QByteArray EOH("\r\n\r\n"); inline QString unquoted(const QString& str) { - if ((str[0] == '\"') && (str[str.length() - 1] == '\"')) - return str.mid(1, str.length() - 2); + if ((str[0] == '\"') && (str[str.length() - 1] == '\"')) + return str.mid(1, str.length() - 2); - return str; + return str; } using namespace Http; RequestParser::ErrorCode RequestParser::parse(const QByteArray& data, Request& request, uint maxContentLength) { - return RequestParser(maxContentLength).parseHttpRequest(data, request); + return RequestParser(maxContentLength).parseHttpRequest(data, request); } RequestParser::RequestParser(uint maxContentLength) - : m_maxContentLength(maxContentLength) + : m_maxContentLength(maxContentLength) { } RequestParser::ErrorCode RequestParser::parseHttpRequest(const QByteArray& data, Request& request) { - m_request = Request(); - - // Parse HTTP request header - const int header_end = data.indexOf(EOH); - if (header_end < 0) - { - qDebug() << Q_FUNC_INFO << "incomplete request"; - return IncompleteRequest; - } - - if (!parseHttpHeader(data.left(header_end))) - { - qWarning() << Q_FUNC_INFO << "header parsing error"; - return BadRequest; - } - - // Parse HTTP request message - int content_length = 0; - if (m_request.headers.contains("content-length")) - { - content_length = m_request.headers["content-length"].toInt(); - if (content_length > static_cast(m_maxContentLength)) - { - qWarning() << Q_FUNC_INFO << "bad request: message too long"; - return BadRequest; + m_request = Request(); + + // Parse HTTP request header + const int header_end = data.indexOf(EOH); + if (header_end < 0) { + qDebug() << Q_FUNC_INFO << "incomplete request"; + return IncompleteRequest; } - QByteArray content = data.mid(header_end + EOH.length(), content_length); - if (content.length() < content_length) - { - qDebug() << Q_FUNC_INFO << "incomplete request"; - return IncompleteRequest; + if (!parseHttpHeader(data.left(header_end))) { + qWarning() << Q_FUNC_INFO << "header parsing error"; + return BadRequest; } - if (!parseContent(content)) - { - qWarning() << Q_FUNC_INFO << "message parsing error"; - return BadRequest; + // Parse HTTP request message + int content_length = 0; + if (m_request.headers.contains("content-length")) { + content_length = m_request.headers["content-length"].toInt(); + if (content_length > static_cast(m_maxContentLength)) { + qWarning() << Q_FUNC_INFO << "bad request: message too long"; + return BadRequest; + } + + 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 = m_request; - return NoError; + + // qDebug() << Q_FUNC_INFO; + // qDebug() << "HTTP Request header:"; + // qDebug() << data.left(header_end) << "\n"; + + request = m_request; + return NoError; } bool RequestParser::parseStartingLine(const QString &line) { - const QRegExp rx("^([A-Z]+)\\s+(\\S+)\\s+HTTP/\\d\\.\\d$"); + const QRegExp rx("^([A-Z]+)\\s+(\\S+)\\s+HTTP/\\d\\.\\d$"); - if (rx.indexIn(line.trimmed()) >= 0) - { - m_request.method = rx.cap(1); + if (rx.indexIn(line.trimmed()) >= 0) { + m_request.method = rx.cap(1); - QUrl url = QUrl::fromEncoded(rx.cap(2).toLatin1()); - m_request.path = url.path(); // Path + QUrl url = QUrl::fromEncoded(rx.cap(2).toLatin1()); + m_request.path = url.path(); // Path - // Parse GET parameters + // Parse GET parameters #if (QT_VERSION < QT_VERSION_CHECK(5, 0, 0)) - QListIterator > i(url.queryItems()); + QListIterator > i(url.queryItems()); #else - QListIterator > i(QUrlQuery(url).queryItems()); + QListIterator > i(QUrlQuery(url).queryItems()); #endif - while (i.hasNext()) - { - QPair pair = i.next(); - m_request.gets[pair.first] = pair.second; - } + while (i.hasNext()) { + QPair pair = i.next(); + m_request.gets[pair.first] = pair.second; + } - return true; - } + return true; + } - qWarning() << Q_FUNC_INFO << "invalid http header:" << line; - return false; + qWarning() << Q_FUNC_INFO << "invalid http header:" << line; + return false; } bool RequestParser::parseHeaderLine(const QString &line, QPair& out) { - int i = line.indexOf(QLatin1Char(':')); - if (i == -1) - { - qWarning() << Q_FUNC_INFO << "invalid http header:" << line; - return false; - } + 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; + out = qMakePair(line.left(i).trimmed().toLower(), line.mid(i + 1).trimmed()); + return true; } bool RequestParser::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); + 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 + if (headerLines.isEmpty()) + return false; // Empty header - QStringList::Iterator it = headerLines.begin(); - if (!parseStartingLine(*it)) - return false; + QStringList::Iterator it = headerLines.begin(); + if (!parseStartingLine(*it)) + return false; - ++it; - for (; it != headerLines.end(); ++it) - { - QPair header; - if (!parseHeaderLine(*it, header)) - return false; + ++it; + for (; it != headerLines.end(); ++it) { + QPair header; + if (!parseHeaderLine(*it, header)) + return false; - m_request.headers[header.first] = header.second; - } + m_request.headers[header.first] = header.second; + } - return true; + return true; } QList RequestParser::splitMultipartData(const QByteArray& data, const QByteArray& boundary) { - QList ret; - QByteArray sep = boundary + EOL; - const int sepLength = sep.size(); - - 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; + QList ret; + QByteArray sep = boundary + EOL; + const int sepLength = sep.size(); + + 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); } - // last or single part - sep = boundary + "--" + EOL; - if ((end = data.indexOf(sep, start)) >= 0) - ret << data.mid(start, end - start); - } - - return ret; + return ret; } bool RequestParser::parseContent(const QByteArray& data) { - // Parse message content - 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 (m_request.headers["content-type"].startsWith("application/x-www-form-urlencoded")) - { - QUrl url; + // Parse message content + 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 (m_request.headers["content-type"].startsWith("application/x-www-form-urlencoded")) { + QUrl url; #if (QT_VERSION < QT_VERSION_CHECK(5, 0, 0)) - url.setEncodedQuery(data); - QListIterator > i(url.queryItems()); + url.setEncodedQuery(data); + QListIterator > i(url.queryItems()); #else - url.setQuery(data); - QListIterator > i(QUrlQuery(url).queryItems(QUrl::FullyDecoded)); + url.setQuery(data); + QListIterator > i(QUrlQuery(url).queryItems(QUrl::FullyDecoded)); #endif - while (i.hasNext()) - { - QPair pair = i.next(); - m_request.posts[pair.first.toLower()] = pair.second; - } + while (i.hasNext()) { + QPair pair = i.next(); + m_request.posts[pair.first.toLower()] = pair.second; + } - return true; - } + return true; + } - // Parse multipart/form data (torrent file) - /** + // Parse multipart/form data (torrent file) + /** data has the following format (if boundary is "cH2ae0GI3KM7GI3Ij5ae0ei4Ij5Ij5") --cH2ae0GI3KM7GI3Ij5ae0ei4Ij5Ij5 @@ -270,108 +251,96 @@ Content-Disposition: form-data; name=\"Upload\" Submit Query --cH2ae0GI3KM7GI3Ij5ae0ei4Ij5Ij5-- **/ - QString content_type = m_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(content_type) < 0) - { - if (boundaryRegexNotQuoted.indexIn(content_type) < 0) - { - qWarning() << "Could not find boundary in multipart/form-data header!"; - return false; - } - else - { - boundary = "--" + boundaryRegexNotQuoted.cap(1).toLatin1(); - } + QString content_type = m_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(content_type) < 0) { + if (boundaryRegexNotQuoted.indexIn(content_type) < 0) { + qWarning() << "Could not find boundary in multipart/form-data header!"; + return false; + } + else { + boundary = "--" + boundaryRegexNotQuoted.cap(1).toLatin1(); + } + } + else { + boundary = "--" + boundaryRegexQuoted.cap(1).toLatin1(); + } + + qDebug() << "Boundary is " << boundary; + QList parts = splitMultipartData(data, boundary); + qDebug() << parts.size() << "parts in data"; + + foreach (const QByteArray& part, parts) { + if (!parseFormData(part)) + return false; + } + + return true; } - else - { - boundary = "--" + boundaryRegexQuoted.cap(1).toLatin1(); + + qWarning() << Q_FUNC_INFO << "unknown content type:" << qPrintable(content_type); + return false; +} + +bool RequestParser::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; } - qDebug() << "Boundary is " << boundary; - QList parts = splitMultipartData(data, boundary); - qDebug() << parts.size() << "parts in data"; + 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; - foreach (const QByteArray& part, parts) - { - if (!parseFormData(part)) + headers[header.first] = header.second; + } + + 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; } - - return true; - } - qWarning() << Q_FUNC_INFO << "unknown content type:" << qPrintable(content_type); - 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()); -bool RequestParser::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; - } - - 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; - - headers[header.first] = header.second; - } - - 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()); - - m_request.files[disposition["name"]] = ufile; - } - else - { - m_request.posts[disposition["name"]] = QString::fromUtf8(data.mid(header_end + EOH.length())); - } - - return true; + m_request.files[disposition["name"]] = ufile; + } + else { + m_request.posts[disposition["name"]] = QString::fromUtf8(data.mid(header_end + EOH.length())); + } + + return true; } bool RequestParser::parseHeaderValue(const QString& value, QStringMap& out) { - QStringList items = value.split(QLatin1Char(';')); - out[""] = items[0]; + 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; + 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()); - } + out[items[i].left(pos).trimmed()] = unquoted(items[i].mid(pos + 1).trimmed()); + } - return true; + return true; } diff --git a/src/core/http/requestparser.h b/src/core/http/requestparser.h index 01ddb7176..ec8265e38 100644 --- a/src/core/http/requestparser.h +++ b/src/core/http/requestparser.h @@ -40,28 +40,33 @@ namespace Http class RequestParser { public: - enum ErrorCode { NoError = 0, IncompleteRequest, BadRequest }; + 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, Request& request, uint maxContentLength = 10000000 /* ~10MB */); + // when result != NoError parsed request is undefined + // Warning! Header names are converted to lower-case. + static ErrorCode parse(const QByteArray &data, Request &request, uint maxContentLength = 10000000 /* ~10MB */); private: - RequestParser(uint maxContentLength); + RequestParser(uint maxContentLength); - ErrorCode parseHttpRequest(const QByteArray& data, Request& request); + ErrorCode parseHttpRequest(const QByteArray &data, Request &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); + 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); + static bool parseHeaderLine(const QString &line, QPair &out); + static bool parseHeaderValue(const QString &value, QStringMap &out); - const uint m_maxContentLength; - Request m_request; + const uint m_maxContentLength; + Request m_request; }; } diff --git a/src/core/http/responsegenerator.cpp b/src/core/http/responsegenerator.cpp index 64f0ffbf7..f6aad3afa 100644 --- a/src/core/http/responsegenerator.cpp +++ b/src/core/http/responsegenerator.cpp @@ -38,96 +38,87 @@ using namespace Http; QByteArray ResponseGenerator::generate(Response response) { - 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; + 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); } - else - { - response.headers.remove(HEADER_CONTENT_ENCODING); - } - } - if (response.content.length() > 0) - response.headers[HEADER_CONTENT_LENGTH] = QString::number(response.content.length()); + 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 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]); - QString header; - foreach (const QString& key, response.headers.keys()) - header += QString("%1: %2\r\n").arg(key).arg(response.headers[key]); + ret = ret.arg(response.status.code).arg(response.status.text).arg(header); - 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; + // qDebug() << Q_FUNC_INFO; + // qDebug() << "HTTP Response header:"; + // qDebug() << ret; + + return ret.toUtf8() + response.content; } bool gCompress(QByteArray data, QByteArray& dest_buffer) { - static const int BUFSIZE = 128 * 1024; - char tmp_buf[BUFSIZE]; - int ret; - - z_stream strm; - strm.zalloc = Z_NULL; - strm.zfree = Z_NULL; - strm.opaque = Z_NULL; - strm.next_in = reinterpret_cast(data.data()); - strm.avail_in = data.length(); - strm.next_out = reinterpret_cast(tmp_buf); - strm.avail_out = BUFSIZE; - - //windowBits = 15+16 to enable gzip - //From the zlib manual: windowBits can also be greater than 15 for optional gzip encoding. Add 16 to windowBits - //to write a simple gzip header and trailer around the compressed data instead of a zlib wrapper. - ret = deflateInit2(&strm, Z_BEST_COMPRESSION, Z_DEFLATED, 15+16, 8, Z_DEFAULT_STRATEGY); - - if (ret != Z_OK) - return false; - - while (strm.avail_in != 0) - { - ret = deflate(&strm, Z_NO_FLUSH); - if (ret != Z_OK) - return false; + static const int BUFSIZE = 128 * 1024; + char tmp_buf[BUFSIZE]; + int ret; + + z_stream strm; + strm.zalloc = Z_NULL; + strm.zfree = Z_NULL; + strm.opaque = Z_NULL; + strm.next_in = reinterpret_cast(data.data()); + strm.avail_in = data.length(); + strm.next_out = reinterpret_cast(tmp_buf); + strm.avail_out = BUFSIZE; + + //windowBits = 15+16 to enable gzip + //From the zlib manual: windowBits can also be greater than 15 for optional gzip encoding. Add 16 to windowBits + //to write a simple gzip header and trailer around the compressed data instead of a zlib wrapper. + ret = deflateInit2(&strm, Z_BEST_COMPRESSION, Z_DEFLATED, 15 + 16, 8, Z_DEFAULT_STRATEGY); - if (strm.avail_out == 0) - { - 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) - { - dest_buffer.append(tmp_buf, BUFSIZE); - strm.next_out = reinterpret_cast(tmp_buf); - strm.avail_out = BUFSIZE; + if (ret != Z_OK) + 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; + } } - deflate_res = deflate(&strm, Z_FINISH); - } + int deflate_res = Z_OK; + 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; + if (deflate_res != Z_STREAM_END) + return false; - dest_buffer.append(tmp_buf, BUFSIZE - strm.avail_out); - deflateEnd(&strm); + dest_buffer.append(tmp_buf, BUFSIZE - strm.avail_out); + deflateEnd(&strm); - return true; + return true; } diff --git a/src/core/http/server.cpp b/src/core/http/server.cpp index 6430f9745..8bf905fec 100644 --- a/src/core/http/server.cpp +++ b/src/core/http/server.cpp @@ -39,8 +39,8 @@ using namespace Http; Server::Server(IRequestHandler *requestHandler, QObject* parent) - : QTcpServer(parent) - , m_requestHandler(requestHandler) + : QTcpServer(parent) + , m_requestHandler(requestHandler) #ifndef QT_NO_OPENSSL , m_https(false) #endif @@ -54,16 +54,16 @@ Server::~Server() #ifndef QT_NO_OPENSSL void Server::enableHttps(const QSslCertificate &certificate, const QSslKey &key) { - m_certificate = certificate; - m_key = key; - m_https = true; + m_certificate = certificate; + m_key = key; + m_https = true; } void Server::disableHttps() { - m_https = false; - m_certificate.clear(); - m_key.clear(); + m_https = false; + m_certificate.clear(); + m_key.clear(); } #endif @@ -73,28 +73,26 @@ void Server::incomingConnection(qintptr socketDescriptor) void Server::incomingConnection(int socketDescriptor) #endif { - QTcpSocket *serverSocket; + QTcpSocket *serverSocket; #ifndef QT_NO_OPENSSL - if (m_https) - serverSocket = new QSslSocket(this); - else + if (m_https) + serverSocket = new QSslSocket(this); + else #endif - serverSocket = new QTcpSocket(this); - if (serverSocket->setSocketDescriptor(socketDescriptor)) - { + serverSocket = new QTcpSocket(this); + + if (serverSocket->setSocketDescriptor(socketDescriptor)) { #ifndef QT_NO_OPENSSL - 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(); - } + 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 - new Connection(serverSocket, m_requestHandler, this); - } - else - { - serverSocket->deleteLater(); - } + new Connection(serverSocket, m_requestHandler, this); + } + else { + serverSocket->deleteLater(); + } } diff --git a/src/core/http/server.h b/src/core/http/server.h index 5ee591421..c8cb774c3 100644 --- a/src/core/http/server.h +++ b/src/core/http/server.h @@ -47,31 +47,31 @@ class Connection; class Server : public QTcpServer { - Q_OBJECT - Q_DISABLE_COPY(Server) + Q_OBJECT + Q_DISABLE_COPY(Server) public: - Server(IRequestHandler *requestHandler, QObject *parent = 0); - ~Server(); + Server(IRequestHandler *requestHandler, QObject *parent = 0); + ~Server(); #ifndef QT_NO_OPENSSL - void enableHttps(const QSslCertificate &certificate, const QSslKey &key); - void disableHttps(); + 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); + void incomingConnection(qintptr socketDescriptor); #else - void incomingConnection(int socketDescriptor); + void incomingConnection(int socketDescriptor); #endif private: - IRequestHandler *m_requestHandler; + IRequestHandler *m_requestHandler; #ifndef QT_NO_OPENSSL - bool m_https; - QSslCertificate m_certificate; - QSslKey m_key; + bool m_https; + QSslCertificate m_certificate; + QSslKey m_key; #endif }; diff --git a/src/core/qtracker.cpp b/src/core/qtracker.cpp index 5afad0d50..cc7146861 100644 --- a/src/core/qtracker.cpp +++ b/src/core/qtracker.cpp @@ -73,27 +73,27 @@ QTracker::QTracker(QObject *parent) QTracker::~QTracker() { - if (m_server->isListening()) - qDebug("Shutting down the embedded tracker..."); - // TODO: Store the torrent list + if (m_server->isListening()) + qDebug("Shutting down the embedded tracker..."); + // TODO: Store the torrent list } bool QTracker::start() { - const int listen_port = Preferences::instance()->getTrackerPort(); - - if (m_server->isListening()) { - if (m_server->serverPort() == listen_port) { - // Already listening on the right port, just return - return true; + const int listen_port = Preferences::instance()->getTrackerPort(); + + 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 + m_server->close(); } - // Wrong port, closing the server - m_server->close(); - } - qDebug("Starting the embedded tracker..."); - // Listen on the predefined port - return m_server->listen(QHostAddress::Any, listen_port); + qDebug("Starting the embedded tracker..."); + // Listen on the predefined port + return m_server->listen(QHostAddress::Any, listen_port); } Http::Response QTracker::processRequest(const Http::Request &request, const Http::Environment &env) @@ -103,12 +103,12 @@ Http::Response QTracker::processRequest(const Http::Request &request, const Http //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"); + 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"); + qDebug("QTracker: Unrecognized path: %s", qPrintable(request.path)); + status(100, "Invalid request type"); } else { // OK, this is a GET request @@ -122,126 +122,127 @@ Http::Response QTracker::processRequest(const Http::Request &request, const Http void QTracker::respondToAnnounceRequest() { - const QStringMap &gets = m_request.gets; - TrackerAnnounceRequest annonce_req; - - // IP - annonce_req.peer.ip = m_env.clientAddress.toString(); - - // 1. Get info_hash - if (!gets.contains("info_hash")) { - qDebug("QTracker: Missing info_hash"); - status(101, "Missing info_hash"); - return; - } - 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()); - status(150, "Invalid infohash"); - return; - }*/ - - // 2. Get peer ID - if (!gets.contains("peer_id")) { - qDebug("QTracker: Missing peer_id"); - status(102, "Missing peer_id"); - return; - } - 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)); - status(151, "Invalid peerid"); - return; - }*/ - - // 3. Get port - if (!gets.contains("port")) { - qDebug("QTracker: Missing port"); - status(103, "Missing port"); - return; - } - bool ok = false; - 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); - status(103, "Missing port"); - return; - } - - // 4. Get event - annonce_req.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 (gets.contains("numwant")) { - int tmp = gets.value("numwant").toInt(); - if (tmp > 0) { - qDebug("QTracker: numwant = %d", tmp); - annonce_req.numwant = tmp; + const QStringMap &gets = m_request.gets; + TrackerAnnounceRequest annonce_req; + + // IP + annonce_req.peer.ip = m_env.clientAddress.toString(); + + // 1. Get info_hash + if (!gets.contains("info_hash")) { + qDebug("QTracker: Missing info_hash"); + status(101, "Missing info_hash"); + return; + } + 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()); + status(150, "Invalid infohash"); + return; + }*/ + + // 2. Get peer ID + if (!gets.contains("peer_id")) { + qDebug("QTracker: Missing peer_id"); + status(102, "Missing peer_id"); + return; + } + 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)); + status(151, "Invalid peerid"); + return; + }*/ + + // 3. Get port + if (!gets.contains("port")) { + qDebug("QTracker: Missing port"); + status(103, "Missing port"); + return; + } + bool ok = false; + 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); + status(103, "Missing port"); + return; + } + + // 4. Get event + annonce_req.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 (gets.contains("numwant")) { + int tmp = gets.value("numwant").toInt(); + if (tmp > 0) { + qDebug("QTracker: numwant = %d", tmp); + annonce_req.numwant = tmp; + } } - } - // 6. no_peer_id (extension) - annonce_req.no_peer_id = false; - if (gets.contains("no_peer_id")) - annonce_req.no_peer_id = true; + // 6. no_peer_id (extension) + annonce_req.no_peer_id = false; + if (gets.contains("no_peer_id")) + annonce_req.no_peer_id = true; - // 7. TODO: support "compact" extension + // 7. TODO: support "compact" extension - // Done parsing, now let's reply - if (m_torrents.contains(annonce_req.info_hash)) { - if (annonce_req.event == "stopped") { - qDebug("QTracker: Peer stopped downloading, deleting it from the list"); - m_torrents[annonce_req.info_hash].remove(annonce_req.peer.qhash()); - return; + // Done parsing, now let's reply + if (m_torrents.contains(annonce_req.info_hash)) { + if (annonce_req.event == "stopped") { + qDebug("QTracker: Peer stopped downloading, deleting it from the list"); + m_torrents[annonce_req.info_hash].remove(annonce_req.peer.qhash()); + return; + } } - } else { - // Unknown torrent - if (m_torrents.size() == MAX_TORRENTS) { - // Reached max size, remove a random torrent - m_torrents.erase(m_torrents.begin()); + else { + // Unknown torrent + if (m_torrents.size() == MAX_TORRENTS) { + // Reached max size, remove a random torrent + m_torrents.erase(m_torrents.begin()); + } } - } - // Register the user - PeerList peers = m_torrents.value(annonce_req.info_hash); - if (peers.size() == MAX_PEERS_PER_TORRENT) { - // Too many peers, remove a random one - peers.erase(peers.begin()); - } - peers[annonce_req.peer.qhash()] = annonce_req.peer; - m_torrents[annonce_req.info_hash] = peers; - - // Reply - replyWithPeerList(annonce_req); + // Register the user + PeerList peers = m_torrents.value(annonce_req.info_hash); + if (peers.size() == MAX_PEERS_PER_TORRENT) { + // Too many peers, remove a random one + peers.erase(peers.begin()); + } + peers[annonce_req.peer.qhash()] = annonce_req.peer; + m_torrents[annonce_req.info_hash] = peers; + + // Reply + replyWithPeerList(annonce_req); } void QTracker::replyWithPeerList(const TrackerAnnounceRequest &annonce_req) { - // Prepare the entry for bencoding - libtorrent::entry::dictionary_type reply_dict; - reply_dict["interval"] = libtorrent::entry(ANNOUNCE_INTERVAL); - QList peers = m_torrents.value(annonce_req.info_hash).values(); - 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"] = libtorrent::entry(peer_list); - libtorrent::entry reply_entry(reply_dict); - // bencode - std::vector buf; - libtorrent::bencode(std::back_inserter(buf), reply_entry); - QByteArray reply(&buf[0], static_cast(buf.size())); - qDebug("QTracker: reply with the following bencoded data:\n %s", reply.constData()); - - // HTTP reply - print(reply, Http::CONTENT_TYPE_TXT); + // Prepare the entry for bencoding + libtorrent::entry::dictionary_type reply_dict; + reply_dict["interval"] = libtorrent::entry(ANNOUNCE_INTERVAL); + QList peers = m_torrents.value(annonce_req.info_hash).values(); + 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"] = libtorrent::entry(peer_list); + libtorrent::entry reply_entry(reply_dict); + // bencode + std::vector buf; + libtorrent::bencode(std::back_inserter(buf), reply_entry); + QByteArray reply(&buf[0], static_cast(buf.size())); + qDebug("QTracker: reply with the following bencoded data:\n %s", reply.constData()); + + // HTTP reply + print(reply, Http::CONTENT_TYPE_TXT); } diff --git a/src/core/qtracker.h b/src/core/qtracker.h index 520480d78..3c1484f7a 100644 --- a/src/core/qtracker.h +++ b/src/core/qtracker.h @@ -74,25 +74,25 @@ namespace Http { class Server; } /* Following http://wiki.theory.org/BitTorrent_Tracker_Protocol */ class QTracker : public Http::ResponseBuilder, public Http::IRequestHandler { - Q_OBJECT - Q_DISABLE_COPY(QTracker) + Q_OBJECT + Q_DISABLE_COPY(QTracker) public: - explicit QTracker(QObject *parent = 0); - ~QTracker(); + explicit QTracker(QObject *parent = 0); + ~QTracker(); - bool start(); - Http::Response processRequest(const Http::Request &request, const Http::Environment &env); + bool start(); + Http::Response processRequest(const Http::Request &request, const Http::Environment &env); private: - void respondToAnnounceRequest(); - void replyWithPeerList(const TrackerAnnounceRequest &annonce_req); + void respondToAnnounceRequest(); + void replyWithPeerList(const TrackerAnnounceRequest &annonce_req); - Http::Server *m_server; - TorrentList m_torrents; + Http::Server *m_server; + TorrentList m_torrents; - Http::Request m_request; - Http::Environment m_env; + Http::Request m_request; + Http::Environment m_env; }; #endif // QTRACKER_H diff --git a/src/webui/abstractwebapplication.cpp b/src/webui/abstractwebapplication.cpp index be73940f9..1e13e1318 100644 --- a/src/webui/abstractwebapplication.cpp +++ b/src/webui/abstractwebapplication.cpp @@ -44,17 +44,17 @@ class UnbanTimer: public QTimer { public: - UnbanTimer(const QHostAddress& peer_ip, QObject *parent) - : QTimer(parent), m_peerIp(peer_ip) - { - setSingleShot(true); - setInterval(BAN_TIME); - } + 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; } + inline const QHostAddress& peerIp() const { return m_peerIp; } private: - QHostAddress m_peerIp; + QHostAddress m_peerIp; }; // WebSession @@ -79,8 +79,8 @@ struct WebSession // AbstractWebApplication AbstractWebApplication::AbstractWebApplication(QObject *parent) - : Http::ResponseBuilder(parent) - , session_(0) + : Http::ResponseBuilder(parent) + , session_(0) { QTimer *timer = new QTimer(this); timer->setInterval(60000); // 1 min. @@ -89,7 +89,7 @@ AbstractWebApplication::AbstractWebApplication(QObject *parent) AbstractWebApplication::~AbstractWebApplication() { - // cleanup sessions data + // cleanup sessions data qDeleteAll(sessions_); } @@ -117,10 +117,10 @@ Http::Response AbstractWebApplication::processRequest(const Http::Request &reque void AbstractWebApplication::UnbanTimerEvent() { - UnbanTimer* ubantimer = static_cast(sender()); - qDebug("Ban period has expired for %s", qPrintable(ubantimer->peerIp().toString())); - clientFailedAttempts_.remove(ubantimer->peerIp()); - ubantimer->deleteLater(); + UnbanTimer* ubantimer = static_cast(sender()); + qDebug("Ban period has expired for %s", qPrintable(ubantimer->peerIp().toString())); + clientFailedAttempts_.remove(ubantimer->peerIp()); + ubantimer->deleteLater(); } void AbstractWebApplication::removeInactiveSessions() @@ -135,84 +135,74 @@ void AbstractWebApplication::removeInactiveSessions() bool AbstractWebApplication::sessionInitialize() { - static const QString SID_START = QLatin1String(C_SID) + QLatin1String("="); + static const QString SID_START = QLatin1String(C_SID) + QLatin1String("="); - if (session_ == 0) - { - QString cookie = request_.headers.value("cookie"); - //qDebug() << Q_FUNC_INFO << "cookie: " << cookie; - - QString sessionId; - int pos = cookie.indexOf(SID_START); - if (pos >= 0) + if (session_ == 0) { - pos += SID_START.length(); - int end = cookie.indexOf(QRegExp("[,;]"), pos); - sessionId = cookie.mid(pos, end >= 0 ? end - pos : end); - } - - // TODO: Additional session check + QString cookie = request_.headers.value("cookie"); + //qDebug() << Q_FUNC_INFO << "cookie: " << cookie; + + QString sessionId; + 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); + } - if (!sessionId.isNull()) - { - if (sessions_.contains(sessionId)) - { - session_ = sessions_[sessionId]; - session_->updateTimestamp(); - return true; - } - else - { - qDebug() << Q_FUNC_INFO << "session does not exist!"; - } + // TODO: Additional session check + + if (!sessionId.isNull()) { + if (sessions_.contains(sessionId)) { + session_ = sessions_[sessionId]; + session_->updateTimestamp(); + return true; + } + else { + qDebug() << Q_FUNC_INFO << "session does not exist!"; + } + } } - } - return false; + return false; } bool AbstractWebApplication::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; + 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(); + 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); + // 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); - } + if (path.endsWith("about.html")) + dataStr.replace("${VERSION}", VERSION); - data = dataStr.toUtf8(); - translatedFiles_[path] = data; // cashing translated file + data = dataStr.toUtf8(); + translatedFiles_[path] = data; // cashing translated file + } } - } - type = CONTENT_TYPE_BY_EXT[ext]; - return true; + type = CONTENT_TYPE_BY_EXT[ext]; + return true; } WebSessionData *AbstractWebApplication::session() @@ -227,102 +217,95 @@ QString AbstractWebApplication::generateSid() QString sid; qsrand(QDateTime::currentDateTime().toTime_t()); - do - { - const size_t size = 6; - quint32 tmp[size]; + do { + const size_t size = 6; + quint32 tmp[size]; - for (size_t i = 0; i < size; ++i) - tmp[i] = qrand(); + 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)); + sid = QByteArray::fromRawData(reinterpret_cast(tmp), sizeof(quint32) * size).toBase64(); + } + while (sessions_.contains(sid)); - return sid; + return sid; } void AbstractWebApplication::translateDocument(QString& data) { - const QRegExp regex("QBT_TR\\((([^\\)]|\\)(?!QBT_TR))+)\\)QBT_TR"); - 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", - "StatusBar" - }; - 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)) - { + const QRegExp regex("QBT_TR\\((([^\\)]|\\)(?!QBT_TR))+)\\)QBT_TR"); + 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", + "StatusBar" + }; + 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); + 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); + translation = qApp->translate(contexts[context_index].c_str(), word.constData(), 0, 1); #endif - ++context_index; + ++context_index; + } + } + // Remove keyboard shortcuts + translation.replace(mnemonic, ""); + + data.replace(i, regex.matchedLength(), translation); + i += translation.length(); + } + else { + found = false; // no more translatable strings } - } - // Remove keyboard shortcuts - translation.replace(mnemonic, ""); - - data.replace(i, regex.matchedLength(), translation); - i += translation.length(); - } - else - { - found = false; // no more translatable strings } - } } bool AbstractWebApplication::isBanned() const { - return clientFailedAttempts_.value(env_.clientAddress, 0) >= MAX_AUTH_FAILED_ATTEMPTS; + return clientFailedAttempts_.value(env_.clientAddress, 0) >= MAX_AUTH_FAILED_ATTEMPTS; } int AbstractWebApplication::failedAttempts() const { - return clientFailedAttempts_.value(env_.clientAddress, 0); + return clientFailedAttempts_.value(env_.clientAddress, 0); } void AbstractWebApplication::resetFailedAttempts() { - clientFailedAttempts_.remove(env_.clientAddress); + clientFailedAttempts_.remove(env_.clientAddress); } void AbstractWebApplication::increaseFailedAttempts() { - const int nb_fail = clientFailedAttempts_.value(env_.clientAddress, 0) + 1; - - clientFailedAttempts_[env_.clientAddress] = nb_fail; - if (nb_fail == MAX_AUTH_FAILED_ATTEMPTS) - { - // Max number of failed attempts reached - // Start ban period - UnbanTimer* ubantimer = new UnbanTimer(env_.clientAddress, this); - connect(ubantimer, SIGNAL(timeout()), SLOT(UnbanTimerEvent())); - ubantimer->start(); - } + const int nb_fail = clientFailedAttempts_.value(env_.clientAddress, 0) + 1; + + clientFailedAttempts_[env_.clientAddress] = nb_fail; + if (nb_fail == MAX_AUTH_FAILED_ATTEMPTS) { + // Max number of failed attempts reached + // Start ban period + UnbanTimer* ubantimer = new UnbanTimer(env_.clientAddress, this); + connect(ubantimer, SIGNAL(timeout()), SLOT(UnbanTimerEvent())); + ubantimer->start(); + } } bool AbstractWebApplication::isAuthNeeded() @@ -347,39 +330,37 @@ void AbstractWebApplication::printFile(const QString& path) bool AbstractWebApplication::sessionStart() { - if (session_ == 0) - { - session_ = new WebSession(generateSid()); - session_->updateTimestamp(); - sessions_[session_->id] = session_; + if (session_ == 0) { + session_ = new WebSession(generateSid()); + session_->updateTimestamp(); + sessions_[session_->id] = session_; - QNetworkCookie cookie(C_SID, session_->id.toUtf8()); - cookie.setPath(QLatin1String("/")); - header(Http::HEADER_SET_COOKIE, cookie.toRawForm()); + QNetworkCookie cookie(C_SID, session_->id.toUtf8()); + cookie.setPath(QLatin1String("/")); + header(Http::HEADER_SET_COOKIE, cookie.toRawForm()); - return true; - } + return true; + } - return false; + return false; } bool AbstractWebApplication::sessionEnd() { - if ((session_ != 0) && (sessions_.contains(session_->id))) - { - QNetworkCookie cookie(C_SID, session_->id.toUtf8()); - cookie.setPath(QLatin1String("/")); - cookie.setExpirationDate(QDateTime::currentDateTime()); + if ((session_ != 0) && (sessions_.contains(session_->id))) { + QNetworkCookie cookie(C_SID, session_->id.toUtf8()); + cookie.setPath(QLatin1String("/")); + cookie.setExpirationDate(QDateTime::currentDateTime()); - sessions_.remove(session_->id); - delete session_; - session_ = 0; + sessions_.remove(session_->id); + delete session_; + session_ = 0; - header(Http::HEADER_SET_COOKIE, cookie.toRawForm()); - return true; - } + header(Http::HEADER_SET_COOKIE, cookie.toRawForm()); + return true; + } - return false; + return false; } QString AbstractWebApplication::saveTmpFile(const QByteArray &data) @@ -398,16 +379,16 @@ QString AbstractWebApplication::saveTmpFile(const QByteArray &data) QStringMap AbstractWebApplication::initializeContentTypeByExtMap() { - QStringMap map; + QStringMap map; - map["htm"] = Http::CONTENT_TYPE_HTML; - map["html"] = Http::CONTENT_TYPE_HTML; - map["css"] = Http::CONTENT_TYPE_CSS; - map["gif"] = Http::CONTENT_TYPE_GIF; - map["png"] = Http::CONTENT_TYPE_PNG; - map["js"] = Http::CONTENT_TYPE_JS; + map["htm"] = Http::CONTENT_TYPE_HTML; + map["html"] = Http::CONTENT_TYPE_HTML; + map["css"] = Http::CONTENT_TYPE_CSS; + map["gif"] = Http::CONTENT_TYPE_GIF; + map["png"] = Http::CONTENT_TYPE_PNG; + map["js"] = Http::CONTENT_TYPE_JS; - return map; + return map; } const QStringMap AbstractWebApplication::CONTENT_TYPE_BY_EXT = AbstractWebApplication::initializeContentTypeByExtMap(); diff --git a/src/webui/extra_translations.h b/src/webui/extra_translations.h index 840228afe..460531152 100644 --- a/src/webui/extra_translations.h +++ b/src/webui/extra_translations.h @@ -33,44 +33,44 @@ // 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"), - QT_TRANSLATE_NOOP("HttpServer", "Upload Failed!") + 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"), + QT_TRANSLATE_NOOP("HttpServer", "Upload Failed!") }; static const struct { const char *source; const char *comment; } __COMMENTED_TRANSLATIONS__[] = { - QT_TRANSLATE_NOOP3("HttpServer", "Downloaded", "Is the file downloaded or not?") + QT_TRANSLATE_NOOP3("HttpServer", "Downloaded", "Is the file downloaded or not?") }; #endif // EXTRA_TRANSLATIONS_H