Home | History | Annotate | Download | only in base
      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