1 // Copyright (c) 2006-2008 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 // Brought to you by the letter D and the number 2. 6 7 #ifndef NET_BASE_COOKIE_MONSTER_H_ 8 #define NET_BASE_COOKIE_MONSTER_H_ 9 10 #include <map> 11 #include <string> 12 #include <utility> 13 #include <vector> 14 15 #include "base/basictypes.h" 16 #include "base/lock.h" 17 #include "base/time.h" 18 #include "net/base/cookie_store.h" 19 20 class GURL; 21 22 namespace net { 23 24 // The cookie monster is the system for storing and retrieving cookies. It has 25 // an in-memory list of all cookies, and synchronizes non-session cookies to an 26 // optional permanent storage that implements the PersistentCookieStore 27 // interface. 28 // 29 // This class IS thread-safe. Normally, it is only used on the I/O thread, but 30 // is also accessed directly through Automation for UI testing. 31 // 32 // TODO(deanm) Implement CookieMonster, the cookie database. 33 // - Verify that our domain enforcement and non-dotted handling is correct 34 class CookieMonster : public CookieStore { 35 public: 36 class ParsedCookie; 37 class CanonicalCookie; 38 class PersistentCookieStore; 39 40 // NOTE(deanm): 41 // I benchmarked hash_multimap vs multimap. We're going to be query-heavy 42 // so it would seem like hashing would help. However they were very 43 // close, with multimap being a tiny bit faster. I think this is because 44 // our map is at max around 1000 entries, and the additional complexity 45 // for the hashing might not overcome the O(log(1000)) for querying 46 // a multimap. Also, multimap is standard, another reason to use it. 47 typedef std::multimap<std::string, CanonicalCookie*> CookieMap; 48 typedef std::pair<CookieMap::iterator, CookieMap::iterator> CookieMapItPair; 49 typedef std::pair<std::string, CanonicalCookie*> KeyedCanonicalCookie; 50 typedef std::pair<std::string, CanonicalCookie> CookieListPair; 51 typedef std::vector<CookieListPair> CookieList; 52 53 54 CookieMonster(); 55 56 // The store passed in should not have had Init() called on it yet. This class 57 // will take care of initializing it. The backing store is NOT owned by this 58 // class, but it must remain valid for the duration of the cookie monster's 59 // existence. 60 CookieMonster(PersistentCookieStore* store); 61 62 #ifdef UNIT_TEST 63 CookieMonster(int last_access_threshold_milliseconds) 64 : initialized_(false), 65 store_(NULL), 66 last_access_threshold_(base::TimeDelta::FromMilliseconds( 67 last_access_threshold_milliseconds)) { 68 SetDefaultCookieableSchemes(); 69 } 70 #endif 71 72 // Parse the string with the cookie time (very forgivingly). 73 static base::Time ParseCookieTime(const std::string& time_string); 74 75 // CookieStore implementation. 76 virtual bool SetCookieWithOptions(const GURL& url, 77 const std::string& cookie_line, 78 const CookieOptions& options); 79 virtual std::string GetCookiesWithOptions(const GURL& url, 80 const CookieOptions& options); 81 virtual void DeleteCookie(const GURL& url, const std::string& cookie_name); 82 virtual CookieMonster* GetCookieMonster() { return this; } 83 84 85 // Exposed for unit testing. 86 bool SetCookieWithCreationTimeAndOptions(const GURL& url, 87 const std::string& cookie_line, 88 const base::Time& creation_time, 89 const CookieOptions& options); 90 bool SetCookieWithCreationTime(const GURL& url, 91 const std::string& cookie_line, 92 const base::Time& creation_time) { 93 return SetCookieWithCreationTimeAndOptions(url, cookie_line, creation_time, 94 CookieOptions()); 95 } 96 97 // Returns all the cookies, for use in management UI, etc. This does not mark 98 // the cookies as having been accessed. 99 CookieList GetAllCookies(); 100 101 // Returns all the cookies, for use in management UI, etc. Filters results 102 // using given url scheme and host / domain. This does not mark the cookies 103 // as having been accessed. 104 CookieList GetAllCookiesForURL(const GURL& url); 105 106 // Delete all of the cookies. 107 int DeleteAll(bool sync_to_store); 108 // Delete all of the cookies that have a creation_date greater than or equal 109 // to |delete_begin| and less than |delete_end| 110 int DeleteAllCreatedBetween(const base::Time& delete_begin, 111 const base::Time& delete_end, 112 bool sync_to_store); 113 // Delete all of the cookies that have a creation_date more recent than the 114 // one passed into the function via |delete_after|. 115 int DeleteAllCreatedAfter(const base::Time& delete_begin, bool sync_to_store); 116 117 // Delete one specific cookie. 118 bool DeleteCookie(const std::string& domain, 119 const CanonicalCookie& cookie, 120 bool sync_to_store); 121 122 // Override the default list of schemes that are allowed to be set in 123 // this cookie store. Calling his overrides the value of 124 // "enable_file_scheme_". 125 void SetCookieableSchemes(const char* schemes[], size_t num_schemes); 126 127 // There are some unknowns about how to correctly handle file:// cookies, 128 // and our implementation for this is not robust enough. This allows you 129 // to enable support, but it should only be used for testing. Bug 1157243. 130 // Must be called before creating a CookieMonster instance. 131 static void EnableFileScheme(); 132 static bool enable_file_scheme_; 133 134 private: 135 ~CookieMonster(); 136 137 // Called by all non-static functions to ensure that the cookies store has 138 // been initialized. This is not done during creating so it doesn't block 139 // the window showing. 140 // Note: this method should always be called with lock_ held. 141 void InitIfNecessary() { 142 if (!initialized_) { 143 if (store_) 144 InitStore(); 145 initialized_ = true; 146 } 147 } 148 149 // Initializes the backing store and reads existing cookies from it. 150 // Should only be called by InitIfNecessary(). 151 void InitStore(); 152 153 void SetDefaultCookieableSchemes(); 154 155 void FindCookiesForHostAndDomain(const GURL& url, 156 const CookieOptions& options, 157 std::vector<CanonicalCookie*>* cookies); 158 159 void FindCookiesForKey(const std::string& key, 160 const GURL& url, 161 const CookieOptions& options, 162 const base::Time& current, 163 std::vector<CanonicalCookie*>* cookies); 164 165 void FindRawCookies(const std::string& key, 166 bool include_secure, 167 CookieList* list); 168 169 // Delete any cookies that are equivalent to |ecc| (same path, key, etc). 170 // If |skip_httponly| is true, httponly cookies will not be deleted. The 171 // return value with be true if |skip_httponly| skipped an httponly cookie. 172 // NOTE: There should never be more than a single matching equivalent cookie. 173 bool DeleteAnyEquivalentCookie(const std::string& key, 174 const CanonicalCookie& ecc, 175 bool skip_httponly); 176 177 void InternalInsertCookie(const std::string& key, 178 CanonicalCookie* cc, 179 bool sync_to_store); 180 181 void InternalUpdateCookieAccessTime(CanonicalCookie* cc); 182 183 void InternalDeleteCookie(CookieMap::iterator it, bool sync_to_store); 184 185 // If the number of cookies for host |key|, or globally, are over preset 186 // maximums, garbage collects, first for the host and then globally, as 187 // described by GarbageCollectRange(). The limits can be found as constants 188 // at the top of the function body. 189 // 190 // Returns the number of cookies deleted (useful for debugging). 191 int GarbageCollect(const base::Time& current, const std::string& key); 192 193 // Deletes all expired cookies in |itpair|; 194 // then, if the number of remaining cookies is greater than |num_max|, 195 // collects the least recently accessed cookies until 196 // (|num_max| - |num_purge|) cookies remain. 197 // 198 // Returns the number of cookies deleted. 199 int GarbageCollectRange(const base::Time& current, 200 const CookieMapItPair& itpair, 201 size_t num_max, 202 size_t num_purge); 203 204 // Helper for GarbageCollectRange(); can be called directly as well. Deletes 205 // all expired cookies in |itpair|. If |cookie_its| is non-NULL, it is 206 // populated with all the non-expired cookies from |itpair|. 207 // 208 // Returns the number of cookies deleted. 209 int GarbageCollectExpired(const base::Time& current, 210 const CookieMapItPair& itpair, 211 std::vector<CookieMap::iterator>* cookie_its); 212 213 bool HasCookieableScheme(const GURL& url); 214 215 CookieMap cookies_; 216 217 // Indicates whether the cookie store has been initialized. This happens 218 // lazily in InitStoreIfNecessary(). 219 bool initialized_; 220 221 scoped_refptr<PersistentCookieStore> store_; 222 223 // The resolution of our time isn't enough, so we do something 224 // ugly and increment when we've seen the same time twice. 225 base::Time CurrentTime(); 226 base::Time last_time_seen_; 227 228 // Minimum delay after updating a cookie's LastAccessDate before we will 229 // update it again. 230 const base::TimeDelta last_access_threshold_; 231 232 std::vector<std::string> cookieable_schemes_; 233 234 // Lock for thread-safety 235 Lock lock_; 236 237 DISALLOW_COPY_AND_ASSIGN(CookieMonster); 238 }; 239 240 class CookieMonster::ParsedCookie { 241 public: 242 typedef std::pair<std::string, std::string> TokenValuePair; 243 typedef std::vector<TokenValuePair> PairList; 244 245 // The maximum length of a cookie string we will try to parse 246 static const size_t kMaxCookieSize = 4096; 247 // The maximum number of Token/Value pairs. Shouldn't have more than 8. 248 static const int kMaxPairs = 16; 249 250 // Construct from a cookie string like "BLAH=1; path=/; domain=.google.com" 251 ParsedCookie(const std::string& cookie_line); 252 ~ParsedCookie() { } 253 254 // You should not call any other methods on the class if !IsValid 255 bool IsValid() const { return is_valid_; } 256 257 const std::string& Name() const { return pairs_[0].first; } 258 const std::string& Token() const { return Name(); } 259 const std::string& Value() const { return pairs_[0].second; } 260 261 bool HasPath() const { return path_index_ != 0; } 262 const std::string& Path() const { return pairs_[path_index_].second; } 263 bool HasDomain() const { return domain_index_ != 0; } 264 const std::string& Domain() const { return pairs_[domain_index_].second; } 265 bool HasExpires() const { return expires_index_ != 0; } 266 const std::string& Expires() const { return pairs_[expires_index_].second; } 267 bool HasMaxAge() const { return maxage_index_ != 0; } 268 const std::string& MaxAge() const { return pairs_[maxage_index_].second; } 269 bool IsSecure() const { return secure_index_ != 0; } 270 bool IsHttpOnly() const { return httponly_index_ != 0; } 271 272 // Return the number of attributes, for example, returning 2 for: 273 // "BLAH=hah; path=/; domain=.google.com" 274 size_t NumberOfAttributes() const { return pairs_.size() - 1; } 275 276 // For debugging only! 277 std::string DebugString() const; 278 279 private: 280 void ParseTokenValuePairs(const std::string& cookie_line); 281 void SetupAttributes(); 282 283 PairList pairs_; 284 bool is_valid_; 285 // These will default to 0, but that should never be valid since the 286 // 0th index is the user supplied token/value, not an attribute. 287 // We're really never going to have more than like 8 attributes, so we 288 // could fit these into 3 bits each if we're worried about size... 289 size_t path_index_; 290 size_t domain_index_; 291 size_t expires_index_; 292 size_t maxage_index_; 293 size_t secure_index_; 294 size_t httponly_index_; 295 296 DISALLOW_COPY_AND_ASSIGN(ParsedCookie); 297 }; 298 299 300 class CookieMonster::CanonicalCookie { 301 public: 302 CanonicalCookie() { } 303 CanonicalCookie(const std::string& name, 304 const std::string& value, 305 const std::string& path, 306 bool secure, 307 bool httponly, 308 const base::Time& creation, 309 const base::Time& last_access, 310 bool has_expires, 311 const base::Time& expires) 312 : name_(name), 313 value_(value), 314 path_(path), 315 creation_date_(creation), 316 last_access_date_(last_access), 317 expiry_date_(expires), 318 has_expires_(has_expires), 319 secure_(secure), 320 httponly_(httponly) { 321 } 322 323 // Supports the default copy constructor. 324 325 const std::string& Name() const { return name_; } 326 const std::string& Value() const { return value_; } 327 const std::string& Path() const { return path_; } 328 const base::Time& CreationDate() const { return creation_date_; } 329 const base::Time& LastAccessDate() const { return last_access_date_; } 330 bool DoesExpire() const { return has_expires_; } 331 bool IsPersistent() const { return DoesExpire(); } 332 const base::Time& ExpiryDate() const { return expiry_date_; } 333 bool IsSecure() const { return secure_; } 334 bool IsHttpOnly() const { return httponly_; } 335 336 bool IsExpired(const base::Time& current) { 337 return has_expires_ && current >= expiry_date_; 338 } 339 340 // Are the cookies considered equivalent in the eyes of the RFC. 341 // This says that the domain and path should string match identically. 342 bool IsEquivalent(const CanonicalCookie& ecc) const { 343 // It seems like it would make sense to take secure and httponly into 344 // account, but the RFC doesn't specify this. 345 return name_ == ecc.Name() && path_ == ecc.Path(); 346 } 347 348 void SetLastAccessDate(const base::Time& date) { 349 last_access_date_ = date; 350 } 351 352 bool IsOnPath(const std::string& url_path) const; 353 354 std::string DebugString() const; 355 private: 356 std::string name_; 357 std::string value_; 358 std::string path_; 359 base::Time creation_date_; 360 base::Time last_access_date_; 361 base::Time expiry_date_; 362 bool has_expires_; 363 bool secure_; 364 bool httponly_; 365 }; 366 367 typedef base::RefCountedThreadSafe<CookieMonster::PersistentCookieStore> 368 RefcountedPersistentCookieStore; 369 370 class CookieMonster::PersistentCookieStore 371 : public RefcountedPersistentCookieStore { 372 public: 373 virtual ~PersistentCookieStore() { } 374 375 // Initializes the store and retrieves the existing cookies. This will be 376 // called only once at startup. 377 virtual bool Load(std::vector<CookieMonster::KeyedCanonicalCookie>*) = 0; 378 379 virtual void AddCookie(const std::string&, const CanonicalCookie&) = 0; 380 virtual void UpdateCookieAccessTime(const CanonicalCookie&) = 0; 381 virtual void DeleteCookie(const CanonicalCookie&) = 0; 382 383 protected: 384 PersistentCookieStore() { } 385 386 private: 387 DISALLOW_COPY_AND_ASSIGN(PersistentCookieStore); 388 }; 389 390 } // namespace net 391 392 #endif // NET_BASE_COOKIE_MONSTER_H_ 393