1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3  * License, v. 2.0. If a copy of the MPL was not distributed with this
4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 
6 #ifndef nsCookieService_h__
7 #define nsCookieService_h__
8 
9 #include "nsICookieService.h"
10 #include "nsICookieManager.h"
11 #include "nsICookieManager2.h"
12 #include "nsIObserver.h"
13 #include "nsWeakReference.h"
14 
15 #include "nsCookie.h"
16 #include "nsString.h"
17 #include "nsAutoPtr.h"
18 #include "nsHashKeys.h"
19 #include "nsIMemoryReporter.h"
20 #include "nsTHashtable.h"
21 #include "mozIStorageStatement.h"
22 #include "mozIStorageAsyncStatement.h"
23 #include "mozIStoragePendingStatement.h"
24 #include "mozIStorageConnection.h"
25 #include "mozIStorageRow.h"
26 #include "mozIStorageCompletionCallback.h"
27 #include "mozIStorageStatementCallback.h"
28 #include "mozIStorageFunction.h"
29 #include "nsIVariant.h"
30 #include "nsIFile.h"
31 #include "mozilla/BasePrincipal.h"
32 #include "mozilla/MemoryReporting.h"
33 #include "mozilla/Maybe.h"
34 
35 using mozilla::NeckoOriginAttributes;
36 using mozilla::OriginAttributes;
37 
38 class nsICookiePermission;
39 class nsIEffectiveTLDService;
40 class nsIIDNService;
41 class nsIPrefBranch;
42 class nsIObserverService;
43 class nsIURI;
44 class nsIChannel;
45 class nsIArray;
46 class mozIStorageService;
47 class mozIThirdPartyUtil;
48 class ReadCookieDBListener;
49 
50 struct nsCookieAttributes;
51 struct nsListIter;
52 
53 namespace mozilla {
54 namespace net {
55 class CookieServiceParent;
56 } // namespace net
57 } // namespace mozilla
58 
59 // hash key class
60 class nsCookieKey : public PLDHashEntryHdr
61 {
62 public:
63   typedef const nsCookieKey& KeyType;
64   typedef const nsCookieKey* KeyTypePointer;
65 
nsCookieKey()66   nsCookieKey()
67   {}
68 
nsCookieKey(const nsCString & baseDomain,const NeckoOriginAttributes & attrs)69   nsCookieKey(const nsCString &baseDomain, const NeckoOriginAttributes &attrs)
70     : mBaseDomain(baseDomain)
71     , mOriginAttributes(attrs)
72   {}
73 
nsCookieKey(KeyTypePointer other)74   explicit nsCookieKey(KeyTypePointer other)
75     : mBaseDomain(other->mBaseDomain)
76     , mOriginAttributes(other->mOriginAttributes)
77   {}
78 
nsCookieKey(KeyType other)79   nsCookieKey(KeyType other)
80     : mBaseDomain(other.mBaseDomain)
81     , mOriginAttributes(other.mOriginAttributes)
82   {}
83 
~nsCookieKey()84   ~nsCookieKey()
85   {}
86 
KeyEquals(KeyTypePointer other)87   bool KeyEquals(KeyTypePointer other) const
88   {
89     return mBaseDomain == other->mBaseDomain &&
90            mOriginAttributes == other->mOriginAttributes;
91   }
92 
KeyToPointer(KeyType aKey)93   static KeyTypePointer KeyToPointer(KeyType aKey)
94   {
95     return &aKey;
96   }
97 
HashKey(KeyTypePointer aKey)98   static PLDHashNumber HashKey(KeyTypePointer aKey)
99   {
100     // TODO: more efficient way to generate hash?
101     nsAutoCString temp(aKey->mBaseDomain);
102     temp.Append('#');
103     nsAutoCString suffix;
104     aKey->mOriginAttributes.CreateSuffix(suffix);
105     temp.Append(suffix);
106     return mozilla::HashString(temp);
107   }
108 
109   size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
110 
111   enum { ALLOW_MEMMOVE = true };
112 
113   nsCString        mBaseDomain;
114   NeckoOriginAttributes mOriginAttributes;
115 };
116 
117 // Inherit from nsCookieKey so this can be stored in nsTHashTable
118 // TODO: why aren't we using nsClassHashTable<nsCookieKey, ArrayType>?
119 class nsCookieEntry : public nsCookieKey
120 {
121   public:
122     // Hash methods
123     typedef nsTArray< RefPtr<nsCookie> > ArrayType;
124     typedef ArrayType::index_type IndexType;
125 
nsCookieEntry(KeyTypePointer aKey)126     explicit nsCookieEntry(KeyTypePointer aKey)
127      : nsCookieKey(aKey)
128     {}
129 
nsCookieEntry(const nsCookieEntry & toCopy)130     nsCookieEntry(const nsCookieEntry& toCopy)
131     {
132       // if we end up here, things will break. nsTHashtable shouldn't
133       // allow this, since we set ALLOW_MEMMOVE to true.
134       NS_NOTREACHED("nsCookieEntry copy constructor is forbidden!");
135     }
136 
~nsCookieEntry()137     ~nsCookieEntry()
138     {}
139 
GetCookies()140     inline ArrayType& GetCookies() { return mCookies; }
141 
142     size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
143 
144   private:
145     ArrayType mCookies;
146 };
147 
148 // encapsulates a (key, nsCookie) tuple for temporary storage purposes.
149 struct CookieDomainTuple
150 {
151   nsCookieKey key;
152   RefPtr<nsCookie> cookie;
153 
154   size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
155 };
156 
157 // encapsulates in-memory and on-disk DB states, so we can
158 // conveniently switch state when entering or exiting private browsing.
159 struct DBState final
160 {
DBStatefinal161   DBState() : cookieCount(0), cookieOldestTime(INT64_MAX), corruptFlag(OK)
162   {
163   }
164 
165 private:
166   // Private destructor, to discourage deletion outside of Release():
~DBStatefinal167   ~DBState()
168   {
169   }
170 
171 public:
172   NS_INLINE_DECL_REFCOUNTING(DBState)
173 
174   size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
175 
176   // State of the database connection.
177   enum CorruptFlag {
178     OK,                   // normal
179     CLOSING_FOR_REBUILD,  // corruption detected, connection closing
180     REBUILDING            // close complete, rebuilding database from memory
181   };
182 
183   nsTHashtable<nsCookieEntry>     hostTable;
184   uint32_t                        cookieCount;
185   int64_t                         cookieOldestTime;
186   nsCOMPtr<nsIFile>               cookieFile;
187   nsCOMPtr<mozIStorageConnection> dbConn;
188   nsCOMPtr<mozIStorageAsyncStatement> stmtInsert;
189   nsCOMPtr<mozIStorageAsyncStatement> stmtDelete;
190   nsCOMPtr<mozIStorageAsyncStatement> stmtUpdate;
191   CorruptFlag                     corruptFlag;
192 
193   // Various parts representing asynchronous read state. These are useful
194   // while the background read is taking place.
195   nsCOMPtr<mozIStorageConnection>       syncConn;
196   nsCOMPtr<mozIStorageStatement>        stmtReadDomain;
197   nsCOMPtr<mozIStoragePendingStatement> pendingRead;
198   // The asynchronous read listener. This is a weak ref (storage has ownership)
199   // since it may need to outlive the DBState's database connection.
200   ReadCookieDBListener*                 readListener;
201   // An array of (baseDomain, cookie) tuples representing data read in
202   // asynchronously. This is merged into hostTable once read is complete.
203   nsTArray<CookieDomainTuple>           hostArray;
204   // A hashset of baseDomains read in synchronously, while the async read is
205   // in flight. This is used to keep track of which data in hostArray is stale
206   // when the time comes to merge.
207   nsTHashtable<nsCookieKey>        readSet;
208 
209   // DB completion handlers.
210   nsCOMPtr<mozIStorageStatementCallback>  insertListener;
211   nsCOMPtr<mozIStorageStatementCallback>  updateListener;
212   nsCOMPtr<mozIStorageStatementCallback>  removeListener;
213   nsCOMPtr<mozIStorageCompletionCallback> closeListener;
214 };
215 
216 // these constants represent a decision about a cookie based on user prefs.
217 enum CookieStatus
218 {
219   STATUS_ACCEPTED,
220   STATUS_ACCEPT_SESSION,
221   STATUS_REJECTED,
222   // STATUS_REJECTED_WITH_ERROR indicates the cookie should be rejected because
223   // of an error (rather than something the user can control). this is used for
224   // notification purposes, since we only want to notify of rejections where
225   // the user can do something about it (e.g. whitelist the site).
226   STATUS_REJECTED_WITH_ERROR
227 };
228 
229 // Result codes for TryInitDB() and Read().
230 enum OpenDBResult
231 {
232   RESULT_OK,
233   RESULT_RETRY,
234   RESULT_FAILURE
235 };
236 
237 /******************************************************************************
238  * nsCookieService:
239  * class declaration
240  ******************************************************************************/
241 
242 class nsCookieService final : public nsICookieService
243                             , public nsICookieManager2
244                             , public nsIObserver
245                             , public nsSupportsWeakReference
246                             , public nsIMemoryReporter
247 {
248   private:
249     size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
250 
251   public:
252     NS_DECL_ISUPPORTS
253     NS_DECL_NSIOBSERVER
254     NS_DECL_NSICOOKIESERVICE
255     NS_DECL_NSICOOKIEMANAGER
256     NS_DECL_NSICOOKIEMANAGER2
257     NS_DECL_NSIMEMORYREPORTER
258 
259     nsCookieService();
260     static nsICookieService*      GetXPCOMSingleton();
261     nsresult                      Init();
262 
263   /**
264    * Start watching the observer service for messages indicating that an app has
265    * been uninstalled.  When an app is uninstalled, we get the cookie service
266    * (thus instantiating it, if necessary) and clear all the cookies for that
267    * app.
268    */
269   static void AppClearDataObserverInit();
270 
271   protected:
272     virtual ~nsCookieService();
273 
274     void                          PrefChanged(nsIPrefBranch *aPrefBranch);
275     void                          InitDBStates();
276     OpenDBResult                  TryInitDB(bool aDeleteExistingDB);
277     nsresult                      CreateTable();
278     nsresult                      CreateTableForSchemaVersion6();
279     nsresult                      CreateTableForSchemaVersion5();
280     void                          CloseDBStates();
281     void                          CleanupCachedStatements();
282     void                          CleanupDefaultDBConnection();
283     void                          HandleDBClosed(DBState* aDBState);
284     void                          HandleCorruptDB(DBState* aDBState);
285     void                          RebuildCorruptDB(DBState* aDBState);
286     OpenDBResult                  Read();
287     template<class T> nsCookie*   GetCookieFromRow(T &aRow, const OriginAttributes& aOriginAttributes);
288     void                          AsyncReadComplete();
289     void                          CancelAsyncRead(bool aPurgeReadSet);
290     void                          EnsureReadDomain(const nsCookieKey &aKey);
291     void                          EnsureReadComplete();
292     nsresult                      NormalizeHost(nsCString &aHost);
293     nsresult                      GetBaseDomain(nsIURI *aHostURI, nsCString &aBaseDomain, bool &aRequireHostMatch);
294     nsresult                      GetBaseDomainFromHost(const nsACString &aHost, nsCString &aBaseDomain);
295     nsresult                      GetCookieStringCommon(nsIURI *aHostURI, nsIChannel *aChannel, bool aHttpBound, char** aCookie);
296     void                          GetCookieStringInternal(nsIURI *aHostURI, bool aIsForeign, bool aHttpBound, const NeckoOriginAttributes aOriginAttrs, bool aIsPrivate, nsCString &aCookie);
297     nsresult                      SetCookieStringCommon(nsIURI *aHostURI, const char *aCookieHeader, const char *aServerTime, nsIChannel *aChannel, bool aFromHttp);
298     void                          SetCookieStringInternal(nsIURI *aHostURI, bool aIsForeign, nsDependentCString &aCookieHeader, const nsCString &aServerTime, bool aFromHttp, const NeckoOriginAttributes &aOriginAttrs, bool aIsPrivate, nsIChannel* aChannel);
299     bool                          SetCookieInternal(nsIURI *aHostURI, const nsCookieKey& aKey, bool aRequireHostMatch, CookieStatus aStatus, nsDependentCString &aCookieHeader, int64_t aServerTime, bool aFromHttp, nsIChannel* aChannel);
300     void                          AddInternal(const nsCookieKey& aKey, nsCookie *aCookie, int64_t aCurrentTimeInUsec, nsIURI *aHostURI, const char *aCookieHeader, bool aFromHttp);
301     void                          RemoveCookieFromList(const nsListIter &aIter, mozIStorageBindingParamsArray *aParamsArray = nullptr);
302     void                          AddCookieToList(const nsCookieKey& aKey, nsCookie *aCookie, DBState *aDBState, mozIStorageBindingParamsArray *aParamsArray, bool aWriteToDB = true);
303     void                          UpdateCookieInList(nsCookie *aCookie, int64_t aLastAccessed, mozIStorageBindingParamsArray *aParamsArray);
304     static bool                   GetTokenValue(nsASingleFragmentCString::const_char_iterator &aIter, nsASingleFragmentCString::const_char_iterator &aEndIter, nsDependentCSubstring &aTokenString, nsDependentCSubstring &aTokenValue, bool &aEqualsFound);
305     static bool                   ParseAttributes(nsDependentCString &aCookieHeader, nsCookieAttributes &aCookie);
306     bool                          RequireThirdPartyCheck();
307     CookieStatus                  CheckPrefs(nsIURI *aHostURI, bool aIsForeign, const char *aCookieHeader);
308     bool                          CheckDomain(nsCookieAttributes &aCookie, nsIURI *aHostURI, const nsCString &aBaseDomain, bool aRequireHostMatch);
309     static bool                   CheckPath(nsCookieAttributes &aCookie, nsIURI *aHostURI);
310     static bool                   CheckPrefixes(nsCookieAttributes &aCookie, bool aSecureRequest);
311     static bool                   GetExpiry(nsCookieAttributes &aCookie, int64_t aServerTime, int64_t aCurrentTime);
312     void                          RemoveAllFromMemory();
313     already_AddRefed<nsIArray>    PurgeCookies(int64_t aCurrentTimeInUsec);
314     bool                          FindCookie(const nsCookieKey& aKey, const nsAFlatCString &aHost, const nsAFlatCString &aName, const nsAFlatCString &aPath, nsListIter &aIter);
315     bool                          FindSecureCookie(const nsCookieKey& aKey, nsCookie* aCookie);
316     int64_t                       FindStaleCookie(nsCookieEntry *aEntry, int64_t aCurrentTime, nsIURI* aSource, mozilla::Maybe<bool> aIsSecure, nsListIter &aIter);
317     void                          TelemetryForEvictingStaleCookie(nsCookie* aEvicted, int64_t oldestCookieTime);
318     void                          NotifyRejected(nsIURI *aHostURI);
319     void                          NotifyThirdParty(nsIURI *aHostURI, bool aAccepted, nsIChannel *aChannel);
320     void                          NotifyChanged(nsISupports *aSubject, const char16_t *aData);
321     void                          NotifyPurged(nsICookie2* aCookie);
322     already_AddRefed<nsIArray>    CreatePurgeList(nsICookie2* aCookie);
323     void                          UpdateCookieOldestTime(DBState* aDBState, nsCookie* aCookie);
324 
325     nsresult                      GetCookiesWithOriginAttributes(const mozilla::OriginAttributesPattern& aPattern, const nsCString& aBaseDomain, nsISimpleEnumerator **aEnumerator);
326     nsresult                      RemoveCookiesWithOriginAttributes(const mozilla::OriginAttributesPattern& aPattern, const nsCString& aBaseDomain);
327 
328     /**
329      * This method is a helper that allows calling nsICookieManager::Remove()
330      * with NeckoOriginAttributes parameter.
331      * NOTE: this could be added to a public interface if we happen to need it.
332      */
333     nsresult Remove(const nsACString& aHost, const NeckoOriginAttributes& aAttrs,
334                     const nsACString& aName, const nsACString& aPath,
335                     bool aBlocked);
336 
337   protected:
338     // cached members.
339     nsCOMPtr<nsICookiePermission>    mPermissionService;
340     nsCOMPtr<mozIThirdPartyUtil>     mThirdPartyUtil;
341     nsCOMPtr<nsIEffectiveTLDService> mTLDService;
342     nsCOMPtr<nsIIDNService>          mIDNService;
343     nsCOMPtr<mozIStorageService>     mStorageService;
344 
345     // we have two separate DB states: one for normal browsing and one for
346     // private browsing, switching between them on a per-cookie-request basis.
347     // this state encapsulates both the in-memory table and the on-disk DB.
348     // note that the private states' dbConn should always be null - we never
349     // want to be dealing with the on-disk DB when in private browsing.
350     DBState                      *mDBState;
351     RefPtr<DBState>             mDefaultDBState;
352     RefPtr<DBState>             mPrivateDBState;
353 
354     // cached prefs
355     uint8_t                       mCookieBehavior; // BEHAVIOR_{ACCEPT, REJECTFOREIGN, REJECT, LIMITFOREIGN}
356     bool                          mThirdPartySession;
357     bool                          mLeaveSecureAlone;
358     uint16_t                      mMaxNumberOfCookies;
359     uint16_t                      mMaxCookiesPerHost;
360     int64_t                       mCookiePurgeAge;
361 
362     // friends!
363     friend class DBListenerErrorHandler;
364     friend class ReadCookieDBListener;
365     friend class CloseCookieDBListener;
366 
367     static nsCookieService*       GetSingleton();
368     friend class mozilla::net::CookieServiceParent;
369 };
370 
371 #endif // nsCookieService_h__
372