Index: net/http/http_util.cc |
diff --git a/net/http/http_util.cc b/net/http/http_util.cc |
index 932f8386dadc401914a559f20805b951f66876cb..06fc15edd3668e367d0551cf4314de04beb73c65 100644 |
--- a/net/http/http_util.cc |
+++ b/net/http/http_util.cc |
@@ -445,54 +445,99 @@ bool HttpUtil::IsQuote(char c) { |
return c == '"' || c == '\''; |
} |
+namespace { |
+bool IsTokenChar(unsigned char c) { |
+ return !(c >= 0x80 || c <= 0x1F || c == 0x7F || c == '(' || c == ')' || |
+ c == '<' || c == '>' || c == '@' || c == ',' || c == ';' || |
+ c == ':' || c == '\\' || c == '"' || c == '/' || c == '[' || |
+ c == ']' || c == '?' || c == '=' || c == '{' || c == '}' || |
+ c == ' ' || c == '\t'); |
+} |
+} // anonymous namespace |
+ |
// See RFC 2616 Sec 2.2 for the definition of |token|. |
bool HttpUtil::IsToken(std::string::const_iterator begin, |
std::string::const_iterator end) { |
if (begin == end) |
return false; |
for (std::string::const_iterator iter = begin; iter != end; ++iter) { |
+ if (!IsTokenChar(*iter)) |
+ return false; |
+ } |
+ return true; |
+} |
+ |
+// See RFC 5987 Sec 3.2.1 for the definition of |parmname|. |
+bool HttpUtil::IsParmName(std::string::const_iterator begin, |
+ std::string::const_iterator end) { |
+ if (begin == end) |
+ return false; |
+ for (std::string::const_iterator iter = begin; iter != end; ++iter) { |
unsigned char c = *iter; |
- if (c >= 0x80 || c <= 0x1F || c == 0x7F || |
- c == '(' || c == ')' || c == '<' || c == '>' || c == '@' || |
- c == ',' || c == ';' || c == ':' || c == '\\' || c == '"' || |
- c == '/' || c == '[' || c == ']' || c == '?' || c == '=' || |
- c == '{' || c == '}' || c == ' ' || c == '\t') |
+ if (!IsTokenChar(c) || c == '*' || c == '\'' || c == '%') |
return false; |
} |
return true; |
} |
-std::string HttpUtil::Unquote(std::string::const_iterator begin, |
- std::string::const_iterator end) { |
+namespace { |
+bool UnquoteImpl(std::string::const_iterator begin, |
+ std::string::const_iterator end, |
+ bool strict_quotes, |
+ std::string* out) { |
// Empty string |
if (begin == end) |
- return std::string(); |
+ return false; |
// Nothing to unquote. |
- if (!IsQuote(*begin)) |
- return std::string(begin, end); |
+ if (!HttpUtil::IsQuote(*begin)) |
+ return false; |
+ |
+ // Anything other than double quotes in strict mode. |
+ if (strict_quotes && *begin != '"') |
+ return false; |
// No terminal quote mark. |
if (end - begin < 2 || *begin != *(end - 1)) |
- return std::string(begin, end); |
+ return false; |
+ |
+ char quote = *begin; |
// Strip quotemarks |
++begin; |
--end; |
// Unescape quoted-pair (defined in RFC 2616 section 2.2) |
- std::string unescaped; |
bool prev_escape = false; |
+ std::string unescaped; |
for (; begin != end; ++begin) { |
char c = *begin; |
if (c == '\\' && !prev_escape) { |
prev_escape = true; |
continue; |
} |
+ if (strict_quotes && !prev_escape && c == quote) |
+ return false; |
prev_escape = false; |
unescaped.push_back(c); |
} |
- return unescaped; |
+ |
+ // Terminal quote is escaped. |
+ if (strict_quotes && prev_escape) |
+ return false; |
+ |
+ *out = std::move(unescaped); |
+ return true; |
+} |
+} // anonymous namespace |
+ |
+std::string HttpUtil::Unquote(std::string::const_iterator begin, |
+ std::string::const_iterator end) { |
+ std::string result; |
+ if (!UnquoteImpl(begin, end, false, &result)) |
+ return std::string(begin, end); |
+ |
+ return result; |
} |
// static |
@@ -501,6 +546,18 @@ std::string HttpUtil::Unquote(const std::string& str) { |
} |
// static |
+bool HttpUtil::StrictUnquote(std::string::const_iterator begin, |
+ std::string::const_iterator end, |
+ std::string* out) { |
+ return UnquoteImpl(begin, end, true, out); |
+} |
+ |
+// static |
+bool HttpUtil::StrictUnquote(const std::string& str, std::string* out) { |
+ return StrictUnquote(str.begin(), str.end(), out); |
+} |
+ |
+// static |
std::string HttpUtil::Quote(const std::string& str) { |
std::string escaped; |
escaped.reserve(2 + str.size()); |
@@ -910,7 +967,8 @@ HttpUtil::NameValuePairsIterator::NameValuePairsIterator( |
std::string::const_iterator begin, |
std::string::const_iterator end, |
char delimiter, |
- OptionalValues optional_values) |
+ Values optional_values, |
+ Quotes strict_quotes) |
: props_(begin, end, delimiter), |
valid_(true), |
name_begin_(end), |
@@ -918,13 +976,21 @@ HttpUtil::NameValuePairsIterator::NameValuePairsIterator( |
value_begin_(end), |
value_end_(end), |
value_is_quoted_(false), |
- values_optional_(optional_values == VALUES_OPTIONAL) {} |
+ values_optional_(optional_values == Values::NOT_REQUIRED), |
+ strict_quotes_(strict_quotes == Quotes::STRICT_QUOTES) { |
+ if (strict_quotes_) |
+ props_.set_quote_chars("\""); |
+} |
HttpUtil::NameValuePairsIterator::NameValuePairsIterator( |
std::string::const_iterator begin, |
std::string::const_iterator end, |
char delimiter) |
- : NameValuePairsIterator(begin, end, delimiter, VALUES_NOT_OPTIONAL) {} |
+ : NameValuePairsIterator(begin, |
+ end, |
+ delimiter, |
+ Values::REQUIRED, |
+ Quotes::NOT_STRICT) {} |
HttpUtil::NameValuePairsIterator::NameValuePairsIterator( |
const NameValuePairsIterator& other) = default; |
@@ -960,7 +1026,7 @@ bool HttpUtil::NameValuePairsIterator::GetNext() { |
// If an equals sign was found, verify that it wasn't inside of quote marks. |
if (equals != value_end_) { |
for (std::string::const_iterator it = value_begin_; it != equals; ++it) { |
- if (HttpUtil::IsQuote(*it)) |
+ if (IsQuote(*it)) |
return valid_ = false; // Malformed, quote appears before equals sign |
} |
} |
@@ -979,7 +1045,15 @@ bool HttpUtil::NameValuePairsIterator::GetNext() { |
return valid_ = false; |
} |
- if (value_begin_ != value_end_ && HttpUtil::IsQuote(*value_begin_)) { |
+ if (value_begin_ != value_end_ && IsQuote(*value_begin_)) { |
+ value_is_quoted_ = true; |
+ |
+ if (strict_quotes_) { |
+ if (!HttpUtil::StrictUnquote(value_begin_, value_end_, &unquoted_value_)) |
+ return valid_ = false; |
+ return true; |
+ } |
+ |
// Trim surrounding quotemarks off the value |
if (*value_begin_ != *(value_end_ - 1) || value_begin_ + 1 == value_end_) { |
// NOTE: This is not as graceful as it sounds: |
@@ -987,9 +1061,9 @@ bool HttpUtil::NameValuePairsIterator::GetNext() { |
// (["\"hello] should give ["hello]). |
// * Does not detect when the final quote is escaped |
// (["value\"] should give [value"]) |
+ value_is_quoted_ = false; |
++value_begin_; // Gracefully recover from mismatching quotes. |
} else { |
- value_is_quoted_ = true; |
// Do not store iterators into this. See declaration of unquoted_value_. |
unquoted_value_ = HttpUtil::Unquote(value_begin_, value_end_); |
} |
@@ -998,4 +1072,10 @@ bool HttpUtil::NameValuePairsIterator::GetNext() { |
return true; |
} |
+bool HttpUtil::NameValuePairsIterator::IsQuote(char c) const { |
+ if (strict_quotes_) |
+ return c == '"'; |
+ return HttpUtil::IsQuote(c); |
+} |
+ |
} // namespace net |