| Index: net/cookies/cookie_util.cc
|
| diff --git a/net/cookies/cookie_util.cc b/net/cookies/cookie_util.cc
|
| index 6e1833f0458ce27631553c68b57ba24a91fd9421..bd1019d2c48b26ef74e2638c4d54184ff1c9f2cf 100644
|
| --- a/net/cookies/cookie_util.cc
|
| +++ b/net/cookies/cookie_util.cc
|
| @@ -4,7 +4,13 @@
|
|
|
| #include "net/cookies/cookie_util.h"
|
|
|
| +#include <cstdio>
|
| +#include <cstdlib>
|
| +
|
| #include "base/logging.h"
|
| +#include "base/string_tokenizer.h"
|
| +#include "base/string_util.h"
|
| +#include "build/build_config.h"
|
| #include "googleurl/src/gurl.h"
|
| #include "net/base/net_util.h"
|
| #include "net/base/registry_controlled_domain.h"
|
| @@ -74,6 +80,124 @@ bool GetCookieDomainWithString(const GURL& url,
|
| return true;
|
| }
|
|
|
| +// Parse a cookie expiration time. We try to be lenient, but we need to
|
| +// assume some order to distinguish the fields. The basic rules:
|
| +// - The month name must be present and prefix the first 3 letters of the
|
| +// full month name (jan for January, jun for June).
|
| +// - If the year is <= 2 digits, it must occur after the day of month.
|
| +// - The time must be of the format hh:mm:ss.
|
| +// An average cookie expiration will look something like this:
|
| +// Sat, 15-Apr-17 21:01:22 GMT
|
| +base::Time ParseCookieTime(const std::string& time_string) {
|
| + static const char* kMonths[] = { "jan", "feb", "mar", "apr", "may", "jun",
|
| + "jul", "aug", "sep", "oct", "nov", "dec" };
|
| + static const int kMonthsLen = arraysize(kMonths);
|
| + // We want to be pretty liberal, and support most non-ascii and non-digit
|
| + // characters as a delimiter. We can't treat : as a delimiter, because it
|
| + // is the delimiter for hh:mm:ss, and we want to keep this field together.
|
| + // We make sure to include - and +, since they could prefix numbers.
|
| + // If the cookie attribute came in in quotes (ex expires="XXX"), the quotes
|
| + // will be preserved, and we will get them here. So we make sure to include
|
| + // quote characters, and also \ for anything that was internally escaped.
|
| + static const char* kDelimiters = "\t !\"#$%&'()*+,-./;<=>?@[\\]^_`{|}~";
|
| +
|
| + base::Time::Exploded exploded = {0};
|
| +
|
| + StringTokenizer tokenizer(time_string, kDelimiters);
|
| +
|
| + bool found_day_of_month = false;
|
| + bool found_month = false;
|
| + bool found_time = false;
|
| + bool found_year = false;
|
| +
|
| + while (tokenizer.GetNext()) {
|
| + const std::string token = tokenizer.token();
|
| + DCHECK(!token.empty());
|
| + bool numerical = IsAsciiDigit(token[0]);
|
| +
|
| + // String field
|
| + if (!numerical) {
|
| + if (!found_month) {
|
| + for (int i = 0; i < kMonthsLen; ++i) {
|
| + // Match prefix, so we could match January, etc
|
| + if (base::strncasecmp(token.c_str(), kMonths[i], 3) == 0) {
|
| + exploded.month = i + 1;
|
| + found_month = true;
|
| + break;
|
| + }
|
| + }
|
| + } else {
|
| + // If we've gotten here, it means we've already found and parsed our
|
| + // month, and we have another string, which we would expect to be the
|
| + // the time zone name. According to the RFC and my experiments with
|
| + // how sites format their expirations, we don't have much of a reason
|
| + // to support timezones. We don't want to ever barf on user input,
|
| + // but this DCHECK should pass for well-formed data.
|
| + // DCHECK(token == "GMT");
|
| + }
|
| + // Numeric field w/ a colon
|
| + } else if (token.find(':') != std::string::npos) {
|
| + if (!found_time &&
|
| +#ifdef COMPILER_MSVC
|
| + sscanf_s(
|
| +#else
|
| + sscanf(
|
| +#endif
|
| + token.c_str(), "%2u:%2u:%2u", &exploded.hour,
|
| + &exploded.minute, &exploded.second) == 3) {
|
| + found_time = true;
|
| + } else {
|
| + // We should only ever encounter one time-like thing. If we're here,
|
| + // it means we've found a second, which shouldn't happen. We keep
|
| + // the first. This check should be ok for well-formed input:
|
| + // NOTREACHED();
|
| + }
|
| + // Numeric field
|
| + } else {
|
| + // Overflow with atoi() is unspecified, so we enforce a max length.
|
| + if (!found_day_of_month && token.length() <= 2) {
|
| + exploded.day_of_month = atoi(token.c_str());
|
| + found_day_of_month = true;
|
| + } else if (!found_year && token.length() <= 5) {
|
| + exploded.year = atoi(token.c_str());
|
| + found_year = true;
|
| + } else {
|
| + // If we're here, it means we've either found an extra numeric field,
|
| + // or a numeric field which was too long. For well-formed input, the
|
| + // following check would be reasonable:
|
| + // NOTREACHED();
|
| + }
|
| + }
|
| + }
|
| +
|
| + if (!found_day_of_month || !found_month || !found_time || !found_year) {
|
| + // We didn't find all of the fields we need. For well-formed input, the
|
| + // following check would be reasonable:
|
| + // NOTREACHED() << "Cookie parse expiration failed: " << time_string;
|
| + return base::Time();
|
| + }
|
| +
|
| + // Normalize the year to expand abbreviated years to the full year.
|
| + if (exploded.year >= 69 && exploded.year <= 99)
|
| + exploded.year += 1900;
|
| + if (exploded.year >= 0 && exploded.year <= 68)
|
| + exploded.year += 2000;
|
| +
|
| + // If our values are within their correct ranges, we got our time.
|
| + if (exploded.day_of_month >= 1 && exploded.day_of_month <= 31 &&
|
| + exploded.month >= 1 && exploded.month <= 12 &&
|
| + exploded.year >= 1601 && exploded.year <= 30827 &&
|
| + exploded.hour <= 23 && exploded.minute <= 59 && exploded.second <= 59) {
|
| + return base::Time::FromUTCExploded(exploded);
|
| + }
|
| +
|
| + // One of our values was out of expected range. For well-formed input,
|
| + // the following check would be reasonable:
|
| + // NOTREACHED() << "Cookie exploded expiration failed: " << time_string;
|
| +
|
| + return base::Time();
|
| +}
|
| +
|
| } // namespace cookie_utils
|
| } // namespace net
|
|
|
|
|