| 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
|
|
|