Chromium Code Reviews| Index: net/cookies/parsed_cookie.cc |
| diff --git a/net/cookies/parsed_cookie.cc b/net/cookies/parsed_cookie.cc |
| index f9af3c9fa94046bc9c6f6e4d8b27209a2508c38a..08c8c9462da26f5de19429eeb453e61810f3b7d7 100644 |
| --- a/net/cookies/parsed_cookie.cc |
| +++ b/net/cookies/parsed_cookie.cc |
| @@ -47,6 +47,25 @@ |
| #include "base/logging.h" |
| #include "base/string_util.h" |
| +namespace { |
| + |
| +const char kPathTokenName[] = "path"; |
| +const char kDomainTokenName[] = "domain"; |
| +const char kMACKeyTokenName[] = "mac-key"; |
| +const char kMACAlgorithmTokenName[] = "mac-algorithm"; |
| +const char kExpiresTokenName[] = "expires"; |
| +const char kMaxAgeTokenName[] = "max-age"; |
| +const char kSecureTokenName[] = "secure"; |
| +const char kHttpOnlyTokenName[] = "httponly"; |
| + |
| +const char kTerminator[] = "\n\r\0"; |
| +const int kTerminatorLen = sizeof(kTerminator) - 1; |
| +const char kWhitespace[] = " \t"; |
| +const char kValueSeparator[] = ";"; |
| +const char kTokenSeparator[] = ";="; |
| + |
| +} // namespace |
| + |
| namespace net { |
| ParsedCookie::ParsedCookie(const std::string& cookie_line) |
| @@ -103,23 +122,140 @@ static inline bool SeekBackPast(std::string::const_iterator* it, |
| return *it == end; |
| } |
| -const char ParsedCookie::kTerminator[] = "\n\r\0"; |
| -const int ParsedCookie::kTerminatorLen = sizeof(kTerminator) - 1; |
| -const char ParsedCookie::kWhitespace[] = " \t"; |
| -const char ParsedCookie::kValueSeparator[] = ";"; |
| -const char ParsedCookie::kTokenSeparator[] = ";="; |
| +// Validate whether |value| is a valid token according to [RFC2616], |
| +// Section 2.2. |
| +static bool IsValidToken(const std::string& value) { |
| + if (value.empty()) |
| + return false; |
| + |
| + // Check that |value| has no separators. |
| + std::string separators = "()<>@,;:\\\"/[]?={} \t"; |
| + if (value.find_first_of(separators) != std::string::npos) |
| + return false; |
| + |
| + // Check that |value| has no CTLs. |
| + for (std::string::const_iterator i = value.begin(); i != value.end(); ++i) { |
| + if ((*i >= 0 && *i <= 31) || *i >= 127) |
| + return false; |
| + } |
| + |
| + return true; |
| +} |
| + |
| +bool ParsedCookie::SetName(const std::string& name) { |
| + if (!IsValidToken(name)) |
| + return false; |
| + if (pairs_.empty()) |
| + pairs_.push_back(std::make_pair("", "")); |
| + pairs_[0].first = name; |
| + is_valid_ = true; |
| + return true; |
| +} |
| + |
| +// Validate value, which may be according to RFC 6265 |
| +// cookie-value = *cookie-octet / ( DQUOTE *cookie-octet DQUOTE ) |
| +// cookie-octet = %x21 / %x23-2B / %x2D-3A / %x3C-5B / %x5D-7E |
| +// ; US-ASCII characters excluding CTLs, |
| +// ; whitespace DQUOTE, comma, semicolon, |
| +// ; and backslash |
| +static bool IsValidCookieValue(const std::string& value) { |
| + // Number of characters to skip in validation at beginning and end of string. |
| + size_t skip = 0; |
| + if (value.size() >= 2 && *value.begin() == '"' && *(value.end()-1) == '"') |
| + skip = 1; |
| + for (std::string::const_iterator i = value.begin() + skip; |
| + i != value.end() - skip; ++i) { |
| + bool valid_octet = |
| + (*i == 0x21 || |
| + (*i >= 0x23 && *i <= 0x2B) || |
| + (*i >= 0x2D && *i <= 0x3A) || |
| + (*i >= 0x3C && *i <= 0x5B) || |
| + (*i >= 0x5D && *i <= 0x7E)); |
| + if (!valid_octet) |
| + return false; |
| + } |
| + return true; |
| +} |
| + |
| +bool ParsedCookie::SetValue(const std::string& value) { |
| + if (!IsValidCookieValue(value)) |
| + return false; |
| + if (pairs_.empty()) |
| + pairs_.push_back(std::make_pair("", "")); |
| + pairs_[0].second = value; |
| + is_valid_ = true; |
| + return true; |
| +} |
| + |
| +bool ParsedCookie::SetPath(const std::string& path) { |
| + if (path.empty()) |
| + return ClearAttributePair(path_index_); |
| + else |
| + return SetAttributePair(&path_index_, kPathTokenName, path); |
| +} |
| + |
| +bool ParsedCookie::SetDomain(const std::string& domain) { |
| + if (domain.empty()) |
| + return ClearAttributePair(domain_index_); |
| + else |
| + return SetAttributePair(&domain_index_, kDomainTokenName, domain); |
| +} |
| + |
| +bool ParsedCookie::SetMACKey(const std::string& mac_key) { |
| + if (mac_key.empty()) |
| + return ClearAttributePair(mac_key_index_); |
| + else |
| + return SetAttributePair(&mac_key_index_, kMACKeyTokenName, mac_key); |
| +} |
| + |
| +bool ParsedCookie::SetMACAlgorithm(const std::string& mac_algorithm) { |
| + if (mac_algorithm.empty()) { |
| + return ClearAttributePair(mac_algorithm_index_); |
| + } else { |
| + return SetAttributePair(&mac_algorithm_index_, kMACAlgorithmTokenName, |
| + mac_algorithm); |
| + } |
| +} |
| + |
| +bool ParsedCookie::SetExpires(const std::string& expires) { |
| + if (expires.empty()) |
| + return ClearAttributePair(expires_index_); |
| + else |
| + return SetAttributePair(&expires_index_, kExpiresTokenName, expires); |
| +} |
| + |
| +bool ParsedCookie::SetMaxAge(const std::string& maxage) { |
| + if (maxage.empty()) |
| + return ClearAttributePair(maxage_index_); |
| + else |
| + return SetAttributePair(&maxage_index_, kMaxAgeTokenName, maxage); |
| +} |
| -// Create a cookie-line for the cookie. For debugging only! |
| -// If we want to use this for something more than debugging, we |
| -// should rewrite it better... |
| -std::string ParsedCookie::DebugString() const { |
| +bool ParsedCookie::SetIsSecure(bool is_secure) { |
| + if (is_secure) |
| + return SetAttributePair(&secure_index_, kSecureTokenName, ""); |
| + else |
| + return ClearAttributePair(secure_index_); |
| +} |
| + |
| +bool ParsedCookie::SetIsHttpOnly(bool is_http_only) { |
| + if (is_http_only) |
| + return SetAttributePair(&httponly_index_, kHttpOnlyTokenName, ""); |
| + else |
| + return ClearAttributePair(httponly_index_); |
| +} |
| + |
| +std::string ParsedCookie::ToCookieLine() const { |
| std::string out; |
| for (PairList::const_iterator it = pairs_.begin(); |
| it != pairs_.end(); ++it) { |
| + if (!out.empty()) |
| + out.append("; "); |
| out.append(it->first); |
| - out.append("="); |
| - out.append(it->second); |
| - out.append("; "); |
| + if (it->first != kSecureTokenName && it->first != kHttpOnlyTokenName) { |
| + out.append("="); |
| + out.append(it->second); |
| + } |
| } |
| return out; |
| } |
| @@ -279,15 +415,6 @@ void ParsedCookie::ParseTokenValuePairs(const std::string& cookie_line) { |
| } |
| void ParsedCookie::SetupAttributes() { |
| - static const char kPathTokenName[] = "path"; |
| - static const char kDomainTokenName[] = "domain"; |
| - static const char kMACKeyTokenName[] = "mac-key"; |
| - static const char kMACAlgorithmTokenName[] = "mac-algorithm"; |
| - static const char kExpiresTokenName[] = "expires"; |
| - static const char kMaxAgeTokenName[] = "max-age"; |
| - static const char kSecureTokenName[] = "secure"; |
| - static const char kHttpOnlyTokenName[] = "httponly"; |
| - |
| // We skip over the first token/value, the user supplied one. |
| for (size_t i = 1; i < pairs_.size(); ++i) { |
| if (pairs_[i].first == kPathTokenName) { |
| @@ -312,4 +439,53 @@ void ParsedCookie::SetupAttributes() { |
| } |
| } |
| +static bool IsValidCookieAttributeValue(const std::string& value) { |
| + // The greatest common denominator of cookie attribute values is |
| + // <any CHAR except CTLs or ";"> according to RFC 6265. |
| + for (std::string::const_iterator i = value.begin(); i != value.end(); ++i) { |
| + if ((*i >= 0 && *i <= 31) || *i == ';') |
| + return false; |
| + } |
| + return true; |
| +} |
| + |
| +bool ParsedCookie::SetAttributePair(size_t* index, |
| + const std::string& key, |
| + const std::string& value) { |
| + if (!IsValidToken(key) || !IsValidCookieAttributeValue(value)) |
| + return false; |
| + if (*index) { |
| + pairs_[*index].second = value; |
| + } else { |
| + // Reserve first spot for cooie name and value. |
|
erikwright (departed)
2012/07/12 15:50:57
Seems to me it would be better to fail if there is
battre
2012/07/12 18:03:10
Done.
|
| + if (pairs_.empty()) |
| + pairs_.push_back(std::make_pair("", "")); |
| + pairs_.push_back(std::make_pair(key, value)); |
| + *index = pairs_.size() - 1; |
| + } |
| + is_valid_ = true; |
| + return true; |
| +} |
| + |
| +bool ParsedCookie::ClearAttributePair(size_t index) { |
| + // The first pair (name/value of cookie at pairs_[0]) cannot be cleared. |
| + // Cookie attributes that don't have a value at the moment, are represented |
| + // with an index being equal to 0. |
| + if (index == 0) |
| + return true; |
| + |
| + size_t* indexes[] = {&path_index_, &domain_index_, &mac_key_index_, |
| + &mac_algorithm_index_, &expires_index_, &maxage_index_, &secure_index_, |
| + &httponly_index_}; |
| + for (size_t i = 0; i < arraysize(indexes); ++i) { |
| + if (*indexes[i] == index) |
| + *indexes[i] = 0; |
| + else if (*indexes[i] > index) |
| + --*indexes[i]; |
| + } |
| + pairs_.erase(pairs_.begin() + index); |
| + is_valid_ = pairs_.empty(); |
|
erikwright (departed)
2012/07/12 15:50:57
It seems that is_valid_ can never be mutated by th
battre
2012/07/12 18:03:10
I have redefined IsValid() as you have suggested.
|
| + return true; |
|
erikwright (departed)
2012/07/12 15:50:57
If you won't ever return false, change return type
battre
2012/07/12 18:03:10
Done, even thought this comes at the cost of makin
|
| +} |
| + |
| } // namespace |