| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 // Portions of this code based on Mozilla: | |
| 6 // (netwerk/cookie/src/nsCookieService.cpp) | |
| 7 /* ***** BEGIN LICENSE BLOCK ***** | |
| 8 * Version: MPL 1.1/GPL 2.0/LGPL 2.1 | |
| 9 * | |
| 10 * The contents of this file are subject to the Mozilla Public License Version | |
| 11 * 1.1 (the "License"); you may not use this file except in compliance with | |
| 12 * the License. You may obtain a copy of the License at | |
| 13 * http://www.mozilla.org/MPL/ | |
| 14 * | |
| 15 * Software distributed under the License is distributed on an "AS IS" basis, | |
| 16 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License | |
| 17 * for the specific language governing rights and limitations under the | |
| 18 * License. | |
| 19 * | |
| 20 * The Original Code is mozilla.org code. | |
| 21 * | |
| 22 * The Initial Developer of the Original Code is | |
| 23 * Netscape Communications Corporation. | |
| 24 * Portions created by the Initial Developer are Copyright (C) 2003 | |
| 25 * the Initial Developer. All Rights Reserved. | |
| 26 * | |
| 27 * Contributor(s): | |
| 28 * Daniel Witte (dwitte@stanford.edu) | |
| 29 * Michiel van Leeuwen (mvl@exedo.nl) | |
| 30 * | |
| 31 * Alternatively, the contents of this file may be used under the terms of | |
| 32 * either the GNU General Public License Version 2 or later (the "GPL"), or | |
| 33 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), | |
| 34 * in which case the provisions of the GPL or the LGPL are applicable instead | |
| 35 * of those above. If you wish to allow use of your version of this file only | |
| 36 * under the terms of either the GPL or the LGPL, and not to allow others to | |
| 37 * use your version of this file under the terms of the MPL, indicate your | |
| 38 * decision by deleting the provisions above and replace them with the notice | |
| 39 * and other provisions required by the GPL or the LGPL. If you do not delete | |
| 40 * the provisions above, a recipient may use your version of this file under | |
| 41 * the terms of any one of the MPL, the GPL or the LGPL. | |
| 42 * | |
| 43 * ***** END LICENSE BLOCK ***** */ | |
| 44 | |
| 45 #include "net/base/cookie_monster.h" | |
| 46 | |
| 47 #include <algorithm> | |
| 48 #include <set> | |
| 49 | |
| 50 #include "base/basictypes.h" | |
| 51 #include "base/bind.h" | |
| 52 #include "base/callback.h" | |
| 53 #include "base/format_macros.h" | |
| 54 #include "base/logging.h" | |
| 55 #include "base/memory/scoped_ptr.h" | |
| 56 #include "base/message_loop.h" | |
| 57 #include "base/message_loop_proxy.h" | |
| 58 #include "base/metrics/histogram.h" | |
| 59 #include "base/string_tokenizer.h" | |
| 60 #include "base/string_util.h" | |
| 61 #include "base/stringprintf.h" | |
| 62 #include "googleurl/src/gurl.h" | |
| 63 #include "googleurl/src/url_canon.h" | |
| 64 #include "net/base/cookie_util.h" | |
| 65 #include "net/base/registry_controlled_domain.h" | |
| 66 | |
| 67 using base::Time; | |
| 68 using base::TimeDelta; | |
| 69 using base::TimeTicks; | |
| 70 | |
| 71 // In steady state, most cookie requests can be satisfied by the in memory | |
| 72 // cookie monster store. However, if a request comes in during the initial | |
| 73 // cookie load, it must be delayed until that load completes. That is done by | |
| 74 // queueing it on CookieMonster::queue_ and running it when notification of | |
| 75 // cookie load completion is received via CookieMonster::OnLoaded. This callback | |
| 76 // is passed to the persistent store from CookieMonster::InitStore(), which is | |
| 77 // called on the first operation invoked on the CookieMonster. | |
| 78 // | |
| 79 // On the browser critical paths (e.g. for loading initial web pages in a | |
| 80 // session restore) it may take too long to wait for the full load. If a cookie | |
| 81 // request is for a specific URL, DoCookieTaskForURL is called, which triggers a | |
| 82 // priority load if the key is not loaded yet by calling PersistentCookieStore | |
| 83 // :: LoadCookiesForKey. The request is queued in CookieMonster::tasks_queued | |
| 84 // and executed upon receiving notification of key load completion via | |
| 85 // CookieMonster::OnKeyLoaded(). If multiple requests for the same eTLD+1 are | |
| 86 // received before key load completion, only the first request calls | |
| 87 // PersistentCookieStore::LoadCookiesForKey, all subsequent requests are queued | |
| 88 // in CookieMonster::tasks_queued and executed upon receiving notification of | |
| 89 // key load completion triggered by the first request for the same eTLD+1. | |
| 90 | |
| 91 static const int kMinutesInTenYears = 10 * 365 * 24 * 60; | |
| 92 | |
| 93 namespace net { | |
| 94 | |
| 95 // See comments at declaration of these variables in cookie_monster.h | |
| 96 // for details. | |
| 97 const size_t CookieMonster::kDomainMaxCookies = 180; | |
| 98 const size_t CookieMonster::kDomainPurgeCookies = 30; | |
| 99 const size_t CookieMonster::kMaxCookies = 3300; | |
| 100 const size_t CookieMonster::kPurgeCookies = 300; | |
| 101 const int CookieMonster::kSafeFromGlobalPurgeDays = 30; | |
| 102 | |
| 103 namespace { | |
| 104 | |
| 105 typedef std::vector<CookieMonster::CanonicalCookie*> CanonicalCookieVector; | |
| 106 | |
| 107 // Default minimum delay after updating a cookie's LastAccessDate before we | |
| 108 // will update it again. | |
| 109 const int kDefaultAccessUpdateThresholdSeconds = 60; | |
| 110 | |
| 111 // Comparator to sort cookies from highest creation date to lowest | |
| 112 // creation date. | |
| 113 struct OrderByCreationTimeDesc { | |
| 114 bool operator()(const CookieMonster::CookieMap::iterator& a, | |
| 115 const CookieMonster::CookieMap::iterator& b) const { | |
| 116 return a->second->CreationDate() > b->second->CreationDate(); | |
| 117 } | |
| 118 }; | |
| 119 | |
| 120 // Constants for use in VLOG | |
| 121 const int kVlogPerCookieMonster = 1; | |
| 122 const int kVlogPeriodic = 3; | |
| 123 const int kVlogGarbageCollection = 5; | |
| 124 const int kVlogSetCookies = 7; | |
| 125 const int kVlogGetCookies = 9; | |
| 126 | |
| 127 #if defined(ENABLE_PERSISTENT_SESSION_COOKIES) | |
| 128 const int kPersistentSessionCookieExpiryInDays = 14; | |
| 129 #endif | |
| 130 | |
| 131 // Mozilla sorts on the path length (longest first), and then it | |
| 132 // sorts by creation time (oldest first). | |
| 133 // The RFC says the sort order for the domain attribute is undefined. | |
| 134 bool CookieSorter(CookieMonster::CanonicalCookie* cc1, | |
| 135 CookieMonster::CanonicalCookie* cc2) { | |
| 136 if (cc1->Path().length() == cc2->Path().length()) | |
| 137 return cc1->CreationDate() < cc2->CreationDate(); | |
| 138 return cc1->Path().length() > cc2->Path().length(); | |
| 139 } | |
| 140 | |
| 141 bool LRUCookieSorter(const CookieMonster::CookieMap::iterator& it1, | |
| 142 const CookieMonster::CookieMap::iterator& it2) { | |
| 143 // Cookies accessed less recently should be deleted first. | |
| 144 if (it1->second->LastAccessDate() != it2->second->LastAccessDate()) | |
| 145 return it1->second->LastAccessDate() < it2->second->LastAccessDate(); | |
| 146 | |
| 147 // In rare cases we might have two cookies with identical last access times. | |
| 148 // To preserve the stability of the sort, in these cases prefer to delete | |
| 149 // older cookies over newer ones. CreationDate() is guaranteed to be unique. | |
| 150 return it1->second->CreationDate() < it2->second->CreationDate(); | |
| 151 } | |
| 152 | |
| 153 // Our strategy to find duplicates is: | |
| 154 // (1) Build a map from (cookiename, cookiepath) to | |
| 155 // {list of cookies with this signature, sorted by creation time}. | |
| 156 // (2) For each list with more than 1 entry, keep the cookie having the | |
| 157 // most recent creation time, and delete the others. | |
| 158 // | |
| 159 // Two cookies are considered equivalent if they have the same domain, | |
| 160 // name, and path. | |
| 161 struct CookieSignature { | |
| 162 public: | |
| 163 CookieSignature(const std::string& name, const std::string& domain, | |
| 164 const std::string& path) | |
| 165 : name(name), | |
| 166 domain(domain), | |
| 167 path(path) {} | |
| 168 | |
| 169 // To be a key for a map this class needs to be assignable, copyable, | |
| 170 // and have an operator<. The default assignment operator | |
| 171 // and copy constructor are exactly what we want. | |
| 172 | |
| 173 bool operator<(const CookieSignature& cs) const { | |
| 174 // Name compare dominates, then domain, then path. | |
| 175 int diff = name.compare(cs.name); | |
| 176 if (diff != 0) | |
| 177 return diff < 0; | |
| 178 | |
| 179 diff = domain.compare(cs.domain); | |
| 180 if (diff != 0) | |
| 181 return diff < 0; | |
| 182 | |
| 183 return path.compare(cs.path) < 0; | |
| 184 } | |
| 185 | |
| 186 std::string name; | |
| 187 std::string domain; | |
| 188 std::string path; | |
| 189 }; | |
| 190 | |
| 191 // Determine the cookie domain to use for setting the specified cookie. | |
| 192 bool GetCookieDomain(const GURL& url, | |
| 193 const CookieMonster::ParsedCookie& pc, | |
| 194 std::string* result) { | |
| 195 std::string domain_string; | |
| 196 if (pc.HasDomain()) | |
| 197 domain_string = pc.Domain(); | |
| 198 return cookie_util::GetCookieDomainWithString(url, domain_string, result); | |
| 199 } | |
| 200 | |
| 201 std::string CanonPathWithString(const GURL& url, | |
| 202 const std::string& path_string) { | |
| 203 // The RFC says the path should be a prefix of the current URL path. | |
| 204 // However, Mozilla allows you to set any path for compatibility with | |
| 205 // broken websites. We unfortunately will mimic this behavior. We try | |
| 206 // to be generous and accept cookies with an invalid path attribute, and | |
| 207 // default the path to something reasonable. | |
| 208 | |
| 209 // The path was supplied in the cookie, we'll take it. | |
| 210 if (!path_string.empty() && path_string[0] == '/') | |
| 211 return path_string; | |
| 212 | |
| 213 // The path was not supplied in the cookie or invalid, we will default | |
| 214 // to the current URL path. | |
| 215 // """Defaults to the path of the request URL that generated the | |
| 216 // Set-Cookie response, up to, but not including, the | |
| 217 // right-most /.""" | |
| 218 // How would this work for a cookie on /? We will include it then. | |
| 219 const std::string& url_path = url.path(); | |
| 220 | |
| 221 size_t idx = url_path.find_last_of('/'); | |
| 222 | |
| 223 // The cookie path was invalid or a single '/'. | |
| 224 if (idx == 0 || idx == std::string::npos) | |
| 225 return std::string("/"); | |
| 226 | |
| 227 // Return up to the rightmost '/'. | |
| 228 return url_path.substr(0, idx); | |
| 229 } | |
| 230 | |
| 231 std::string CanonPath(const GURL& url, | |
| 232 const CookieMonster::ParsedCookie& pc) { | |
| 233 std::string path_string; | |
| 234 if (pc.HasPath()) | |
| 235 path_string = pc.Path(); | |
| 236 return CanonPathWithString(url, path_string); | |
| 237 } | |
| 238 | |
| 239 Time CanonExpiration(const CookieMonster::ParsedCookie& pc, | |
| 240 const Time& current) { | |
| 241 // First, try the Max-Age attribute. | |
| 242 uint64 max_age = 0; | |
| 243 if (pc.HasMaxAge() && | |
| 244 #ifdef COMPILER_MSVC | |
| 245 sscanf_s( | |
| 246 #else | |
| 247 sscanf( | |
| 248 #endif | |
| 249 pc.MaxAge().c_str(), " %" PRIu64, &max_age) == 1) { | |
| 250 return current + TimeDelta::FromSeconds(max_age); | |
| 251 } | |
| 252 | |
| 253 // Try the Expires attribute. | |
| 254 if (pc.HasExpires()) | |
| 255 return CookieMonster::ParseCookieTime(pc.Expires()); | |
| 256 | |
| 257 // Invalid or no expiration, persistent cookie. | |
| 258 return Time(); | |
| 259 } | |
| 260 | |
| 261 // Helper for GarbageCollection. If |cookie_its->size() > num_max|, remove the | |
| 262 // |num_max - num_purge| most recently accessed cookies from cookie_its. | |
| 263 // (In other words, leave the entries that are candidates for | |
| 264 // eviction in cookie_its.) The cookies returned will be in order sorted by | |
| 265 // access time, least recently accessed first. The access time of the least | |
| 266 // recently accessed entry not returned will be placed in | |
| 267 // |*lra_removed| if that pointer is set. FindLeastRecentlyAccessed | |
| 268 // returns false if no manipulation is done (because the list size is less | |
| 269 // than num_max), true otherwise. | |
| 270 bool FindLeastRecentlyAccessed( | |
| 271 size_t num_max, | |
| 272 size_t num_purge, | |
| 273 Time* lra_removed, | |
| 274 std::vector<CookieMonster::CookieMap::iterator>* cookie_its) { | |
| 275 DCHECK_LE(num_purge, num_max); | |
| 276 if (cookie_its->size() > num_max) { | |
| 277 VLOG(kVlogGarbageCollection) | |
| 278 << "FindLeastRecentlyAccessed() Deep Garbage Collect."; | |
| 279 num_purge += cookie_its->size() - num_max; | |
| 280 DCHECK_GT(cookie_its->size(), num_purge); | |
| 281 | |
| 282 // Add 1 so that we can get the last time left in the store. | |
| 283 std::partial_sort(cookie_its->begin(), cookie_its->begin() + num_purge + 1, | |
| 284 cookie_its->end(), LRUCookieSorter); | |
| 285 *lra_removed = | |
| 286 (*(cookie_its->begin() + num_purge))->second->LastAccessDate(); | |
| 287 cookie_its->erase(cookie_its->begin() + num_purge, cookie_its->end()); | |
| 288 return true; | |
| 289 } | |
| 290 return false; | |
| 291 } | |
| 292 | |
| 293 // Mapping between DeletionCause and Delegate::ChangeCause; the mapping also | |
| 294 // provides a boolean that specifies whether or not an OnCookieChanged | |
| 295 // notification ought to be generated. | |
| 296 typedef struct ChangeCausePair_struct { | |
| 297 CookieMonster::Delegate::ChangeCause cause; | |
| 298 bool notify; | |
| 299 } ChangeCausePair; | |
| 300 ChangeCausePair ChangeCauseMapping[] = { | |
| 301 // DELETE_COOKIE_EXPLICIT | |
| 302 { CookieMonster::Delegate::CHANGE_COOKIE_EXPLICIT, true }, | |
| 303 // DELETE_COOKIE_OVERWRITE | |
| 304 { CookieMonster::Delegate::CHANGE_COOKIE_OVERWRITE, true }, | |
| 305 // DELETE_COOKIE_EXPIRED | |
| 306 { CookieMonster::Delegate::CHANGE_COOKIE_EXPIRED, true }, | |
| 307 // DELETE_COOKIE_EVICTED | |
| 308 { CookieMonster::Delegate::CHANGE_COOKIE_EVICTED, true }, | |
| 309 // DELETE_COOKIE_DUPLICATE_IN_BACKING_STORE | |
| 310 { CookieMonster::Delegate::CHANGE_COOKIE_EXPLICIT, false }, | |
| 311 // DELETE_COOKIE_DONT_RECORD | |
| 312 { CookieMonster::Delegate::CHANGE_COOKIE_EXPLICIT, false }, | |
| 313 // DELETE_COOKIE_EVICTED_DOMAIN | |
| 314 { CookieMonster::Delegate::CHANGE_COOKIE_EVICTED, true }, | |
| 315 // DELETE_COOKIE_EVICTED_GLOBAL | |
| 316 { CookieMonster::Delegate::CHANGE_COOKIE_EVICTED, true }, | |
| 317 // DELETE_COOKIE_EVICTED_DOMAIN_PRE_SAFE | |
| 318 { CookieMonster::Delegate::CHANGE_COOKIE_EVICTED, true }, | |
| 319 // DELETE_COOKIE_EVICTED_DOMAIN_POST_SAFE | |
| 320 { CookieMonster::Delegate::CHANGE_COOKIE_EVICTED, true }, | |
| 321 // DELETE_COOKIE_EXPIRED_OVERWRITE | |
| 322 { CookieMonster::Delegate::CHANGE_COOKIE_EXPIRED_OVERWRITE, true }, | |
| 323 // DELETE_COOKIE_LAST_ENTRY | |
| 324 { CookieMonster::Delegate::CHANGE_COOKIE_EXPLICIT, false } | |
| 325 }; | |
| 326 | |
| 327 std::string BuildCookieLine(const CanonicalCookieVector& cookies) { | |
| 328 std::string cookie_line; | |
| 329 for (CanonicalCookieVector::const_iterator it = cookies.begin(); | |
| 330 it != cookies.end(); ++it) { | |
| 331 if (it != cookies.begin()) | |
| 332 cookie_line += "; "; | |
| 333 // In Mozilla if you set a cookie like AAAA, it will have an empty token | |
| 334 // and a value of AAAA. When it sends the cookie back, it will send AAAA, | |
| 335 // so we need to avoid sending =AAAA for a blank token value. | |
| 336 if (!(*it)->Name().empty()) | |
| 337 cookie_line += (*it)->Name() + "="; | |
| 338 cookie_line += (*it)->Value(); | |
| 339 } | |
| 340 return cookie_line; | |
| 341 } | |
| 342 | |
| 343 void BuildCookieInfoList(const CanonicalCookieVector& cookies, | |
| 344 std::vector<CookieStore::CookieInfo>* cookie_infos) { | |
| 345 for (CanonicalCookieVector::const_iterator it = cookies.begin(); | |
| 346 it != cookies.end(); ++it) { | |
| 347 const CookieMonster::CanonicalCookie* cookie = *it; | |
| 348 CookieStore::CookieInfo cookie_info; | |
| 349 | |
| 350 cookie_info.name = cookie->Name(); | |
| 351 cookie_info.creation_date = cookie->CreationDate(); | |
| 352 cookie_info.mac_key = cookie->MACKey(); | |
| 353 cookie_info.mac_algorithm = cookie->MACAlgorithm(); | |
| 354 | |
| 355 cookie_infos->push_back(cookie_info); | |
| 356 } | |
| 357 } | |
| 358 | |
| 359 } // namespace | |
| 360 | |
| 361 // static | |
| 362 bool CookieMonster::enable_file_scheme_ = false; | |
| 363 | |
| 364 CookieMonster::CookieMonster(PersistentCookieStore* store, Delegate* delegate) | |
| 365 : initialized_(false), | |
| 366 loaded_(false), | |
| 367 store_(store), | |
| 368 last_access_threshold_( | |
| 369 TimeDelta::FromSeconds(kDefaultAccessUpdateThresholdSeconds)), | |
| 370 delegate_(delegate), | |
| 371 last_statistic_record_time_(Time::Now()), | |
| 372 keep_expired_cookies_(false), | |
| 373 persist_session_cookies_(false) { | |
| 374 InitializeHistograms(); | |
| 375 SetDefaultCookieableSchemes(); | |
| 376 } | |
| 377 | |
| 378 CookieMonster::CookieMonster(PersistentCookieStore* store, | |
| 379 Delegate* delegate, | |
| 380 int last_access_threshold_milliseconds) | |
| 381 : initialized_(false), | |
| 382 loaded_(false), | |
| 383 store_(store), | |
| 384 last_access_threshold_(base::TimeDelta::FromMilliseconds( | |
| 385 last_access_threshold_milliseconds)), | |
| 386 delegate_(delegate), | |
| 387 last_statistic_record_time_(base::Time::Now()), | |
| 388 keep_expired_cookies_(false), | |
| 389 persist_session_cookies_(false) { | |
| 390 InitializeHistograms(); | |
| 391 SetDefaultCookieableSchemes(); | |
| 392 } | |
| 393 | |
| 394 // Parse a cookie expiration time. We try to be lenient, but we need to | |
| 395 // assume some order to distinguish the fields. The basic rules: | |
| 396 // - The month name must be present and prefix the first 3 letters of the | |
| 397 // full month name (jan for January, jun for June). | |
| 398 // - If the year is <= 2 digits, it must occur after the day of month. | |
| 399 // - The time must be of the format hh:mm:ss. | |
| 400 // An average cookie expiration will look something like this: | |
| 401 // Sat, 15-Apr-17 21:01:22 GMT | |
| 402 Time CookieMonster::ParseCookieTime(const std::string& time_string) { | |
| 403 static const char* kMonths[] = { "jan", "feb", "mar", "apr", "may", "jun", | |
| 404 "jul", "aug", "sep", "oct", "nov", "dec" }; | |
| 405 static const int kMonthsLen = arraysize(kMonths); | |
| 406 // We want to be pretty liberal, and support most non-ascii and non-digit | |
| 407 // characters as a delimiter. We can't treat : as a delimiter, because it | |
| 408 // is the delimiter for hh:mm:ss, and we want to keep this field together. | |
| 409 // We make sure to include - and +, since they could prefix numbers. | |
| 410 // If the cookie attribute came in in quotes (ex expires="XXX"), the quotes | |
| 411 // will be preserved, and we will get them here. So we make sure to include | |
| 412 // quote characters, and also \ for anything that was internally escaped. | |
| 413 static const char* kDelimiters = "\t !\"#$%&'()*+,-./;<=>?@[\\]^_`{|}~"; | |
| 414 | |
| 415 Time::Exploded exploded = {0}; | |
| 416 | |
| 417 StringTokenizer tokenizer(time_string, kDelimiters); | |
| 418 | |
| 419 bool found_day_of_month = false; | |
| 420 bool found_month = false; | |
| 421 bool found_time = false; | |
| 422 bool found_year = false; | |
| 423 | |
| 424 while (tokenizer.GetNext()) { | |
| 425 const std::string token = tokenizer.token(); | |
| 426 DCHECK(!token.empty()); | |
| 427 bool numerical = IsAsciiDigit(token[0]); | |
| 428 | |
| 429 // String field | |
| 430 if (!numerical) { | |
| 431 if (!found_month) { | |
| 432 for (int i = 0; i < kMonthsLen; ++i) { | |
| 433 // Match prefix, so we could match January, etc | |
| 434 if (base::strncasecmp(token.c_str(), kMonths[i], 3) == 0) { | |
| 435 exploded.month = i + 1; | |
| 436 found_month = true; | |
| 437 break; | |
| 438 } | |
| 439 } | |
| 440 } else { | |
| 441 // If we've gotten here, it means we've already found and parsed our | |
| 442 // month, and we have another string, which we would expect to be the | |
| 443 // the time zone name. According to the RFC and my experiments with | |
| 444 // how sites format their expirations, we don't have much of a reason | |
| 445 // to support timezones. We don't want to ever barf on user input, | |
| 446 // but this DCHECK should pass for well-formed data. | |
| 447 // DCHECK(token == "GMT"); | |
| 448 } | |
| 449 // Numeric field w/ a colon | |
| 450 } else if (token.find(':') != std::string::npos) { | |
| 451 if (!found_time && | |
| 452 #ifdef COMPILER_MSVC | |
| 453 sscanf_s( | |
| 454 #else | |
| 455 sscanf( | |
| 456 #endif | |
| 457 token.c_str(), "%2u:%2u:%2u", &exploded.hour, | |
| 458 &exploded.minute, &exploded.second) == 3) { | |
| 459 found_time = true; | |
| 460 } else { | |
| 461 // We should only ever encounter one time-like thing. If we're here, | |
| 462 // it means we've found a second, which shouldn't happen. We keep | |
| 463 // the first. This check should be ok for well-formed input: | |
| 464 // NOTREACHED(); | |
| 465 } | |
| 466 // Numeric field | |
| 467 } else { | |
| 468 // Overflow with atoi() is unspecified, so we enforce a max length. | |
| 469 if (!found_day_of_month && token.length() <= 2) { | |
| 470 exploded.day_of_month = atoi(token.c_str()); | |
| 471 found_day_of_month = true; | |
| 472 } else if (!found_year && token.length() <= 5) { | |
| 473 exploded.year = atoi(token.c_str()); | |
| 474 found_year = true; | |
| 475 } else { | |
| 476 // If we're here, it means we've either found an extra numeric field, | |
| 477 // or a numeric field which was too long. For well-formed input, the | |
| 478 // following check would be reasonable: | |
| 479 // NOTREACHED(); | |
| 480 } | |
| 481 } | |
| 482 } | |
| 483 | |
| 484 if (!found_day_of_month || !found_month || !found_time || !found_year) { | |
| 485 // We didn't find all of the fields we need. For well-formed input, the | |
| 486 // following check would be reasonable: | |
| 487 // NOTREACHED() << "Cookie parse expiration failed: " << time_string; | |
| 488 return Time(); | |
| 489 } | |
| 490 | |
| 491 // Normalize the year to expand abbreviated years to the full year. | |
| 492 if (exploded.year >= 69 && exploded.year <= 99) | |
| 493 exploded.year += 1900; | |
| 494 if (exploded.year >= 0 && exploded.year <= 68) | |
| 495 exploded.year += 2000; | |
| 496 | |
| 497 // If our values are within their correct ranges, we got our time. | |
| 498 if (exploded.day_of_month >= 1 && exploded.day_of_month <= 31 && | |
| 499 exploded.month >= 1 && exploded.month <= 12 && | |
| 500 exploded.year >= 1601 && exploded.year <= 30827 && | |
| 501 exploded.hour <= 23 && exploded.minute <= 59 && exploded.second <= 59) { | |
| 502 return Time::FromUTCExploded(exploded); | |
| 503 } | |
| 504 | |
| 505 // One of our values was out of expected range. For well-formed input, | |
| 506 // the following check would be reasonable: | |
| 507 // NOTREACHED() << "Cookie exploded expiration failed: " << time_string; | |
| 508 | |
| 509 return Time(); | |
| 510 } | |
| 511 | |
| 512 // Task classes for queueing the coming request. | |
| 513 | |
| 514 class CookieMonster::CookieMonsterTask | |
| 515 : public base::RefCountedThreadSafe<CookieMonsterTask> { | |
| 516 public: | |
| 517 // Runs the task and invokes the client callback on the thread that | |
| 518 // originally constructed the task. | |
| 519 virtual void Run() = 0; | |
| 520 | |
| 521 protected: | |
| 522 explicit CookieMonsterTask(CookieMonster* cookie_monster); | |
| 523 virtual ~CookieMonsterTask(); | |
| 524 | |
| 525 // Invokes the callback immediately, if the current thread is the one | |
| 526 // that originated the task, or queues the callback for execution on the | |
| 527 // appropriate thread. Maintains a reference to this CookieMonsterTask | |
| 528 // instance until the callback completes. | |
| 529 void InvokeCallback(base::Closure callback); | |
| 530 | |
| 531 CookieMonster* cookie_monster() { | |
| 532 return cookie_monster_; | |
| 533 } | |
| 534 | |
| 535 friend class base::RefCountedThreadSafe<CookieMonsterTask>; | |
| 536 | |
| 537 private: | |
| 538 CookieMonster* cookie_monster_; | |
| 539 scoped_refptr<base::MessageLoopProxy> thread_; | |
| 540 | |
| 541 DISALLOW_COPY_AND_ASSIGN(CookieMonsterTask); | |
| 542 }; | |
| 543 | |
| 544 CookieMonster::CookieMonsterTask::CookieMonsterTask( | |
| 545 CookieMonster* cookie_monster) | |
| 546 : cookie_monster_(cookie_monster), | |
| 547 thread_(base::MessageLoopProxy::current()) { } | |
| 548 | |
| 549 CookieMonster::CookieMonsterTask::~CookieMonsterTask() { } | |
| 550 | |
| 551 // Unfortunately, one cannot re-bind a Callback with parameters into a closure. | |
| 552 // Therefore, the closure passed to InvokeCallback is a clumsy binding of | |
| 553 // Callback::Run on a wrapped Callback instance. Since Callback is not | |
| 554 // reference counted, we bind to an instance that is a member of the | |
| 555 // CookieMonsterTask subclass. Then, we cannot simply post the callback to a | |
| 556 // message loop because the underlying instance may be destroyed (along with the | |
| 557 // CookieMonsterTask instance) in the interim. Therefore, we post a callback | |
| 558 // bound to the CookieMonsterTask, which *is* reference counted (thus preventing | |
| 559 // destruction of the original callback), and which invokes the closure (which | |
| 560 // invokes the original callback with the returned data). | |
| 561 void CookieMonster::CookieMonsterTask::InvokeCallback(base::Closure callback) { | |
| 562 if (thread_->BelongsToCurrentThread()) { | |
| 563 callback.Run(); | |
| 564 } else { | |
| 565 thread_->PostTask(FROM_HERE, base::Bind( | |
| 566 &CookieMonster::CookieMonsterTask::InvokeCallback, this, callback)); | |
| 567 } | |
| 568 } | |
| 569 | |
| 570 // Task class for SetCookieWithDetails call. | |
| 571 class CookieMonster::SetCookieWithDetailsTask | |
| 572 : public CookieMonster::CookieMonsterTask { | |
| 573 public: | |
| 574 SetCookieWithDetailsTask( | |
| 575 CookieMonster* cookie_monster, | |
| 576 const GURL& url, const std::string& name, const std::string& value, | |
| 577 const std::string& domain, const std::string& path, | |
| 578 const base::Time& expiration_time, bool secure, bool http_only, | |
| 579 const CookieMonster::SetCookiesCallback& callback) | |
| 580 : CookieMonsterTask(cookie_monster), | |
| 581 url_(url), | |
| 582 name_(name), | |
| 583 value_(value), | |
| 584 domain_(domain), | |
| 585 path_(path), | |
| 586 expiration_time_(expiration_time), | |
| 587 secure_(secure), | |
| 588 http_only_(http_only), | |
| 589 callback_(callback) { } | |
| 590 | |
| 591 virtual void Run() OVERRIDE; | |
| 592 | |
| 593 private: | |
| 594 GURL url_; | |
| 595 std::string name_; | |
| 596 std::string value_; | |
| 597 std::string domain_; | |
| 598 std::string path_; | |
| 599 base::Time expiration_time_; | |
| 600 bool secure_; | |
| 601 bool http_only_; | |
| 602 CookieMonster::SetCookiesCallback callback_; | |
| 603 | |
| 604 DISALLOW_COPY_AND_ASSIGN(SetCookieWithDetailsTask); | |
| 605 }; | |
| 606 | |
| 607 void CookieMonster::SetCookieWithDetailsTask::Run() { | |
| 608 bool success = this->cookie_monster()-> | |
| 609 SetCookieWithDetails(url_, name_, value_, domain_, path_, | |
| 610 expiration_time_, secure_, http_only_); | |
| 611 if (!callback_.is_null()) { | |
| 612 this->InvokeCallback(base::Bind(&CookieMonster::SetCookiesCallback::Run, | |
| 613 base::Unretained(&callback_), success)); | |
| 614 } | |
| 615 } | |
| 616 | |
| 617 // Task class for GetAllCookies call. | |
| 618 class CookieMonster::GetAllCookiesTask | |
| 619 : public CookieMonster::CookieMonsterTask { | |
| 620 public: | |
| 621 GetAllCookiesTask(CookieMonster* cookie_monster, | |
| 622 const CookieMonster::GetCookieListCallback& callback) | |
| 623 : CookieMonsterTask(cookie_monster), | |
| 624 callback_(callback) { } | |
| 625 | |
| 626 virtual void Run() OVERRIDE; | |
| 627 | |
| 628 private: | |
| 629 CookieMonster::GetCookieListCallback callback_; | |
| 630 | |
| 631 DISALLOW_COPY_AND_ASSIGN(GetAllCookiesTask); | |
| 632 }; | |
| 633 | |
| 634 void CookieMonster::GetAllCookiesTask::Run() { | |
| 635 if (!callback_.is_null()) { | |
| 636 CookieList cookies = this->cookie_monster()->GetAllCookies(); | |
| 637 this->InvokeCallback(base::Bind(&CookieMonster::GetCookieListCallback::Run, | |
| 638 base::Unretained(&callback_), cookies)); | |
| 639 } | |
| 640 } | |
| 641 | |
| 642 // Task class for GetAllCookiesForURLWithOptions call. | |
| 643 class CookieMonster::GetAllCookiesForURLWithOptionsTask | |
| 644 : public CookieMonster::CookieMonsterTask { | |
| 645 public: | |
| 646 GetAllCookiesForURLWithOptionsTask( | |
| 647 CookieMonster* cookie_monster, | |
| 648 const GURL& url, | |
| 649 const CookieOptions& options, | |
| 650 const CookieMonster::GetCookieListCallback& callback) | |
| 651 : CookieMonsterTask(cookie_monster), | |
| 652 url_(url), | |
| 653 options_(options), | |
| 654 callback_(callback) { } | |
| 655 | |
| 656 virtual void Run() OVERRIDE; | |
| 657 | |
| 658 private: | |
| 659 GURL url_; | |
| 660 CookieOptions options_; | |
| 661 CookieMonster::GetCookieListCallback callback_; | |
| 662 | |
| 663 DISALLOW_COPY_AND_ASSIGN(GetAllCookiesForURLWithOptionsTask); | |
| 664 }; | |
| 665 | |
| 666 void CookieMonster::GetAllCookiesForURLWithOptionsTask::Run() { | |
| 667 if (!callback_.is_null()) { | |
| 668 CookieList cookies = this->cookie_monster()-> | |
| 669 GetAllCookiesForURLWithOptions(url_, options_); | |
| 670 this->InvokeCallback(base::Bind(&CookieMonster::GetCookieListCallback::Run, | |
| 671 base::Unretained(&callback_), cookies)); | |
| 672 } | |
| 673 } | |
| 674 | |
| 675 // Task class for DeleteAll call. | |
| 676 class CookieMonster::DeleteAllTask : public CookieMonster::CookieMonsterTask { | |
| 677 public: | |
| 678 DeleteAllTask(CookieMonster* cookie_monster, | |
| 679 const CookieMonster::DeleteCallback& callback) | |
| 680 : CookieMonsterTask(cookie_monster), | |
| 681 callback_(callback) { } | |
| 682 | |
| 683 virtual void Run() OVERRIDE; | |
| 684 | |
| 685 private: | |
| 686 CookieMonster::DeleteCallback callback_; | |
| 687 | |
| 688 DISALLOW_COPY_AND_ASSIGN(DeleteAllTask); | |
| 689 }; | |
| 690 | |
| 691 void CookieMonster::DeleteAllTask::Run() { | |
| 692 int num_deleted = this->cookie_monster()->DeleteAll(true); | |
| 693 if (!callback_.is_null()) { | |
| 694 this->InvokeCallback(base::Bind(&CookieMonster::DeleteCallback::Run, | |
| 695 base::Unretained(&callback_), num_deleted)); | |
| 696 } | |
| 697 } | |
| 698 | |
| 699 // Task class for DeleteAllCreatedBetween call. | |
| 700 class CookieMonster::DeleteAllCreatedBetweenTask | |
| 701 : public CookieMonster::CookieMonsterTask { | |
| 702 public: | |
| 703 DeleteAllCreatedBetweenTask( | |
| 704 CookieMonster* cookie_monster, | |
| 705 const Time& delete_begin, | |
| 706 const Time& delete_end, | |
| 707 const CookieMonster::DeleteCallback& callback) | |
| 708 : CookieMonsterTask(cookie_monster), | |
| 709 delete_begin_(delete_begin), | |
| 710 delete_end_(delete_end), | |
| 711 callback_(callback) { } | |
| 712 | |
| 713 virtual void Run() OVERRIDE; | |
| 714 | |
| 715 private: | |
| 716 Time delete_begin_; | |
| 717 Time delete_end_; | |
| 718 CookieMonster::DeleteCallback callback_; | |
| 719 | |
| 720 DISALLOW_COPY_AND_ASSIGN(DeleteAllCreatedBetweenTask); | |
| 721 }; | |
| 722 | |
| 723 void CookieMonster::DeleteAllCreatedBetweenTask::Run() { | |
| 724 int num_deleted = this->cookie_monster()-> | |
| 725 DeleteAllCreatedBetween(delete_begin_, delete_end_); | |
| 726 if (!callback_.is_null()) { | |
| 727 this->InvokeCallback(base::Bind(&CookieMonster::DeleteCallback::Run, | |
| 728 base::Unretained(&callback_), num_deleted)); | |
| 729 } | |
| 730 } | |
| 731 | |
| 732 // Task class for DeleteAllForHost call. | |
| 733 class CookieMonster::DeleteAllForHostTask | |
| 734 : public CookieMonster::CookieMonsterTask { | |
| 735 public: | |
| 736 DeleteAllForHostTask(CookieMonster* cookie_monster, | |
| 737 const GURL& url, | |
| 738 const CookieMonster::DeleteCallback& callback) | |
| 739 : CookieMonsterTask(cookie_monster), | |
| 740 url_(url), | |
| 741 callback_(callback) { } | |
| 742 | |
| 743 virtual void Run() OVERRIDE; | |
| 744 | |
| 745 private: | |
| 746 GURL url_; | |
| 747 CookieMonster::DeleteCallback callback_; | |
| 748 | |
| 749 DISALLOW_COPY_AND_ASSIGN(DeleteAllForHostTask); | |
| 750 }; | |
| 751 | |
| 752 void CookieMonster::DeleteAllForHostTask::Run() { | |
| 753 int num_deleted = this->cookie_monster()->DeleteAllForHost(url_); | |
| 754 if (!callback_.is_null()) { | |
| 755 this->InvokeCallback(base::Bind(&CookieMonster::DeleteCallback::Run, | |
| 756 base::Unretained(&callback_), num_deleted)); | |
| 757 } | |
| 758 } | |
| 759 | |
| 760 // Task class for DeleteCanonicalCookie call. | |
| 761 class CookieMonster::DeleteCanonicalCookieTask | |
| 762 : public CookieMonster::CookieMonsterTask { | |
| 763 public: | |
| 764 DeleteCanonicalCookieTask( | |
| 765 CookieMonster* cookie_monster, | |
| 766 const CookieMonster::CanonicalCookie& cookie, | |
| 767 const CookieMonster::DeleteCookieCallback& callback) | |
| 768 : CookieMonsterTask(cookie_monster), | |
| 769 cookie_(cookie), | |
| 770 callback_(callback) { } | |
| 771 | |
| 772 virtual void Run() OVERRIDE; | |
| 773 | |
| 774 private: | |
| 775 CookieMonster::CanonicalCookie cookie_; | |
| 776 CookieMonster::DeleteCookieCallback callback_; | |
| 777 | |
| 778 DISALLOW_COPY_AND_ASSIGN(DeleteCanonicalCookieTask); | |
| 779 }; | |
| 780 | |
| 781 void CookieMonster::DeleteCanonicalCookieTask::Run() { | |
| 782 bool result = this->cookie_monster()->DeleteCanonicalCookie(cookie_); | |
| 783 if (!callback_.is_null()) { | |
| 784 this->InvokeCallback(base::Bind(&CookieMonster::DeleteCookieCallback::Run, | |
| 785 base::Unretained(&callback_), result)); | |
| 786 } | |
| 787 } | |
| 788 | |
| 789 // Task class for SetCookieWithOptions call. | |
| 790 class CookieMonster::SetCookieWithOptionsTask | |
| 791 : public CookieMonster::CookieMonsterTask { | |
| 792 public: | |
| 793 SetCookieWithOptionsTask(CookieMonster* cookie_monster, | |
| 794 const GURL& url, | |
| 795 const std::string& cookie_line, | |
| 796 const CookieOptions& options, | |
| 797 const CookieMonster::SetCookiesCallback& callback) | |
| 798 : CookieMonsterTask(cookie_monster), | |
| 799 url_(url), | |
| 800 cookie_line_(cookie_line), | |
| 801 options_(options), | |
| 802 callback_(callback) { } | |
| 803 | |
| 804 virtual void Run() OVERRIDE; | |
| 805 | |
| 806 private: | |
| 807 GURL url_; | |
| 808 std::string cookie_line_; | |
| 809 CookieOptions options_; | |
| 810 CookieMonster::SetCookiesCallback callback_; | |
| 811 | |
| 812 DISALLOW_COPY_AND_ASSIGN(SetCookieWithOptionsTask); | |
| 813 }; | |
| 814 | |
| 815 void CookieMonster::SetCookieWithOptionsTask::Run() { | |
| 816 bool result = this->cookie_monster()-> | |
| 817 SetCookieWithOptions(url_, cookie_line_, options_); | |
| 818 if (!callback_.is_null()) { | |
| 819 this->InvokeCallback(base::Bind(&CookieMonster::SetCookiesCallback::Run, | |
| 820 base::Unretained(&callback_), result)); | |
| 821 } | |
| 822 } | |
| 823 | |
| 824 // Task class for GetCookiesWithOptions call. | |
| 825 class CookieMonster::GetCookiesWithOptionsTask | |
| 826 : public CookieMonster::CookieMonsterTask { | |
| 827 public: | |
| 828 GetCookiesWithOptionsTask(CookieMonster* cookie_monster, | |
| 829 const GURL& url, | |
| 830 const CookieOptions& options, | |
| 831 const CookieMonster::GetCookiesCallback& callback) | |
| 832 : CookieMonsterTask(cookie_monster), | |
| 833 url_(url), | |
| 834 options_(options), | |
| 835 callback_(callback) { } | |
| 836 | |
| 837 virtual void Run() OVERRIDE; | |
| 838 | |
| 839 private: | |
| 840 GURL url_; | |
| 841 CookieOptions options_; | |
| 842 CookieMonster::GetCookiesCallback callback_; | |
| 843 | |
| 844 DISALLOW_COPY_AND_ASSIGN(GetCookiesWithOptionsTask); | |
| 845 }; | |
| 846 | |
| 847 void CookieMonster::GetCookiesWithOptionsTask::Run() { | |
| 848 std::string cookie = this->cookie_monster()-> | |
| 849 GetCookiesWithOptions(url_, options_); | |
| 850 if (!callback_.is_null()) { | |
| 851 this->InvokeCallback(base::Bind(&CookieMonster::GetCookiesCallback::Run, | |
| 852 base::Unretained(&callback_), cookie)); | |
| 853 } | |
| 854 } | |
| 855 | |
| 856 // Task class for GetCookiesWithInfo call. | |
| 857 class CookieMonster::GetCookiesWithInfoTask | |
| 858 : public CookieMonster::CookieMonsterTask { | |
| 859 public: | |
| 860 GetCookiesWithInfoTask(CookieMonster* cookie_monster, | |
| 861 const GURL& url, | |
| 862 const CookieOptions& options, | |
| 863 const CookieMonster::GetCookieInfoCallback& callback) | |
| 864 : CookieMonsterTask(cookie_monster), | |
| 865 url_(url), | |
| 866 options_(options), | |
| 867 callback_(callback) { } | |
| 868 | |
| 869 virtual void Run() OVERRIDE; | |
| 870 | |
| 871 private: | |
| 872 GURL url_; | |
| 873 CookieOptions options_; | |
| 874 CookieMonster::GetCookieInfoCallback callback_; | |
| 875 | |
| 876 DISALLOW_COPY_AND_ASSIGN(GetCookiesWithInfoTask); | |
| 877 }; | |
| 878 | |
| 879 void CookieMonster::GetCookiesWithInfoTask::Run() { | |
| 880 if (!callback_.is_null()) { | |
| 881 std::string cookie_line; | |
| 882 std::vector<CookieMonster::CookieInfo> cookie_infos; | |
| 883 this->cookie_monster()-> | |
| 884 GetCookiesWithInfo(url_, options_, &cookie_line, &cookie_infos); | |
| 885 this->InvokeCallback(base::Bind(&CookieMonster::GetCookieInfoCallback::Run, | |
| 886 base::Unretained(&callback_), | |
| 887 cookie_line, cookie_infos)); | |
| 888 } | |
| 889 } | |
| 890 | |
| 891 // Task class for DeleteCookie call. | |
| 892 class CookieMonster::DeleteCookieTask | |
| 893 : public CookieMonster::CookieMonsterTask { | |
| 894 public: | |
| 895 DeleteCookieTask(CookieMonster* cookie_monster, | |
| 896 const GURL& url, | |
| 897 const std::string& cookie_name, | |
| 898 const base::Closure& callback) | |
| 899 : CookieMonsterTask(cookie_monster), | |
| 900 url_(url), | |
| 901 cookie_name_(cookie_name), | |
| 902 callback_(callback) { } | |
| 903 | |
| 904 virtual void Run() OVERRIDE; | |
| 905 | |
| 906 private: | |
| 907 GURL url_; | |
| 908 std::string cookie_name_; | |
| 909 base::Closure callback_; | |
| 910 | |
| 911 DISALLOW_COPY_AND_ASSIGN(DeleteCookieTask); | |
| 912 }; | |
| 913 | |
| 914 void CookieMonster::DeleteCookieTask::Run() { | |
| 915 this->cookie_monster()->DeleteCookie(url_, cookie_name_); | |
| 916 if (!callback_.is_null()) { | |
| 917 this->InvokeCallback(callback_); | |
| 918 } | |
| 919 } | |
| 920 | |
| 921 // Asynchronous CookieMonster API | |
| 922 | |
| 923 void CookieMonster::SetCookieWithDetailsAsync( | |
| 924 const GURL& url, const std::string& name, const std::string& value, | |
| 925 const std::string& domain, const std::string& path, | |
| 926 const base::Time& expiration_time, bool secure, bool http_only, | |
| 927 const SetCookiesCallback& callback) { | |
| 928 scoped_refptr<SetCookieWithDetailsTask> task = | |
| 929 new SetCookieWithDetailsTask(this, url, name, value, domain, path, | |
| 930 expiration_time, secure, http_only, | |
| 931 callback); | |
| 932 | |
| 933 DoCookieTaskForURL(task, url); | |
| 934 } | |
| 935 | |
| 936 void CookieMonster::GetAllCookiesAsync(const GetCookieListCallback& callback) { | |
| 937 scoped_refptr<GetAllCookiesTask> task = | |
| 938 new GetAllCookiesTask(this, callback); | |
| 939 | |
| 940 DoCookieTask(task); | |
| 941 } | |
| 942 | |
| 943 | |
| 944 void CookieMonster::GetAllCookiesForURLWithOptionsAsync( | |
| 945 const GURL& url, | |
| 946 const CookieOptions& options, | |
| 947 const GetCookieListCallback& callback) { | |
| 948 scoped_refptr<GetAllCookiesForURLWithOptionsTask> task = | |
| 949 new GetAllCookiesForURLWithOptionsTask(this, url, options, callback); | |
| 950 | |
| 951 DoCookieTaskForURL(task, url); | |
| 952 } | |
| 953 | |
| 954 void CookieMonster::GetAllCookiesForURLAsync( | |
| 955 const GURL& url, const GetCookieListCallback& callback) { | |
| 956 CookieOptions options; | |
| 957 options.set_include_httponly(); | |
| 958 scoped_refptr<GetAllCookiesForURLWithOptionsTask> task = | |
| 959 new GetAllCookiesForURLWithOptionsTask(this, url, options, callback); | |
| 960 | |
| 961 DoCookieTaskForURL(task, url); | |
| 962 } | |
| 963 | |
| 964 void CookieMonster::DeleteAllAsync(const DeleteCallback& callback) { | |
| 965 scoped_refptr<DeleteAllTask> task = | |
| 966 new DeleteAllTask(this, callback); | |
| 967 | |
| 968 DoCookieTask(task); | |
| 969 } | |
| 970 | |
| 971 void CookieMonster::DeleteAllCreatedBetweenAsync( | |
| 972 const Time& delete_begin, const Time& delete_end, | |
| 973 const DeleteCallback& callback) { | |
| 974 scoped_refptr<DeleteAllCreatedBetweenTask> task = | |
| 975 new DeleteAllCreatedBetweenTask(this, delete_begin, delete_end, | |
| 976 callback); | |
| 977 | |
| 978 DoCookieTask(task); | |
| 979 } | |
| 980 | |
| 981 void CookieMonster::DeleteAllForHostAsync( | |
| 982 const GURL& url, const DeleteCallback& callback) { | |
| 983 scoped_refptr<DeleteAllForHostTask> task = | |
| 984 new DeleteAllForHostTask(this, url, callback); | |
| 985 | |
| 986 DoCookieTaskForURL(task, url); | |
| 987 } | |
| 988 | |
| 989 void CookieMonster::DeleteCanonicalCookieAsync( | |
| 990 const CanonicalCookie& cookie, | |
| 991 const DeleteCookieCallback& callback) { | |
| 992 scoped_refptr<DeleteCanonicalCookieTask> task = | |
| 993 new DeleteCanonicalCookieTask(this, cookie, callback); | |
| 994 | |
| 995 DoCookieTask(task); | |
| 996 } | |
| 997 | |
| 998 void CookieMonster::SetCookieWithOptionsAsync( | |
| 999 const GURL& url, | |
| 1000 const std::string& cookie_line, | |
| 1001 const CookieOptions& options, | |
| 1002 const SetCookiesCallback& callback) { | |
| 1003 scoped_refptr<SetCookieWithOptionsTask> task = | |
| 1004 new SetCookieWithOptionsTask(this, url, cookie_line, options, callback); | |
| 1005 | |
| 1006 DoCookieTaskForURL(task, url); | |
| 1007 } | |
| 1008 | |
| 1009 void CookieMonster::GetCookiesWithOptionsAsync( | |
| 1010 const GURL& url, | |
| 1011 const CookieOptions& options, | |
| 1012 const GetCookiesCallback& callback) { | |
| 1013 scoped_refptr<GetCookiesWithOptionsTask> task = | |
| 1014 new GetCookiesWithOptionsTask(this, url, options, callback); | |
| 1015 | |
| 1016 DoCookieTaskForURL(task, url); | |
| 1017 } | |
| 1018 | |
| 1019 void CookieMonster::GetCookiesWithInfoAsync( | |
| 1020 const GURL& url, | |
| 1021 const CookieOptions& options, | |
| 1022 const GetCookieInfoCallback& callback) { | |
| 1023 scoped_refptr<GetCookiesWithInfoTask> task = | |
| 1024 new GetCookiesWithInfoTask(this, url, options, callback); | |
| 1025 | |
| 1026 DoCookieTaskForURL(task, url); | |
| 1027 } | |
| 1028 | |
| 1029 void CookieMonster::DeleteCookieAsync(const GURL& url, | |
| 1030 const std::string& cookie_name, | |
| 1031 const base::Closure& callback) { | |
| 1032 scoped_refptr<DeleteCookieTask> task = | |
| 1033 new DeleteCookieTask(this, url, cookie_name, callback); | |
| 1034 | |
| 1035 DoCookieTaskForURL(task, url); | |
| 1036 } | |
| 1037 | |
| 1038 void CookieMonster::DoCookieTask( | |
| 1039 const scoped_refptr<CookieMonsterTask>& task_item) { | |
| 1040 { | |
| 1041 base::AutoLock autolock(lock_); | |
| 1042 InitIfNecessary(); | |
| 1043 if (!loaded_) { | |
| 1044 queue_.push(task_item); | |
| 1045 return; | |
| 1046 } | |
| 1047 } | |
| 1048 | |
| 1049 task_item->Run(); | |
| 1050 } | |
| 1051 | |
| 1052 void CookieMonster::DoCookieTaskForURL( | |
| 1053 const scoped_refptr<CookieMonsterTask>& task_item, | |
| 1054 const GURL& url) { | |
| 1055 { | |
| 1056 base::AutoLock autolock(lock_); | |
| 1057 InitIfNecessary(); | |
| 1058 // If cookies for the requested domain key (eTLD+1) have been loaded from DB | |
| 1059 // then run the task, otherwise load from DB. | |
| 1060 if (!loaded_) { | |
| 1061 // Checks if the domain key has been loaded. | |
| 1062 std::string key(cookie_util::GetEffectiveDomain(url.scheme(), | |
| 1063 url.host())); | |
| 1064 if (keys_loaded_.find(key) == keys_loaded_.end()) { | |
| 1065 std::map<std::string, std::deque<scoped_refptr<CookieMonsterTask> > > | |
| 1066 ::iterator it = tasks_queued_.find(key); | |
| 1067 if (it == tasks_queued_.end()) { | |
| 1068 store_->LoadCookiesForKey(key, | |
| 1069 base::Bind(&CookieMonster::OnKeyLoaded, this, key)); | |
| 1070 it = tasks_queued_.insert(std::make_pair(key, | |
| 1071 std::deque<scoped_refptr<CookieMonsterTask> >())).first; | |
| 1072 } | |
| 1073 it->second.push_back(task_item); | |
| 1074 return; | |
| 1075 } | |
| 1076 } | |
| 1077 } | |
| 1078 task_item->Run(); | |
| 1079 } | |
| 1080 | |
| 1081 bool CookieMonster::SetCookieWithDetails( | |
| 1082 const GURL& url, const std::string& name, const std::string& value, | |
| 1083 const std::string& domain, const std::string& path, | |
| 1084 const base::Time& expiration_time, bool secure, bool http_only) { | |
| 1085 base::AutoLock autolock(lock_); | |
| 1086 | |
| 1087 if (!HasCookieableScheme(url)) | |
| 1088 return false; | |
| 1089 | |
| 1090 Time creation_time = CurrentTime(); | |
| 1091 last_time_seen_ = creation_time; | |
| 1092 | |
| 1093 // TODO(abarth): Take these values as parameters. | |
| 1094 std::string mac_key; | |
| 1095 std::string mac_algorithm; | |
| 1096 | |
| 1097 scoped_ptr<CanonicalCookie> cc; | |
| 1098 cc.reset(CanonicalCookie::Create( | |
| 1099 url, name, value, domain, path, | |
| 1100 mac_key, mac_algorithm, | |
| 1101 creation_time, expiration_time, | |
| 1102 secure, http_only, !expiration_time.is_null())); | |
| 1103 | |
| 1104 if (!cc.get()) | |
| 1105 return false; | |
| 1106 | |
| 1107 CookieOptions options; | |
| 1108 options.set_include_httponly(); | |
| 1109 return SetCanonicalCookie(&cc, creation_time, options); | |
| 1110 } | |
| 1111 | |
| 1112 bool CookieMonster::InitializeFrom(const CookieList& list) { | |
| 1113 base::AutoLock autolock(lock_); | |
| 1114 InitIfNecessary(); | |
| 1115 for (net::CookieList::const_iterator iter = list.begin(); | |
| 1116 iter != list.end(); ++iter) { | |
| 1117 scoped_ptr<net::CookieMonster::CanonicalCookie> cookie; | |
| 1118 cookie.reset(new net::CookieMonster::CanonicalCookie(*iter)); | |
| 1119 net::CookieOptions options; | |
| 1120 options.set_include_httponly(); | |
| 1121 if (!SetCanonicalCookie(&cookie, cookie->CreationDate(), | |
| 1122 options)) { | |
| 1123 return false; | |
| 1124 } | |
| 1125 } | |
| 1126 return true; | |
| 1127 } | |
| 1128 | |
| 1129 CookieList CookieMonster::GetAllCookies() { | |
| 1130 base::AutoLock autolock(lock_); | |
| 1131 | |
| 1132 // This function is being called to scrape the cookie list for management UI | |
| 1133 // or similar. We shouldn't show expired cookies in this list since it will | |
| 1134 // just be confusing to users, and this function is called rarely enough (and | |
| 1135 // is already slow enough) that it's OK to take the time to garbage collect | |
| 1136 // the expired cookies now. | |
| 1137 // | |
| 1138 // Note that this does not prune cookies to be below our limits (if we've | |
| 1139 // exceeded them) the way that calling GarbageCollect() would. | |
| 1140 GarbageCollectExpired(Time::Now(), | |
| 1141 CookieMapItPair(cookies_.begin(), cookies_.end()), | |
| 1142 NULL); | |
| 1143 | |
| 1144 // Copy the CanonicalCookie pointers from the map so that we can use the same | |
| 1145 // sorter as elsewhere, then copy the result out. | |
| 1146 std::vector<CanonicalCookie*> cookie_ptrs; | |
| 1147 cookie_ptrs.reserve(cookies_.size()); | |
| 1148 for (CookieMap::iterator it = cookies_.begin(); it != cookies_.end(); ++it) | |
| 1149 cookie_ptrs.push_back(it->second); | |
| 1150 std::sort(cookie_ptrs.begin(), cookie_ptrs.end(), CookieSorter); | |
| 1151 | |
| 1152 CookieList cookie_list; | |
| 1153 cookie_list.reserve(cookie_ptrs.size()); | |
| 1154 for (std::vector<CanonicalCookie*>::const_iterator it = cookie_ptrs.begin(); | |
| 1155 it != cookie_ptrs.end(); ++it) | |
| 1156 cookie_list.push_back(**it); | |
| 1157 | |
| 1158 return cookie_list; | |
| 1159 } | |
| 1160 | |
| 1161 CookieList CookieMonster::GetAllCookiesForURLWithOptions( | |
| 1162 const GURL& url, | |
| 1163 const CookieOptions& options) { | |
| 1164 base::AutoLock autolock(lock_); | |
| 1165 | |
| 1166 std::vector<CanonicalCookie*> cookie_ptrs; | |
| 1167 FindCookiesForHostAndDomain(url, options, false, &cookie_ptrs); | |
| 1168 std::sort(cookie_ptrs.begin(), cookie_ptrs.end(), CookieSorter); | |
| 1169 | |
| 1170 CookieList cookies; | |
| 1171 for (std::vector<CanonicalCookie*>::const_iterator it = cookie_ptrs.begin(); | |
| 1172 it != cookie_ptrs.end(); it++) | |
| 1173 cookies.push_back(**it); | |
| 1174 | |
| 1175 return cookies; | |
| 1176 } | |
| 1177 | |
| 1178 CookieList CookieMonster::GetAllCookiesForURL(const GURL& url) { | |
| 1179 CookieOptions options; | |
| 1180 options.set_include_httponly(); | |
| 1181 | |
| 1182 return GetAllCookiesForURLWithOptions(url, options); | |
| 1183 } | |
| 1184 | |
| 1185 int CookieMonster::DeleteAll(bool sync_to_store) { | |
| 1186 base::AutoLock autolock(lock_); | |
| 1187 | |
| 1188 int num_deleted = 0; | |
| 1189 for (CookieMap::iterator it = cookies_.begin(); it != cookies_.end();) { | |
| 1190 CookieMap::iterator curit = it; | |
| 1191 ++it; | |
| 1192 InternalDeleteCookie(curit, sync_to_store, | |
| 1193 sync_to_store ? DELETE_COOKIE_EXPLICIT : | |
| 1194 DELETE_COOKIE_DONT_RECORD /* Destruction. */); | |
| 1195 ++num_deleted; | |
| 1196 } | |
| 1197 | |
| 1198 return num_deleted; | |
| 1199 } | |
| 1200 | |
| 1201 int CookieMonster::DeleteAllCreatedBetween(const Time& delete_begin, | |
| 1202 const Time& delete_end) { | |
| 1203 base::AutoLock autolock(lock_); | |
| 1204 | |
| 1205 int num_deleted = 0; | |
| 1206 for (CookieMap::iterator it = cookies_.begin(); it != cookies_.end();) { | |
| 1207 CookieMap::iterator curit = it; | |
| 1208 CanonicalCookie* cc = curit->second; | |
| 1209 ++it; | |
| 1210 | |
| 1211 if (cc->CreationDate() >= delete_begin && | |
| 1212 (delete_end.is_null() || cc->CreationDate() < delete_end)) { | |
| 1213 InternalDeleteCookie(curit, | |
| 1214 true, /*sync_to_store*/ | |
| 1215 DELETE_COOKIE_EXPLICIT); | |
| 1216 ++num_deleted; | |
| 1217 } | |
| 1218 } | |
| 1219 | |
| 1220 return num_deleted; | |
| 1221 } | |
| 1222 | |
| 1223 int CookieMonster::DeleteAllForHost(const GURL& url) { | |
| 1224 base::AutoLock autolock(lock_); | |
| 1225 | |
| 1226 if (!HasCookieableScheme(url)) | |
| 1227 return 0; | |
| 1228 | |
| 1229 const std::string scheme(url.scheme()); | |
| 1230 const std::string host(url.host()); | |
| 1231 | |
| 1232 // We store host cookies in the store by their canonical host name; | |
| 1233 // domain cookies are stored with a leading ".". So this is a pretty | |
| 1234 // simple lookup and per-cookie delete. | |
| 1235 int num_deleted = 0; | |
| 1236 for (CookieMapItPair its = cookies_.equal_range(GetKey(host)); | |
| 1237 its.first != its.second;) { | |
| 1238 CookieMap::iterator curit = its.first; | |
| 1239 ++its.first; | |
| 1240 | |
| 1241 const CanonicalCookie* const cc = curit->second; | |
| 1242 | |
| 1243 // Delete only on a match as a host cookie. | |
| 1244 if (cc->IsHostCookie() && cc->IsDomainMatch(scheme, host)) { | |
| 1245 num_deleted++; | |
| 1246 | |
| 1247 InternalDeleteCookie(curit, true, DELETE_COOKIE_EXPLICIT); | |
| 1248 } | |
| 1249 } | |
| 1250 return num_deleted; | |
| 1251 } | |
| 1252 | |
| 1253 bool CookieMonster::DeleteCanonicalCookie(const CanonicalCookie& cookie) { | |
| 1254 base::AutoLock autolock(lock_); | |
| 1255 | |
| 1256 for (CookieMapItPair its = cookies_.equal_range(GetKey(cookie.Domain())); | |
| 1257 its.first != its.second; ++its.first) { | |
| 1258 // The creation date acts as our unique index... | |
| 1259 if (its.first->second->CreationDate() == cookie.CreationDate()) { | |
| 1260 InternalDeleteCookie(its.first, true, DELETE_COOKIE_EXPLICIT); | |
| 1261 return true; | |
| 1262 } | |
| 1263 } | |
| 1264 return false; | |
| 1265 } | |
| 1266 | |
| 1267 void CookieMonster::SetCookieableSchemes( | |
| 1268 const char* schemes[], size_t num_schemes) { | |
| 1269 base::AutoLock autolock(lock_); | |
| 1270 | |
| 1271 // Cookieable Schemes must be set before first use of function. | |
| 1272 DCHECK(!initialized_); | |
| 1273 | |
| 1274 cookieable_schemes_.clear(); | |
| 1275 cookieable_schemes_.insert(cookieable_schemes_.end(), | |
| 1276 schemes, schemes + num_schemes); | |
| 1277 } | |
| 1278 | |
| 1279 void CookieMonster::SetKeepExpiredCookies() { | |
| 1280 keep_expired_cookies_ = true; | |
| 1281 } | |
| 1282 | |
| 1283 void CookieMonster::SetClearPersistentStoreOnExit(bool clear_local_store) { | |
| 1284 if (store_) | |
| 1285 store_->SetClearLocalStateOnExit(clear_local_store); | |
| 1286 } | |
| 1287 | |
| 1288 // static | |
| 1289 void CookieMonster::EnableFileScheme() { | |
| 1290 enable_file_scheme_ = true; | |
| 1291 } | |
| 1292 | |
| 1293 void CookieMonster::FlushStore(const base::Closure& callback) { | |
| 1294 base::AutoLock autolock(lock_); | |
| 1295 if (initialized_ && store_) | |
| 1296 store_->Flush(callback); | |
| 1297 else if (!callback.is_null()) | |
| 1298 MessageLoop::current()->PostTask(FROM_HERE, callback); | |
| 1299 } | |
| 1300 | |
| 1301 bool CookieMonster::SetCookieWithOptions(const GURL& url, | |
| 1302 const std::string& cookie_line, | |
| 1303 const CookieOptions& options) { | |
| 1304 base::AutoLock autolock(lock_); | |
| 1305 | |
| 1306 if (!HasCookieableScheme(url)) { | |
| 1307 return false; | |
| 1308 } | |
| 1309 | |
| 1310 return SetCookieWithCreationTimeAndOptions(url, cookie_line, Time(), options); | |
| 1311 } | |
| 1312 | |
| 1313 std::string CookieMonster::GetCookiesWithOptions(const GURL& url, | |
| 1314 const CookieOptions& options) { | |
| 1315 base::AutoLock autolock(lock_); | |
| 1316 | |
| 1317 if (!HasCookieableScheme(url)) | |
| 1318 return std::string(); | |
| 1319 | |
| 1320 TimeTicks start_time(TimeTicks::Now()); | |
| 1321 | |
| 1322 std::vector<CanonicalCookie*> cookies; | |
| 1323 FindCookiesForHostAndDomain(url, options, true, &cookies); | |
| 1324 std::sort(cookies.begin(), cookies.end(), CookieSorter); | |
| 1325 | |
| 1326 std::string cookie_line = BuildCookieLine(cookies); | |
| 1327 | |
| 1328 histogram_time_get_->AddTime(TimeTicks::Now() - start_time); | |
| 1329 | |
| 1330 VLOG(kVlogGetCookies) << "GetCookies() result: " << cookie_line; | |
| 1331 | |
| 1332 return cookie_line; | |
| 1333 } | |
| 1334 | |
| 1335 void CookieMonster::GetCookiesWithInfo(const GURL& url, | |
| 1336 const CookieOptions& options, | |
| 1337 std::string* cookie_line, | |
| 1338 std::vector<CookieInfo>* cookie_infos) { | |
| 1339 DCHECK(cookie_line->empty()); | |
| 1340 DCHECK(cookie_infos->empty()); | |
| 1341 | |
| 1342 base::AutoLock autolock(lock_); | |
| 1343 | |
| 1344 if (!HasCookieableScheme(url)) | |
| 1345 return; | |
| 1346 | |
| 1347 TimeTicks start_time(TimeTicks::Now()); | |
| 1348 | |
| 1349 std::vector<CanonicalCookie*> cookies; | |
| 1350 FindCookiesForHostAndDomain(url, options, true, &cookies); | |
| 1351 std::sort(cookies.begin(), cookies.end(), CookieSorter); | |
| 1352 *cookie_line = BuildCookieLine(cookies); | |
| 1353 | |
| 1354 histogram_time_get_->AddTime(TimeTicks::Now() - start_time); | |
| 1355 | |
| 1356 TimeTicks mac_start_time = TimeTicks::Now(); | |
| 1357 BuildCookieInfoList(cookies, cookie_infos); | |
| 1358 histogram_time_mac_->AddTime(TimeTicks::Now() - mac_start_time); | |
| 1359 } | |
| 1360 | |
| 1361 void CookieMonster::DeleteCookie(const GURL& url, | |
| 1362 const std::string& cookie_name) { | |
| 1363 base::AutoLock autolock(lock_); | |
| 1364 | |
| 1365 if (!HasCookieableScheme(url)) | |
| 1366 return; | |
| 1367 | |
| 1368 CookieOptions options; | |
| 1369 options.set_include_httponly(); | |
| 1370 // Get the cookies for this host and its domain(s). | |
| 1371 std::vector<CanonicalCookie*> cookies; | |
| 1372 FindCookiesForHostAndDomain(url, options, true, &cookies); | |
| 1373 std::set<CanonicalCookie*> matching_cookies; | |
| 1374 | |
| 1375 for (std::vector<CanonicalCookie*>::const_iterator it = cookies.begin(); | |
| 1376 it != cookies.end(); ++it) { | |
| 1377 if ((*it)->Name() != cookie_name) | |
| 1378 continue; | |
| 1379 if (url.path().find((*it)->Path())) | |
| 1380 continue; | |
| 1381 matching_cookies.insert(*it); | |
| 1382 } | |
| 1383 | |
| 1384 for (CookieMap::iterator it = cookies_.begin(); it != cookies_.end();) { | |
| 1385 CookieMap::iterator curit = it; | |
| 1386 ++it; | |
| 1387 if (matching_cookies.find(curit->second) != matching_cookies.end()) { | |
| 1388 InternalDeleteCookie(curit, true, DELETE_COOKIE_EXPLICIT); | |
| 1389 } | |
| 1390 } | |
| 1391 } | |
| 1392 | |
| 1393 CookieMonster* CookieMonster::GetCookieMonster() { | |
| 1394 return this; | |
| 1395 } | |
| 1396 | |
| 1397 void CookieMonster::SetPersistSessionCookies(bool persist_session_cookies) { | |
| 1398 // This function must be called before the CookieMonster is used. | |
| 1399 DCHECK(!initialized_); | |
| 1400 persist_session_cookies_ = persist_session_cookies; | |
| 1401 } | |
| 1402 | |
| 1403 void CookieMonster::SaveSessionCookies() { | |
| 1404 if (store_) { | |
| 1405 store_->SetClearLocalStateOnExit(false); | |
| 1406 } | |
| 1407 } | |
| 1408 | |
| 1409 CookieMonster::~CookieMonster() { | |
| 1410 DeleteAll(false); | |
| 1411 } | |
| 1412 | |
| 1413 bool CookieMonster::SetCookieWithCreationTime(const GURL& url, | |
| 1414 const std::string& cookie_line, | |
| 1415 const base::Time& creation_time) { | |
| 1416 DCHECK(!store_) << "This method is only to be used by unit-tests."; | |
| 1417 base::AutoLock autolock(lock_); | |
| 1418 | |
| 1419 if (!HasCookieableScheme(url)) { | |
| 1420 return false; | |
| 1421 } | |
| 1422 | |
| 1423 InitIfNecessary(); | |
| 1424 return SetCookieWithCreationTimeAndOptions(url, cookie_line, creation_time, | |
| 1425 CookieOptions()); | |
| 1426 } | |
| 1427 | |
| 1428 void CookieMonster::InitStore() { | |
| 1429 DCHECK(store_) << "Store must exist to initialize"; | |
| 1430 | |
| 1431 // We bind in the current time so that we can report the wall-clock time for | |
| 1432 // loading cookies. | |
| 1433 store_->Load(base::Bind(&CookieMonster::OnLoaded, this, TimeTicks::Now())); | |
| 1434 } | |
| 1435 | |
| 1436 void CookieMonster::OnLoaded(TimeTicks beginning_time, | |
| 1437 const std::vector<CanonicalCookie*>& cookies) { | |
| 1438 StoreLoadedCookies(cookies); | |
| 1439 histogram_time_blocked_on_load_->AddTime(TimeTicks::Now() - beginning_time); | |
| 1440 | |
| 1441 // Invoke the task queue of cookie request. | |
| 1442 InvokeQueue(); | |
| 1443 } | |
| 1444 | |
| 1445 void CookieMonster::OnKeyLoaded(const std::string& key, | |
| 1446 const std::vector<CanonicalCookie*>& cookies) { | |
| 1447 // This function does its own separate locking. | |
| 1448 StoreLoadedCookies(cookies); | |
| 1449 | |
| 1450 std::deque<scoped_refptr<CookieMonsterTask> > tasks_queued; | |
| 1451 { | |
| 1452 base::AutoLock autolock(lock_); | |
| 1453 keys_loaded_.insert(key); | |
| 1454 std::map<std::string, std::deque<scoped_refptr<CookieMonsterTask> > > | |
| 1455 ::iterator it = tasks_queued_.find(key); | |
| 1456 if (it == tasks_queued_.end()) | |
| 1457 return; | |
| 1458 it->second.swap(tasks_queued); | |
| 1459 tasks_queued_.erase(it); | |
| 1460 } | |
| 1461 | |
| 1462 while (!tasks_queued.empty()) { | |
| 1463 scoped_refptr<CookieMonsterTask> task = tasks_queued.front(); | |
| 1464 task->Run(); | |
| 1465 tasks_queued.pop_front(); | |
| 1466 } | |
| 1467 } | |
| 1468 | |
| 1469 void CookieMonster::StoreLoadedCookies( | |
| 1470 const std::vector<CanonicalCookie*>& cookies) { | |
| 1471 // Initialize the store and sync in any saved persistent cookies. We don't | |
| 1472 // care if it's expired, insert it so it can be garbage collected, removed, | |
| 1473 // and sync'd. | |
| 1474 base::AutoLock autolock(lock_); | |
| 1475 | |
| 1476 for (std::vector<CanonicalCookie*>::const_iterator it = cookies.begin(); | |
| 1477 it != cookies.end(); ++it) { | |
| 1478 int64 cookie_creation_time = (*it)->CreationDate().ToInternalValue(); | |
| 1479 | |
| 1480 if (creation_times_.insert(cookie_creation_time).second) { | |
| 1481 InternalInsertCookie(GetKey((*it)->Domain()), *it, false); | |
| 1482 const Time cookie_access_time((*it)->LastAccessDate()); | |
| 1483 if (earliest_access_time_.is_null() || | |
| 1484 cookie_access_time < earliest_access_time_) | |
| 1485 earliest_access_time_ = cookie_access_time; | |
| 1486 } else { | |
| 1487 LOG(ERROR) << base::StringPrintf("Found cookies with duplicate creation " | |
| 1488 "times in backing store: " | |
| 1489 "{name='%s', domain='%s', path='%s'}", | |
| 1490 (*it)->Name().c_str(), | |
| 1491 (*it)->Domain().c_str(), | |
| 1492 (*it)->Path().c_str()); | |
| 1493 // We've been given ownership of the cookie and are throwing it | |
| 1494 // away; reclaim the space. | |
| 1495 delete (*it); | |
| 1496 } | |
| 1497 } | |
| 1498 | |
| 1499 // After importing cookies from the PersistentCookieStore, verify that | |
| 1500 // none of our other constraints are violated. | |
| 1501 // In particular, the backing store might have given us duplicate cookies. | |
| 1502 | |
| 1503 // This method could be called multiple times due to priority loading, thus | |
| 1504 // cookies loaded in previous runs will be validated again, but this is OK | |
| 1505 // since they are expected to be much fewer than total DB. | |
| 1506 EnsureCookiesMapIsValid(); | |
| 1507 } | |
| 1508 | |
| 1509 void CookieMonster::InvokeQueue() { | |
| 1510 while (true) { | |
| 1511 scoped_refptr<CookieMonsterTask> request_task; | |
| 1512 { | |
| 1513 base::AutoLock autolock(lock_); | |
| 1514 if (queue_.empty()) { | |
| 1515 loaded_ = true; | |
| 1516 creation_times_.clear(); | |
| 1517 keys_loaded_.clear(); | |
| 1518 break; | |
| 1519 } | |
| 1520 request_task = queue_.front(); | |
| 1521 queue_.pop(); | |
| 1522 } | |
| 1523 request_task->Run(); | |
| 1524 } | |
| 1525 } | |
| 1526 | |
| 1527 void CookieMonster::EnsureCookiesMapIsValid() { | |
| 1528 lock_.AssertAcquired(); | |
| 1529 | |
| 1530 int num_duplicates_trimmed = 0; | |
| 1531 | |
| 1532 // Iterate through all the of the cookies, grouped by host. | |
| 1533 CookieMap::iterator prev_range_end = cookies_.begin(); | |
| 1534 while (prev_range_end != cookies_.end()) { | |
| 1535 CookieMap::iterator cur_range_begin = prev_range_end; | |
| 1536 const std::string key = cur_range_begin->first; // Keep a copy. | |
| 1537 CookieMap::iterator cur_range_end = cookies_.upper_bound(key); | |
| 1538 prev_range_end = cur_range_end; | |
| 1539 | |
| 1540 // Ensure no equivalent cookies for this host. | |
| 1541 num_duplicates_trimmed += | |
| 1542 TrimDuplicateCookiesForKey(key, cur_range_begin, cur_range_end); | |
| 1543 } | |
| 1544 | |
| 1545 // Record how many duplicates were found in the database. | |
| 1546 // See InitializeHistograms() for details. | |
| 1547 histogram_cookie_deletion_cause_->Add(num_duplicates_trimmed); | |
| 1548 } | |
| 1549 | |
| 1550 int CookieMonster::TrimDuplicateCookiesForKey( | |
| 1551 const std::string& key, | |
| 1552 CookieMap::iterator begin, | |
| 1553 CookieMap::iterator end) { | |
| 1554 lock_.AssertAcquired(); | |
| 1555 | |
| 1556 // Set of cookies ordered by creation time. | |
| 1557 typedef std::set<CookieMap::iterator, OrderByCreationTimeDesc> CookieSet; | |
| 1558 | |
| 1559 // Helper map we populate to find the duplicates. | |
| 1560 typedef std::map<CookieSignature, CookieSet> EquivalenceMap; | |
| 1561 EquivalenceMap equivalent_cookies; | |
| 1562 | |
| 1563 // The number of duplicate cookies that have been found. | |
| 1564 int num_duplicates = 0; | |
| 1565 | |
| 1566 // Iterate through all of the cookies in our range, and insert them into | |
| 1567 // the equivalence map. | |
| 1568 for (CookieMap::iterator it = begin; it != end; ++it) { | |
| 1569 DCHECK_EQ(key, it->first); | |
| 1570 CanonicalCookie* cookie = it->second; | |
| 1571 | |
| 1572 CookieSignature signature(cookie->Name(), cookie->Domain(), | |
| 1573 cookie->Path()); | |
| 1574 CookieSet& set = equivalent_cookies[signature]; | |
| 1575 | |
| 1576 // We found a duplicate! | |
| 1577 if (!set.empty()) | |
| 1578 num_duplicates++; | |
| 1579 | |
| 1580 // We save the iterator into |cookies_| rather than the actual cookie | |
| 1581 // pointer, since we may need to delete it later. | |
| 1582 bool insert_success = set.insert(it).second; | |
| 1583 DCHECK(insert_success) << | |
| 1584 "Duplicate creation times found in duplicate cookie name scan."; | |
| 1585 } | |
| 1586 | |
| 1587 // If there were no duplicates, we are done! | |
| 1588 if (num_duplicates == 0) | |
| 1589 return 0; | |
| 1590 | |
| 1591 // Make sure we find everything below that we did above. | |
| 1592 int num_duplicates_found = 0; | |
| 1593 | |
| 1594 // Otherwise, delete all the duplicate cookies, both from our in-memory store | |
| 1595 // and from the backing store. | |
| 1596 for (EquivalenceMap::iterator it = equivalent_cookies.begin(); | |
| 1597 it != equivalent_cookies.end(); | |
| 1598 ++it) { | |
| 1599 const CookieSignature& signature = it->first; | |
| 1600 CookieSet& dupes = it->second; | |
| 1601 | |
| 1602 if (dupes.size() <= 1) | |
| 1603 continue; // This cookiename/path has no duplicates. | |
| 1604 num_duplicates_found += dupes.size() - 1; | |
| 1605 | |
| 1606 // Since |dups| is sorted by creation time (descending), the first cookie | |
| 1607 // is the most recent one, so we will keep it. The rest are duplicates. | |
| 1608 dupes.erase(dupes.begin()); | |
| 1609 | |
| 1610 LOG(ERROR) << base::StringPrintf( | |
| 1611 "Found %d duplicate cookies for host='%s', " | |
| 1612 "with {name='%s', domain='%s', path='%s'}", | |
| 1613 static_cast<int>(dupes.size()), | |
| 1614 key.c_str(), | |
| 1615 signature.name.c_str(), | |
| 1616 signature.domain.c_str(), | |
| 1617 signature.path.c_str()); | |
| 1618 | |
| 1619 // Remove all the cookies identified by |dupes|. It is valid to delete our | |
| 1620 // list of iterators one at a time, since |cookies_| is a multimap (they | |
| 1621 // don't invalidate existing iterators following deletion). | |
| 1622 for (CookieSet::iterator dupes_it = dupes.begin(); | |
| 1623 dupes_it != dupes.end(); | |
| 1624 ++dupes_it) { | |
| 1625 InternalDeleteCookie(*dupes_it, true, | |
| 1626 DELETE_COOKIE_DUPLICATE_IN_BACKING_STORE); | |
| 1627 } | |
| 1628 } | |
| 1629 DCHECK_EQ(num_duplicates, num_duplicates_found); | |
| 1630 | |
| 1631 return num_duplicates; | |
| 1632 } | |
| 1633 | |
| 1634 // Note: file must be the last scheme. | |
| 1635 const char* CookieMonster::kDefaultCookieableSchemes[] = | |
| 1636 { "http", "https", "file" }; | |
| 1637 const int CookieMonster::kDefaultCookieableSchemesCount = | |
| 1638 arraysize(CookieMonster::kDefaultCookieableSchemes); | |
| 1639 | |
| 1640 void CookieMonster::SetDefaultCookieableSchemes() { | |
| 1641 int num_schemes = enable_file_scheme_ ? | |
| 1642 kDefaultCookieableSchemesCount : kDefaultCookieableSchemesCount - 1; | |
| 1643 SetCookieableSchemes(kDefaultCookieableSchemes, num_schemes); | |
| 1644 } | |
| 1645 | |
| 1646 | |
| 1647 void CookieMonster::FindCookiesForHostAndDomain( | |
| 1648 const GURL& url, | |
| 1649 const CookieOptions& options, | |
| 1650 bool update_access_time, | |
| 1651 std::vector<CanonicalCookie*>* cookies) { | |
| 1652 lock_.AssertAcquired(); | |
| 1653 | |
| 1654 const Time current_time(CurrentTime()); | |
| 1655 | |
| 1656 // Probe to save statistics relatively frequently. We do it here rather | |
| 1657 // than in the set path as many websites won't set cookies, and we | |
| 1658 // want to collect statistics whenever the browser's being used. | |
| 1659 RecordPeriodicStats(current_time); | |
| 1660 | |
| 1661 // Can just dispatch to FindCookiesForKey | |
| 1662 const std::string key(GetKey(url.host())); | |
| 1663 FindCookiesForKey(key, url, options, current_time, | |
| 1664 update_access_time, cookies); | |
| 1665 } | |
| 1666 | |
| 1667 void CookieMonster::FindCookiesForKey( | |
| 1668 const std::string& key, | |
| 1669 const GURL& url, | |
| 1670 const CookieOptions& options, | |
| 1671 const Time& current, | |
| 1672 bool update_access_time, | |
| 1673 std::vector<CanonicalCookie*>* cookies) { | |
| 1674 lock_.AssertAcquired(); | |
| 1675 | |
| 1676 const std::string scheme(url.scheme()); | |
| 1677 const std::string host(url.host()); | |
| 1678 bool secure = url.SchemeIsSecure(); | |
| 1679 | |
| 1680 for (CookieMapItPair its = cookies_.equal_range(key); | |
| 1681 its.first != its.second; ) { | |
| 1682 CookieMap::iterator curit = its.first; | |
| 1683 CanonicalCookie* cc = curit->second; | |
| 1684 ++its.first; | |
| 1685 | |
| 1686 // If the cookie is expired, delete it. | |
| 1687 if (cc->IsExpired(current) && !keep_expired_cookies_) { | |
| 1688 InternalDeleteCookie(curit, true, DELETE_COOKIE_EXPIRED); | |
| 1689 continue; | |
| 1690 } | |
| 1691 | |
| 1692 // Filter out HttpOnly cookies, per options. | |
| 1693 if (options.exclude_httponly() && cc->IsHttpOnly()) | |
| 1694 continue; | |
| 1695 | |
| 1696 // Filter out secure cookies unless we're https. | |
| 1697 if (!secure && cc->IsSecure()) | |
| 1698 continue; | |
| 1699 | |
| 1700 // Filter out cookies that don't apply to this domain. | |
| 1701 if (!cc->IsDomainMatch(scheme, host)) | |
| 1702 continue; | |
| 1703 | |
| 1704 if (!cc->IsOnPath(url.path())) | |
| 1705 continue; | |
| 1706 | |
| 1707 // Add this cookie to the set of matching cookies. Update the access | |
| 1708 // time if we've been requested to do so. | |
| 1709 if (update_access_time) { | |
| 1710 InternalUpdateCookieAccessTime(cc, current); | |
| 1711 } | |
| 1712 cookies->push_back(cc); | |
| 1713 } | |
| 1714 } | |
| 1715 | |
| 1716 bool CookieMonster::DeleteAnyEquivalentCookie(const std::string& key, | |
| 1717 const CanonicalCookie& ecc, | |
| 1718 bool skip_httponly, | |
| 1719 bool already_expired) { | |
| 1720 lock_.AssertAcquired(); | |
| 1721 | |
| 1722 bool found_equivalent_cookie = false; | |
| 1723 bool skipped_httponly = false; | |
| 1724 for (CookieMapItPair its = cookies_.equal_range(key); | |
| 1725 its.first != its.second; ) { | |
| 1726 CookieMap::iterator curit = its.first; | |
| 1727 CanonicalCookie* cc = curit->second; | |
| 1728 ++its.first; | |
| 1729 | |
| 1730 if (ecc.IsEquivalent(*cc)) { | |
| 1731 // We should never have more than one equivalent cookie, since they should | |
| 1732 // overwrite each other. | |
| 1733 CHECK(!found_equivalent_cookie) << | |
| 1734 "Duplicate equivalent cookies found, cookie store is corrupted."; | |
| 1735 if (skip_httponly && cc->IsHttpOnly()) { | |
| 1736 skipped_httponly = true; | |
| 1737 } else { | |
| 1738 InternalDeleteCookie(curit, true, already_expired ? | |
| 1739 DELETE_COOKIE_EXPIRED_OVERWRITE : DELETE_COOKIE_OVERWRITE); | |
| 1740 } | |
| 1741 found_equivalent_cookie = true; | |
| 1742 } | |
| 1743 } | |
| 1744 return skipped_httponly; | |
| 1745 } | |
| 1746 | |
| 1747 void CookieMonster::InternalInsertCookie(const std::string& key, | |
| 1748 CanonicalCookie* cc, | |
| 1749 bool sync_to_store) { | |
| 1750 lock_.AssertAcquired(); | |
| 1751 | |
| 1752 if ((cc->IsPersistent() || persist_session_cookies_) && | |
| 1753 store_ && sync_to_store) | |
| 1754 store_->AddCookie(*cc); | |
| 1755 cookies_.insert(CookieMap::value_type(key, cc)); | |
| 1756 if (delegate_.get()) { | |
| 1757 delegate_->OnCookieChanged( | |
| 1758 *cc, false, CookieMonster::Delegate::CHANGE_COOKIE_EXPLICIT); | |
| 1759 } | |
| 1760 } | |
| 1761 | |
| 1762 bool CookieMonster::SetCookieWithCreationTimeAndOptions( | |
| 1763 const GURL& url, | |
| 1764 const std::string& cookie_line, | |
| 1765 const Time& creation_time_or_null, | |
| 1766 const CookieOptions& options) { | |
| 1767 lock_.AssertAcquired(); | |
| 1768 | |
| 1769 VLOG(kVlogSetCookies) << "SetCookie() line: " << cookie_line; | |
| 1770 | |
| 1771 Time creation_time = creation_time_or_null; | |
| 1772 if (creation_time.is_null()) { | |
| 1773 creation_time = CurrentTime(); | |
| 1774 last_time_seen_ = creation_time; | |
| 1775 } | |
| 1776 | |
| 1777 // Parse the cookie. | |
| 1778 ParsedCookie pc(cookie_line); | |
| 1779 | |
| 1780 if (!pc.IsValid()) { | |
| 1781 VLOG(kVlogSetCookies) << "WARNING: Couldn't parse cookie"; | |
| 1782 return false; | |
| 1783 } | |
| 1784 | |
| 1785 if (options.exclude_httponly() && pc.IsHttpOnly()) { | |
| 1786 VLOG(kVlogSetCookies) << "SetCookie() not setting httponly cookie"; | |
| 1787 return false; | |
| 1788 } | |
| 1789 | |
| 1790 std::string cookie_domain; | |
| 1791 if (!GetCookieDomain(url, pc, &cookie_domain)) { | |
| 1792 return false; | |
| 1793 } | |
| 1794 | |
| 1795 std::string cookie_path = CanonPath(url, pc); | |
| 1796 std::string mac_key = pc.HasMACKey() ? pc.MACKey() : std::string(); | |
| 1797 std::string mac_algorithm = pc.HasMACAlgorithm() ? | |
| 1798 pc.MACAlgorithm() : std::string(); | |
| 1799 | |
| 1800 scoped_ptr<CanonicalCookie> cc; | |
| 1801 Time cookie_expires = CanonExpiration(pc, creation_time); | |
| 1802 | |
| 1803 bool session_only = options.force_session() || cookie_expires.is_null(); | |
| 1804 cc.reset(new CanonicalCookie(url, pc.Name(), pc.Value(), cookie_domain, | |
| 1805 cookie_path, mac_key, mac_algorithm, | |
| 1806 creation_time, cookie_expires, | |
| 1807 creation_time, pc.IsSecure(), pc.IsHttpOnly(), | |
| 1808 !cookie_expires.is_null(), | |
| 1809 !session_only)); | |
| 1810 | |
| 1811 if (!cc.get()) { | |
| 1812 VLOG(kVlogSetCookies) << "WARNING: Failed to allocate CanonicalCookie"; | |
| 1813 return false; | |
| 1814 } | |
| 1815 return SetCanonicalCookie(&cc, creation_time, options); | |
| 1816 } | |
| 1817 | |
| 1818 bool CookieMonster::SetCanonicalCookie(scoped_ptr<CanonicalCookie>* cc, | |
| 1819 const Time& creation_time, | |
| 1820 const CookieOptions& options) { | |
| 1821 const std::string key(GetKey((*cc)->Domain())); | |
| 1822 bool already_expired = (*cc)->IsExpired(creation_time); | |
| 1823 if (DeleteAnyEquivalentCookie(key, **cc, options.exclude_httponly(), | |
| 1824 already_expired)) { | |
| 1825 VLOG(kVlogSetCookies) << "SetCookie() not clobbering httponly cookie"; | |
| 1826 return false; | |
| 1827 } | |
| 1828 | |
| 1829 VLOG(kVlogSetCookies) << "SetCookie() key: " << key << " cc: " | |
| 1830 << (*cc)->DebugString(); | |
| 1831 | |
| 1832 // Realize that we might be setting an expired cookie, and the only point | |
| 1833 // was to delete the cookie which we've already done. | |
| 1834 if (!already_expired || keep_expired_cookies_) { | |
| 1835 // See InitializeHistograms() for details. | |
| 1836 if ((*cc)->DoesExpire()) { | |
| 1837 histogram_expiration_duration_minutes_->Add( | |
| 1838 ((*cc)->ExpiryDate() - creation_time).InMinutes()); | |
| 1839 } | |
| 1840 | |
| 1841 InternalInsertCookie(key, cc->release(), true); | |
| 1842 } | |
| 1843 | |
| 1844 // We assume that hopefully setting a cookie will be less common than | |
| 1845 // querying a cookie. Since setting a cookie can put us over our limits, | |
| 1846 // make sure that we garbage collect... We can also make the assumption that | |
| 1847 // if a cookie was set, in the common case it will be used soon after, | |
| 1848 // and we will purge the expired cookies in GetCookies(). | |
| 1849 GarbageCollect(creation_time, key); | |
| 1850 | |
| 1851 return true; | |
| 1852 } | |
| 1853 | |
| 1854 void CookieMonster::InternalUpdateCookieAccessTime(CanonicalCookie* cc, | |
| 1855 const Time& current) { | |
| 1856 lock_.AssertAcquired(); | |
| 1857 | |
| 1858 // Based off the Mozilla code. When a cookie has been accessed recently, | |
| 1859 // don't bother updating its access time again. This reduces the number of | |
| 1860 // updates we do during pageload, which in turn reduces the chance our storage | |
| 1861 // backend will hit its batch thresholds and be forced to update. | |
| 1862 if ((current - cc->LastAccessDate()) < last_access_threshold_) | |
| 1863 return; | |
| 1864 | |
| 1865 // See InitializeHistograms() for details. | |
| 1866 histogram_between_access_interval_minutes_->Add( | |
| 1867 (current - cc->LastAccessDate()).InMinutes()); | |
| 1868 | |
| 1869 cc->SetLastAccessDate(current); | |
| 1870 if ((cc->IsPersistent() || persist_session_cookies_) && store_) | |
| 1871 store_->UpdateCookieAccessTime(*cc); | |
| 1872 } | |
| 1873 | |
| 1874 void CookieMonster::InternalDeleteCookie(CookieMap::iterator it, | |
| 1875 bool sync_to_store, | |
| 1876 DeletionCause deletion_cause) { | |
| 1877 lock_.AssertAcquired(); | |
| 1878 | |
| 1879 // Ideally, this would be asserted up where we define ChangeCauseMapping, | |
| 1880 // but DeletionCause's visibility (or lack thereof) forces us to make | |
| 1881 // this check here. | |
| 1882 COMPILE_ASSERT(arraysize(ChangeCauseMapping) == DELETE_COOKIE_LAST_ENTRY + 1, | |
| 1883 ChangeCauseMapping_size_not_eq_DeletionCause_enum_size); | |
| 1884 | |
| 1885 // See InitializeHistograms() for details. | |
| 1886 if (deletion_cause != DELETE_COOKIE_DONT_RECORD) | |
| 1887 histogram_cookie_deletion_cause_->Add(deletion_cause); | |
| 1888 | |
| 1889 CanonicalCookie* cc = it->second; | |
| 1890 VLOG(kVlogSetCookies) << "InternalDeleteCookie() cc: " << cc->DebugString(); | |
| 1891 | |
| 1892 if ((cc->IsPersistent() || persist_session_cookies_) | |
| 1893 && store_ && sync_to_store) | |
| 1894 store_->DeleteCookie(*cc); | |
| 1895 if (delegate_.get()) { | |
| 1896 ChangeCausePair mapping = ChangeCauseMapping[deletion_cause]; | |
| 1897 | |
| 1898 if (mapping.notify) | |
| 1899 delegate_->OnCookieChanged(*cc, true, mapping.cause); | |
| 1900 } | |
| 1901 cookies_.erase(it); | |
| 1902 delete cc; | |
| 1903 } | |
| 1904 | |
| 1905 // Domain expiry behavior is unchanged by key/expiry scheme (the | |
| 1906 // meaning of the key is different, but that's not visible to this | |
| 1907 // routine). | |
| 1908 int CookieMonster::GarbageCollect(const Time& current, | |
| 1909 const std::string& key) { | |
| 1910 lock_.AssertAcquired(); | |
| 1911 | |
| 1912 int num_deleted = 0; | |
| 1913 | |
| 1914 // Collect garbage for this key. | |
| 1915 if (cookies_.count(key) > kDomainMaxCookies) { | |
| 1916 VLOG(kVlogGarbageCollection) << "GarbageCollect() key: " << key; | |
| 1917 | |
| 1918 std::vector<CookieMap::iterator> cookie_its; | |
| 1919 num_deleted += GarbageCollectExpired( | |
| 1920 current, cookies_.equal_range(key), &cookie_its); | |
| 1921 base::Time oldest_removed; | |
| 1922 if (FindLeastRecentlyAccessed(kDomainMaxCookies, kDomainPurgeCookies, | |
| 1923 &oldest_removed, &cookie_its)) { | |
| 1924 // Delete in two passes so we can figure out what we're nuking | |
| 1925 // that would be kept at the global level. | |
| 1926 int num_subject_to_global_purge = | |
| 1927 GarbageCollectDeleteList( | |
| 1928 current, | |
| 1929 Time::Now() - TimeDelta::FromDays(kSafeFromGlobalPurgeDays), | |
| 1930 DELETE_COOKIE_EVICTED_DOMAIN_PRE_SAFE, | |
| 1931 cookie_its); | |
| 1932 num_deleted += num_subject_to_global_purge; | |
| 1933 // Correct because FindLeastRecentlyAccessed returns a sorted list. | |
| 1934 cookie_its.erase(cookie_its.begin(), | |
| 1935 cookie_its.begin() + num_subject_to_global_purge); | |
| 1936 num_deleted += | |
| 1937 GarbageCollectDeleteList( | |
| 1938 current, | |
| 1939 Time(), | |
| 1940 DELETE_COOKIE_EVICTED_DOMAIN_POST_SAFE, | |
| 1941 cookie_its); | |
| 1942 } | |
| 1943 } | |
| 1944 | |
| 1945 // Collect garbage for everything. With firefox style we want to | |
| 1946 // preserve cookies touched in kSafeFromGlobalPurgeDays, otherwise | |
| 1947 // not. | |
| 1948 if (cookies_.size() > kMaxCookies && | |
| 1949 (earliest_access_time_ < | |
| 1950 Time::Now() - TimeDelta::FromDays(kSafeFromGlobalPurgeDays))) { | |
| 1951 VLOG(kVlogGarbageCollection) << "GarbageCollect() everything"; | |
| 1952 std::vector<CookieMap::iterator> cookie_its; | |
| 1953 base::Time oldest_left; | |
| 1954 num_deleted += GarbageCollectExpired( | |
| 1955 current, CookieMapItPair(cookies_.begin(), cookies_.end()), | |
| 1956 &cookie_its); | |
| 1957 if (FindLeastRecentlyAccessed(kMaxCookies, kPurgeCookies, | |
| 1958 &oldest_left, &cookie_its)) { | |
| 1959 Time oldest_safe_cookie( | |
| 1960 (Time::Now() - TimeDelta::FromDays(kSafeFromGlobalPurgeDays))); | |
| 1961 int num_evicted = GarbageCollectDeleteList( | |
| 1962 current, | |
| 1963 oldest_safe_cookie, | |
| 1964 DELETE_COOKIE_EVICTED_GLOBAL, | |
| 1965 cookie_its); | |
| 1966 | |
| 1967 // If no cookies were preserved by the time limit, the global last | |
| 1968 // access is set to the value returned from FindLeastRecentlyAccessed. | |
| 1969 // If the time limit preserved some cookies, we use the last access of | |
| 1970 // the oldest preserved cookie. | |
| 1971 if (num_evicted == static_cast<int>(cookie_its.size())) { | |
| 1972 earliest_access_time_ = oldest_left; | |
| 1973 } else { | |
| 1974 earliest_access_time_ = | |
| 1975 (*(cookie_its.begin() + num_evicted))->second->LastAccessDate(); | |
| 1976 } | |
| 1977 num_deleted += num_evicted; | |
| 1978 } | |
| 1979 } | |
| 1980 | |
| 1981 return num_deleted; | |
| 1982 } | |
| 1983 | |
| 1984 int CookieMonster::GarbageCollectExpired( | |
| 1985 const Time& current, | |
| 1986 const CookieMapItPair& itpair, | |
| 1987 std::vector<CookieMap::iterator>* cookie_its) { | |
| 1988 if (keep_expired_cookies_) | |
| 1989 return 0; | |
| 1990 | |
| 1991 lock_.AssertAcquired(); | |
| 1992 | |
| 1993 int num_deleted = 0; | |
| 1994 for (CookieMap::iterator it = itpair.first, end = itpair.second; it != end;) { | |
| 1995 CookieMap::iterator curit = it; | |
| 1996 ++it; | |
| 1997 | |
| 1998 if (curit->second->IsExpired(current)) { | |
| 1999 InternalDeleteCookie(curit, true, DELETE_COOKIE_EXPIRED); | |
| 2000 ++num_deleted; | |
| 2001 } else if (cookie_its) { | |
| 2002 cookie_its->push_back(curit); | |
| 2003 } | |
| 2004 } | |
| 2005 | |
| 2006 return num_deleted; | |
| 2007 } | |
| 2008 | |
| 2009 int CookieMonster::GarbageCollectDeleteList( | |
| 2010 const Time& current, | |
| 2011 const Time& keep_accessed_after, | |
| 2012 DeletionCause cause, | |
| 2013 std::vector<CookieMap::iterator>& cookie_its) { | |
| 2014 int num_deleted = 0; | |
| 2015 for (std::vector<CookieMap::iterator>::iterator it = cookie_its.begin(); | |
| 2016 it != cookie_its.end(); it++) { | |
| 2017 if (keep_accessed_after.is_null() || | |
| 2018 (*it)->second->LastAccessDate() < keep_accessed_after) { | |
| 2019 histogram_evicted_last_access_minutes_->Add( | |
| 2020 (current - (*it)->second->LastAccessDate()).InMinutes()); | |
| 2021 InternalDeleteCookie((*it), true, cause); | |
| 2022 num_deleted++; | |
| 2023 } | |
| 2024 } | |
| 2025 return num_deleted; | |
| 2026 } | |
| 2027 | |
| 2028 // A wrapper around RegistryControlledDomainService::GetDomainAndRegistry | |
| 2029 // to make clear we're creating a key for our local map. Here and | |
| 2030 // in FindCookiesForHostAndDomain() are the only two places where | |
| 2031 // we need to conditionalize based on key type. | |
| 2032 // | |
| 2033 // Note that this key algorithm explicitly ignores the scheme. This is | |
| 2034 // because when we're entering cookies into the map from the backing store, | |
| 2035 // we in general won't have the scheme at that point. | |
| 2036 // In practical terms, this means that file cookies will be stored | |
| 2037 // in the map either by an empty string or by UNC name (and will be | |
| 2038 // limited by kMaxCookiesPerHost), and extension cookies will be stored | |
| 2039 // based on the single extension id, as the extension id won't have the | |
| 2040 // form of a DNS host and hence GetKey() will return it unchanged. | |
| 2041 // | |
| 2042 // Arguably the right thing to do here is to make the key | |
| 2043 // algorithm dependent on the scheme, and make sure that the scheme is | |
| 2044 // available everywhere the key must be obtained (specfically at backing | |
| 2045 // store load time). This would require either changing the backing store | |
| 2046 // database schema to include the scheme (far more trouble than it's worth), or | |
| 2047 // separating out file cookies into their own CookieMonster instance and | |
| 2048 // thus restricting each scheme to a single cookie monster (which might | |
| 2049 // be worth it, but is still too much trouble to solve what is currently a | |
| 2050 // non-problem). | |
| 2051 std::string CookieMonster::GetKey(const std::string& domain) const { | |
| 2052 std::string effective_domain( | |
| 2053 RegistryControlledDomainService::GetDomainAndRegistry(domain)); | |
| 2054 if (effective_domain.empty()) | |
| 2055 effective_domain = domain; | |
| 2056 | |
| 2057 if (!effective_domain.empty() && effective_domain[0] == '.') | |
| 2058 return effective_domain.substr(1); | |
| 2059 return effective_domain; | |
| 2060 } | |
| 2061 | |
| 2062 bool CookieMonster::HasCookieableScheme(const GURL& url) { | |
| 2063 lock_.AssertAcquired(); | |
| 2064 | |
| 2065 // Make sure the request is on a cookie-able url scheme. | |
| 2066 for (size_t i = 0; i < cookieable_schemes_.size(); ++i) { | |
| 2067 // We matched a scheme. | |
| 2068 if (url.SchemeIs(cookieable_schemes_[i].c_str())) { | |
| 2069 // We've matched a supported scheme. | |
| 2070 return true; | |
| 2071 } | |
| 2072 } | |
| 2073 | |
| 2074 // The scheme didn't match any in our whitelist. | |
| 2075 VLOG(kVlogPerCookieMonster) << "WARNING: Unsupported cookie scheme: " | |
| 2076 << url.scheme(); | |
| 2077 return false; | |
| 2078 } | |
| 2079 | |
| 2080 // Test to see if stats should be recorded, and record them if so. | |
| 2081 // The goal here is to get sampling for the average browser-hour of | |
| 2082 // activity. We won't take samples when the web isn't being surfed, | |
| 2083 // and when the web is being surfed, we'll take samples about every | |
| 2084 // kRecordStatisticsIntervalSeconds. | |
| 2085 // last_statistic_record_time_ is initialized to Now() rather than null | |
| 2086 // in the constructor so that we won't take statistics right after | |
| 2087 // startup, to avoid bias from browsers that are started but not used. | |
| 2088 void CookieMonster::RecordPeriodicStats(const base::Time& current_time) { | |
| 2089 const base::TimeDelta kRecordStatisticsIntervalTime( | |
| 2090 base::TimeDelta::FromSeconds(kRecordStatisticsIntervalSeconds)); | |
| 2091 | |
| 2092 // If we've taken statistics recently, return. | |
| 2093 if (current_time - last_statistic_record_time_ <= | |
| 2094 kRecordStatisticsIntervalTime) { | |
| 2095 return; | |
| 2096 } | |
| 2097 | |
| 2098 // See InitializeHistograms() for details. | |
| 2099 histogram_count_->Add(cookies_.size()); | |
| 2100 | |
| 2101 // More detailed statistics on cookie counts at different granularities. | |
| 2102 TimeTicks beginning_of_time(TimeTicks::Now()); | |
| 2103 | |
| 2104 for (CookieMap::const_iterator it_key = cookies_.begin(); | |
| 2105 it_key != cookies_.end(); ) { | |
| 2106 const std::string& key(it_key->first); | |
| 2107 | |
| 2108 int key_count = 0; | |
| 2109 typedef std::map<std::string, unsigned int> DomainMap; | |
| 2110 DomainMap domain_map; | |
| 2111 CookieMapItPair its_cookies = cookies_.equal_range(key); | |
| 2112 while (its_cookies.first != its_cookies.second) { | |
| 2113 key_count++; | |
| 2114 const std::string& cookie_domain(its_cookies.first->second->Domain()); | |
| 2115 domain_map[cookie_domain]++; | |
| 2116 | |
| 2117 its_cookies.first++; | |
| 2118 } | |
| 2119 histogram_etldp1_count_->Add(key_count); | |
| 2120 histogram_domain_per_etldp1_count_->Add(domain_map.size()); | |
| 2121 for (DomainMap::const_iterator domain_map_it = domain_map.begin(); | |
| 2122 domain_map_it != domain_map.end(); domain_map_it++) | |
| 2123 histogram_domain_count_->Add(domain_map_it->second); | |
| 2124 | |
| 2125 it_key = its_cookies.second; | |
| 2126 } | |
| 2127 | |
| 2128 VLOG(kVlogPeriodic) | |
| 2129 << "Time for recording cookie stats (us): " | |
| 2130 << (TimeTicks::Now() - beginning_of_time).InMicroseconds(); | |
| 2131 | |
| 2132 last_statistic_record_time_ = current_time; | |
| 2133 } | |
| 2134 | |
| 2135 // Initialize all histogram counter variables used in this class. | |
| 2136 // | |
| 2137 // Normal histogram usage involves using the macros defined in | |
| 2138 // histogram.h, which automatically takes care of declaring these | |
| 2139 // variables (as statics), initializing them, and accumulating into | |
| 2140 // them, all from a single entry point. Unfortunately, that solution | |
| 2141 // doesn't work for the CookieMonster, as it's vulnerable to races between | |
| 2142 // separate threads executing the same functions and hence initializing the | |
| 2143 // same static variables. There isn't a race danger in the histogram | |
| 2144 // accumulation calls; they are written to be resilient to simultaneous | |
| 2145 // calls from multiple threads. | |
| 2146 // | |
| 2147 // The solution taken here is to have per-CookieMonster instance | |
| 2148 // variables that are constructed during CookieMonster construction. | |
| 2149 // Note that these variables refer to the same underlying histogram, | |
| 2150 // so we still race (but safely) with other CookieMonster instances | |
| 2151 // for accumulation. | |
| 2152 // | |
| 2153 // To do this we've expanded out the individual histogram macros calls, | |
| 2154 // with declarations of the variables in the class decl, initialization here | |
| 2155 // (done from the class constructor) and direct calls to the accumulation | |
| 2156 // methods where needed. The specific histogram macro calls on which the | |
| 2157 // initialization is based are included in comments below. | |
| 2158 void CookieMonster::InitializeHistograms() { | |
| 2159 // From UMA_HISTOGRAM_CUSTOM_COUNTS | |
| 2160 histogram_expiration_duration_minutes_ = base::Histogram::FactoryGet( | |
| 2161 "Cookie.ExpirationDurationMinutes", | |
| 2162 1, kMinutesInTenYears, 50, | |
| 2163 base::Histogram::kUmaTargetedHistogramFlag); | |
| 2164 histogram_between_access_interval_minutes_ = base::Histogram::FactoryGet( | |
| 2165 "Cookie.BetweenAccessIntervalMinutes", | |
| 2166 1, kMinutesInTenYears, 50, | |
| 2167 base::Histogram::kUmaTargetedHistogramFlag); | |
| 2168 histogram_evicted_last_access_minutes_ = base::Histogram::FactoryGet( | |
| 2169 "Cookie.EvictedLastAccessMinutes", | |
| 2170 1, kMinutesInTenYears, 50, | |
| 2171 base::Histogram::kUmaTargetedHistogramFlag); | |
| 2172 histogram_count_ = base::Histogram::FactoryGet( | |
| 2173 "Cookie.Count", 1, 4000, 50, | |
| 2174 base::Histogram::kUmaTargetedHistogramFlag); | |
| 2175 histogram_domain_count_ = base::Histogram::FactoryGet( | |
| 2176 "Cookie.DomainCount", 1, 4000, 50, | |
| 2177 base::Histogram::kUmaTargetedHistogramFlag); | |
| 2178 histogram_etldp1_count_ = base::Histogram::FactoryGet( | |
| 2179 "Cookie.Etldp1Count", 1, 4000, 50, | |
| 2180 base::Histogram::kUmaTargetedHistogramFlag); | |
| 2181 histogram_domain_per_etldp1_count_ = base::Histogram::FactoryGet( | |
| 2182 "Cookie.DomainPerEtldp1Count", 1, 4000, 50, | |
| 2183 base::Histogram::kUmaTargetedHistogramFlag); | |
| 2184 | |
| 2185 // From UMA_HISTOGRAM_COUNTS_10000 & UMA_HISTOGRAM_CUSTOM_COUNTS | |
| 2186 histogram_number_duplicate_db_cookies_ = base::Histogram::FactoryGet( | |
| 2187 "Net.NumDuplicateCookiesInDb", 1, 10000, 50, | |
| 2188 base::Histogram::kUmaTargetedHistogramFlag); | |
| 2189 | |
| 2190 // From UMA_HISTOGRAM_ENUMERATION | |
| 2191 histogram_cookie_deletion_cause_ = base::LinearHistogram::FactoryGet( | |
| 2192 "Cookie.DeletionCause", 1, | |
| 2193 DELETE_COOKIE_LAST_ENTRY - 1, DELETE_COOKIE_LAST_ENTRY, | |
| 2194 base::Histogram::kUmaTargetedHistogramFlag); | |
| 2195 | |
| 2196 // From UMA_HISTOGRAM_{CUSTOM_,}TIMES | |
| 2197 histogram_time_get_ = base::Histogram::FactoryTimeGet("Cookie.TimeGet", | |
| 2198 base::TimeDelta::FromMilliseconds(1), base::TimeDelta::FromMinutes(1), | |
| 2199 50, base::Histogram::kUmaTargetedHistogramFlag); | |
| 2200 histogram_time_mac_ = base::Histogram::FactoryTimeGet("Cookie.TimeGetMac", | |
| 2201 base::TimeDelta::FromMilliseconds(1), base::TimeDelta::FromMinutes(1), | |
| 2202 50, base::Histogram::kUmaTargetedHistogramFlag); | |
| 2203 histogram_time_blocked_on_load_ = base::Histogram::FactoryTimeGet( | |
| 2204 "Cookie.TimeBlockedOnLoad", | |
| 2205 base::TimeDelta::FromMilliseconds(1), base::TimeDelta::FromMinutes(1), | |
| 2206 50, base::Histogram::kUmaTargetedHistogramFlag); | |
| 2207 } | |
| 2208 | |
| 2209 | |
| 2210 // The system resolution is not high enough, so we can have multiple | |
| 2211 // set cookies that result in the same system time. When this happens, we | |
| 2212 // increment by one Time unit. Let's hope computers don't get too fast. | |
| 2213 Time CookieMonster::CurrentTime() { | |
| 2214 return std::max(Time::Now(), | |
| 2215 Time::FromInternalValue(last_time_seen_.ToInternalValue() + 1)); | |
| 2216 } | |
| 2217 | |
| 2218 CookieMonster::ParsedCookie::ParsedCookie(const std::string& cookie_line) | |
| 2219 : is_valid_(false), | |
| 2220 path_index_(0), | |
| 2221 domain_index_(0), | |
| 2222 mac_key_index_(0), | |
| 2223 mac_algorithm_index_(0), | |
| 2224 expires_index_(0), | |
| 2225 maxage_index_(0), | |
| 2226 secure_index_(0), | |
| 2227 httponly_index_(0) { | |
| 2228 | |
| 2229 if (cookie_line.size() > kMaxCookieSize) { | |
| 2230 VLOG(1) << "Not parsing cookie, too large: " << cookie_line.size(); | |
| 2231 return; | |
| 2232 } | |
| 2233 | |
| 2234 ParseTokenValuePairs(cookie_line); | |
| 2235 if (!pairs_.empty()) { | |
| 2236 is_valid_ = true; | |
| 2237 SetupAttributes(); | |
| 2238 } | |
| 2239 } | |
| 2240 | |
| 2241 CookieMonster::ParsedCookie::~ParsedCookie() { | |
| 2242 } | |
| 2243 | |
| 2244 // Returns true if |c| occurs in |chars| | |
| 2245 // TODO(erikwright): maybe make this take an iterator, could check for end also? | |
| 2246 static inline bool CharIsA(const char c, const char* chars) { | |
| 2247 return strchr(chars, c) != NULL; | |
| 2248 } | |
| 2249 // Seek the iterator to the first occurrence of a character in |chars|. | |
| 2250 // Returns true if it hit the end, false otherwise. | |
| 2251 static inline bool SeekTo(std::string::const_iterator* it, | |
| 2252 const std::string::const_iterator& end, | |
| 2253 const char* chars) { | |
| 2254 for (; *it != end && !CharIsA(**it, chars); ++(*it)) {} | |
| 2255 return *it == end; | |
| 2256 } | |
| 2257 // Seek the iterator to the first occurrence of a character not in |chars|. | |
| 2258 // Returns true if it hit the end, false otherwise. | |
| 2259 static inline bool SeekPast(std::string::const_iterator* it, | |
| 2260 const std::string::const_iterator& end, | |
| 2261 const char* chars) { | |
| 2262 for (; *it != end && CharIsA(**it, chars); ++(*it)) {} | |
| 2263 return *it == end; | |
| 2264 } | |
| 2265 static inline bool SeekBackPast(std::string::const_iterator* it, | |
| 2266 const std::string::const_iterator& end, | |
| 2267 const char* chars) { | |
| 2268 for (; *it != end && CharIsA(**it, chars); --(*it)) {} | |
| 2269 return *it == end; | |
| 2270 } | |
| 2271 | |
| 2272 const char CookieMonster::ParsedCookie::kTerminator[] = "\n\r\0"; | |
| 2273 const int CookieMonster::ParsedCookie::kTerminatorLen = | |
| 2274 sizeof(kTerminator) - 1; | |
| 2275 const char CookieMonster::ParsedCookie::kWhitespace[] = " \t"; | |
| 2276 const char CookieMonster::ParsedCookie::kValueSeparator[] = ";"; | |
| 2277 const char CookieMonster::ParsedCookie::kTokenSeparator[] = ";="; | |
| 2278 | |
| 2279 // Create a cookie-line for the cookie. For debugging only! | |
| 2280 // If we want to use this for something more than debugging, we | |
| 2281 // should rewrite it better... | |
| 2282 std::string CookieMonster::ParsedCookie::DebugString() const { | |
| 2283 std::string out; | |
| 2284 for (PairList::const_iterator it = pairs_.begin(); | |
| 2285 it != pairs_.end(); ++it) { | |
| 2286 out.append(it->first); | |
| 2287 out.append("="); | |
| 2288 out.append(it->second); | |
| 2289 out.append("; "); | |
| 2290 } | |
| 2291 return out; | |
| 2292 } | |
| 2293 | |
| 2294 std::string::const_iterator CookieMonster::ParsedCookie::FindFirstTerminator( | |
| 2295 const std::string& s) { | |
| 2296 std::string::const_iterator end = s.end(); | |
| 2297 size_t term_pos = | |
| 2298 s.find_first_of(std::string(kTerminator, kTerminatorLen)); | |
| 2299 if (term_pos != std::string::npos) { | |
| 2300 // We found a character we should treat as an end of string. | |
| 2301 end = s.begin() + term_pos; | |
| 2302 } | |
| 2303 return end; | |
| 2304 } | |
| 2305 | |
| 2306 bool CookieMonster::ParsedCookie::ParseToken( | |
| 2307 std::string::const_iterator* it, | |
| 2308 const std::string::const_iterator& end, | |
| 2309 std::string::const_iterator* token_start, | |
| 2310 std::string::const_iterator* token_end) { | |
| 2311 DCHECK(it && token_start && token_end); | |
| 2312 std::string::const_iterator token_real_end; | |
| 2313 | |
| 2314 // Seek past any whitespace before the "token" (the name). | |
| 2315 // token_start should point at the first character in the token | |
| 2316 if (SeekPast(it, end, kWhitespace)) | |
| 2317 return false; // No token, whitespace or empty. | |
| 2318 *token_start = *it; | |
| 2319 | |
| 2320 // Seek over the token, to the token separator. | |
| 2321 // token_real_end should point at the token separator, i.e. '='. | |
| 2322 // If it == end after the seek, we probably have a token-value. | |
| 2323 SeekTo(it, end, kTokenSeparator); | |
| 2324 token_real_end = *it; | |
| 2325 | |
| 2326 // Ignore any whitespace between the token and the token separator. | |
| 2327 // token_end should point after the last interesting token character, | |
| 2328 // pointing at either whitespace, or at '=' (and equal to token_real_end). | |
| 2329 if (*it != *token_start) { // We could have an empty token name. | |
| 2330 --(*it); // Go back before the token separator. | |
| 2331 // Skip over any whitespace to the first non-whitespace character. | |
| 2332 SeekBackPast(it, *token_start, kWhitespace); | |
| 2333 // Point after it. | |
| 2334 ++(*it); | |
| 2335 } | |
| 2336 *token_end = *it; | |
| 2337 | |
| 2338 // Seek us back to the end of the token. | |
| 2339 *it = token_real_end; | |
| 2340 return true; | |
| 2341 } | |
| 2342 | |
| 2343 void CookieMonster::ParsedCookie::ParseValue( | |
| 2344 std::string::const_iterator* it, | |
| 2345 const std::string::const_iterator& end, | |
| 2346 std::string::const_iterator* value_start, | |
| 2347 std::string::const_iterator* value_end) { | |
| 2348 DCHECK(it && value_start && value_end); | |
| 2349 | |
| 2350 // Seek past any whitespace that might in-between the token and value. | |
| 2351 SeekPast(it, end, kWhitespace); | |
| 2352 // value_start should point at the first character of the value. | |
| 2353 *value_start = *it; | |
| 2354 | |
| 2355 // Just look for ';' to terminate ('=' allowed). | |
| 2356 // We can hit the end, maybe they didn't terminate. | |
| 2357 SeekTo(it, end, kValueSeparator); | |
| 2358 | |
| 2359 // Will be pointed at the ; seperator or the end. | |
| 2360 *value_end = *it; | |
| 2361 | |
| 2362 // Ignore any unwanted whitespace after the value. | |
| 2363 if (*value_end != *value_start) { // Could have an empty value | |
| 2364 --(*value_end); | |
| 2365 SeekBackPast(value_end, *value_start, kWhitespace); | |
| 2366 ++(*value_end); | |
| 2367 } | |
| 2368 } | |
| 2369 | |
| 2370 std::string CookieMonster::ParsedCookie::ParseTokenString( | |
| 2371 const std::string& token) { | |
| 2372 std::string::const_iterator it = token.begin(); | |
| 2373 std::string::const_iterator end = FindFirstTerminator(token); | |
| 2374 | |
| 2375 std::string::const_iterator token_start, token_end; | |
| 2376 if (ParseToken(&it, end, &token_start, &token_end)) | |
| 2377 return std::string(token_start, token_end); | |
| 2378 return std::string(); | |
| 2379 } | |
| 2380 | |
| 2381 std::string CookieMonster::ParsedCookie::ParseValueString( | |
| 2382 const std::string& value) { | |
| 2383 std::string::const_iterator it = value.begin(); | |
| 2384 std::string::const_iterator end = FindFirstTerminator(value); | |
| 2385 | |
| 2386 std::string::const_iterator value_start, value_end; | |
| 2387 ParseValue(&it, end, &value_start, &value_end); | |
| 2388 return std::string(value_start, value_end); | |
| 2389 } | |
| 2390 | |
| 2391 // Parse all token/value pairs and populate pairs_. | |
| 2392 void CookieMonster::ParsedCookie::ParseTokenValuePairs( | |
| 2393 const std::string& cookie_line) { | |
| 2394 pairs_.clear(); | |
| 2395 | |
| 2396 // Ok, here we go. We should be expecting to be starting somewhere | |
| 2397 // before the cookie line, not including any header name... | |
| 2398 std::string::const_iterator start = cookie_line.begin(); | |
| 2399 std::string::const_iterator it = start; | |
| 2400 | |
| 2401 // TODO(erikwright): Make sure we're stripping \r\n in the network code. | |
| 2402 // Then we can log any unexpected terminators. | |
| 2403 std::string::const_iterator end = FindFirstTerminator(cookie_line); | |
| 2404 | |
| 2405 for (int pair_num = 0; pair_num < kMaxPairs && it != end; ++pair_num) { | |
| 2406 TokenValuePair pair; | |
| 2407 | |
| 2408 std::string::const_iterator token_start, token_end; | |
| 2409 if (!ParseToken(&it, end, &token_start, &token_end)) | |
| 2410 break; | |
| 2411 | |
| 2412 if (it == end || *it != '=') { | |
| 2413 // We have a token-value, we didn't have any token name. | |
| 2414 if (pair_num == 0) { | |
| 2415 // For the first time around, we want to treat single values | |
| 2416 // as a value with an empty name. (Mozilla bug 169091). | |
| 2417 // IE seems to also have this behavior, ex "AAA", and "AAA=10" will | |
| 2418 // set 2 different cookies, and setting "BBB" will then replace "AAA". | |
| 2419 pair.first = ""; | |
| 2420 // Rewind to the beginning of what we thought was the token name, | |
| 2421 // and let it get parsed as a value. | |
| 2422 it = token_start; | |
| 2423 } else { | |
| 2424 // Any not-first attribute we want to treat a value as a | |
| 2425 // name with an empty value... This is so something like | |
| 2426 // "secure;" will get parsed as a Token name, and not a value. | |
| 2427 pair.first = std::string(token_start, token_end); | |
| 2428 } | |
| 2429 } else { | |
| 2430 // We have a TOKEN=VALUE. | |
| 2431 pair.first = std::string(token_start, token_end); | |
| 2432 ++it; // Skip past the '='. | |
| 2433 } | |
| 2434 | |
| 2435 // OK, now try to parse a value. | |
| 2436 std::string::const_iterator value_start, value_end; | |
| 2437 ParseValue(&it, end, &value_start, &value_end); | |
| 2438 // OK, we're finished with a Token/Value. | |
| 2439 pair.second = std::string(value_start, value_end); | |
| 2440 | |
| 2441 // From RFC2109: "Attributes (names) (attr) are case-insensitive." | |
| 2442 if (pair_num != 0) | |
| 2443 StringToLowerASCII(&pair.first); | |
| 2444 pairs_.push_back(pair); | |
| 2445 | |
| 2446 // We've processed a token/value pair, we're either at the end of | |
| 2447 // the string or a ValueSeparator like ';', which we want to skip. | |
| 2448 if (it != end) | |
| 2449 ++it; | |
| 2450 } | |
| 2451 } | |
| 2452 | |
| 2453 void CookieMonster::ParsedCookie::SetupAttributes() { | |
| 2454 static const char kPathTokenName[] = "path"; | |
| 2455 static const char kDomainTokenName[] = "domain"; | |
| 2456 static const char kMACKeyTokenName[] = "mac-key"; | |
| 2457 static const char kMACAlgorithmTokenName[] = "mac-algorithm"; | |
| 2458 static const char kExpiresTokenName[] = "expires"; | |
| 2459 static const char kMaxAgeTokenName[] = "max-age"; | |
| 2460 static const char kSecureTokenName[] = "secure"; | |
| 2461 static const char kHttpOnlyTokenName[] = "httponly"; | |
| 2462 | |
| 2463 // We skip over the first token/value, the user supplied one. | |
| 2464 for (size_t i = 1; i < pairs_.size(); ++i) { | |
| 2465 if (pairs_[i].first == kPathTokenName) { | |
| 2466 path_index_ = i; | |
| 2467 } else if (pairs_[i].first == kDomainTokenName) { | |
| 2468 domain_index_ = i; | |
| 2469 } else if (pairs_[i].first == kMACKeyTokenName) { | |
| 2470 mac_key_index_ = i; | |
| 2471 } else if (pairs_[i].first == kMACAlgorithmTokenName) { | |
| 2472 mac_algorithm_index_ = i; | |
| 2473 } else if (pairs_[i].first == kExpiresTokenName) { | |
| 2474 expires_index_ = i; | |
| 2475 } else if (pairs_[i].first == kMaxAgeTokenName) { | |
| 2476 maxage_index_ = i; | |
| 2477 } else if (pairs_[i].first == kSecureTokenName) { | |
| 2478 secure_index_ = i; | |
| 2479 } else if (pairs_[i].first == kHttpOnlyTokenName) { | |
| 2480 httponly_index_ = i; | |
| 2481 } else { | |
| 2482 /* some attribute we don't know or don't care about. */ | |
| 2483 } | |
| 2484 } | |
| 2485 } | |
| 2486 | |
| 2487 CookieMonster::CanonicalCookie::CanonicalCookie() | |
| 2488 : secure_(false), | |
| 2489 httponly_(false), | |
| 2490 has_expires_(false), | |
| 2491 is_persistent_(false) { | |
| 2492 SetSessionCookieExpiryTime(); | |
| 2493 } | |
| 2494 | |
| 2495 CookieMonster::CanonicalCookie::CanonicalCookie( | |
| 2496 const GURL& url, const std::string& name, const std::string& value, | |
| 2497 const std::string& domain, const std::string& path, | |
| 2498 const std::string& mac_key, const std::string& mac_algorithm, | |
| 2499 const base::Time& creation, const base::Time& expiration, | |
| 2500 const base::Time& last_access, bool secure, bool httponly, bool has_expires, | |
| 2501 bool is_persistent) | |
| 2502 : source_(GetCookieSourceFromURL(url)), | |
| 2503 name_(name), | |
| 2504 value_(value), | |
| 2505 domain_(domain), | |
| 2506 path_(path), | |
| 2507 mac_key_(mac_key), | |
| 2508 mac_algorithm_(mac_algorithm), | |
| 2509 creation_date_(creation), | |
| 2510 expiry_date_(expiration), | |
| 2511 last_access_date_(last_access), | |
| 2512 secure_(secure), | |
| 2513 httponly_(httponly), | |
| 2514 has_expires_(has_expires), | |
| 2515 is_persistent_(is_persistent) { | |
| 2516 if (!has_expires_) { | |
| 2517 DCHECK(!is_persistent_); | |
| 2518 SetSessionCookieExpiryTime(); | |
| 2519 } | |
| 2520 } | |
| 2521 | |
| 2522 CookieMonster::CanonicalCookie::CanonicalCookie(const GURL& url, | |
| 2523 const ParsedCookie& pc) | |
| 2524 : source_(GetCookieSourceFromURL(url)), | |
| 2525 name_(pc.Name()), | |
| 2526 value_(pc.Value()), | |
| 2527 path_(CanonPath(url, pc)), | |
| 2528 mac_key_(pc.MACKey()), | |
| 2529 mac_algorithm_(pc.MACAlgorithm()), | |
| 2530 creation_date_(Time::Now()), | |
| 2531 last_access_date_(Time()), | |
| 2532 secure_(pc.IsSecure()), | |
| 2533 httponly_(pc.IsHttpOnly()), | |
| 2534 has_expires_(pc.HasExpires()), | |
| 2535 is_persistent_(pc.HasExpires()) { | |
| 2536 if (has_expires_) | |
| 2537 expiry_date_ = CanonExpiration(pc, creation_date_); | |
| 2538 else | |
| 2539 SetSessionCookieExpiryTime(); | |
| 2540 | |
| 2541 // Do the best we can with the domain. | |
| 2542 std::string cookie_domain; | |
| 2543 std::string domain_string; | |
| 2544 if (pc.HasDomain()) { | |
| 2545 domain_string = pc.Domain(); | |
| 2546 } | |
| 2547 bool result | |
| 2548 = cookie_util::GetCookieDomainWithString(url, domain_string, | |
| 2549 &cookie_domain); | |
| 2550 // Caller is responsible for passing in good arguments. | |
| 2551 DCHECK(result); | |
| 2552 domain_ = cookie_domain; | |
| 2553 } | |
| 2554 | |
| 2555 CookieMonster::CanonicalCookie::~CanonicalCookie() { | |
| 2556 } | |
| 2557 | |
| 2558 std::string CookieMonster::CanonicalCookie::GetCookieSourceFromURL( | |
| 2559 const GURL& url) { | |
| 2560 if (url.SchemeIsFile()) | |
| 2561 return url.spec(); | |
| 2562 | |
| 2563 url_canon::Replacements<char> replacements; | |
| 2564 replacements.ClearPort(); | |
| 2565 if (url.SchemeIsSecure()) | |
| 2566 replacements.SetScheme("http", url_parse::Component(0, 4)); | |
| 2567 | |
| 2568 return url.GetOrigin().ReplaceComponents(replacements).spec(); | |
| 2569 } | |
| 2570 | |
| 2571 void CookieMonster::CanonicalCookie::SetSessionCookieExpiryTime() { | |
| 2572 #if defined(ENABLE_PERSISTENT_SESSION_COOKIES) | |
| 2573 // Mobile apps can sometimes be shut down without any warning, so the session | |
| 2574 // cookie has to be persistent and given a default expiration time. | |
| 2575 expiry_date_ = base::Time::Now() + | |
| 2576 base::TimeDelta::FromDays(kPersistentSessionCookieExpiryInDays); | |
| 2577 has_expires_ = true; | |
| 2578 #endif | |
| 2579 } | |
| 2580 | |
| 2581 CookieMonster::CanonicalCookie* CookieMonster::CanonicalCookie::Create( | |
| 2582 const GURL& url, | |
| 2583 const ParsedCookie& pc) { | |
| 2584 if (!pc.IsValid()) { | |
| 2585 return NULL; | |
| 2586 } | |
| 2587 | |
| 2588 std::string domain_string; | |
| 2589 if (!GetCookieDomain(url, pc, &domain_string)) { | |
| 2590 return NULL; | |
| 2591 } | |
| 2592 std::string path_string = CanonPath(url, pc); | |
| 2593 std::string mac_key = pc.HasMACKey() ? pc.MACKey() : std::string(); | |
| 2594 std::string mac_algorithm = pc.HasMACAlgorithm() ? | |
| 2595 pc.MACAlgorithm() : std::string(); | |
| 2596 Time creation_time = Time::Now(); | |
| 2597 Time expiration_time; | |
| 2598 if (pc.HasExpires()) | |
| 2599 expiration_time = net::CookieMonster::ParseCookieTime(pc.Expires()); | |
| 2600 | |
| 2601 return (Create(url, pc.Name(), pc.Value(), domain_string, path_string, | |
| 2602 mac_key, mac_algorithm, creation_time, expiration_time, | |
| 2603 pc.IsSecure(), pc.IsHttpOnly(), !expiration_time.is_null())); | |
| 2604 } | |
| 2605 | |
| 2606 CookieMonster::CanonicalCookie* CookieMonster::CanonicalCookie::Create( | |
| 2607 const GURL& url, | |
| 2608 const std::string& name, | |
| 2609 const std::string& value, | |
| 2610 const std::string& domain, | |
| 2611 const std::string& path, | |
| 2612 const std::string& mac_key, | |
| 2613 const std::string& mac_algorithm, | |
| 2614 const base::Time& creation, | |
| 2615 const base::Time& expiration, | |
| 2616 bool secure, | |
| 2617 bool http_only, | |
| 2618 bool is_persistent) { | |
| 2619 // Expect valid attribute tokens and values, as defined by the ParsedCookie | |
| 2620 // logic, otherwise don't create the cookie. | |
| 2621 std::string parsed_name = ParsedCookie::ParseTokenString(name); | |
| 2622 if (parsed_name != name) | |
| 2623 return NULL; | |
| 2624 std::string parsed_value = ParsedCookie::ParseValueString(value); | |
| 2625 if (parsed_value != value) | |
| 2626 return NULL; | |
| 2627 | |
| 2628 std::string parsed_domain = ParsedCookie::ParseValueString(domain); | |
| 2629 if (parsed_domain != domain) | |
| 2630 return NULL; | |
| 2631 std::string cookie_domain; | |
| 2632 if (!cookie_util::GetCookieDomainWithString(url, parsed_domain, | |
| 2633 &cookie_domain)) { | |
| 2634 return NULL; | |
| 2635 } | |
| 2636 | |
| 2637 std::string parsed_path = ParsedCookie::ParseValueString(path); | |
| 2638 if (parsed_path != path) | |
| 2639 return NULL; | |
| 2640 | |
| 2641 std::string cookie_path = CanonPathWithString(url, parsed_path); | |
| 2642 // Expect that the path was either not specified (empty), or is valid. | |
| 2643 if (!parsed_path.empty() && cookie_path != parsed_path) | |
| 2644 return NULL; | |
| 2645 // Canonicalize path again to make sure it escapes characters as needed. | |
| 2646 url_parse::Component path_component(0, cookie_path.length()); | |
| 2647 url_canon::RawCanonOutputT<char> canon_path; | |
| 2648 url_parse::Component canon_path_component; | |
| 2649 url_canon::CanonicalizePath(cookie_path.data(), path_component, | |
| 2650 &canon_path, &canon_path_component); | |
| 2651 cookie_path = std::string(canon_path.data() + canon_path_component.begin, | |
| 2652 canon_path_component.len); | |
| 2653 | |
| 2654 return new CanonicalCookie(url, parsed_name, parsed_value, cookie_domain, | |
| 2655 cookie_path, mac_key, mac_algorithm, creation, | |
| 2656 expiration, creation, secure, http_only, | |
| 2657 !expiration.is_null(), is_persistent); | |
| 2658 } | |
| 2659 | |
| 2660 bool CookieMonster::CanonicalCookie::IsOnPath( | |
| 2661 const std::string& url_path) const { | |
| 2662 | |
| 2663 // A zero length would be unsafe for our trailing '/' checks, and | |
| 2664 // would also make no sense for our prefix match. The code that | |
| 2665 // creates a CanonicalCookie should make sure the path is never zero length, | |
| 2666 // but we double check anyway. | |
| 2667 if (path_.empty()) | |
| 2668 return false; | |
| 2669 | |
| 2670 // The Mozilla code broke it into 3 cases, if it's strings lengths | |
| 2671 // are less than, equal, or greater. I think this is simpler: | |
| 2672 | |
| 2673 // Make sure the cookie path is a prefix of the url path. If the | |
| 2674 // url path is shorter than the cookie path, then the cookie path | |
| 2675 // can't be a prefix. | |
| 2676 if (url_path.find(path_) != 0) | |
| 2677 return false; | |
| 2678 | |
| 2679 // Now we know that url_path is >= cookie_path, and that cookie_path | |
| 2680 // is a prefix of url_path. If they are the are the same length then | |
| 2681 // they are identical, otherwise we need an additional check: | |
| 2682 | |
| 2683 // In order to avoid in correctly matching a cookie path of /blah | |
| 2684 // with a request path of '/blahblah/', we need to make sure that either | |
| 2685 // the cookie path ends in a trailing '/', or that we prefix up to a '/' | |
| 2686 // in the url path. Since we know that the url path length is greater | |
| 2687 // than the cookie path length, it's safe to index one byte past. | |
| 2688 if (path_.length() != url_path.length() && | |
| 2689 path_[path_.length() - 1] != '/' && | |
| 2690 url_path[path_.length()] != '/') | |
| 2691 return false; | |
| 2692 | |
| 2693 return true; | |
| 2694 } | |
| 2695 | |
| 2696 bool CookieMonster::CanonicalCookie::IsDomainMatch( | |
| 2697 const std::string& scheme, | |
| 2698 const std::string& host) const { | |
| 2699 // Can domain match in two ways; as a domain cookie (where the cookie | |
| 2700 // domain begins with ".") or as a host cookie (where it doesn't). | |
| 2701 | |
| 2702 // Some consumers of the CookieMonster expect to set cookies on | |
| 2703 // URLs like http://.strange.url. To retrieve cookies in this instance, | |
| 2704 // we allow matching as a host cookie even when the domain_ starts with | |
| 2705 // a period. | |
| 2706 if (host == domain_) | |
| 2707 return true; | |
| 2708 | |
| 2709 // Domain cookie must have an initial ".". To match, it must be | |
| 2710 // equal to url's host with initial period removed, or a suffix of | |
| 2711 // it. | |
| 2712 | |
| 2713 // Arguably this should only apply to "http" or "https" cookies, but | |
| 2714 // extension cookie tests currently use the funtionality, and if we | |
| 2715 // ever decide to implement that it should be done by preventing | |
| 2716 // such cookies from being set. | |
| 2717 if (domain_.empty() || domain_[0] != '.') | |
| 2718 return false; | |
| 2719 | |
| 2720 // The host with a "." prefixed. | |
| 2721 if (domain_.compare(1, std::string::npos, host) == 0) | |
| 2722 return true; | |
| 2723 | |
| 2724 // A pure suffix of the host (ok since we know the domain already | |
| 2725 // starts with a ".") | |
| 2726 return (host.length() > domain_.length() && | |
| 2727 host.compare(host.length() - domain_.length(), | |
| 2728 domain_.length(), domain_) == 0); | |
| 2729 } | |
| 2730 | |
| 2731 std::string CookieMonster::CanonicalCookie::DebugString() const { | |
| 2732 return base::StringPrintf( | |
| 2733 "name: %s value: %s domain: %s path: %s creation: %" | |
| 2734 PRId64, | |
| 2735 name_.c_str(), value_.c_str(), | |
| 2736 domain_.c_str(), path_.c_str(), | |
| 2737 static_cast<int64>(creation_date_.ToTimeT())); | |
| 2738 } | |
| 2739 | |
| 2740 } // namespace | |
| OLD | NEW |