Browse Source

Parse URL query string at application level

adaptive-webui-19844
Vladimir Golovnev (Glassez) 7 years ago
parent
commit
1aca3b0adc
No known key found for this signature in database
GPG Key ID: 52A2C7DEE2DFA6F7
  1. 37
      src/base/bittorrent/tracker.cpp
  2. 6
      src/base/bittorrent/tracker.h
  3. 17
      src/base/http/requestparser.cpp
  4. 2
      src/base/http/types.h
  5. 30
      src/webui/webapplication.cpp
  6. 1
      src/webui/webapplication.h

37
src/base/bittorrent/tracker.cpp

@ -32,8 +32,10 @@
#include <libtorrent/bencode.hpp> #include <libtorrent/bencode.hpp>
#include <libtorrent/entry.hpp> #include <libtorrent/entry.hpp>
#include "base/global.h"
#include "base/http/server.h" #include "base/http/server.h"
#include "base/preferences.h" #include "base/preferences.h"
#include "base/utils/bytearray.h"
#include "base/utils/string.h" #include "base/utils/string.h"
#include "tracker.h" #include "tracker.h"
@ -130,19 +132,30 @@ Http::Response Tracker::processRequest(const Http::Request &request, const Http:
void Tracker::respondToAnnounceRequest() void Tracker::respondToAnnounceRequest()
{ {
const QStringMap &gets = m_request.gets; QMap<QString, QByteArray> queryParams;
// Parse GET parameters
using namespace Utils::ByteArray;
for (const QByteArray &param : copyAsConst(splitToViews(m_request.query, "&"))) {
const int sepPos = param.indexOf('=');
if (sepPos <= 0) continue; // ignores params without name
const QString paramName {QString::fromUtf8(param.constData(), sepPos)};
const QByteArray paramValue {param.mid(sepPos + 1)};
queryParams[paramName] = paramValue;
}
TrackerAnnounceRequest annonceReq; TrackerAnnounceRequest annonceReq;
// IP // IP
annonceReq.peer.ip = m_env.clientAddress.toString(); annonceReq.peer.ip = m_env.clientAddress.toString();
// 1. Get info_hash // 1. Get info_hash
if (!gets.contains("info_hash")) { if (!queryParams.contains("info_hash")) {
qDebug("Tracker: Missing info_hash"); qDebug("Tracker: Missing info_hash");
status(101, "Missing info_hash"); status(101, "Missing info_hash");
return; return;
} }
annonceReq.infoHash = gets.value("info_hash"); annonceReq.infoHash = queryParams.value("info_hash");
// info_hash cannot be longer than 20 bytes // info_hash cannot be longer than 20 bytes
/*if (annonce_req.info_hash.toLatin1().length() > 20) { /*if (annonce_req.info_hash.toLatin1().length() > 20) {
qDebug("Tracker: Info_hash is not 20 byte long: %s (%d)", qUtf8Printable(annonce_req.info_hash), annonce_req.info_hash.toLatin1().length()); qDebug("Tracker: Info_hash is not 20 byte long: %s (%d)", qUtf8Printable(annonce_req.info_hash), annonce_req.info_hash.toLatin1().length());
@ -151,12 +164,12 @@ void Tracker::respondToAnnounceRequest()
}*/ }*/
// 2. Get peer ID // 2. Get peer ID
if (!gets.contains("peer_id")) { if (!queryParams.contains("peer_id")) {
qDebug("Tracker: Missing peer_id"); qDebug("Tracker: Missing peer_id");
status(102, "Missing peer_id"); status(102, "Missing peer_id");
return; return;
} }
annonceReq.peer.peerId = gets.value("peer_id"); annonceReq.peer.peerId = queryParams.value("peer_id");
// peer_id cannot be longer than 20 bytes // peer_id cannot be longer than 20 bytes
/*if (annonce_req.peer.peer_id.length() > 20) { /*if (annonce_req.peer.peer_id.length() > 20) {
qDebug("Tracker: peer_id is not 20 byte long: %s", qUtf8Printable(annonce_req.peer.peer_id)); qDebug("Tracker: peer_id is not 20 byte long: %s", qUtf8Printable(annonce_req.peer.peer_id));
@ -165,13 +178,13 @@ void Tracker::respondToAnnounceRequest()
}*/ }*/
// 3. Get port // 3. Get port
if (!gets.contains("port")) { if (!queryParams.contains("port")) {
qDebug("Tracker: Missing port"); qDebug("Tracker: Missing port");
status(103, "Missing port"); status(103, "Missing port");
return; return;
} }
bool ok = false; bool ok = false;
annonceReq.peer.port = gets.value("port").toInt(&ok); annonceReq.peer.port = queryParams.value("port").toInt(&ok);
if (!ok || annonceReq.peer.port < 1 || annonceReq.peer.port > 65535) { if (!ok || annonceReq.peer.port < 1 || annonceReq.peer.port > 65535) {
qDebug("Tracker: Invalid port number (%d)", annonceReq.peer.port); qDebug("Tracker: Invalid port number (%d)", annonceReq.peer.port);
status(103, "Missing port"); status(103, "Missing port");
@ -180,15 +193,15 @@ void Tracker::respondToAnnounceRequest()
// 4. Get event // 4. Get event
annonceReq.event = ""; annonceReq.event = "";
if (gets.contains("event")) { if (queryParams.contains("event")) {
annonceReq.event = gets.value("event"); annonceReq.event = queryParams.value("event");
qDebug("Tracker: event is %s", qUtf8Printable(annonceReq.event)); qDebug("Tracker: event is %s", qUtf8Printable(annonceReq.event));
} }
// 5. Get numwant // 5. Get numwant
annonceReq.numwant = 50; annonceReq.numwant = 50;
if (gets.contains("numwant")) { if (queryParams.contains("numwant")) {
int tmp = gets.value("numwant").toInt(); int tmp = queryParams.value("numwant").toInt();
if (tmp > 0) { if (tmp > 0) {
qDebug("Tracker: numwant = %d", tmp); qDebug("Tracker: numwant = %d", tmp);
annonceReq.numwant = tmp; annonceReq.numwant = tmp;
@ -197,7 +210,7 @@ void Tracker::respondToAnnounceRequest()
// 6. no_peer_id (extension) // 6. no_peer_id (extension)
annonceReq.noPeerId = false; annonceReq.noPeerId = false;
if (gets.contains("no_peer_id")) if (queryParams.contains("no_peer_id"))
annonceReq.noPeerId = true; annonceReq.noPeerId = true;
// 7. TODO: support "compact" extension // 7. TODO: support "compact" extension

6
src/base/bittorrent/tracker.h

@ -52,7 +52,7 @@ namespace BitTorrent
struct Peer struct Peer
{ {
QString ip; QString ip;
QString peerId; QByteArray peerId;
int port; int port;
bool operator!=(const Peer &other) const; bool operator!=(const Peer &other) const;
@ -63,7 +63,7 @@ namespace BitTorrent
struct TrackerAnnounceRequest struct TrackerAnnounceRequest
{ {
QString infoHash; QByteArray infoHash;
QString event; QString event;
int numwant; int numwant;
Peer peer; Peer peer;
@ -72,7 +72,7 @@ namespace BitTorrent
}; };
typedef QHash<QString, Peer> PeerList; typedef QHash<QString, Peer> PeerList;
typedef QHash<QString, PeerList> TorrentList; typedef QHash<QByteArray, PeerList> TorrentList;
/* Basic Bittorrent tracker implementation in Qt */ /* Basic Bittorrent tracker implementation in Qt */
/* Following http://wiki.theory.org/BitTorrent_Tracker_Protocol */ /* Following http://wiki.theory.org/BitTorrent_Tracker_Protocol */

17
src/base/http/requestparser.cpp

@ -40,14 +40,13 @@
#include "base/utils/string.h" #include "base/utils/string.h"
using namespace Http; using namespace Http;
using namespace Utils::ByteArray;
using QStringPair = QPair<QString, QString>; using QStringPair = QPair<QString, QString>;
namespace namespace
{ {
const QByteArray EOH = QByteArray(CRLF).repeated(2); const QByteArray EOH = QByteArray(CRLF).repeated(2);
using namespace Utils::ByteArray;
const QByteArray viewWithoutEndingWith(const QByteArray &in, const QByteArray &str) const QByteArray viewWithoutEndingWith(const QByteArray &in, const QByteArray &str)
{ {
if (in.endsWith(str)) if (in.endsWith(str))
@ -181,15 +180,11 @@ bool RequestParser::parseRequestLine(const QString &line)
m_request.method = match.captured(1); m_request.method = match.captured(1);
// Request Target // Request Target
const QUrl url = QUrl::fromEncoded(match.captured(2).toLatin1()); const QByteArray decodedUrl {QByteArray::fromPercentEncoding(match.captured(2).toLatin1())};
m_request.path = url.path(); const int sepPos = decodedUrl.indexOf('?');
m_request.path = QString::fromUtf8(decodedUrl.constData(), (sepPos == -1 ? decodedUrl.size() : sepPos));
// parse queries if (sepPos >= 0)
QListIterator<QStringPair> i(QUrlQuery(url).queryItems()); m_request.query = decodedUrl.mid(sepPos + 1);
while (i.hasNext()) {
const QStringPair pair = i.next();
m_request.gets[pair.first] = pair.second;
}
// HTTP-version // HTTP-version
m_request.version = match.captured(3); m_request.version = match.captured(3);

2
src/base/http/types.h

@ -98,8 +98,8 @@ namespace Http
QString version; QString version;
QString method; QString method;
QString path; QString path;
QByteArray query;
QStringMap headers; QStringMap headers;
QStringMap gets;
QStringMap posts; QStringMap posts;
QVector<UploadedFile> files; QVector<UploadedFile> files;
}; };

30
src/webui/webapplication.cpp

@ -46,10 +46,12 @@
#include <QTimer> #include <QTimer>
#include <QUrl> #include <QUrl>
#include "base/global.h"
#include "base/http/httperror.h" #include "base/http/httperror.h"
#include "base/iconprovider.h" #include "base/iconprovider.h"
#include "base/logger.h" #include "base/logger.h"
#include "base/preferences.h" #include "base/preferences.h"
#include "base/utils/bytearray.h"
#include "base/utils/fs.h" #include "base/utils/fs.h"
#include "base/utils/misc.h" #include "base/utils/misc.h"
#include "base/utils/net.h" #include "base/utils/net.h"
@ -245,8 +247,6 @@ const Http::Environment &WebApplication::env() const
void WebApplication::doProcessRequest() void WebApplication::doProcessRequest()
{ {
QStringMap *params = &((request().method == QLatin1String("GET"))
? m_request.gets : m_request.posts);
QString scope, action; QString scope, action;
const auto findAPICall = [&]() -> bool const auto findAPICall = [&]() -> bool
@ -337,13 +337,13 @@ void WebApplication::doProcessRequest()
action = compatInfo.action; action = compatInfo.action;
if (legacyAction == QLatin1String("command/delete")) if (legacyAction == QLatin1String("command/delete"))
(*params)["deleteFiles"] = "false"; m_params["deleteFiles"] = "false";
else if (legacyAction == QLatin1String("command/deletePerm")) else if (legacyAction == QLatin1String("command/deletePerm"))
(*params)["deleteFiles"] = "true"; m_params["deleteFiles"] = "true";
const QString hash {match.captured(QLatin1String("hash"))}; const QString hash {match.captured(QLatin1String("hash"))};
if (!hash.isEmpty()) if (!hash.isEmpty())
(*params)[QLatin1String("hash")] = hash; m_params[QLatin1String("hash")] = hash;
return true; return true;
}; };
@ -379,7 +379,7 @@ void WebApplication::doProcessRequest()
data[torrent.filename] = torrent.data; data[torrent.filename] = torrent.data;
try { try {
const QVariant result = controller->run(action, *params, data); const QVariant result = controller->run(action, m_params, data);
switch (result.userType()) { switch (result.userType()) {
case QMetaType::QString: case QMetaType::QString:
print(result.toString(), Http::CONTENT_TYPE_TXT); print(result.toString(), Http::CONTENT_TYPE_TXT);
@ -486,6 +486,24 @@ Http::Response WebApplication::processRequest(const Http::Request &request, cons
m_currentSession = nullptr; m_currentSession = nullptr;
m_request = request; m_request = request;
m_env = env; m_env = env;
m_params.clear();
if (m_request.method == Http::METHOD_GET) {
// Parse GET parameters
using namespace Utils::ByteArray;
for (const QByteArray &param : copyAsConst(splitToViews(m_request.query, "&"))) {
const int sepPos = param.indexOf('=');
if (sepPos <= 0) continue; // ignores params without name
const QString paramName {QString::fromUtf8(param.constData(), sepPos)};
const int valuePos = sepPos + 1;
const QString paramValue {
QString::fromUtf8(param.constData() + valuePos, param.size() - valuePos)};
m_params[paramName] = paramValue;
}
}
else {
m_params = m_request.posts;
}
// clear response // clear response
clear(); clear();

1
src/webui/webapplication.h

@ -124,6 +124,7 @@ private:
WebSession *m_currentSession = nullptr; WebSession *m_currentSession = nullptr;
Http::Request m_request; Http::Request m_request;
Http::Environment m_env; Http::Environment m_env;
QMap<QString, QString> m_params;
const QRegularExpression m_apiPathPattern {(QLatin1String("^/api/v2/(?<scope>[A-Za-z_][A-Za-z_0-9]*)/(?<action>[A-Za-z_][A-Za-z_0-9]*)$"))}; const QRegularExpression m_apiPathPattern {(QLatin1String("^/api/v2/(?<scope>[A-Za-z_][A-Za-z_0-9]*)/(?<action>[A-Za-z_][A-Za-z_0-9]*)$"))};
const QRegularExpression m_apiLegacyPathPattern {QLatin1String("^/(?<action>((sync|command|query)/[A-Za-z_][A-Za-z_0-9]*|login|logout))(/(?<hash>[^/]+))?$")}; const QRegularExpression m_apiLegacyPathPattern {QLatin1String("^/(?<action>((sync|command|query)/[A-Za-z_][A-Za-z_0-9]*|login|logout))(/(?<hash>[^/]+))?$")};

Loading…
Cancel
Save