This source file includes following definitions.
- CharIsA
- SeekTo
- SeekPast
- SeekBackPast
- IsValidToken
- IsValidCookieValue
- IsControlCharacter
- IsValidCookieAttributeValue
- priority_index_
- IsValid
- SetName
- SetValue
- SetPath
- SetDomain
- SetExpires
- SetMaxAge
- SetIsSecure
- SetIsHttpOnly
- SetPriority
- ToCookieLine
- FindFirstTerminator
- ParseToken
- ParseValue
- ParseTokenString
- ParseValueString
- ParseTokenValuePairs
- SetupAttributes
- SetString
- SetBool
- SetAttributePair
- ClearAttributePair
#include "net/cookies/parsed_cookie.h"
#include "base/logging.h"
#include "base/strings/string_util.h"
namespace {
const char kPathTokenName[] = "path";
const char kDomainTokenName[] = "domain";
const char kExpiresTokenName[] = "expires";
const char kMaxAgeTokenName[] = "max-age";
const char kSecureTokenName[] = "secure";
const char kHttpOnlyTokenName[] = "httponly";
const char kPriorityTokenName[] = "priority";
const char kTerminator[] = "\n\r\0";
const int kTerminatorLen = sizeof(kTerminator) - 1;
const char kWhitespace[] = " \t";
const char kValueSeparator[] = ";";
const char kTokenSeparator[] = ";=";
inline bool CharIsA(const char c, const char* chars) {
return strchr(chars, c) != NULL;
}
inline bool SeekTo(std::string::const_iterator* it,
const std::string::const_iterator& end,
const char* chars) {
for (; *it != end && !CharIsA(**it, chars); ++(*it)) {}
return *it == end;
}
inline bool SeekPast(std::string::const_iterator* it,
const std::string::const_iterator& end,
const char* chars) {
for (; *it != end && CharIsA(**it, chars); ++(*it)) {}
return *it == end;
}
inline bool SeekBackPast(std::string::const_iterator* it,
const std::string::const_iterator& end,
const char* chars) {
for (; *it != end && CharIsA(**it, chars); --(*it)) {}
return *it == end;
}
bool IsValidToken(const std::string& value) {
if (value.empty())
return false;
std::string separators = "()<>@,;:\\\"/[]?={} \t";
if (value.find_first_of(separators) != std::string::npos)
return false;
for (std::string::const_iterator i = value.begin(); i != value.end(); ++i) {
if ((*i >= 0 && *i <= 31) || *i >= 127)
return false;
}
return true;
}
bool IsValidCookieValue(const std::string& value) {
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 IsControlCharacter(unsigned char c) {
return (c >= 0) && (c <= 31);
}
bool IsValidCookieAttributeValue(const std::string& value) {
for (std::string::const_iterator i = value.begin(); i != value.end(); ++i) {
if (IsControlCharacter(*i) || *i == ';')
return false;
}
return true;
}
}
namespace net {
ParsedCookie::ParsedCookie(const std::string& cookie_line)
: path_index_(0),
domain_index_(0),
expires_index_(0),
maxage_index_(0),
secure_index_(0),
httponly_index_(0),
priority_index_(0) {
if (cookie_line.size() > kMaxCookieSize) {
VLOG(1) << "Not parsing cookie, too large: " << cookie_line.size();
return;
}
ParseTokenValuePairs(cookie_line);
if (!pairs_.empty())
SetupAttributes();
}
ParsedCookie::~ParsedCookie() {
}
bool ParsedCookie::IsValid() const {
return !pairs_.empty();
}
CookiePriority ParsedCookie::Priority() const {
return (priority_index_ == 0) ? COOKIE_PRIORITY_DEFAULT :
StringToCookiePriority(pairs_[priority_index_].second);
}
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;
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;
return true;
}
bool ParsedCookie::SetPath(const std::string& path) {
return SetString(&path_index_, kPathTokenName, path);
}
bool ParsedCookie::SetDomain(const std::string& domain) {
return SetString(&domain_index_, kDomainTokenName, domain);
}
bool ParsedCookie::SetExpires(const std::string& expires) {
return SetString(&expires_index_, kExpiresTokenName, expires);
}
bool ParsedCookie::SetMaxAge(const std::string& maxage) {
return SetString(&maxage_index_, kMaxAgeTokenName, maxage);
}
bool ParsedCookie::SetIsSecure(bool is_secure) {
return SetBool(&secure_index_, kSecureTokenName, is_secure);
}
bool ParsedCookie::SetIsHttpOnly(bool is_http_only) {
return SetBool(&httponly_index_, kHttpOnlyTokenName, is_http_only);
}
bool ParsedCookie::SetPriority(const std::string& priority) {
return SetString(&priority_index_, kPriorityTokenName, priority);
}
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);
if (it->first != kSecureTokenName && it->first != kHttpOnlyTokenName) {
out.append("=");
out.append(it->second);
}
}
return out;
}
std::string::const_iterator ParsedCookie::FindFirstTerminator(
const std::string& s) {
std::string::const_iterator end = s.end();
size_t term_pos =
s.find_first_of(std::string(kTerminator, kTerminatorLen));
if (term_pos != std::string::npos) {
end = s.begin() + term_pos;
}
return end;
}
bool ParsedCookie::ParseToken(std::string::const_iterator* it,
const std::string::const_iterator& end,
std::string::const_iterator* token_start,
std::string::const_iterator* token_end) {
DCHECK(it && token_start && token_end);
std::string::const_iterator token_real_end;
if (SeekPast(it, end, kWhitespace))
return false;
*token_start = *it;
SeekTo(it, end, kTokenSeparator);
token_real_end = *it;
if (*it != *token_start) {
--(*it);
SeekBackPast(it, *token_start, kWhitespace);
++(*it);
}
*token_end = *it;
*it = token_real_end;
return true;
}
void ParsedCookie::ParseValue(std::string::const_iterator* it,
const std::string::const_iterator& end,
std::string::const_iterator* value_start,
std::string::const_iterator* value_end) {
DCHECK(it && value_start && value_end);
SeekPast(it, end, kWhitespace);
*value_start = *it;
SeekTo(it, end, kValueSeparator);
*value_end = *it;
if (*value_end != *value_start) {
--(*value_end);
SeekBackPast(value_end, *value_start, kWhitespace);
++(*value_end);
}
}
std::string ParsedCookie::ParseTokenString(const std::string& token) {
std::string::const_iterator it = token.begin();
std::string::const_iterator end = FindFirstTerminator(token);
std::string::const_iterator token_start, token_end;
if (ParseToken(&it, end, &token_start, &token_end))
return std::string(token_start, token_end);
return std::string();
}
std::string ParsedCookie::ParseValueString(const std::string& value) {
std::string::const_iterator it = value.begin();
std::string::const_iterator end = FindFirstTerminator(value);
std::string::const_iterator value_start, value_end;
ParseValue(&it, end, &value_start, &value_end);
return std::string(value_start, value_end);
}
void ParsedCookie::ParseTokenValuePairs(const std::string& cookie_line) {
pairs_.clear();
std::string::const_iterator start = cookie_line.begin();
std::string::const_iterator it = start;
std::string::const_iterator end = FindFirstTerminator(cookie_line);
for (int pair_num = 0; pair_num < kMaxPairs && it != end; ++pair_num) {
TokenValuePair pair;
std::string::const_iterator token_start, token_end;
if (!ParseToken(&it, end, &token_start, &token_end))
break;
if (it == end || *it != '=') {
if (pair_num == 0) {
pair.first = "";
it = token_start;
} else {
pair.first = std::string(token_start, token_end);
}
} else {
pair.first = std::string(token_start, token_end);
++it;
}
std::string::const_iterator value_start, value_end;
ParseValue(&it, end, &value_start, &value_end);
pair.second = std::string(value_start, value_end);
if (pair_num != 0)
StringToLowerASCII(&pair.first);
if (!IsValidCookieAttributeValue(pair.first) ||
!IsValidCookieAttributeValue(pair.second)) {
pairs_.clear();
break;
}
pairs_.push_back(pair);
if (it != end)
++it;
}
}
void ParsedCookie::SetupAttributes() {
for (size_t i = 1; i < pairs_.size(); ++i) {
if (pairs_[i].first == kPathTokenName) {
path_index_ = i;
} else if (pairs_[i].first == kDomainTokenName) {
domain_index_ = i;
} else if (pairs_[i].first == kExpiresTokenName) {
expires_index_ = i;
} else if (pairs_[i].first == kMaxAgeTokenName) {
maxage_index_ = i;
} else if (pairs_[i].first == kSecureTokenName) {
secure_index_ = i;
} else if (pairs_[i].first == kHttpOnlyTokenName) {
httponly_index_ = i;
} else if (pairs_[i].first == kPriorityTokenName) {
priority_index_ = i;
} else {
}
}
}
bool ParsedCookie::SetString(size_t* index,
const std::string& key,
const std::string& value) {
if (value.empty()) {
ClearAttributePair(*index);
return true;
} else {
return SetAttributePair(index, key, value);
}
}
bool ParsedCookie::SetBool(size_t* index,
const std::string& key,
bool value) {
if (!value) {
ClearAttributePair(*index);
return true;
} else {
return SetAttributePair(index, key, std::string());
}
}
bool ParsedCookie::SetAttributePair(size_t* index,
const std::string& key,
const std::string& value) {
if (!(IsValidToken(key) && IsValidCookieAttributeValue(value)))
return false;
if (!IsValid())
return false;
if (*index) {
pairs_[*index].second = value;
} else {
pairs_.push_back(std::make_pair(key, value));
*index = pairs_.size() - 1;
}
return true;
}
void ParsedCookie::ClearAttributePair(size_t index) {
if (index == 0)
return;
size_t* indexes[] = { &path_index_, &domain_index_, &expires_index_,
&maxage_index_, &secure_index_, &httponly_index_,
&priority_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);
}
}