summaryrefslogtreecommitdiffstats
path: root/src/network/access/qrestreply.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/network/access/qrestreply.cpp')
-rw-r--r--src/network/access/qrestreply.cpp64
1 files changed, 56 insertions, 8 deletions
diff --git a/src/network/access/qrestreply.cpp b/src/network/access/qrestreply.cpp
index 155e5877fdc..cdd264ba6eb 100644
--- a/src/network/access/qrestreply.cpp
+++ b/src/network/access/qrestreply.cpp
@@ -420,6 +420,54 @@ static constexpr bool is_tchar(char ch) noexcept
}
}
+static constexpr auto parse_comment(QByteArrayView data) noexcept
+{
+ struct R {
+ QByteArrayView comment, tail;
+ constexpr explicit operator bool() const noexcept { return !comment.isEmpty(); }
+ };
+
+ const auto invalid = R{{}, data}; // preserves original `data`
+
+ // comment = "(" *( ctext / quoted-pair / comment ) ")"
+ // ctext = HTAB / SP / %x21-27 / %x2A-5B / %x5D-7E / obs-text
+
+ if (!data.startsWith('('))
+ return invalid;
+
+ qsizetype i = 1;
+ qsizetype level = 1;
+ while (i < data.size()) {
+ switch (data[i++]) {
+ case '(': // nested comment
+ ++level;
+ break;
+ case ')': // end of comment
+ if (--level == 0)
+ return R{data.first(i), data.sliced(i)};
+ break;
+ case '\\': // quoted-pair
+ if (i == data.size())
+ return invalid; // premature end
+ ++i; // eat escaped character
+ break;
+ default:
+ ; // don't validate ctext - accept everything (Postel's Law)
+ }
+ }
+
+ return invalid; // premature end / unbalanced nesting levels
+}
+
+static constexpr void eat_CWS(QByteArrayView &data) noexcept
+{
+ eat_OWS(data);
+ while (const auto comment = parse_comment(data)) {
+ data = comment.tail;
+ eat_OWS(data);
+ }
+}
+
static constexpr auto parse_token(QByteArrayView data) noexcept
{
struct R {
@@ -452,13 +500,13 @@ static constexpr auto parse_parameter(QByteArrayView data, qxp::function_ref<voi
return invalid;
data = name.tail;
- eat_OWS(data); // not in the grammar, but accepted under Postel's Law
+ eat_CWS(data); // not in the grammar, but accepted under Postel's Law
if (!data.startsWith('='))
return invalid;
data = data.sliced(1);
- eat_OWS(data); // not in the grammar, but accepted under Postel's Law
+ eat_CWS(data); // not in the grammar, but accepted under Postel's Law
if (Q_UNLIKELY(data.startsWith('"'))) { // value is a quoted-string
@@ -488,27 +536,27 @@ static auto parse_content_type(QByteArrayView data)
constexpr explicit operator bool() const noexcept { return !type.isEmpty(); }
};
- eat_OWS(data); // not in the grammar, but accepted under Postel's Law
+ eat_CWS(data); // not in the grammar, but accepted under Postel's Law
const auto type = parse_token(data);
if (!type)
return R{};
data = type.tail;
- eat_OWS(data); // not in the grammar, but accepted under Postel's Law
+ eat_CWS(data); // not in the grammar, but accepted under Postel's Law
if (!data.startsWith('/'))
return R{};
data = data.sliced(1);
- eat_OWS(data); // not in the grammar, but accepted under Postel's Law
+ eat_CWS(data); // not in the grammar, but accepted under Postel's Law
const auto subtype = parse_token(data);
if (!subtype)
return R{};
data = subtype.tail;
- eat_OWS(data);
+ eat_CWS(data);
auto r = R{QLatin1StringView{type.token}, QLatin1StringView{subtype.token}, {}};
@@ -516,7 +564,7 @@ static auto parse_content_type(QByteArrayView data)
data = data.sliced(1); // eat ';'
- eat_OWS(data);
+ eat_CWS(data);
const auto param = parse_parameter(data, [&](char ch) { r.charset.append(1, ch); });
if (param.name.compare("charset"_L1, Qt::CaseInsensitive) == 0) {
@@ -530,7 +578,7 @@ static auto parse_content_type(QByteArrayView data)
// otherwise, continue (accepting e.g. `;;`)
data = param.tail;
- eat_OWS(data);
+ eat_CWS(data);
}
return r; // no charset found