From 712e6a0e5c9fdafb6fd9920cfd5b6faefac042ee Mon Sep 17 00:00:00 2001 From: Chocobo1 Date: Wed, 24 May 2017 23:24:48 +0800 Subject: [PATCH 1/2] Refactor out helper function Utils::String::unquote Remove redundant include --- src/app/cmdoptions.cpp | 32 ++++++++++---------------------- src/base/utils/string.h | 17 +++++++++++++++-- 2 files changed, 25 insertions(+), 24 deletions(-) diff --git a/src/app/cmdoptions.cpp b/src/app/cmdoptions.cpp index c3fa354c9..8e9dc919b 100644 --- a/src/app/cmdoptions.cpp +++ b/src/app/cmdoptions.cpp @@ -44,6 +44,7 @@ #endif #include "base/utils/misc.h" +#include "base/utils/string.h" namespace { @@ -86,7 +87,7 @@ namespace static QString padUsageText(const QString &usage) { QString res = QString(USAGE_INDENTATION, ' ') + usage; - + if ((USAGE_TEXT_COLUMN - usage.length() - 4) > 0) return res + QString(USAGE_TEXT_COLUMN - usage.length() - 4, ' '); else @@ -153,7 +154,7 @@ namespace { QStringList parts = arg.split(QLatin1Char('=')); if (parts.size() == 2) - return unquote(parts[1]); + return Utils::String::unquote(parts[1], QLatin1String("'\"")); throw CommandLineParameterError(QObject::tr("Parameter '%1' must follow syntax '%1=%2'", "e.g. Parameter '--webui-port' must follow syntax '--webui-port=value'") .arg(fullParameter()).arg(QLatin1String(""))); @@ -162,7 +163,7 @@ namespace QString value(const QProcessEnvironment &env, const QString &defaultValue = QString()) const { QString val = env.value(envVarName()); - return val.isEmpty() ? defaultValue : unquote(val); + return val.isEmpty() ? defaultValue : Utils::String::unquote(val, QLatin1String("'\"")); } QString usage(const QString &valueName) const @@ -175,19 +176,6 @@ namespace { return fullParameter() + QLatin1Char('='); } - - static QString unquote(const QString &s) - { - auto isStringQuoted = - [](const QString &s, QChar quoteChar) - { - return (s.startsWith(quoteChar) && s.endsWith(quoteChar)); - }; - - if ((s.size() >= 2) && (isStringQuoted(s, QLatin1Char('\'')) || isStringQuoted(s, QLatin1Char('"')))) - return s.mid(1, s.size() - 2); - return s; - } }; bool operator==(const QString &s, const StringOption &o) @@ -290,7 +278,7 @@ namespace TriStateBool value(const QProcessEnvironment &env) const { QString val = env.value(envVarName(), "-1"); - + if (val.isEmpty()) { return TriStateBool(m_defaultValue); } @@ -374,7 +362,7 @@ QStringList QBtCommandLineParameters::paramList() const // specified by the user, and placing them at the beginning of the // string listr so that they will be processed before the list of // torrent paths or URLs. - + if (!savePath.isEmpty()) result.append(QString("@savePath=%1").arg(savePath)); @@ -384,7 +372,7 @@ QStringList QBtCommandLineParameters::paramList() const else if (addPaused == TriStateBool::False) { result.append(QLatin1String("@addPaused=0")); } - + if (skipChecking) result.append(QLatin1String("@skipChecking")); @@ -510,7 +498,7 @@ QString wrapText(const QString &text, int initialIndentation = USAGE_TEXT_COLUMN QStringList words = text.split(' '); QStringList lines = {words.first()}; int currentLineMaxLength = wrapAtColumn - initialIndentation; - + foreach (const QString &word, words.mid(1)) { if (lines.last().length() + word.length() + 1 < currentLineMaxLength) { lines.last().append(" " + word); @@ -520,7 +508,7 @@ QString wrapText(const QString &text, int initialIndentation = USAGE_TEXT_COLUMN currentLineMaxLength = wrapAtColumn; } } - + return lines.join("\n"); } @@ -559,7 +547,7 @@ QString makeUsage(const QString &prgName) stream << Option::padUsageText(QObject::tr("files or urls")) << wrapText(QObject::tr("Downloads the torrents passed by the user")) << '\n' << '\n'; - + stream << wrapText(QObject::tr("Options when adding new torrents:"), 0) << '\n'; stream << SAVE_PATH_OPTION.usage(QObject::tr("path")) << wrapText(QObject::tr("Torrent save path")) << '\n'; stream << PAUSED_OPTION.usage() << wrapText(QObject::tr("Add torrents as started or paused")) << '\n'; diff --git a/src/base/utils/string.h b/src/base/utils/string.h index 763b4e600..e5257de14 100644 --- a/src/base/utils/string.h +++ b/src/base/utils/string.h @@ -30,10 +30,10 @@ #ifndef UTILS_STRING_H #define UTILS_STRING_H -#include +#include class QByteArray; -class QString; +class QLatin1String; namespace Utils { @@ -49,6 +49,19 @@ namespace Utils bool naturalCompareCaseInsensitive(const QString &left, const QString &right); QString wildcardToRegex(const QString &pattern); + + template + T unquote(const T &str, const QString "es = QLatin1String("\"")) + { + if (str.length() < 2) return str; + + for (auto const quote : quotes) { + if (str.startsWith(quote) && str.endsWith(quote)) + return str.mid(1, str.length() - 2); + } + + return str; + } } } From f35a5c8085d0b78062ba002022576d8909051868 Mon Sep 17 00:00:00 2001 From: Chocobo1 Date: Tue, 23 May 2017 14:23:03 +0800 Subject: [PATCH 2/2] [WebUI] Make cookie parsing robust Previously cookie name such as "SID" will be mistakenly accepted as "SID" session ID, this commit fixes it. Use QString::isEmpty() --- src/webui/abstractwebapplication.cpp | 36 ++++++++++++++++++---------- src/webui/abstractwebapplication.h | 2 ++ 2 files changed, 25 insertions(+), 13 deletions(-) diff --git a/src/webui/abstractwebapplication.cpp b/src/webui/abstractwebapplication.cpp index 7f45153d5..77bb3b605 100644 --- a/src/webui/abstractwebapplication.cpp +++ b/src/webui/abstractwebapplication.cpp @@ -40,6 +40,7 @@ #include "base/preferences.h" #include "base/utils/fs.h" #include "base/utils/random.h" +#include "base/utils/string.h" #include "websessiondata.h" // UnbanTimer @@ -147,24 +148,13 @@ void AbstractWebApplication::removeInactiveSessions() bool AbstractWebApplication::sessionInitialize() { - 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) { - pos += SID_START.length(); - int end = cookie.indexOf(QRegExp("[,;]"), pos); - sessionId = cookie.mid(pos, end >= 0 ? end - pos : end); - } + const QString sessionId = parseCookie(request_).value(C_SID); // TODO: Additional session check - if (!sessionId.isNull()) { + if (!sessionId.isEmpty()) { if (sessions_.contains(sessionId)) { session_ = sessions_[sessionId]; session_->updateTimestamp(); @@ -386,3 +376,23 @@ const QStringMap AbstractWebApplication::CONTENT_TYPE_BY_EXT = { { "png", Http::CONTENT_TYPE_PNG }, { "js", Http::CONTENT_TYPE_JS } }; + +QStringMap AbstractWebApplication::parseCookie(const Http::Request &request) const +{ + // [rfc6265] 4.2.1. Syntax + QStringMap ret; + const QString cookieStr = request.headers.value(QLatin1String("cookie")); + const QVector cookies = cookieStr.splitRef(';', QString::SkipEmptyParts); + + for (const auto &cookie : cookies) { + const int idx = cookie.indexOf('='); + if (idx < 0) + continue; + + const QString name = cookie.left(idx).trimmed().toString(); + const QString value = Utils::String::unquote(cookie.mid(idx + 1).trimmed()) + .toString(); + ret.insert(name, value); + } + return ret; +} diff --git a/src/webui/abstractwebapplication.h b/src/webui/abstractwebapplication.h index d05ea90ba..0e5577eed 100644 --- a/src/webui/abstractwebapplication.h +++ b/src/webui/abstractwebapplication.h @@ -100,6 +100,8 @@ private: QString generateSid(); bool sessionInitialize(); + QStringMap parseCookie(const Http::Request &request) const; + static void translateDocument(QString &data); static const QStringMap CONTENT_TYPE_BY_EXT;