mirror of
https://github.com/qbittorrent/qBittorrent
synced 2025-07-31 03:50:20 -07:00
Follow project coding style (Issue #2192).
This commit is contained in:
parent
2707f5205f
commit
898d454b78
11 changed files with 710 additions and 766 deletions
|
@ -31,7 +31,6 @@
|
|||
|
||||
#include <QStringList>
|
||||
#include <QUrl>
|
||||
//#include <QVariant>
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
|
||||
#include <QUrlQuery>
|
||||
#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();
|
||||
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<int>(m_maxContentLength))
|
||||
{
|
||||
qWarning() << Q_FUNC_INFO << "bad request: message too long";
|
||||
return BadRequest;
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
||||
// qDebug() << Q_FUNC_INFO;
|
||||
// qDebug() << "HTTP Request header:";
|
||||
// qDebug() << data.left(header_end) << "\n";
|
||||
// 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<int>(m_maxContentLength)) {
|
||||
qWarning() << Q_FUNC_INFO << "bad request: message too long";
|
||||
return BadRequest;
|
||||
}
|
||||
|
||||
request = m_request;
|
||||
return NoError;
|
||||
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;
|
||||
}
|
||||
|
||||
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<QPair<QString, QString> > i(url.queryItems());
|
||||
QListIterator<QPair<QString, QString> > i(url.queryItems());
|
||||
#else
|
||||
QListIterator<QPair<QString, QString> > i(QUrlQuery(url).queryItems());
|
||||
QListIterator<QPair<QString, QString> > i(QUrlQuery(url).queryItems());
|
||||
#endif
|
||||
while (i.hasNext())
|
||||
{
|
||||
QPair<QString, QString> pair = i.next();
|
||||
m_request.gets[pair.first] = pair.second;
|
||||
while (i.hasNext()) {
|
||||
QPair<QString, QString> 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<QString, QString>& 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);
|
||||
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();
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
headerLines.append(line);
|
||||
|
||||
if (headerLines.isEmpty())
|
||||
return false; // Empty header
|
||||
|
||||
QStringList::Iterator it = headerLines.begin();
|
||||
if (!parseStartingLine(*it))
|
||||
return false;
|
||||
|
||||
++it;
|
||||
for (; it != headerLines.end(); ++it) {
|
||||
QPair<QString, QString> header;
|
||||
if (!parseHeaderLine(*it, header))
|
||||
return false;
|
||||
|
||||
m_request.headers[header.first] = header.second;
|
||||
}
|
||||
}
|
||||
|
||||
if (headerLines.isEmpty())
|
||||
return false; // Empty header
|
||||
|
||||
QStringList::Iterator it = headerLines.begin();
|
||||
if (!parseStartingLine(*it))
|
||||
return false;
|
||||
|
||||
++it;
|
||||
for (; it != headerLines.end(); ++it)
|
||||
{
|
||||
QPair<QString, QString> header;
|
||||
if (!parseHeaderLine(*it, header))
|
||||
return false;
|
||||
|
||||
m_request.headers[header.first] = header.second;
|
||||
}
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
QList<QByteArray> RequestParser::splitMultipartData(const QByteArray& data, const QByteArray& boundary)
|
||||
{
|
||||
QList<QByteArray> ret;
|
||||
QByteArray sep = boundary + EOL;
|
||||
const int sepLength = sep.size();
|
||||
QList<QByteArray> 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
|
||||
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;
|
||||
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 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 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<QPair<QString, QString> > i(url.queryItems());
|
||||
url.setEncodedQuery(data);
|
||||
QListIterator<QPair<QString, QString> > i(url.queryItems());
|
||||
#else
|
||||
url.setQuery(data);
|
||||
QListIterator<QPair<QString, QString> > i(QUrlQuery(url).queryItems(QUrl::FullyDecoded));
|
||||
url.setQuery(data);
|
||||
QListIterator<QPair<QString, QString> > i(QUrlQuery(url).queryItems(QUrl::FullyDecoded));
|
||||
#endif
|
||||
while (i.hasNext())
|
||||
{
|
||||
QPair<QString, QString> pair = i.next();
|
||||
m_request.posts[pair.first.toLower()] = pair.second;
|
||||
while (i.hasNext()) {
|
||||
QPair<QString, QString> 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'()+,-\\./:=\\?]+)");
|
||||
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();
|
||||
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<QByteArray> parts = splitMultipartData(data, boundary);
|
||||
qDebug() << parts.size() << "parts in data";
|
||||
|
||||
foreach (const QByteArray& part, parts) {
|
||||
if (!parseFormData(part))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
qDebug() << "Boundary is " << boundary;
|
||||
QList<QByteArray> parts = splitMultipartData(data, boundary);
|
||||
qDebug() << parts.size() << "parts in data";
|
||||
|
||||
foreach (const QByteArray& part, parts)
|
||||
{
|
||||
if (!parseFormData(part))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
qWarning() << Q_FUNC_INFO << "unknown content type:" << qPrintable(content_type);
|
||||
return false;
|
||||
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;
|
||||
}
|
||||
// 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<QString, QString> header;
|
||||
if (!parseHeaderLine(line, header))
|
||||
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<QString, QString> header;
|
||||
if (!parseHeaderLine(line, header))
|
||||
return false;
|
||||
|
||||
headers[header.first] = header.second;
|
||||
}
|
||||
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;
|
||||
}
|
||||
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());
|
||||
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()));
|
||||
}
|
||||
m_request.files[disposition["name"]] = ufile;
|
||||
}
|
||||
else {
|
||||
m_request.posts[disposition["name"]] = QString::fromUtf8(data.mid(header_end + EOH.length()));
|
||||
}
|
||||
|
||||
return true;
|
||||
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;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue