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