LCOV - code coverage report
Current view: directory - netwerk/cookie - nsCookieService.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 1466 1246 85.0 %
Date: 2012-06-02 Functions: 124 112 90.3 %

       1                 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2                 : /* ***** BEGIN LICENSE BLOCK *****
       3                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
       4                 :  *
       5                 :  * The contents of this file are subject to the Mozilla Public License Version
       6                 :  * 1.1 (the "License"); you may not use this file except in compliance with
       7                 :  * the License. You may obtain a copy of the License at
       8                 :  * http://www.mozilla.org/MPL/
       9                 :  *
      10                 :  * Software distributed under the License is distributed on an "AS IS" basis,
      11                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
      12                 :  * for the specific language governing rights and limitations under the
      13                 :  * License.
      14                 :  *
      15                 :  * The Original Code is mozilla.org code.
      16                 :  *
      17                 :  * The Initial Developer of the Original Code is
      18                 :  * Netscape Communications Corporation.
      19                 :  * Portions created by the Initial Developer are Copyright (C) 2003
      20                 :  * the Initial Developer. All Rights Reserved.
      21                 :  *
      22                 :  * Contributor(s):
      23                 :  *   Daniel Witte (dwitte@stanford.edu)
      24                 :  *   Michiel van Leeuwen (mvl@exedo.nl)
      25                 :  *   Michael Ventnor <m.ventnor@gmail.com>
      26                 :  *   Ehsan Akhgari <ehsan.akhgari@gmail.com>
      27                 :  *
      28                 :  * Alternatively, the contents of this file may be used under the terms of
      29                 :  * either the GNU General Public License Version 2 or later (the "GPL"), or
      30                 :  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      31                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      32                 :  * of those above. If you wish to allow use of your version of this file only
      33                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      34                 :  * use your version of this file under the terms of the MPL, indicate your
      35                 :  * decision by deleting the provisions above and replace them with the notice
      36                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      37                 :  * the provisions above, a recipient may use your version of this file under
      38                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      39                 :  *
      40                 :  * ***** END LICENSE BLOCK ***** */
      41                 : 
      42                 : 
      43                 : #ifdef MOZ_LOGGING
      44                 : // this next define has to appear before the include of prlog.h
      45                 : #define FORCE_PR_LOG // Allow logging in the release build
      46                 : #endif
      47                 : 
      48                 : #include "mozilla/net/CookieServiceChild.h"
      49                 : #include "mozilla/net/NeckoCommon.h"
      50                 : 
      51                 : #include "nsCookieService.h"
      52                 : #include "nsIServiceManager.h"
      53                 : 
      54                 : #include "nsIIOService.h"
      55                 : #include "nsIPrefBranch.h"
      56                 : #include "nsIPrefService.h"
      57                 : #include "nsICookiePermission.h"
      58                 : #include "nsIURI.h"
      59                 : #include "nsIURL.h"
      60                 : #include "nsIChannel.h"
      61                 : #include "nsIFile.h"
      62                 : #include "nsIObserverService.h"
      63                 : #include "nsILineInputStream.h"
      64                 : #include "nsIEffectiveTLDService.h"
      65                 : #include "nsIIDNService.h"
      66                 : #include "mozIThirdPartyUtil.h"
      67                 : 
      68                 : #include "nsTArray.h"
      69                 : #include "nsCOMArray.h"
      70                 : #include "nsIMutableArray.h"
      71                 : #include "nsArrayEnumerator.h"
      72                 : #include "nsEnumeratorUtils.h"
      73                 : #include "nsAutoPtr.h"
      74                 : #include "nsReadableUtils.h"
      75                 : #include "nsCRT.h"
      76                 : #include "prtime.h"
      77                 : #include "prprf.h"
      78                 : #include "nsNetUtil.h"
      79                 : #include "nsNetCID.h"
      80                 : #include "nsAppDirectoryServiceDefs.h"
      81                 : #include "nsIPrivateBrowsingService.h"
      82                 : #include "nsNetCID.h"
      83                 : #include "mozilla/storage.h"
      84                 : #include "mozilla/FunctionTimer.h"
      85                 : #include "mozilla/Util.h" // for DebugOnly
      86                 : 
      87                 : using namespace mozilla::net;
      88                 : 
      89                 : /******************************************************************************
      90                 :  * nsCookieService impl:
      91                 :  * useful types & constants
      92                 :  ******************************************************************************/
      93                 : 
      94                 : static nsCookieService *gCookieService;
      95                 : 
      96                 : // XXX_hack. See bug 178993.
      97                 : // This is a hack to hide HttpOnly cookies from older browsers
      98                 : static const char kHttpOnlyPrefix[] = "#HttpOnly_";
      99                 : 
     100                 : #define COOKIES_FILE "cookies.sqlite"
     101                 : #define COOKIES_SCHEMA_VERSION 4
     102                 : 
     103                 : static const PRInt64 kCookieStaleThreshold = 60 * PR_USEC_PER_SEC; // 1 minute in microseconds
     104                 : static const PRInt64 kCookiePurgeAge =
     105                 :   PRInt64(30 * 24 * 60 * 60) * PR_USEC_PER_SEC; // 30 days in microseconds
     106                 : 
     107                 : static const char kOldCookieFileName[] = "cookies.txt";
     108                 : 
     109                 : #undef  LIMIT
     110                 : #define LIMIT(x, low, high, default) ((x) >= (low) && (x) <= (high) ? (x) : (default))
     111                 : 
     112                 : #undef  ADD_TEN_PERCENT
     113                 : #define ADD_TEN_PERCENT(i) ((i) + (i)/10)
     114                 : 
     115                 : // default limits for the cookie list. these can be tuned by the
     116                 : // network.cookie.maxNumber and network.cookie.maxPerHost prefs respectively.
     117                 : static const PRUint32 kMaxNumberOfCookies = 3000;
     118                 : static const PRUint32 kMaxCookiesPerHost  = 150;
     119                 : static const PRUint32 kMaxBytesPerCookie  = 4096;
     120                 : static const PRUint32 kMaxBytesPerPath    = 1024;
     121                 : 
     122                 : // behavior pref constants
     123                 : static const PRUint32 BEHAVIOR_ACCEPT        = 0;
     124                 : static const PRUint32 BEHAVIOR_REJECTFOREIGN = 1;
     125                 : static const PRUint32 BEHAVIOR_REJECT        = 2;
     126                 : 
     127                 : // pref string constants
     128                 : static const char kPrefCookieBehavior[]     = "network.cookie.cookieBehavior";
     129                 : static const char kPrefMaxNumberOfCookies[] = "network.cookie.maxNumber";
     130                 : static const char kPrefMaxCookiesPerHost[]  = "network.cookie.maxPerHost";
     131                 : static const char kPrefCookiePurgeAge[]     = "network.cookie.purgeAge";
     132                 : static const char kPrefThirdPartySession[]  = "network.cookie.thirdparty.sessionOnly";
     133                 : 
     134                 : static void
     135                 : bindCookieParameters(mozIStorageBindingParamsArray *aParamsArray,
     136                 :                      const nsCString &aBaseDomain,
     137                 :                      const nsCookie *aCookie);
     138                 : 
     139                 : // struct for temporarily storing cookie attributes during header parsing
     140                 : struct nsCookieAttributes
     141           30466 : {
     142                 :   nsCAutoString name;
     143                 :   nsCAutoString value;
     144                 :   nsCAutoString host;
     145                 :   nsCAutoString path;
     146                 :   nsCAutoString expires;
     147                 :   nsCAutoString maxage;
     148                 :   PRInt64 expiryTime;
     149                 :   bool isSession;
     150                 :   bool isSecure;
     151                 :   bool isHttpOnly;
     152                 : };
     153                 : 
     154                 : // stores the nsCookieEntry entryclass and an index into the cookie array
     155                 : // within that entryclass, for purposes of storing an iteration state that
     156                 : // points to a certain cookie.
     157                 : struct nsListIter
     158             155 : {
     159                 :   // default (non-initializing) constructor.
     160           16540 :   nsListIter()
     161                 :   {
     162           16540 :   }
     163                 : 
     164                 :   // explicit constructor to a given iterator state with entryclass 'aEntry'
     165                 :   // and index 'aIndex'.
     166                 :   explicit
     167             704 :   nsListIter(nsCookieEntry *aEntry, nsCookieEntry::IndexType aIndex)
     168                 :    : entry(aEntry)
     169             704 :    , index(aIndex)
     170                 :   {
     171             704 :   }
     172                 : 
     173                 :   // get the nsCookie * the iterator currently points to.
     174            4495 :   nsCookie * Cookie() const
     175                 :   {
     176            4495 :     return entry->GetCookies()[index];
     177                 :   }
     178                 : 
     179                 :   nsCookieEntry            *entry;
     180                 :   nsCookieEntry::IndexType  index;
     181                 : };
     182                 : 
     183                 : /******************************************************************************
     184                 :  * Cookie logging handlers
     185                 :  * used for logging in nsCookieService
     186                 :  ******************************************************************************/
     187                 : 
     188                 : // logging handlers
     189                 : #ifdef MOZ_LOGGING
     190                 : // in order to do logging, the following environment variables need to be set:
     191                 : //
     192                 : //    set NSPR_LOG_MODULES=cookie:3 -- shows rejected cookies
     193                 : //    set NSPR_LOG_MODULES=cookie:4 -- shows accepted and rejected cookies
     194                 : //    set NSPR_LOG_FILE=cookie.log
     195                 : //
     196                 : #include "prlog.h"
     197                 : #endif
     198                 : 
     199                 : // define logging macros for convenience
     200                 : #define SET_COOKIE true
     201                 : #define GET_COOKIE false
     202                 : 
     203                 : #ifdef PR_LOGGING
     204            1464 : static PRLogModuleInfo *sCookieLog = PR_NewLogModule("cookie");
     205                 : 
     206                 : #define COOKIE_LOGFAILURE(a, b, c, d)    LogFailure(a, b, c, d)
     207                 : #define COOKIE_LOGSUCCESS(a, b, c, d, e) LogSuccess(a, b, c, d, e)
     208                 : 
     209                 : #define COOKIE_LOGEVICTED(a, details)          \
     210                 :   PR_BEGIN_MACRO                               \
     211                 :     if (PR_LOG_TEST(sCookieLog, PR_LOG_DEBUG)) \
     212                 :       LogEvicted(a, details);                  \
     213                 :   PR_END_MACRO
     214                 : 
     215                 : #define COOKIE_LOGSTRING(lvl, fmt)   \
     216                 :   PR_BEGIN_MACRO                     \
     217                 :     PR_LOG(sCookieLog, lvl, fmt);    \
     218                 :     PR_LOG(sCookieLog, lvl, ("\n")); \
     219                 :   PR_END_MACRO
     220                 : 
     221                 : static void
     222              69 : LogFailure(bool aSetCookie, nsIURI *aHostURI, const char *aCookieString, const char *aReason)
     223                 : {
     224                 :   // if logging isn't enabled, return now to save cycles
     225              69 :   if (!PR_LOG_TEST(sCookieLog, PR_LOG_WARNING))
     226              69 :     return;
     227                 : 
     228               0 :   nsCAutoString spec;
     229               0 :   if (aHostURI)
     230               0 :     aHostURI->GetAsciiSpec(spec);
     231                 : 
     232               0 :   PR_LOG(sCookieLog, PR_LOG_WARNING,
     233                 :     ("===== %s =====\n", aSetCookie ? "COOKIE NOT ACCEPTED" : "COOKIE NOT SENT"));
     234               0 :   PR_LOG(sCookieLog, PR_LOG_WARNING,("request URL: %s\n", spec.get()));
     235               0 :   if (aSetCookie)
     236               0 :     PR_LOG(sCookieLog, PR_LOG_WARNING,("cookie string: %s\n", aCookieString));
     237                 : 
     238                 :   PRExplodedTime explodedTime;
     239               0 :   PR_ExplodeTime(PR_Now(), PR_GMTParameters, &explodedTime);
     240                 :   char timeString[40];
     241               0 :   PR_FormatTimeUSEnglish(timeString, 40, "%c GMT", &explodedTime);
     242                 : 
     243               0 :   PR_LOG(sCookieLog, PR_LOG_WARNING,("current time: %s", timeString));
     244               0 :   PR_LOG(sCookieLog, PR_LOG_WARNING,("rejected because %s\n", aReason));
     245               0 :   PR_LOG(sCookieLog, PR_LOG_WARNING,("\n"));
     246                 : }
     247                 : 
     248                 : static void
     249               0 : LogCookie(nsCookie *aCookie)
     250                 : {
     251                 :   PRExplodedTime explodedTime;
     252               0 :   PR_ExplodeTime(PR_Now(), PR_GMTParameters, &explodedTime);
     253                 :   char timeString[40];
     254               0 :   PR_FormatTimeUSEnglish(timeString, 40, "%c GMT", &explodedTime);
     255                 : 
     256               0 :   PR_LOG(sCookieLog, PR_LOG_DEBUG,("current time: %s", timeString));
     257                 : 
     258               0 :   if (aCookie) {
     259               0 :     PR_LOG(sCookieLog, PR_LOG_DEBUG,("----------------\n"));
     260               0 :     PR_LOG(sCookieLog, PR_LOG_DEBUG,("name: %s\n", aCookie->Name().get()));
     261               0 :     PR_LOG(sCookieLog, PR_LOG_DEBUG,("value: %s\n", aCookie->Value().get()));
     262               0 :     PR_LOG(sCookieLog, PR_LOG_DEBUG,("%s: %s\n", aCookie->IsDomain() ? "domain" : "host", aCookie->Host().get()));
     263               0 :     PR_LOG(sCookieLog, PR_LOG_DEBUG,("path: %s\n", aCookie->Path().get()));
     264                 : 
     265               0 :     PR_ExplodeTime(aCookie->Expiry() * PRInt64(PR_USEC_PER_SEC),
     266               0 :                    PR_GMTParameters, &explodedTime);
     267               0 :     PR_FormatTimeUSEnglish(timeString, 40, "%c GMT", &explodedTime);
     268               0 :     PR_LOG(sCookieLog, PR_LOG_DEBUG,
     269                 :       ("expires: %s%s", timeString, aCookie->IsSession() ? " (at end of session)" : ""));
     270                 : 
     271               0 :     PR_ExplodeTime(aCookie->CreationTime(), PR_GMTParameters, &explodedTime);
     272               0 :     PR_FormatTimeUSEnglish(timeString, 40, "%c GMT", &explodedTime);
     273               0 :     PR_LOG(sCookieLog, PR_LOG_DEBUG,("created: %s", timeString));
     274                 : 
     275               0 :     PR_LOG(sCookieLog, PR_LOG_DEBUG,("is secure: %s\n", aCookie->IsSecure() ? "true" : "false"));
     276               0 :     PR_LOG(sCookieLog, PR_LOG_DEBUG,("is httpOnly: %s\n", aCookie->IsHttpOnly() ? "true" : "false"));
     277                 :   }
     278               0 : }
     279                 : 
     280                 : static void
     281           16256 : LogSuccess(bool aSetCookie, nsIURI *aHostURI, const char *aCookieString, nsCookie *aCookie, bool aReplacing)
     282                 : {
     283                 :   // if logging isn't enabled, return now to save cycles
     284           16256 :   if (!PR_LOG_TEST(sCookieLog, PR_LOG_DEBUG)) {
     285           16256 :     return;
     286                 :   }
     287                 : 
     288               0 :   nsCAutoString spec;
     289               0 :   if (aHostURI)
     290               0 :     aHostURI->GetAsciiSpec(spec);
     291                 : 
     292               0 :   PR_LOG(sCookieLog, PR_LOG_DEBUG,
     293                 :     ("===== %s =====\n", aSetCookie ? "COOKIE ACCEPTED" : "COOKIE SENT"));
     294               0 :   PR_LOG(sCookieLog, PR_LOG_DEBUG,("request URL: %s\n", spec.get()));
     295               0 :   PR_LOG(sCookieLog, PR_LOG_DEBUG,("cookie string: %s\n", aCookieString));
     296               0 :   if (aSetCookie)
     297               0 :     PR_LOG(sCookieLog, PR_LOG_DEBUG,("replaces existing cookie: %s\n", aReplacing ? "true" : "false"));
     298                 : 
     299               0 :   LogCookie(aCookie);
     300                 : 
     301               0 :   PR_LOG(sCookieLog, PR_LOG_DEBUG,("\n"));
     302                 : }
     303                 : 
     304                 : static void
     305               0 : LogEvicted(nsCookie *aCookie, const char* details)
     306                 : {
     307               0 :   PR_LOG(sCookieLog, PR_LOG_DEBUG,("===== COOKIE EVICTED =====\n"));
     308               0 :   PR_LOG(sCookieLog, PR_LOG_DEBUG,("%s\n", details));
     309                 : 
     310               0 :   LogCookie(aCookie);
     311                 : 
     312               0 :   PR_LOG(sCookieLog, PR_LOG_DEBUG,("\n"));
     313               0 : }
     314                 : 
     315                 : // inline wrappers to make passing in nsAFlatCStrings easier
     316                 : static inline void
     317              21 : LogFailure(bool aSetCookie, nsIURI *aHostURI, const nsAFlatCString &aCookieString, const char *aReason)
     318                 : {
     319              21 :   LogFailure(aSetCookie, aHostURI, aCookieString.get(), aReason);
     320              21 : }
     321                 : 
     322                 : static inline void
     323              57 : LogSuccess(bool aSetCookie, nsIURI *aHostURI, const nsAFlatCString &aCookieString, nsCookie *aCookie, bool aReplacing)
     324                 : {
     325              57 :   LogSuccess(aSetCookie, aHostURI, aCookieString.get(), aCookie, aReplacing);
     326              57 : }
     327                 : 
     328                 : #else
     329                 : #define COOKIE_LOGFAILURE(a, b, c, d)    PR_BEGIN_MACRO /* nothing */ PR_END_MACRO
     330                 : #define COOKIE_LOGSUCCESS(a, b, c, d, e) PR_BEGIN_MACRO /* nothing */ PR_END_MACRO
     331                 : #define COOKIE_LOGEVICTED(a, b)          PR_BEGIN_MACRO /* nothing */ PR_END_MACRO
     332                 : #define COOKIE_LOGSTRING(a, b)           PR_BEGIN_MACRO /* nothing */ PR_END_MACRO
     333                 : #endif
     334                 : 
     335                 : #ifdef DEBUG
     336                 : #define NS_ASSERT_SUCCESS(res)                                               \
     337                 :   PR_BEGIN_MACRO                                                             \
     338                 :   nsresult __rv = res; /* Do not evaluate |res| more than once! */           \
     339                 :   if (NS_FAILED(__rv)) {                                                     \
     340                 :     char *msg = PR_smprintf("NS_ASSERT_SUCCESS(%s) failed with result 0x%X", \
     341                 :                             #res, __rv);                                     \
     342                 :     NS_ASSERTION(NS_SUCCEEDED(__rv), msg);                                   \
     343                 :     PR_smprintf_free(msg);                                                   \
     344                 :   }                                                                          \
     345                 :   PR_END_MACRO
     346                 : #else
     347                 : #define NS_ASSERT_SUCCESS(res) PR_BEGIN_MACRO /* nothing */ PR_END_MACRO
     348                 : #endif
     349                 : 
     350                 : /******************************************************************************
     351                 :  * DBListenerErrorHandler impl:
     352                 :  * Parent class for our async storage listeners that handles the logging of
     353                 :  * errors.
     354                 :  ******************************************************************************/
     355                 : class DBListenerErrorHandler : public mozIStorageStatementCallback
     356             790 : {
     357                 : protected:
     358             790 :   DBListenerErrorHandler(DBState* dbState) : mDBState(dbState) { }
     359                 :   nsRefPtr<DBState> mDBState;
     360                 :   virtual const char *GetOpType() = 0;
     361                 : 
     362                 : public:
     363                 :   NS_DECL_ISUPPORTS
     364                 : 
     365               7 :   NS_IMETHOD HandleError(mozIStorageError* aError)
     366                 :   {
     367               7 :     PRInt32 result = -1;
     368               7 :     aError->GetResult(&result);
     369                 : 
     370                 : #ifdef PR_LOGGING
     371              14 :     nsCAutoString message;
     372               7 :     aError->GetMessage(message);
     373               7 :     COOKIE_LOGSTRING(PR_LOG_WARNING,
     374                 :       ("DBListenerErrorHandler::HandleError(): Error %d occurred while "
     375                 :        "performing operation '%s' with message '%s'; rebuilding database.",
     376                 :        result, GetOpType(), message.get()));
     377                 : #endif
     378                 : 
     379                 :     // Rebuild the database.
     380               7 :     gCookieService->HandleCorruptDB(mDBState);
     381                 : 
     382               7 :     return NS_OK;
     383                 :   }
     384                 : };
     385                 : 
     386           38834 : NS_IMPL_ISUPPORTS1(DBListenerErrorHandler, mozIStorageStatementCallback)
     387                 : 
     388                 : /******************************************************************************
     389                 :  * InsertCookieDBListener impl:
     390                 :  * mozIStorageStatementCallback used to track asynchronous insertion operations.
     391                 :  ******************************************************************************/
     392                 : class InsertCookieDBListener : public DBListenerErrorHandler
     393                 : {
     394                 : protected:
     395               0 :   virtual const char *GetOpType() { return "INSERT"; }
     396                 : 
     397                 : public:
     398             250 :   InsertCookieDBListener(DBState* dbState) : DBListenerErrorHandler(dbState) { }
     399               0 :   NS_IMETHOD HandleResult(mozIStorageResultSet*)
     400                 :   {
     401               0 :     NS_NOTREACHED("Unexpected call to InsertCookieDBListener::HandleResult");
     402               0 :     return NS_OK;
     403                 :   }
     404           15701 :   NS_IMETHOD HandleCompletion(PRUint16 aReason)
     405                 :   {
     406                 :     // If we were rebuilding the db and we succeeded, make our corruptFlag say
     407                 :     // so.
     408           15701 :     if (mDBState->corruptFlag == DBState::REBUILDING &&
     409                 :         aReason == mozIStorageStatementCallback::REASON_FINISHED) {
     410               4 :       COOKIE_LOGSTRING(PR_LOG_DEBUG,
     411                 :         ("InsertCookieDBListener::HandleCompletion(): rebuild complete"));
     412               4 :       mDBState->corruptFlag = DBState::OK;
     413                 :     }
     414           15701 :     return NS_OK;
     415                 :   }
     416                 : };
     417                 : 
     418                 : /******************************************************************************
     419                 :  * UpdateCookieDBListener impl:
     420                 :  * mozIStorageStatementCallback used to track asynchronous update operations.
     421                 :  ******************************************************************************/
     422                 : class UpdateCookieDBListener : public DBListenerErrorHandler
     423                 : {
     424                 : protected:
     425               0 :   virtual const char *GetOpType() { return "UPDATE"; }
     426                 : 
     427                 : public:
     428             250 :   UpdateCookieDBListener(DBState* dbState) : DBListenerErrorHandler(dbState) { }
     429               0 :   NS_IMETHOD HandleResult(mozIStorageResultSet*)
     430                 :   {
     431               0 :     NS_NOTREACHED("Unexpected call to UpdateCookieDBListener::HandleResult");
     432               0 :     return NS_OK;
     433                 :   }
     434               0 :   NS_IMETHOD HandleCompletion(PRUint16 aReason)
     435                 :   {
     436               0 :     return NS_OK;
     437                 :   }
     438                 : };
     439                 : 
     440                 : /******************************************************************************
     441                 :  * RemoveCookieDBListener impl:
     442                 :  * mozIStorageStatementCallback used to track asynchronous removal operations.
     443                 :  ******************************************************************************/
     444                 : class RemoveCookieDBListener :  public DBListenerErrorHandler
     445                 : {
     446                 : protected:
     447               0 :   virtual const char *GetOpType() { return "REMOVE"; }
     448                 : 
     449                 : public:
     450             250 :   RemoveCookieDBListener(DBState* dbState) : DBListenerErrorHandler(dbState) { }
     451               0 :   NS_IMETHOD HandleResult(mozIStorageResultSet*)
     452                 :   {
     453               0 :     NS_NOTREACHED("Unexpected call to RemoveCookieDBListener::HandleResult");
     454               0 :     return NS_OK;
     455                 :   }
     456             257 :   NS_IMETHOD HandleCompletion(PRUint16 aReason)
     457                 :   {
     458             257 :     return NS_OK;
     459                 :   }
     460                 : };
     461                 : 
     462                 : /******************************************************************************
     463                 :  * ReadCookieDBListener impl:
     464                 :  * mozIStorageStatementCallback used to track asynchronous removal operations.
     465                 :  ******************************************************************************/
     466                 : class ReadCookieDBListener :  public DBListenerErrorHandler
     467                 : {
     468                 : protected:
     469               0 :   virtual const char *GetOpType() { return "READ"; }
     470                 :   bool mCanceled;
     471                 : 
     472                 : public:
     473              40 :   ReadCookieDBListener(DBState* dbState)
     474                 :     : DBListenerErrorHandler(dbState)
     475              40 :     , mCanceled(false)
     476                 :   {
     477              40 :   }
     478                 : 
     479              32 :   void Cancel() { mCanceled = true; }
     480                 : 
     481             759 :   NS_IMETHOD HandleResult(mozIStorageResultSet *aResult)
     482                 :   {
     483                 :     nsresult rv;
     484            1518 :     nsCOMPtr<mozIStorageRow> row;
     485                 : 
     486           11319 :     while (1) {
     487           12078 :       rv = aResult->GetNextRow(getter_AddRefs(row));
     488           12078 :       NS_ASSERT_SUCCESS(rv);
     489                 : 
     490           12078 :       if (!row)
     491                 :         break;
     492                 : 
     493           11319 :       CookieDomainTuple *tuple = mDBState->hostArray.AppendElement();
     494           11319 :       row->GetUTF8String(9, tuple->baseDomain);
     495           11319 :       tuple->cookie = gCookieService->GetCookieFromRow(row);
     496                 :     }
     497                 : 
     498             759 :     return NS_OK;
     499                 :   }
     500              40 :   NS_IMETHOD HandleCompletion(PRUint16 aReason)
     501                 :   {
     502                 :     // Process the completion of the read operation. If we have been canceled,
     503                 :     // we cannot assume that the cookieservice still has an open connection
     504                 :     // or that it even refers to the same database, so we must return early.
     505                 :     // Conversely, the cookieservice guarantees that if we have not been
     506                 :     // canceled, the database connection is still alive and we can safely
     507                 :     // operate on it.
     508                 : 
     509              40 :     if (mCanceled) {
     510                 :       // We may receive a REASON_FINISHED after being canceled;
     511                 :       // tweak the reason accordingly.
     512              32 :       aReason = mozIStorageStatementCallback::REASON_CANCELED;
     513                 :     }
     514                 : 
     515              40 :     switch (aReason) {
     516                 :     case mozIStorageStatementCallback::REASON_FINISHED:
     517               8 :       gCookieService->AsyncReadComplete();
     518               8 :       break;
     519                 :     case mozIStorageStatementCallback::REASON_CANCELED:
     520                 :       // Nothing more to do here. The partially read data has already been
     521                 :       // thrown away.
     522              32 :       COOKIE_LOGSTRING(PR_LOG_DEBUG, ("Read canceled"));
     523              32 :       break;
     524                 :     case mozIStorageStatementCallback::REASON_ERROR:
     525                 :       // Nothing more to do here. DBListenerErrorHandler::HandleError()
     526                 :       // can handle it.
     527               0 :       COOKIE_LOGSTRING(PR_LOG_DEBUG, ("Read error"));
     528               0 :       break;
     529                 :     default:
     530               0 :       NS_NOTREACHED("invalid reason");
     531                 :     }
     532              40 :     return NS_OK;
     533                 :   }
     534                 : };
     535                 : 
     536                 : /******************************************************************************
     537                 :  * CloseCookieDBListener imp:
     538                 :  * Static mozIStorageCompletionCallback used to notify when the database is
     539                 :  * successfully closed.
     540                 :  ******************************************************************************/
     541                 : class CloseCookieDBListener :  public mozIStorageCompletionCallback
     542             250 : {
     543                 : public:
     544             250 :   CloseCookieDBListener(DBState* dbState) : mDBState(dbState) { }
     545                 :   nsRefPtr<DBState> mDBState;
     546                 :   NS_DECL_ISUPPORTS
     547                 : 
     548             245 :   NS_IMETHOD Complete()
     549                 :   {
     550             245 :     gCookieService->HandleDBClosed(mDBState);
     551             245 :     return NS_OK;
     552                 :   }
     553                 : };
     554                 : 
     555            2725 : NS_IMPL_ISUPPORTS1(CloseCookieDBListener, mozIStorageCompletionCallback)
     556                 : 
     557                 : /******************************************************************************
     558                 :  * nsCookieService impl:
     559                 :  * singleton instance ctor/dtor methods
     560                 :  ******************************************************************************/
     561                 : 
     562                 : nsICookieService*
     563             310 : nsCookieService::GetXPCOMSingleton()
     564                 : {
     565             310 :   if (IsNeckoChild())
     566               0 :     return CookieServiceChild::GetSingleton();
     567                 : 
     568             310 :   return GetSingleton();
     569                 : }
     570                 : 
     571                 : nsCookieService*
     572             310 : nsCookieService::GetSingleton()
     573                 : {
     574             310 :   NS_ASSERTION(!IsNeckoChild(), "not a parent process");
     575                 : 
     576             310 :   if (gCookieService) {
     577              16 :     NS_ADDREF(gCookieService);
     578              16 :     return gCookieService;
     579                 :   }
     580                 : 
     581                 :   // Create a new singleton nsCookieService.
     582                 :   // We AddRef only once since XPCOM has rules about the ordering of module
     583                 :   // teardowns - by the time our module destructor is called, it's too late to
     584                 :   // Release our members (e.g. nsIObserverService and nsIPrefBranch), since GC
     585                 :   // cycles have already been completed and would result in serious leaks.
     586                 :   // See bug 209571.
     587             294 :   gCookieService = new nsCookieService();
     588             294 :   if (gCookieService) {
     589             294 :     NS_ADDREF(gCookieService);
     590             294 :     if (NS_FAILED(gCookieService->Init())) {
     591               0 :       NS_RELEASE(gCookieService);
     592                 :     }
     593                 :   }
     594                 : 
     595             294 :   return gCookieService;
     596                 : }
     597                 : 
     598                 : /******************************************************************************
     599                 :  * nsCookieService impl:
     600                 :  * public methods
     601                 :  ******************************************************************************/
     602                 : 
     603           22485 : NS_IMPL_ISUPPORTS5(nsCookieService,
     604                 :                    nsICookieService,
     605                 :                    nsICookieManager,
     606                 :                    nsICookieManager2,
     607                 :                    nsIObserver,
     608                 :                    nsISupportsWeakReference)
     609                 : 
     610             294 : nsCookieService::nsCookieService()
     611                 :  : mDBState(NULL)
     612                 :  , mCookieBehavior(BEHAVIOR_ACCEPT)
     613                 :  , mThirdPartySession(false)
     614                 :  , mMaxNumberOfCookies(kMaxNumberOfCookies)
     615                 :  , mMaxCookiesPerHost(kMaxCookiesPerHost)
     616             294 :  , mCookiePurgeAge(kCookiePurgeAge)
     617                 : {
     618             294 : }
     619                 : 
     620                 : nsresult
     621             294 : nsCookieService::Init()
     622                 : {
     623                 :   NS_TIME_FUNCTION;
     624                 : 
     625                 :   nsresult rv;
     626             294 :   mTLDService = do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID, &rv);
     627             294 :   NS_ENSURE_SUCCESS(rv, rv);
     628                 : 
     629             294 :   mIDNService = do_GetService(NS_IDNSERVICE_CONTRACTID, &rv);
     630             294 :   NS_ENSURE_SUCCESS(rv, rv);
     631                 : 
     632                 :   // init our pref and observer
     633             588 :   nsCOMPtr<nsIPrefBranch> prefBranch = do_GetService(NS_PREFSERVICE_CONTRACTID);
     634             294 :   if (prefBranch) {
     635             294 :     prefBranch->AddObserver(kPrefCookieBehavior,     this, true);
     636             294 :     prefBranch->AddObserver(kPrefMaxNumberOfCookies, this, true);
     637             294 :     prefBranch->AddObserver(kPrefMaxCookiesPerHost,  this, true);
     638             294 :     prefBranch->AddObserver(kPrefCookiePurgeAge,     this, true);
     639             294 :     prefBranch->AddObserver(kPrefThirdPartySession,  this, true);
     640             294 :     PrefChanged(prefBranch);
     641                 :   }
     642                 : 
     643             294 :   mStorageService = do_GetService("@mozilla.org/storage/service;1", &rv);
     644             294 :   NS_ENSURE_SUCCESS(rv, rv);
     645                 : 
     646                 :   // Init our default, and possibly private DBStates.
     647             294 :   InitDBStates();
     648                 : 
     649             294 :   mObserverService = mozilla::services::GetObserverService();
     650             294 :   NS_ENSURE_STATE(mObserverService);
     651             294 :   mObserverService->AddObserver(this, "profile-before-change", true);
     652             294 :   mObserverService->AddObserver(this, "profile-do-change", true);
     653             294 :   mObserverService->AddObserver(this, NS_PRIVATE_BROWSING_SWITCH_TOPIC, true);
     654                 : 
     655             294 :   mPermissionService = do_GetService(NS_COOKIEPERMISSION_CONTRACTID);
     656             294 :   if (!mPermissionService) {
     657               0 :     NS_WARNING("nsICookiePermission implementation not available - some features won't work!");
     658               0 :     COOKIE_LOGSTRING(PR_LOG_WARNING, ("Init(): nsICookiePermission implementation not available"));
     659                 :   }
     660                 : 
     661             294 :   return NS_OK;
     662                 : }
     663                 : 
     664                 : void
     665             343 : nsCookieService::InitDBStates()
     666                 : {
     667             343 :   NS_ASSERTION(!mDBState, "already have a DBState");
     668             343 :   NS_ASSERTION(!mDefaultDBState, "already have a default DBState");
     669             343 :   NS_ASSERTION(!mPrivateDBState, "already have a private DBState");
     670                 : 
     671                 :   // Create a new default DBState and set our current one.
     672             343 :   mDefaultDBState = new DBState();
     673             343 :   mDBState = mDefaultDBState;
     674                 : 
     675                 :   // If we're in private browsing mode, create a private DBState.
     676                 :   nsCOMPtr<nsIPrivateBrowsingService> pbs =
     677             686 :     do_GetService(NS_PRIVATE_BROWSING_SERVICE_CONTRACTID);
     678             343 :   if (pbs) {
     679             343 :     bool inPrivateBrowsing = false;
     680             343 :     pbs->GetPrivateBrowsingEnabled(&inPrivateBrowsing);
     681             343 :     if (inPrivateBrowsing) {
     682               3 :       mPrivateDBState = new DBState();
     683               3 :       mDBState = mPrivateDBState;
     684                 :     }
     685                 :   }
     686                 : 
     687                 :   // Get our cookie file.
     688                 :   nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
     689             343 :     getter_AddRefs(mDefaultDBState->cookieFile));
     690             343 :   if (NS_FAILED(rv)) {
     691                 :     // We've already set up our DBStates appropriately; nothing more to do.
     692             104 :     COOKIE_LOGSTRING(PR_LOG_WARNING,
     693                 :       ("InitDBStates(): couldn't get cookie file"));
     694                 :     return;
     695                 :   }
     696             239 :   mDefaultDBState->cookieFile->AppendNative(NS_LITERAL_CSTRING(COOKIES_FILE));
     697                 : 
     698                 :   // Attempt to open and read the database. If TryInitDB() returns RESULT_RETRY,
     699                 :   // do so.
     700             239 :   OpenDBResult result = TryInitDB(false);
     701             239 :   if (result == RESULT_RETRY) {
     702                 :     // Database may be corrupt. Synchronously close the connection, clean up the
     703                 :     // default DBState, and try again.
     704               6 :     COOKIE_LOGSTRING(PR_LOG_WARNING, ("InitDBStates(): retrying TryInitDB()"));
     705                 : 
     706               6 :     CloseDefaultDBConnection();
     707               6 :     result = TryInitDB(true);
     708               6 :     if (result == RESULT_RETRY) {
     709                 :       // We're done. Change the code to failure so we clean up below.
     710               0 :       result = RESULT_FAILURE;
     711                 :     }
     712                 :   }
     713                 : 
     714             239 :   if (result == RESULT_FAILURE) {
     715               0 :     COOKIE_LOGSTRING(PR_LOG_WARNING,
     716                 :       ("InitDBStates(): TryInitDB() failed, closing connection"));
     717                 : 
     718                 :     // Connection failure is unrecoverable. Clean up our connection. We can run
     719                 :     // fine without persistent storage -- e.g. if there's no profile.
     720               0 :     CloseDefaultDBConnection();
     721                 :   }
     722                 : }
     723                 : 
     724                 : /* Attempt to open and read the database. If 'aRecreateDB' is true, try to
     725                 :  * move the existing database file out of the way and create a new one.
     726                 :  *
     727                 :  * @returns RESULT_OK if opening or creating the database succeeded;
     728                 :  *          RESULT_RETRY if the database cannot be opened, is corrupt, or some
     729                 :  *          other failure occurred that might be resolved by recreating the
     730                 :  *          database; or RESULT_FAILED if there was an unrecoverable error and
     731                 :  *          we must run without a database.
     732                 :  *
     733                 :  * If RESULT_RETRY or RESULT_FAILED is returned, the caller should perform
     734                 :  * cleanup of the default DBState.
     735                 :  */
     736                 : OpenDBResult
     737             251 : nsCookieService::TryInitDB(bool aRecreateDB)
     738                 : {
     739             251 :   NS_ASSERTION(!mDefaultDBState->dbConn, "nonnull dbConn");
     740             251 :   NS_ASSERTION(!mDefaultDBState->stmtInsert, "nonnull stmtInsert");
     741             251 :   NS_ASSERTION(!mDefaultDBState->insertListener, "nonnull insertListener");
     742             251 :   NS_ASSERTION(!mDefaultDBState->syncConn, "nonnull syncConn");
     743                 : 
     744                 :   // Ditch an existing db, if we've been told to (i.e. it's corrupt). We don't
     745                 :   // want to delete it outright, since it may be useful for debugging purposes,
     746                 :   // so we move it out of the way.
     747                 :   nsresult rv;
     748             251 :   if (aRecreateDB) {
     749              24 :     nsCOMPtr<nsIFile> backupFile;
     750              12 :     mDefaultDBState->cookieFile->Clone(getter_AddRefs(backupFile));
     751              12 :     rv = backupFile->MoveToNative(NULL,
     752              12 :       NS_LITERAL_CSTRING(COOKIES_FILE ".bak"));
     753              12 :     NS_ENSURE_SUCCESS(rv, RESULT_FAILURE);
     754                 :   }
     755                 : 
     756                 :   // open a connection to the cookie database, and only cache our connection
     757                 :   // and statements upon success. The connection is opened unshared to eliminate
     758                 :   // cache contention between the main and background threads.
     759             502 :   rv = mStorageService->OpenUnsharedDatabase(mDefaultDBState->cookieFile,
     760             502 :     getter_AddRefs(mDefaultDBState->dbConn));
     761             251 :   NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
     762                 : 
     763                 :   // Set up our listeners.
     764             500 :   mDefaultDBState->insertListener = new InsertCookieDBListener(mDefaultDBState);
     765             500 :   mDefaultDBState->updateListener = new UpdateCookieDBListener(mDefaultDBState);
     766             500 :   mDefaultDBState->removeListener = new RemoveCookieDBListener(mDefaultDBState);
     767             500 :   mDefaultDBState->closeListener = new CloseCookieDBListener(mDefaultDBState);
     768                 : 
     769                 :   // Grow cookie db in 512KB increments
     770             250 :   mDefaultDBState->dbConn->SetGrowthIncrement(512 * 1024, EmptyCString());
     771                 : 
     772             250 :   bool tableExists = false;
     773             500 :   mDefaultDBState->dbConn->TableExists(NS_LITERAL_CSTRING("moz_cookies"),
     774             250 :     &tableExists);
     775             250 :   if (!tableExists) {
     776             205 :     rv = CreateTable();
     777             205 :     NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
     778                 : 
     779                 :   } else {
     780                 :     // table already exists; check the schema version before reading
     781                 :     PRInt32 dbSchemaVersion;
     782              45 :     rv = mDefaultDBState->dbConn->GetSchemaVersion(&dbSchemaVersion);
     783              45 :     NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
     784                 : 
     785                 :     // Start a transaction for the whole migration block.
     786              90 :     mozStorageTransaction transaction(mDefaultDBState->dbConn, true);
     787                 : 
     788              45 :     switch (dbSchemaVersion) {
     789                 :     // Upgrading.
     790                 :     // Every time you increment the database schema, you need to implement
     791                 :     // the upgrading code from the previous version to the new one. If migration
     792                 :     // fails for any reason, it's a bug -- so we return RESULT_RETRY such that
     793                 :     // the original database will be saved, in the hopes that we might one day
     794                 :     // see it and fix it.
     795                 :     case 1:
     796                 :       {
     797                 :         // Add the lastAccessed column to the table.
     798               2 :         rv = mDefaultDBState->dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
     799               1 :           "ALTER TABLE moz_cookies ADD lastAccessed INTEGER"));
     800               1 :         NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
     801                 :       }
     802                 :       // Fall through to the next upgrade.
     803                 : 
     804                 :     case 2:
     805                 :       {
     806                 :         // Add the baseDomain column and index to the table.
     807               6 :         rv = mDefaultDBState->dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
     808               3 :           "ALTER TABLE moz_cookies ADD baseDomain TEXT"));
     809               3 :         NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
     810                 : 
     811                 :         // Compute the baseDomains for the table. This must be done eagerly
     812                 :         // otherwise we won't be able to synchronously read in individual
     813                 :         // domains on demand.
     814               4 :         nsCOMPtr<mozIStorageStatement> select;
     815               4 :         rv = mDefaultDBState->dbConn->CreateStatement(NS_LITERAL_CSTRING(
     816               4 :           "SELECT id, host FROM moz_cookies"), getter_AddRefs(select));
     817               2 :         NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
     818                 : 
     819               4 :         nsCOMPtr<mozIStorageStatement> update;
     820               4 :         rv = mDefaultDBState->dbConn->CreateStatement(NS_LITERAL_CSTRING(
     821                 :           "UPDATE moz_cookies SET baseDomain = :baseDomain WHERE id = :id"),
     822               4 :           getter_AddRefs(update));
     823               2 :         NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
     824                 : 
     825               4 :         nsCString baseDomain, host;
     826                 :         bool hasResult;
     827              60 :         while (1) {
     828              62 :           rv = select->ExecuteStep(&hasResult);
     829              62 :           NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
     830                 : 
     831              62 :           if (!hasResult)
     832                 :             break;
     833                 : 
     834              61 :           PRInt64 id = select->AsInt64(0);
     835              61 :           select->GetUTF8String(1, host);
     836                 : 
     837              61 :           rv = GetBaseDomainFromHost(host, baseDomain);
     838              61 :           NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
     839                 : 
     840             120 :           mozStorageStatementScoper scoper(update);
     841                 : 
     842             120 :           rv = update->BindUTF8StringByName(NS_LITERAL_CSTRING("baseDomain"),
     843              60 :                                             baseDomain);
     844              60 :           NS_ASSERT_SUCCESS(rv);
     845             120 :           rv = update->BindInt64ByName(NS_LITERAL_CSTRING("id"),
     846              60 :                                        id);
     847              60 :           NS_ASSERT_SUCCESS(rv);
     848                 : 
     849              60 :           rv = update->ExecuteStep(&hasResult);
     850              60 :           NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
     851                 :         }
     852                 : 
     853                 :         // Create an index on baseDomain.
     854               2 :         rv = mDefaultDBState->dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
     855               1 :           "CREATE INDEX moz_basedomain ON moz_cookies (baseDomain)"));
     856               1 :         NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
     857                 :       }
     858                 :       // Fall through to the next upgrade.
     859                 : 
     860                 :     case 3:
     861                 :       {
     862                 :         // Add the creationTime column to the table, and create a unique index
     863                 :         // on (name, host, path). Before we do this, we have to purge the table
     864                 :         // of expired cookies such that we know that the (name, host, path)
     865                 :         // index is truly unique -- otherwise we can't create the index. Note
     866                 :         // that we can't just execute a statement to delete all rows where the
     867                 :         // expiry column is in the past -- doing so would rely on the clock
     868                 :         // (both now and when previous cookies were set) being monotonic.
     869                 : 
     870                 :         // Select the whole table, and order by the fields we're interested in.
     871                 :         // This means we can simply do a linear traversal of the results and
     872                 :         // check for duplicates as we go.
     873              10 :         nsCOMPtr<mozIStorageStatement> select;
     874              10 :         rv = mDefaultDBState->dbConn->CreateStatement(NS_LITERAL_CSTRING(
     875                 :           "SELECT id, name, host, path FROM moz_cookies "
     876                 :             "ORDER BY name ASC, host ASC, path ASC, expiry ASC"),
     877              10 :           getter_AddRefs(select));
     878               5 :         NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
     879                 : 
     880              10 :         nsCOMPtr<mozIStorageStatement> deleteExpired;
     881              10 :         rv = mDefaultDBState->dbConn->CreateStatement(NS_LITERAL_CSTRING(
     882                 :           "DELETE FROM moz_cookies WHERE id = :id"),
     883              10 :           getter_AddRefs(deleteExpired));
     884               5 :         NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
     885                 : 
     886                 :         // Read the first row.
     887                 :         bool hasResult;
     888               5 :         rv = select->ExecuteStep(&hasResult);
     889               5 :         NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
     890                 : 
     891               5 :         if (hasResult) {
     892              10 :           nsCString name1, host1, path1;
     893               5 :           PRInt64 id1 = select->AsInt64(0);
     894               5 :           select->GetUTF8String(1, name1);
     895               5 :           select->GetUTF8String(2, host1);
     896               5 :           select->GetUTF8String(3, path1);
     897                 : 
     898              10 :           nsCString name2, host2, path2;
     899             118 :           while (1) {
     900                 :             // Read the second row.
     901             123 :             rv = select->ExecuteStep(&hasResult);
     902             123 :             NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
     903                 : 
     904             123 :             if (!hasResult)
     905                 :               break;
     906                 : 
     907             118 :             PRInt64 id2 = select->AsInt64(0);
     908             118 :             select->GetUTF8String(1, name2);
     909             118 :             select->GetUTF8String(2, host2);
     910             118 :             select->GetUTF8String(3, path2);
     911                 : 
     912                 :             // If the two rows match in (name, host, path), we know the earlier
     913                 :             // row has an earlier expiry time. Delete it.
     914             118 :             if (name1 == name2 && host1 == host2 && path1 == path2) {
     915              76 :               mozStorageStatementScoper scoper(deleteExpired);
     916                 : 
     917              76 :               rv = deleteExpired->BindInt64ByName(NS_LITERAL_CSTRING("id"),
     918              38 :                 id1);
     919              38 :               NS_ASSERT_SUCCESS(rv);
     920                 : 
     921              38 :               rv = deleteExpired->ExecuteStep(&hasResult);
     922              38 :               NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
     923                 :             }
     924                 : 
     925                 :             // Make the second row the first for the next iteration.
     926             118 :             name1 = name2;
     927             118 :             host1 = host2;
     928             118 :             path1 = path2;
     929             118 :             id1 = id2;
     930                 :           }
     931                 :         }
     932                 : 
     933                 :         // Add the creationTime column to the table.
     934              10 :         rv = mDefaultDBState->dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
     935               5 :           "ALTER TABLE moz_cookies ADD creationTime INTEGER"));
     936               5 :         NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
     937                 : 
     938                 :         // Copy the id of each row into the new creationTime column.
     939               8 :         rv = mDefaultDBState->dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
     940                 :           "UPDATE moz_cookies SET creationTime = "
     941               4 :             "(SELECT id WHERE id = moz_cookies.id)"));
     942               4 :         NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
     943                 : 
     944                 :         // Create a unique index on (name, host, path) to allow fast lookup.
     945               8 :         rv = mDefaultDBState->dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
     946                 :           "CREATE UNIQUE INDEX moz_uniqueid "
     947               4 :           "ON moz_cookies (name, host, path)"));
     948               4 :         NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
     949                 :       }
     950                 :       // Fall through to the next upgrade.
     951                 : 
     952                 :       // No more upgrades. Update the schema version.
     953               3 :       rv = mDefaultDBState->dbConn->SetSchemaVersion(COOKIES_SCHEMA_VERSION);
     954               3 :       NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
     955                 : 
     956                 :     case COOKIES_SCHEMA_VERSION:
     957              39 :       break;
     958                 : 
     959                 :     case 0:
     960                 :       {
     961               0 :         NS_WARNING("couldn't get schema version!");
     962                 :           
     963                 :         // the table may be usable; someone might've just clobbered the schema
     964                 :         // version. we can treat this case like a downgrade using the codepath
     965                 :         // below, by verifying the columns we care about are all there. for now,
     966                 :         // re-set the schema version in the db, in case the checks succeed (if
     967                 :         // they don't, we're dropping the table anyway).
     968               0 :         rv = mDefaultDBState->dbConn->SetSchemaVersion(COOKIES_SCHEMA_VERSION);
     969               0 :         NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
     970                 :       }
     971                 :       // fall through to downgrade check
     972                 : 
     973                 :     // downgrading.
     974                 :     // if columns have been added to the table, we can still use the ones we
     975                 :     // understand safely. if columns have been deleted or altered, just
     976                 :     // blow away the table and start from scratch! if you change the way
     977                 :     // a column is interpreted, make sure you also change its name so this
     978                 :     // check will catch it.
     979                 :     default:
     980                 :       {
     981                 :         // check if all the expected columns exist
     982               2 :         nsCOMPtr<mozIStorageStatement> stmt;
     983               2 :         rv = mDefaultDBState->dbConn->CreateStatement(NS_LITERAL_CSTRING(
     984                 :           "SELECT "
     985                 :             "id, "
     986                 :             "baseDomain, "
     987                 :             "name, "
     988                 :             "value, "
     989                 :             "host, "
     990                 :             "path, "
     991                 :             "expiry, "
     992                 :             "lastAccessed, "
     993                 :             "creationTime, "
     994                 :             "isSecure, "
     995                 :             "isHttpOnly "
     996               2 :           "FROM moz_cookies"), getter_AddRefs(stmt));
     997               1 :         if (NS_SUCCEEDED(rv))
     998                 :           break;
     999                 : 
    1000                 :         // our columns aren't there - drop the table!
    1001               2 :         rv = mDefaultDBState->dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    1002               1 :           "DROP TABLE moz_cookies"));
    1003               1 :         NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
    1004                 : 
    1005               1 :         rv = CreateTable();
    1006               1 :         NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
    1007                 :       }
    1008               1 :       break;
    1009                 :     }
    1010                 :   }
    1011                 : 
    1012                 :   // make operations on the table asynchronous, for performance
    1013             490 :   mDefaultDBState->dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    1014             245 :     "PRAGMA synchronous = OFF"));
    1015                 : 
    1016                 :   // Use write-ahead-logging for performance. We cap the autocheckpoint limit at
    1017                 :   // 16 pages (around 500KB).
    1018             490 :   mDefaultDBState->dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    1019             245 :     MOZ_STORAGE_UNIQUIFY_QUERY_STR "PRAGMA journal_mode = WAL"));
    1020             490 :   mDefaultDBState->dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    1021             245 :     "PRAGMA wal_autocheckpoint = 16"));
    1022                 : 
    1023                 :   // cache frequently used statements (for insertion, deletion, and updating)
    1024             490 :   rv = mDefaultDBState->dbConn->CreateAsyncStatement(NS_LITERAL_CSTRING(
    1025                 :     "INSERT INTO moz_cookies ("
    1026                 :       "baseDomain, "
    1027                 :       "name, "
    1028                 :       "value, "
    1029                 :       "host, "
    1030                 :       "path, "
    1031                 :       "expiry, "
    1032                 :       "lastAccessed, "
    1033                 :       "creationTime, "
    1034                 :       "isSecure, "
    1035                 :       "isHttpOnly"
    1036                 :     ") VALUES ("
    1037                 :       ":baseDomain, "
    1038                 :       ":name, "
    1039                 :       ":value, "
    1040                 :       ":host, "
    1041                 :       ":path, "
    1042                 :       ":expiry, "
    1043                 :       ":lastAccessed, "
    1044                 :       ":creationTime, "
    1045                 :       ":isSecure, "
    1046                 :       ":isHttpOnly"
    1047                 :     ")"),
    1048             490 :     getter_AddRefs(mDefaultDBState->stmtInsert));
    1049             245 :   NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
    1050                 : 
    1051             490 :   rv = mDefaultDBState->dbConn->CreateAsyncStatement(NS_LITERAL_CSTRING(
    1052                 :     "DELETE FROM moz_cookies "
    1053                 :     "WHERE name = :name AND host = :host AND path = :path"),
    1054             490 :     getter_AddRefs(mDefaultDBState->stmtDelete));
    1055             245 :   NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
    1056                 : 
    1057             490 :   rv = mDefaultDBState->dbConn->CreateAsyncStatement(NS_LITERAL_CSTRING(
    1058                 :     "UPDATE moz_cookies SET lastAccessed = :lastAccessed "
    1059                 :     "WHERE name = :name AND host = :host AND path = :path"),
    1060             490 :     getter_AddRefs(mDefaultDBState->stmtUpdate));
    1061             245 :   NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
    1062                 : 
    1063                 :   // if we deleted a corrupt db, don't attempt to import - return now
    1064             245 :   if (aRecreateDB)
    1065              12 :     return RESULT_OK;
    1066                 : 
    1067                 :   // check whether to import or just read in the db
    1068             233 :   if (tableExists)
    1069              40 :     return Read();
    1070                 : 
    1071             386 :   nsCOMPtr<nsIFile> oldCookieFile;
    1072                 :   rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
    1073             193 :     getter_AddRefs(oldCookieFile));
    1074             193 :   if (NS_FAILED(rv)) return RESULT_OK;
    1075                 : 
    1076                 :   // Import cookies, and clean up the old file regardless of success or failure.
    1077                 :   // Note that we have to switch out our DBState temporarily, in case we're in
    1078                 :   // private browsing mode; otherwise ImportCookies() won't be happy.
    1079             193 :   DBState* initialState = mDBState;
    1080             193 :   mDBState = mDefaultDBState;
    1081             193 :   oldCookieFile->AppendNative(NS_LITERAL_CSTRING(kOldCookieFileName));
    1082             193 :   ImportCookies(oldCookieFile);
    1083             193 :   oldCookieFile->Remove(false);
    1084             193 :   mDBState = initialState;
    1085                 : 
    1086             193 :   return RESULT_OK;
    1087                 : }
    1088                 : 
    1089                 : // Sets the schema version and creates the moz_cookies table.
    1090                 : nsresult
    1091             206 : nsCookieService::CreateTable()
    1092                 : {
    1093                 :   // Set the schema version, before creating the table.
    1094             206 :   nsresult rv = mDefaultDBState->dbConn->SetSchemaVersion(
    1095             206 :     COOKIES_SCHEMA_VERSION);
    1096             206 :   if (NS_FAILED(rv)) return rv;
    1097                 : 
    1098                 :   // Create the table.
    1099             412 :   rv = mDefaultDBState->dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    1100                 :     "CREATE TABLE moz_cookies ("
    1101                 :       "id INTEGER PRIMARY KEY, "
    1102                 :       "baseDomain TEXT, "
    1103                 :       "name TEXT, "
    1104                 :       "value TEXT, "
    1105                 :       "host TEXT, "
    1106                 :       "path TEXT, "
    1107                 :       "expiry INTEGER, "
    1108                 :       "lastAccessed INTEGER, "
    1109                 :       "creationTime INTEGER, "
    1110                 :       "isSecure INTEGER, "
    1111                 :       "isHttpOnly INTEGER, "
    1112                 :       "CONSTRAINT moz_uniqueid UNIQUE (name, host, path)"
    1113             206 :     ")"));
    1114             206 :   if (NS_FAILED(rv)) return rv;
    1115                 : 
    1116                 :   // Create an index on baseDomain.
    1117             412 :   return mDefaultDBState->dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    1118             206 :     "CREATE INDEX moz_basedomain ON moz_cookies (baseDomain)"));
    1119                 : }
    1120                 : 
    1121                 : void
    1122             536 : nsCookieService::CloseDBStates()
    1123                 : {
    1124                 :   // Null out our private and pointer DBStates regardless.
    1125             536 :   mPrivateDBState = NULL;
    1126             536 :   mDBState = NULL;
    1127                 : 
    1128                 :   // If we don't have a default DBState, we're done.
    1129             536 :   if (!mDefaultDBState)
    1130             193 :     return;
    1131                 : 
    1132             343 :   if (mDefaultDBState->dbConn) {
    1133                 :     // Cancel any pending read. No further results will be received by our
    1134                 :     // read listener.
    1135             236 :     if (mDefaultDBState->pendingRead) {
    1136               9 :       CancelAsyncRead(true);
    1137                 :     }
    1138                 : 
    1139                 :     // Asynchronously close the connection. We will null it below.
    1140             236 :     mDefaultDBState->dbConn->AsyncClose(mDefaultDBState->closeListener);
    1141                 :   }
    1142                 : 
    1143             343 :   CloseDefaultDBConnection();
    1144                 : 
    1145             343 :   mDefaultDBState = NULL;
    1146                 : }
    1147                 : 
    1148                 : // Close the default connection by nulling out statements, listeners, and the
    1149                 : // connection itself. This will not cancel a pending read or asynchronously
    1150                 : // close the connection -- these must be done beforehand if necessary.
    1151                 : void
    1152             358 : nsCookieService::CloseDefaultDBConnection()
    1153                 : {
    1154                 :   // Destroy our statements before we close the db.
    1155             358 :   mDefaultDBState->stmtInsert = NULL;
    1156             358 :   mDefaultDBState->stmtDelete = NULL;
    1157             358 :   mDefaultDBState->stmtUpdate = NULL;
    1158                 : 
    1159                 :   // Null out the database connections. If 'dbConn' has not been used for any
    1160                 :   // asynchronous operations yet, this will synchronously close it; otherwise,
    1161                 :   // it's expected that the caller has performed an AsyncClose prior.
    1162             358 :   mDefaultDBState->dbConn = NULL;
    1163             358 :   mDefaultDBState->syncConn = NULL;
    1164                 : 
    1165                 :   // Manually null out our listeners. This is necessary because they hold a
    1166                 :   // strong ref to the DBState itself. They'll stay alive until whatever
    1167                 :   // statements are still executing complete.
    1168             358 :   mDefaultDBState->readListener = NULL;
    1169             358 :   mDefaultDBState->insertListener = NULL;
    1170             358 :   mDefaultDBState->updateListener = NULL;
    1171             358 :   mDefaultDBState->removeListener = NULL;
    1172             358 :   mDefaultDBState->closeListener = NULL;
    1173             358 : }
    1174                 : 
    1175                 : void
    1176             245 : nsCookieService::HandleDBClosed(DBState* aDBState)
    1177                 : {
    1178             245 :   COOKIE_LOGSTRING(PR_LOG_DEBUG,
    1179                 :     ("HandleDBClosed(): DBState %x closed", aDBState));
    1180                 : 
    1181             245 :   switch (aDBState->corruptFlag) {
    1182                 :   case DBState::OK: {
    1183                 :     // Database is healthy. Notify of closure.
    1184             236 :     mObserverService->NotifyObservers(nsnull, "cookie-db-closed", nsnull);
    1185             236 :     break;
    1186                 :   }
    1187                 :   case DBState::CLOSING_FOR_REBUILD: {
    1188                 :     // Our close finished. Start the rebuild, and notify of db closure later.
    1189               8 :     RebuildCorruptDB(aDBState);
    1190               8 :     break;
    1191                 :   }
    1192                 :   case DBState::REBUILDING: {
    1193                 :     // We encountered an error during rebuild, closed the database, and now
    1194                 :     // here we are. We already have a 'cookies.sqlite.bak' from the original
    1195                 :     // dead database; we don't want to overwrite it, so let's move this one to
    1196                 :     // 'cookies.sqlite.bak-rebuild'.
    1197               2 :     nsCOMPtr<nsIFile> backupFile;
    1198               1 :     aDBState->cookieFile->Clone(getter_AddRefs(backupFile));
    1199               1 :     nsresult rv = backupFile->MoveToNative(NULL,
    1200               1 :       NS_LITERAL_CSTRING(COOKIES_FILE ".bak-rebuild"));
    1201                 : 
    1202               1 :     COOKIE_LOGSTRING(PR_LOG_WARNING,
    1203                 :       ("HandleDBClosed(): DBState %x encountered error rebuilding db; move to "
    1204                 :        "'cookies.sqlite.bak-rebuild' gave rv 0x%x", aDBState, rv));
    1205               1 :     mObserverService->NotifyObservers(nsnull, "cookie-db-closed", nsnull);
    1206                 :     break;
    1207                 :   }
    1208                 :   }
    1209             245 : }
    1210                 : 
    1211                 : void
    1212              11 : nsCookieService::HandleCorruptDB(DBState* aDBState)
    1213                 : {
    1214              11 :   if (mDefaultDBState != aDBState) {
    1215                 :     // We've either closed the state or we've switched profiles. It's getting
    1216                 :     // a bit late to rebuild -- bail instead.
    1217               2 :     COOKIE_LOGSTRING(PR_LOG_WARNING,
    1218                 :       ("HandleCorruptDB(): DBState %x is already closed, aborting", aDBState));
    1219               2 :     return;
    1220                 :   }
    1221                 : 
    1222               9 :   COOKIE_LOGSTRING(PR_LOG_DEBUG,
    1223                 :     ("HandleCorruptDB(): DBState %x has corruptFlag %u", aDBState,
    1224                 :       aDBState->corruptFlag));
    1225                 : 
    1226                 :   // Mark the database corrupt, so the close listener can begin reconstructing
    1227                 :   // it.
    1228               9 :   switch (mDefaultDBState->corruptFlag) {
    1229                 :   case DBState::OK: {
    1230                 :     // Move to 'closing' state.
    1231               8 :     mDefaultDBState->corruptFlag = DBState::CLOSING_FOR_REBUILD;
    1232                 : 
    1233                 :     // Cancel any pending read and close the database. If we do have an
    1234                 :     // in-flight read we want to throw away all the results so far -- we have no
    1235                 :     // idea how consistent the database is. Note that we may have already
    1236                 :     // canceled the read but not emptied our readSet; do so now.
    1237               8 :     mDefaultDBState->readSet.Clear();
    1238               8 :     if (mDefaultDBState->pendingRead) {
    1239               4 :       CancelAsyncRead(true);
    1240               4 :       mDefaultDBState->syncConn = nsnull;
    1241                 :     }
    1242                 : 
    1243               8 :     mDefaultDBState->dbConn->AsyncClose(mDefaultDBState->closeListener);
    1244               8 :     CloseDefaultDBConnection();
    1245               8 :     break;
    1246                 :   }
    1247                 :   case DBState::CLOSING_FOR_REBUILD: {
    1248                 :     // We had an error while waiting for close completion. That's OK, just
    1249                 :     // ignore it -- we're rebuilding anyway.
    1250               0 :     return;
    1251                 :   }
    1252                 :   case DBState::REBUILDING: {
    1253                 :     // We had an error while rebuilding the DB. Game over. Close the database
    1254                 :     // and let the close handler do nothing; then we'll move it out of the way.
    1255               1 :     if (mDefaultDBState->dbConn) {
    1256               1 :       mDefaultDBState->dbConn->AsyncClose(mDefaultDBState->closeListener);
    1257                 :     }
    1258               1 :     CloseDefaultDBConnection();
    1259               1 :     break;
    1260                 :   }
    1261                 :   }
    1262                 : }
    1263                 : 
    1264                 : static PLDHashOperator
    1265               7 : RebuildDBCallback(nsCookieEntry *aEntry,
    1266                 :                   void          *aArg)
    1267                 : {
    1268                 :   mozIStorageBindingParamsArray* paramsArray =
    1269               7 :     static_cast<mozIStorageBindingParamsArray*>(aArg);
    1270                 : 
    1271               7 :   const nsCookieEntry::ArrayType &cookies = aEntry->GetCookies();
    1272              24 :   for (nsCookieEntry::IndexType i = 0; i < cookies.Length(); ++i) {
    1273              17 :     nsCookie* cookie = cookies[i];
    1274                 : 
    1275              17 :     if (!cookie->IsSession()) {
    1276              17 :       bindCookieParameters(paramsArray, aEntry->GetKey(), cookie);
    1277                 :     }
    1278                 :   }
    1279                 : 
    1280               7 :   return PL_DHASH_NEXT;
    1281                 : }
    1282                 : 
    1283                 : void
    1284               8 : nsCookieService::RebuildCorruptDB(DBState* aDBState)
    1285                 : {
    1286               8 :   NS_ASSERTION(!aDBState->dbConn, "shouldn't have an open db connection");
    1287               8 :   NS_ASSERTION(aDBState->corruptFlag == DBState::CLOSING_FOR_REBUILD,
    1288                 :     "should be in CLOSING_FOR_REBUILD state");
    1289                 : 
    1290               8 :   aDBState->corruptFlag = DBState::REBUILDING;
    1291                 : 
    1292               8 :   if (mDefaultDBState != aDBState) {
    1293                 :     // We've either closed the state or we've switched profiles. It's getting
    1294                 :     // a bit late to rebuild -- bail instead. In any case, we were waiting
    1295                 :     // on rebuild completion to notify of the db closure, which won't happen --
    1296                 :     // do so now.
    1297               2 :     COOKIE_LOGSTRING(PR_LOG_WARNING,
    1298                 :       ("RebuildCorruptDB(): DBState %x is stale, aborting", aDBState));
    1299               2 :     mObserverService->NotifyObservers(nsnull, "cookie-db-closed", nsnull);
    1300               2 :     return;
    1301                 :   }
    1302                 : 
    1303               6 :   COOKIE_LOGSTRING(PR_LOG_DEBUG,
    1304                 :     ("RebuildCorruptDB(): creating new database"));
    1305                 : 
    1306                 :   // The database has been closed, and we're ready to rebuild. Open a
    1307                 :   // connection.
    1308               6 :   OpenDBResult result = TryInitDB(true);
    1309               6 :   if (result != RESULT_OK) {
    1310                 :     // We're done. Reset our DB connection and statements, and notify of
    1311                 :     // closure.
    1312               0 :     COOKIE_LOGSTRING(PR_LOG_WARNING,
    1313                 :       ("RebuildCorruptDB(): TryInitDB() failed with result %u", result));
    1314               0 :     CloseDefaultDBConnection();
    1315               0 :     mDefaultDBState->corruptFlag = DBState::OK;
    1316               0 :     mObserverService->NotifyObservers(nsnull, "cookie-db-closed", nsnull);
    1317               0 :     return;
    1318                 :   }
    1319                 : 
    1320                 :   // Notify observers that we're beginning the rebuild.
    1321               6 :   mObserverService->NotifyObservers(nsnull, "cookie-db-rebuilding", nsnull);
    1322                 : 
    1323                 :   // Enumerate the hash, and add cookies to the params array.
    1324               6 :   mozIStorageAsyncStatement* stmt = aDBState->stmtInsert;
    1325              12 :   nsCOMPtr<mozIStorageBindingParamsArray> paramsArray;
    1326               6 :   stmt->NewBindingParamsArray(getter_AddRefs(paramsArray));
    1327               6 :   aDBState->hostTable.EnumerateEntries(RebuildDBCallback, paramsArray.get());
    1328                 : 
    1329                 :   // Make sure we've got something to write. If we don't, we're done.
    1330                 :   PRUint32 length;
    1331               6 :   paramsArray->GetLength(&length);
    1332               6 :   if (length == 0) {
    1333               1 :     COOKIE_LOGSTRING(PR_LOG_DEBUG,
    1334                 :       ("RebuildCorruptDB(): nothing to write, rebuild complete"));
    1335               1 :     mDefaultDBState->corruptFlag = DBState::OK;
    1336                 :     return;
    1337                 :   }
    1338                 : 
    1339                 :   // Execute the statement. If any errors crop up, we won't try again.
    1340               5 :   nsresult rv = stmt->BindParameters(paramsArray);
    1341               5 :   NS_ASSERT_SUCCESS(rv);
    1342              10 :   nsCOMPtr<mozIStoragePendingStatement> handle;
    1343               5 :   rv = stmt->ExecuteAsync(aDBState->insertListener, getter_AddRefs(handle));
    1344               5 :   NS_ASSERT_SUCCESS(rv);    
    1345                 : }
    1346                 : 
    1347             876 : nsCookieService::~nsCookieService()
    1348                 : {
    1349             292 :   CloseDBStates();
    1350                 : 
    1351             292 :   gCookieService = nsnull;
    1352            1168 : }
    1353                 : 
    1354                 : NS_IMETHODIMP
    1355             314 : nsCookieService::Observe(nsISupports     *aSubject,
    1356                 :                          const char      *aTopic,
    1357                 :                          const PRUnichar *aData)
    1358                 : {
    1359                 :   // check the topic
    1360             314 :   if (!strcmp(aTopic, "profile-before-change")) {
    1361                 :     // The profile is about to change,
    1362                 :     // or is going away because the application is shutting down.
    1363             956 :     if (mDBState && mDBState->dbConn &&
    1364             712 :         !nsCRT::strcmp(aData, NS_LITERAL_STRING("shutdown-cleanse").get())) {
    1365                 :       // Clear the cookie db if we're in the default DBState.
    1366               2 :       RemoveAll();
    1367                 :     }
    1368                 : 
    1369                 :     // Close the default DB connection and null out our DBStates before
    1370                 :     // changing.
    1371             244 :     CloseDBStates();
    1372                 : 
    1373              70 :   } else if (!strcmp(aTopic, "profile-do-change")) {
    1374              49 :     NS_ASSERTION(!mDefaultDBState, "shouldn't have a default DBState");
    1375              49 :     NS_ASSERTION(!mPrivateDBState, "shouldn't have a private DBState");
    1376                 : 
    1377                 :     // the profile has already changed; init the db from the new location.
    1378                 :     // if we are in the private browsing state, however, we do not want to read
    1379                 :     // data into it - we should instead put it into the default state, so it's
    1380                 :     // ready for us if and when we switch back to it.
    1381              49 :     InitDBStates();
    1382                 : 
    1383              21 :   } else if (!strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
    1384              12 :     nsCOMPtr<nsIPrefBranch> prefBranch = do_QueryInterface(aSubject);
    1385               6 :     if (prefBranch)
    1386               6 :       PrefChanged(prefBranch);
    1387                 : 
    1388              15 :   } else if (!strcmp(aTopic, NS_PRIVATE_BROWSING_SWITCH_TOPIC)) {
    1389              15 :     if (NS_LITERAL_STRING(NS_PRIVATE_BROWSING_ENTER).Equals(aData)) {
    1390               8 :       NS_ASSERTION(mDefaultDBState, "don't have a default state");
    1391               8 :       NS_ASSERTION(mDBState == mDefaultDBState, "not in default state");
    1392               8 :       NS_ASSERTION(!mPrivateDBState, "already have a private state");
    1393                 : 
    1394                 :       // Create a new DBState, and swap it in.
    1395               8 :       mPrivateDBState = new DBState();
    1396               8 :       mDBState = mPrivateDBState;
    1397                 : 
    1398               7 :     } else if (NS_LITERAL_STRING(NS_PRIVATE_BROWSING_LEAVE).Equals(aData)) {
    1399               7 :       NS_ASSERTION(mDefaultDBState, "don't have a default state");
    1400               7 :       NS_ASSERTION(mDBState == mPrivateDBState, "not in private state");
    1401               7 :       NS_ASSERTION(!mPrivateDBState->dbConn, "private DB connection not null");
    1402                 : 
    1403                 :       // Clear the private DBState, and restore the default one.
    1404               7 :       mPrivateDBState = NULL;
    1405               7 :       mDBState = mDefaultDBState;
    1406                 :     }
    1407                 : 
    1408              15 :     NotifyChanged(nsnull, NS_LITERAL_STRING("reload").get());
    1409                 :   }
    1410                 : 
    1411             314 :   return NS_OK;
    1412                 : }
    1413                 : 
    1414                 : NS_IMETHODIMP
    1415              16 : nsCookieService::GetCookieString(nsIURI     *aHostURI,
    1416                 :                                  nsIChannel *aChannel,
    1417                 :                                  char       **aCookie)
    1418                 : {
    1419              16 :   return GetCookieStringCommon(aHostURI, aChannel, false, aCookie);
    1420                 : }
    1421                 : 
    1422                 : NS_IMETHODIMP
    1423            3586 : nsCookieService::GetCookieStringFromHttp(nsIURI     *aHostURI,
    1424                 :                                          nsIURI     *aFirstURI,
    1425                 :                                          nsIChannel *aChannel,
    1426                 :                                          char       **aCookie)
    1427                 : {
    1428            3586 :   return GetCookieStringCommon(aHostURI, aChannel, true, aCookie);
    1429                 : }
    1430                 : 
    1431                 : nsresult
    1432            3602 : nsCookieService::GetCookieStringCommon(nsIURI *aHostURI,
    1433                 :                                        nsIChannel *aChannel,
    1434                 :                                        bool aHttpBound,
    1435                 :                                        char** aCookie)
    1436                 : {
    1437            3602 :   NS_ENSURE_ARG(aHostURI);
    1438            3602 :   NS_ENSURE_ARG(aCookie);
    1439                 : 
    1440                 :   // Determine whether the request is foreign. Failure is acceptable.
    1441            3602 :   bool isForeign = true;
    1442            3602 :   if (RequireThirdPartyCheck())
    1443               2 :     mThirdPartyUtil->IsThirdPartyChannel(aChannel, aHostURI, &isForeign);
    1444                 : 
    1445            7204 :   nsCAutoString result;
    1446            3602 :   GetCookieStringInternal(aHostURI, isForeign, aHttpBound, result);
    1447            3602 :   *aCookie = result.IsEmpty() ? nsnull : ToNewCString(result);
    1448            3602 :   return NS_OK;
    1449                 : }
    1450                 : 
    1451                 : NS_IMETHODIMP
    1452           15075 : nsCookieService::SetCookieString(nsIURI     *aHostURI,
    1453                 :                                  nsIPrompt  *aPrompt,
    1454                 :                                  const char *aCookieHeader,
    1455                 :                                  nsIChannel *aChannel)
    1456                 : {
    1457           15075 :   return SetCookieStringCommon(aHostURI, aCookieHeader, NULL, aChannel, false);
    1458                 : }
    1459                 : 
    1460                 : NS_IMETHODIMP
    1461             163 : nsCookieService::SetCookieStringFromHttp(nsIURI     *aHostURI,
    1462                 :                                          nsIURI     *aFirstURI,
    1463                 :                                          nsIPrompt  *aPrompt,
    1464                 :                                          const char *aCookieHeader,
    1465                 :                                          const char *aServerTime,
    1466                 :                                          nsIChannel *aChannel) 
    1467                 : {
    1468                 :   return SetCookieStringCommon(aHostURI, aCookieHeader, aServerTime, aChannel,
    1469             163 :                                true);
    1470                 : }
    1471                 : 
    1472                 : nsresult
    1473           15238 : nsCookieService::SetCookieStringCommon(nsIURI *aHostURI,
    1474                 :                                        const char *aCookieHeader,
    1475                 :                                        const char *aServerTime,
    1476                 :                                        nsIChannel *aChannel,
    1477                 :                                        bool aFromHttp) 
    1478                 : {
    1479           15238 :   NS_ENSURE_ARG(aHostURI);
    1480           15238 :   NS_ENSURE_ARG(aCookieHeader);
    1481                 : 
    1482                 :   // Determine whether the request is foreign. Failure is acceptable.
    1483           15238 :   bool isForeign = true;
    1484           15238 :   if (RequireThirdPartyCheck())
    1485              25 :     mThirdPartyUtil->IsThirdPartyChannel(aChannel, aHostURI, &isForeign);
    1486                 : 
    1487           30476 :   nsDependentCString cookieString(aCookieHeader);
    1488           30476 :   nsDependentCString serverTime(aServerTime ? aServerTime : "");
    1489                 :   SetCookieStringInternal(aHostURI, isForeign, cookieString,
    1490           15238 :                           serverTime, aFromHttp);
    1491           15238 :   return NS_OK;
    1492                 : }
    1493                 : 
    1494                 : void
    1495           15238 : nsCookieService::SetCookieStringInternal(nsIURI             *aHostURI,
    1496                 :                                          bool                aIsForeign,
    1497                 :                                          nsDependentCString &aCookieHeader,
    1498                 :                                          const nsCString    &aServerTime,
    1499                 :                                          bool                aFromHttp)
    1500                 : {
    1501           15238 :   NS_ASSERTION(aHostURI, "null host!");
    1502                 : 
    1503           15238 :   if (!mDBState) {
    1504               2 :     NS_WARNING("No DBState! Profile already closed?");
    1505               2 :     return;
    1506                 :   }
    1507                 : 
    1508                 :   // get the base domain for the host URI.
    1509                 :   // e.g. for "www.bbc.co.uk", this would be "bbc.co.uk".
    1510                 :   // file:// URI's (i.e. with an empty host) are allowed, but any other
    1511                 :   // scheme must have a non-empty host. A trailing dot in the host
    1512                 :   // is acceptable.
    1513                 :   bool requireHostMatch;
    1514           30472 :   nsCAutoString baseDomain;
    1515           15236 :   nsresult rv = GetBaseDomain(aHostURI, baseDomain, requireHostMatch);
    1516           15236 :   if (NS_FAILED(rv)) {
    1517               2 :     COOKIE_LOGFAILURE(SET_COOKIE, aHostURI, aCookieHeader, 
    1518               2 :                       "couldn't get base domain from URI");
    1519                 :     return;
    1520                 :   }
    1521                 : 
    1522                 :   // check default prefs
    1523                 :   CookieStatus cookieStatus = CheckPrefs(aHostURI, aIsForeign, baseDomain,
    1524           15234 :                                          requireHostMatch, aCookieHeader.get());
    1525                 :   // fire a notification if cookie was rejected (but not if there was an error)
    1526           15234 :   switch (cookieStatus) {
    1527                 :   case STATUS_REJECTED:
    1528              14 :     NotifyRejected(aHostURI);
    1529                 :     return;
    1530                 :   case STATUS_REJECTED_WITH_ERROR:
    1531                 :     return;
    1532                 :   default:
    1533                 :     break;
    1534                 :   }
    1535                 : 
    1536                 :   // parse server local time. this is not just done here for efficiency
    1537                 :   // reasons - if there's an error parsing it, and we need to default it
    1538                 :   // to the current time, we must do it here since the current time in
    1539                 :   // SetCookieInternal() will change for each cookie processed (e.g. if the
    1540                 :   // user is prompted).
    1541                 :   PRTime tempServerTime;
    1542                 :   PRInt64 serverTime;
    1543                 :   PRStatus result = PR_ParseTimeString(aServerTime.get(), true,
    1544           15220 :                                        &tempServerTime);
    1545           15220 :   if (result == PR_SUCCESS) {
    1546               2 :     serverTime = tempServerTime / PRInt64(PR_USEC_PER_SEC);
    1547                 :   } else {
    1548           15218 :     serverTime = PR_Now() / PR_USEC_PER_SEC;
    1549                 :   }
    1550                 : 
    1551                 :   // process each cookie in the header
    1552           30453 :   while (SetCookieInternal(aHostURI, baseDomain, requireHostMatch, cookieStatus,
    1553           15233 :                            aCookieHeader, serverTime, aFromHttp)) {
    1554                 :     // document.cookie can only set one cookie at a time
    1555              14 :     if (!aFromHttp)
    1556               1 :       break;
    1557                 :   }
    1558                 : }
    1559                 : 
    1560                 : // notify observers that a cookie was rejected due to the users' prefs.
    1561                 : void
    1562              15 : nsCookieService::NotifyRejected(nsIURI *aHostURI)
    1563                 : {
    1564              15 :   if (mObserverService)
    1565              15 :     mObserverService->NotifyObservers(aHostURI, "cookie-rejected", nsnull);
    1566              15 : }
    1567                 : 
    1568                 : // notify observers that the cookie list changed. there are five possible
    1569                 : // values for aData:
    1570                 : // "deleted" means a cookie was deleted. aSubject is the deleted cookie.
    1571                 : // "added"   means a cookie was added. aSubject is the added cookie.
    1572                 : // "changed" means a cookie was altered. aSubject is the new cookie.
    1573                 : // "cleared" means the entire cookie list was cleared. aSubject is null.
    1574                 : // "batch-deleted" means a set of cookies was purged. aSubject is the list of
    1575                 : // cookies.
    1576                 : void
    1577           16563 : nsCookieService::NotifyChanged(nsISupports     *aSubject,
    1578                 :                                const PRUnichar *aData)
    1579                 : {
    1580           16563 :   if (mObserverService)
    1581           16563 :     mObserverService->NotifyObservers(aSubject, "cookie-changed", aData);
    1582           16563 : }
    1583                 : 
    1584                 : already_AddRefed<nsIArray>
    1585              71 : nsCookieService::CreatePurgeList(nsICookie2* aCookie)
    1586                 : {
    1587                 :   nsCOMPtr<nsIMutableArray> removedList =
    1588             142 :     do_CreateInstance(NS_ARRAY_CONTRACTID);
    1589              71 :   removedList->AppendElement(aCookie, false);
    1590              71 :   return removedList.forget();
    1591                 : }
    1592                 : 
    1593                 : /******************************************************************************
    1594                 :  * nsCookieService:
    1595                 :  * pref observer impl
    1596                 :  ******************************************************************************/
    1597                 : 
    1598                 : void
    1599             300 : nsCookieService::PrefChanged(nsIPrefBranch *aPrefBranch)
    1600                 : {
    1601                 :   PRInt32 val;
    1602             300 :   if (NS_SUCCEEDED(aPrefBranch->GetIntPref(kPrefCookieBehavior, &val)))
    1603             300 :     mCookieBehavior = (PRUint8) LIMIT(val, 0, 2, 0);
    1604                 : 
    1605             300 :   if (NS_SUCCEEDED(aPrefBranch->GetIntPref(kPrefMaxNumberOfCookies, &val)))
    1606               1 :     mMaxNumberOfCookies = (PRUint16) LIMIT(val, 1, 0xFFFF, kMaxNumberOfCookies);
    1607                 : 
    1608             300 :   if (NS_SUCCEEDED(aPrefBranch->GetIntPref(kPrefMaxCookiesPerHost, &val)))
    1609               3 :     mMaxCookiesPerHost = (PRUint16) LIMIT(val, 1, 0xFFFF, kMaxCookiesPerHost);
    1610                 : 
    1611             300 :   if (NS_SUCCEEDED(aPrefBranch->GetIntPref(kPrefCookiePurgeAge, &val))) {
    1612                 :     mCookiePurgeAge =
    1613               1 :       PRInt64(LIMIT(val, 0, PR_INT32_MAX, PR_INT32_MAX)) * PR_USEC_PER_SEC;
    1614                 :   }
    1615                 : 
    1616                 :   bool boolval;
    1617             300 :   if (NS_SUCCEEDED(aPrefBranch->GetBoolPref(kPrefThirdPartySession, &boolval)))
    1618             300 :     mThirdPartySession = boolval;
    1619                 : 
    1620                 :   // Lazily instantiate the third party service if necessary.
    1621             300 :   if (!mThirdPartyUtil && RequireThirdPartyCheck()) {
    1622               3 :     mThirdPartyUtil = do_GetService(THIRDPARTYUTIL_CONTRACTID);
    1623               3 :     NS_ABORT_IF_FALSE(mThirdPartyUtil, "require ThirdPartyUtil service");
    1624                 :   }
    1625             300 : }
    1626                 : 
    1627                 : /******************************************************************************
    1628                 :  * nsICookieManager impl:
    1629                 :  * nsICookieManager
    1630                 :  ******************************************************************************/
    1631                 : 
    1632                 : NS_IMETHODIMP
    1633              45 : nsCookieService::RemoveAll()
    1634                 : {
    1635              45 :   if (!mDBState) {
    1636               1 :     NS_WARNING("No DBState! Profile already closed?");
    1637               1 :     return NS_ERROR_NOT_AVAILABLE;
    1638                 :   }
    1639                 : 
    1640              44 :   RemoveAllFromMemory();
    1641                 : 
    1642                 :   // clear the cookie file
    1643              44 :   if (mDBState->dbConn) {
    1644               9 :     NS_ASSERTION(mDBState == mDefaultDBState, "not in default DB state");
    1645                 : 
    1646                 :     // Cancel any pending read. No further results will be received by our
    1647                 :     // read listener.
    1648               9 :     if (mDefaultDBState->pendingRead) {
    1649               2 :       CancelAsyncRead(true);
    1650                 :     }
    1651                 : 
    1652              18 :     nsCOMPtr<mozIStorageAsyncStatement> stmt;
    1653              18 :     nsresult rv = mDefaultDBState->dbConn->CreateAsyncStatement(NS_LITERAL_CSTRING(
    1654              18 :       "DELETE FROM moz_cookies"), getter_AddRefs(stmt));
    1655               9 :     if (NS_SUCCEEDED(rv)) {
    1656              18 :       nsCOMPtr<mozIStoragePendingStatement> handle;
    1657              18 :       rv = stmt->ExecuteAsync(mDefaultDBState->removeListener,
    1658              18 :         getter_AddRefs(handle));
    1659               9 :       NS_ASSERT_SUCCESS(rv);
    1660                 :     } else {
    1661                 :       // Recreate the database.
    1662               0 :       COOKIE_LOGSTRING(PR_LOG_DEBUG,
    1663                 :         ("RemoveAll(): corruption detected with rv 0x%x", rv));
    1664               0 :       HandleCorruptDB(mDefaultDBState);
    1665                 :     }
    1666                 :   }
    1667                 : 
    1668              44 :   NotifyChanged(nsnull, NS_LITERAL_STRING("cleared").get());
    1669              44 :   return NS_OK;
    1670                 : }
    1671                 : 
    1672                 : static PLDHashOperator
    1673           15082 : COMArrayCallback(nsCookieEntry *aEntry,
    1674                 :                  void          *aArg)
    1675                 : {
    1676           15082 :   nsCOMArray<nsICookie> *data = static_cast<nsCOMArray<nsICookie> *>(aArg);
    1677                 : 
    1678           15082 :   const nsCookieEntry::ArrayType &cookies = aEntry->GetCookies();
    1679           31423 :   for (nsCookieEntry::IndexType i = 0; i < cookies.Length(); ++i) {
    1680           16341 :     data->AppendObject(cookies[i]);
    1681                 :   }
    1682                 : 
    1683           15082 :   return PL_DHASH_NEXT;
    1684                 : }
    1685                 : 
    1686                 : NS_IMETHODIMP
    1687              57 : nsCookieService::GetEnumerator(nsISimpleEnumerator **aEnumerator)
    1688                 : {
    1689              57 :   if (!mDBState) {
    1690               1 :     NS_WARNING("No DBState! Profile already closed?");
    1691               1 :     return NS_ERROR_NOT_AVAILABLE;
    1692                 :   }
    1693                 : 
    1694              56 :   EnsureReadComplete();
    1695                 : 
    1696             112 :   nsCOMArray<nsICookie> cookieList(mDBState->cookieCount);
    1697              56 :   mDBState->hostTable.EnumerateEntries(COMArrayCallback, &cookieList);
    1698                 : 
    1699              56 :   return NS_NewArrayEnumerator(aEnumerator, cookieList);
    1700                 : }
    1701                 : 
    1702                 : NS_IMETHODIMP
    1703            1019 : nsCookieService::Add(const nsACString &aHost,
    1704                 :                      const nsACString &aPath,
    1705                 :                      const nsACString &aName,
    1706                 :                      const nsACString &aValue,
    1707                 :                      bool              aIsSecure,
    1708                 :                      bool              aIsHttpOnly,
    1709                 :                      bool              aIsSession,
    1710                 :                      PRInt64           aExpiry)
    1711                 : {
    1712            1019 :   if (!mDBState) {
    1713               1 :     NS_WARNING("No DBState! Profile already closed?");
    1714               1 :     return NS_ERROR_NOT_AVAILABLE;
    1715                 :   }
    1716                 : 
    1717                 :   // first, normalize the hostname, and fail if it contains illegal characters.
    1718            2036 :   nsCAutoString host(aHost);
    1719            1018 :   nsresult rv = NormalizeHost(host);
    1720            1018 :   NS_ENSURE_SUCCESS(rv, rv);
    1721                 : 
    1722                 :   // get the base domain for the host URI.
    1723                 :   // e.g. for "www.bbc.co.uk", this would be "bbc.co.uk".
    1724            2036 :   nsCAutoString baseDomain;
    1725            1018 :   rv = GetBaseDomainFromHost(host, baseDomain);
    1726            1018 :   NS_ENSURE_SUCCESS(rv, rv);
    1727                 : 
    1728            1017 :   PRInt64 currentTimeInUsec = PR_Now();
    1729                 : 
    1730                 :   nsRefPtr<nsCookie> cookie =
    1731                 :     nsCookie::Create(aName, aValue, host, aPath,
    1732                 :                      aExpiry,
    1733                 :                      currentTimeInUsec,
    1734                 :                      nsCookie::GenerateUniqueCreationTime(currentTimeInUsec),
    1735                 :                      aIsSession,
    1736                 :                      aIsSecure,
    1737            2034 :                      aIsHttpOnly);
    1738            1017 :   if (!cookie) {
    1739               0 :     return NS_ERROR_OUT_OF_MEMORY;
    1740                 :   }
    1741                 : 
    1742            1017 :   AddInternal(baseDomain, cookie, currentTimeInUsec, nsnull, nsnull, true);
    1743            1017 :   return NS_OK;
    1744                 : }
    1745                 : 
    1746                 : NS_IMETHODIMP
    1747             212 : nsCookieService::Remove(const nsACString &aHost,
    1748                 :                         const nsACString &aName,
    1749                 :                         const nsACString &aPath,
    1750                 :                         bool             aBlocked)
    1751                 : {
    1752             212 :   if (!mDBState) {
    1753               1 :     NS_WARNING("No DBState! Profile already closed?");
    1754               1 :     return NS_ERROR_NOT_AVAILABLE;
    1755                 :   }
    1756                 : 
    1757                 :   // first, normalize the hostname, and fail if it contains illegal characters.
    1758             422 :   nsCAutoString host(aHost);
    1759             211 :   nsresult rv = NormalizeHost(host);
    1760             211 :   NS_ENSURE_SUCCESS(rv, rv);
    1761                 : 
    1762             422 :   nsCAutoString baseDomain;
    1763             211 :   rv = GetBaseDomainFromHost(host, baseDomain);
    1764             211 :   NS_ENSURE_SUCCESS(rv, rv);
    1765                 : 
    1766             210 :   nsListIter matchIter;
    1767             420 :   nsRefPtr<nsCookie> cookie;
    1768             420 :   if (FindCookie(baseDomain,
    1769                 :                  host,
    1770             210 :                  PromiseFlatCString(aName),
    1771             210 :                  PromiseFlatCString(aPath),
    1772             210 :                  matchIter)) {
    1773             208 :     cookie = matchIter.Cookie();
    1774             208 :     RemoveCookieFromList(matchIter);
    1775                 :   }
    1776                 : 
    1777                 :   // check if we need to add the host to the permissions blacklist.
    1778             210 :   if (aBlocked && mPermissionService) {
    1779                 :     // strip off the domain dot, if necessary
    1780               1 :     if (!host.IsEmpty() && host.First() == '.')
    1781               0 :       host.Cut(0, 1);
    1782                 : 
    1783               1 :     host.Insert(NS_LITERAL_CSTRING("http://"), 0);
    1784                 : 
    1785               2 :     nsCOMPtr<nsIURI> uri;
    1786               1 :     NS_NewURI(getter_AddRefs(uri), host);
    1787                 : 
    1788               1 :     if (uri)
    1789               1 :       mPermissionService->SetAccess(uri, nsICookiePermission::ACCESS_DENY);
    1790                 :   }
    1791                 : 
    1792             210 :   if (cookie) {
    1793                 :     // Everything's done. Notify observers.
    1794             208 :     NotifyChanged(cookie, NS_LITERAL_STRING("deleted").get());
    1795                 :   }
    1796                 : 
    1797             210 :   return NS_OK;
    1798                 : }
    1799                 : 
    1800                 : /******************************************************************************
    1801                 :  * nsCookieService impl:
    1802                 :  * private file I/O functions
    1803                 :  ******************************************************************************/
    1804                 : 
    1805                 : // Begin an asynchronous read from the database.
    1806                 : OpenDBResult
    1807              40 : nsCookieService::Read()
    1808                 : {
    1809                 :   // Set up a statement for the read. Note that our query specifies that
    1810                 :   // 'baseDomain' not be NULL -- see below for why.
    1811              80 :   nsCOMPtr<mozIStorageAsyncStatement> stmtRead;
    1812              80 :   nsresult rv = mDefaultDBState->dbConn->CreateAsyncStatement(NS_LITERAL_CSTRING(
    1813                 :     "SELECT "
    1814                 :       "name, "
    1815                 :       "value, "
    1816                 :       "host, "
    1817                 :       "path, "
    1818                 :       "expiry, "
    1819                 :       "lastAccessed, "
    1820                 :       "creationTime, "
    1821                 :       "isSecure, "
    1822                 :       "isHttpOnly, "
    1823                 :       "baseDomain "
    1824                 :     "FROM moz_cookies "
    1825              80 :     "WHERE baseDomain NOTNULL"), getter_AddRefs(stmtRead));
    1826              40 :   NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
    1827                 : 
    1828                 :   // Set up a statement to delete any rows with a NULL 'baseDomain'
    1829                 :   // column. This takes care of any cookies set by browsers that don't
    1830                 :   // understand the 'baseDomain' column, where the database schema version
    1831                 :   // is from one that does. (This would occur when downgrading.)
    1832              80 :   nsCOMPtr<mozIStorageAsyncStatement> stmtDeleteNull;
    1833              80 :   rv = mDefaultDBState->dbConn->CreateAsyncStatement(NS_LITERAL_CSTRING(
    1834                 :     "DELETE FROM moz_cookies WHERE baseDomain ISNULL"),
    1835              80 :     getter_AddRefs(stmtDeleteNull));
    1836              40 :   NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
    1837                 : 
    1838                 :   // Start a new connection for sync reads, to reduce contention with the
    1839                 :   // background thread. We need to do this before we kick off write statements,
    1840                 :   // since they can lock the database and prevent connections from being opened.
    1841              80 :   rv = mStorageService->OpenUnsharedDatabase(mDefaultDBState->cookieFile,
    1842              80 :     getter_AddRefs(mDefaultDBState->syncConn));
    1843              40 :   NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
    1844                 : 
    1845                 :   // Init our readSet hash and execute the statements. Note that, after this
    1846                 :   // point, we cannot fail without altering the cleanup code in InitDBStates()
    1847                 :   // to handle closing of the now-asynchronous connection.
    1848              40 :   mDefaultDBState->readSet.Init();
    1849              40 :   mDefaultDBState->hostArray.SetCapacity(kMaxNumberOfCookies);
    1850                 : 
    1851              80 :   mDefaultDBState->readListener = new ReadCookieDBListener(mDefaultDBState);
    1852              80 :   rv = stmtRead->ExecuteAsync(mDefaultDBState->readListener,
    1853              80 :     getter_AddRefs(mDefaultDBState->pendingRead));
    1854              40 :   NS_ASSERT_SUCCESS(rv);
    1855                 : 
    1856              80 :   nsCOMPtr<mozIStoragePendingStatement> handle;
    1857              80 :   rv = stmtDeleteNull->ExecuteAsync(mDefaultDBState->removeListener,
    1858              80 :     getter_AddRefs(handle));
    1859              40 :   NS_ASSERT_SUCCESS(rv);
    1860                 : 
    1861              40 :   return RESULT_OK;
    1862                 : }
    1863                 : 
    1864                 : // Extract data from a single result row and create an nsCookie.
    1865                 : // This is templated since 'T' is different for sync vs async results.
    1866                 : template<class T> nsCookie*
    1867           14414 : nsCookieService::GetCookieFromRow(T &aRow)
    1868                 : {
    1869                 :   // Skip reading 'baseDomain' -- up to the caller.
    1870           51466 :   nsCString name, value, host, path;
    1871           25733 :   nsresult rv = aRow->GetUTF8String(0, name);
    1872           25733 :   NS_ASSERT_SUCCESS(rv);
    1873           25733 :   rv = aRow->GetUTF8String(1, value);
    1874           25733 :   NS_ASSERT_SUCCESS(rv);
    1875           25733 :   rv = aRow->GetUTF8String(2, host);
    1876           25733 :   NS_ASSERT_SUCCESS(rv);
    1877           25733 :   rv = aRow->GetUTF8String(3, path);
    1878           25733 :   NS_ASSERT_SUCCESS(rv);
    1879                 : 
    1880           25733 :   PRInt64 expiry = aRow->AsInt64(4);
    1881           25733 :   PRInt64 lastAccessed = aRow->AsInt64(5);
    1882           25733 :   PRInt64 creationTime = aRow->AsInt64(6);
    1883           25733 :   bool isSecure = 0 != aRow->AsInt32(7);
    1884           25733 :   bool isHttpOnly = 0 != aRow->AsInt32(8);
    1885                 : 
    1886                 :   // Create a new nsCookie and assign the data.
    1887                 :   return nsCookie::Create(name, value, host, path,
    1888                 :                           expiry,
    1889                 :                           lastAccessed,
    1890                 :                           creationTime,
    1891                 :                           false,
    1892                 :                           isSecure,
    1893           25733 :                           isHttpOnly);
    1894                 : }
    1895                 : 
    1896                 : void
    1897               8 : nsCookieService::AsyncReadComplete()
    1898                 : {
    1899                 :   // We may be in the private browsing DB state, with a pending read on the
    1900                 :   // default DB state. (This would occur if we started up in private browsing
    1901                 :   // mode.) As long as we do all our operations on the default state, we're OK.
    1902               8 :   NS_ASSERTION(mDefaultDBState, "no default DBState");
    1903               8 :   NS_ASSERTION(mDefaultDBState->pendingRead, "no pending read");
    1904               8 :   NS_ASSERTION(mDefaultDBState->readListener, "no read listener");
    1905                 : 
    1906                 :   // Merge the data read on the background thread with the data synchronously
    1907                 :   // read on the main thread. Note that transactions on the cookie table may
    1908                 :   // have occurred on the main thread since, making the background data stale.
    1909            2912 :   for (PRUint32 i = 0; i < mDefaultDBState->hostArray.Length(); ++i) {
    1910            2904 :     const CookieDomainTuple &tuple = mDefaultDBState->hostArray[i];
    1911                 : 
    1912                 :     // Tiebreak: if the given base domain has already been read in, ignore
    1913                 :     // the background data. Note that readSet may contain domains that were
    1914                 :     // queried but found not to be in the db -- that's harmless.
    1915            2904 :     if (mDefaultDBState->readSet.GetEntry(tuple.baseDomain))
    1916              21 :       continue;
    1917                 : 
    1918                 :     AddCookieToList(tuple.baseDomain, tuple.cookie, mDefaultDBState, NULL,
    1919            2883 :       false);
    1920                 :   }
    1921                 : 
    1922               8 :   mDefaultDBState->stmtReadDomain = nsnull;
    1923               8 :   mDefaultDBState->pendingRead = nsnull;
    1924               8 :   mDefaultDBState->readListener = nsnull;
    1925               8 :   mDefaultDBState->syncConn = nsnull;
    1926               8 :   mDefaultDBState->hostArray.Clear();
    1927               8 :   mDefaultDBState->readSet.Clear();
    1928                 : 
    1929               8 :   COOKIE_LOGSTRING(PR_LOG_DEBUG, ("Read(): %ld cookies read",
    1930                 :                                   mDefaultDBState->cookieCount));
    1931                 : 
    1932               8 :   mObserverService->NotifyObservers(nsnull, "cookie-db-read", nsnull);
    1933               8 : }
    1934                 : 
    1935                 : void
    1936              32 : nsCookieService::CancelAsyncRead(bool aPurgeReadSet)
    1937                 : {
    1938                 :   // We may be in the private browsing DB state, with a pending read on the
    1939                 :   // default DB state. (This would occur if we started up in private browsing
    1940                 :   // mode.) As long as we do all our operations on the default state, we're OK.
    1941              32 :   NS_ASSERTION(mDefaultDBState, "no default DBState");
    1942              32 :   NS_ASSERTION(mDefaultDBState->pendingRead, "no pending read");
    1943              32 :   NS_ASSERTION(mDefaultDBState->readListener, "no read listener");
    1944                 : 
    1945                 :   // Cancel the pending read, kill the read listener, and empty the array
    1946                 :   // of data already read in on the background thread.
    1947              32 :   mDefaultDBState->readListener->Cancel();
    1948              64 :   mozilla::DebugOnly<nsresult> rv = mDefaultDBState->pendingRead->Cancel();
    1949              32 :   NS_ASSERT_SUCCESS(rv);
    1950                 : 
    1951              32 :   mDefaultDBState->stmtReadDomain = nsnull;
    1952              32 :   mDefaultDBState->pendingRead = nsnull;
    1953              32 :   mDefaultDBState->readListener = nsnull;
    1954              32 :   mDefaultDBState->hostArray.Clear();
    1955                 : 
    1956                 :   // Only clear the 'readSet' table if we no longer need to know what set of
    1957                 :   // data is already accounted for.
    1958              32 :   if (aPurgeReadSet)
    1959              15 :     mDefaultDBState->readSet.Clear();
    1960              32 : }
    1961                 : 
    1962                 : void
    1963           26161 : nsCookieService::EnsureReadDomain(const nsCString &aBaseDomain)
    1964                 : {
    1965           26161 :   NS_ASSERTION(!mDBState->dbConn || mDBState == mDefaultDBState,
    1966                 :     "not in default db state");
    1967                 : 
    1968                 :   // Fast path 1: nothing to read, or we've already finished reading.
    1969           26161 :   if (NS_LIKELY(!mDBState->dbConn || !mDefaultDBState->pendingRead))
    1970           25880 :     return;
    1971                 : 
    1972                 :   // Fast path 2: already read in this particular domain.
    1973             281 :   if (NS_LIKELY(mDefaultDBState->readSet.GetEntry(aBaseDomain)))
    1974              36 :     return;
    1975                 : 
    1976                 :   // Read in the data synchronously.
    1977                 :   nsresult rv;
    1978             245 :   if (!mDefaultDBState->stmtReadDomain) {
    1979                 :     // Cache the statement, since it's likely to be used again.
    1980              46 :     rv = mDefaultDBState->syncConn->CreateStatement(NS_LITERAL_CSTRING(
    1981                 :       "SELECT "
    1982                 :         "name, "
    1983                 :         "value, "
    1984                 :         "host, "
    1985                 :         "path, "
    1986                 :         "expiry, "
    1987                 :         "lastAccessed, "
    1988                 :         "creationTime, "
    1989                 :         "isSecure, "
    1990                 :         "isHttpOnly "
    1991                 :       "FROM moz_cookies "
    1992                 :       "WHERE baseDomain = :baseDomain"),
    1993              46 :       getter_AddRefs(mDefaultDBState->stmtReadDomain));
    1994                 : 
    1995              23 :     if (NS_FAILED(rv)) {
    1996                 :       // Recreate the database.
    1997               0 :       COOKIE_LOGSTRING(PR_LOG_DEBUG,
    1998                 :         ("EnsureReadDomain(): corruption detected when creating statement "
    1999                 :          "with rv 0x%x", rv));
    2000               0 :       HandleCorruptDB(mDefaultDBState);
    2001               0 :       return;
    2002                 :     }
    2003                 :   }
    2004                 : 
    2005             245 :   NS_ASSERTION(mDefaultDBState->syncConn, "should have a sync db connection");
    2006                 : 
    2007             490 :   mozStorageStatementScoper scoper(mDefaultDBState->stmtReadDomain);
    2008                 : 
    2009             245 :   rv = mDefaultDBState->stmtReadDomain->BindUTF8StringByName(
    2010             245 :     NS_LITERAL_CSTRING("baseDomain"), aBaseDomain);
    2011             245 :   NS_ASSERT_SUCCESS(rv);
    2012                 : 
    2013                 :   bool hasResult;
    2014             490 :   nsCString name, value, host, path;
    2015             490 :   nsAutoTArray<nsRefPtr<nsCookie>, kMaxCookiesPerHost> array;
    2016            2758 :   while (1) {
    2017            3003 :     rv = mDefaultDBState->stmtReadDomain->ExecuteStep(&hasResult);
    2018            3003 :     if (NS_FAILED(rv)) {
    2019                 :       // Recreate the database.
    2020               1 :       COOKIE_LOGSTRING(PR_LOG_DEBUG,
    2021                 :         ("EnsureReadDomain(): corruption detected when reading result "
    2022                 :          "with rv 0x%x", rv));
    2023               1 :       HandleCorruptDB(mDefaultDBState);
    2024                 :       return;
    2025                 :     }
    2026                 : 
    2027            3002 :     if (!hasResult)
    2028                 :       break;
    2029                 : 
    2030            2758 :     array.AppendElement(GetCookieFromRow(mDefaultDBState->stmtReadDomain));
    2031                 :   }
    2032                 : 
    2033                 :   // Add the cookies to the table in a single operation. This makes sure that
    2034                 :   // either all the cookies get added, or in the case of corruption, none.
    2035             623 :   for (PRUint32 i = 0; i < array.Length(); ++i) {
    2036             379 :     AddCookieToList(aBaseDomain, array[i], mDefaultDBState, NULL, false);
    2037                 :   }
    2038                 : 
    2039                 :   // Add it to the hashset of read entries, so we don't read it again.
    2040             244 :   mDefaultDBState->readSet.PutEntry(aBaseDomain);
    2041                 : 
    2042             244 :   COOKIE_LOGSTRING(PR_LOG_DEBUG,
    2043                 :     ("EnsureReadDomain(): %ld cookies read for base domain %s",
    2044                 :      array.Length(), aBaseDomain.get()));
    2045                 : }
    2046                 : 
    2047                 : void
    2048              60 : nsCookieService::EnsureReadComplete()
    2049                 : {
    2050              60 :   NS_ASSERTION(!mDBState->dbConn || mDBState == mDefaultDBState,
    2051                 :     "not in default db state");
    2052                 : 
    2053                 :   // Fast path 1: nothing to read, or we've already finished reading.
    2054              60 :   if (NS_LIKELY(!mDBState->dbConn || !mDefaultDBState->pendingRead))
    2055              43 :     return;
    2056                 : 
    2057                 :   // Cancel the pending read, so we don't get any more results.
    2058              17 :   CancelAsyncRead(false);
    2059                 : 
    2060                 :   // Read in the data synchronously.
    2061              34 :   nsCOMPtr<mozIStorageStatement> stmt;
    2062              34 :   nsresult rv = mDefaultDBState->syncConn->CreateStatement(NS_LITERAL_CSTRING(
    2063                 :     "SELECT "
    2064                 :       "name, "
    2065                 :       "value, "
    2066                 :       "host, "
    2067                 :       "path, "
    2068                 :       "expiry, "
    2069                 :       "lastAccessed, "
    2070                 :       "creationTime, "
    2071                 :       "isSecure, "
    2072                 :       "isHttpOnly, "
    2073                 :       "baseDomain "
    2074                 :     "FROM moz_cookies "
    2075              34 :     "WHERE baseDomain NOTNULL"), getter_AddRefs(stmt));
    2076                 : 
    2077              17 :   if (NS_FAILED(rv)) {
    2078                 :     // Recreate the database.
    2079               2 :     COOKIE_LOGSTRING(PR_LOG_DEBUG,
    2080                 :       ("EnsureReadComplete(): corruption detected when creating statement "
    2081                 :        "with rv 0x%x", rv));
    2082               2 :     HandleCorruptDB(mDefaultDBState);
    2083                 :     return;
    2084                 :   }
    2085                 : 
    2086              30 :   nsCString baseDomain, name, value, host, path;
    2087                 :   bool hasResult;
    2088              30 :   nsAutoTArray<CookieDomainTuple, kMaxNumberOfCookies> array;
    2089           11862 :   while (1) {
    2090           11877 :     rv = stmt->ExecuteStep(&hasResult);
    2091           11877 :     if (NS_FAILED(rv)) {
    2092                 :       // Recreate the database.
    2093               1 :       COOKIE_LOGSTRING(PR_LOG_DEBUG,
    2094                 :         ("EnsureReadComplete(): corruption detected when reading result "
    2095                 :          "with rv 0x%x", rv));
    2096               1 :       HandleCorruptDB(mDefaultDBState);
    2097                 :       return;
    2098                 :     }
    2099                 : 
    2100           11876 :     if (!hasResult)
    2101                 :       break;
    2102                 : 
    2103                 :     // Make sure we haven't already read the data.
    2104           11862 :     stmt->GetUTF8String(9, baseDomain);
    2105           11862 :     if (mDefaultDBState->readSet.GetEntry(baseDomain))
    2106             206 :       continue;
    2107                 : 
    2108           11656 :     CookieDomainTuple* tuple = array.AppendElement();
    2109           11656 :     tuple->baseDomain = baseDomain;
    2110           11656 :     tuple->cookie = GetCookieFromRow(stmt);
    2111                 :   }
    2112                 : 
    2113                 :   // Add the cookies to the table in a single operation. This makes sure that
    2114                 :   // either all the cookies get added, or in the case of corruption, none.
    2115            9281 :   for (PRUint32 i = 0; i < array.Length(); ++i) {
    2116            9267 :     CookieDomainTuple& tuple = array[i];
    2117                 :     AddCookieToList(tuple.baseDomain, tuple.cookie, mDefaultDBState, NULL,
    2118            9267 :       false);
    2119                 :   }
    2120                 : 
    2121              14 :   mDefaultDBState->syncConn = nsnull;
    2122              14 :   mDefaultDBState->readSet.Clear();
    2123                 : 
    2124              14 :   COOKIE_LOGSTRING(PR_LOG_DEBUG,
    2125                 :     ("EnsureReadComplete(): %ld cookies read", array.Length()));
    2126                 : }
    2127                 : 
    2128                 : NS_IMETHODIMP
    2129             195 : nsCookieService::ImportCookies(nsIFile *aCookieFile)
    2130                 : {
    2131             195 :   if (!mDBState) {
    2132               1 :     NS_WARNING("No DBState! Profile already closed?");
    2133               1 :     return NS_ERROR_NOT_AVAILABLE;
    2134                 :   }
    2135                 : 
    2136                 :   // Make sure we're in the default DB state. We don't want people importing
    2137                 :   // cookies into a private browsing session!
    2138             194 :   if (mDBState != mDefaultDBState) {
    2139               1 :     NS_WARNING("Trying to import cookies in a private browsing session!");
    2140               1 :     return NS_ERROR_NOT_AVAILABLE;
    2141                 :   }
    2142                 : 
    2143                 :   nsresult rv;
    2144             386 :   nsCOMPtr<nsIInputStream> fileInputStream;
    2145             193 :   rv = NS_NewLocalFileInputStream(getter_AddRefs(fileInputStream), aCookieFile);
    2146             193 :   if (NS_FAILED(rv)) return rv;
    2147                 : 
    2148               0 :   nsCOMPtr<nsILineInputStream> lineInputStream = do_QueryInterface(fileInputStream, &rv);
    2149               0 :   if (NS_FAILED(rv)) return rv;
    2150                 : 
    2151                 :   // First, ensure we've read in everything from the database, if we have one.
    2152               0 :   EnsureReadComplete();
    2153                 : 
    2154                 :   static const char kTrue[] = "TRUE";
    2155                 : 
    2156               0 :   nsCAutoString buffer, baseDomain;
    2157               0 :   bool isMore = true;
    2158                 :   PRInt32 hostIndex, isDomainIndex, pathIndex, secureIndex, expiresIndex, nameIndex, cookieIndex;
    2159                 :   nsASingleFragmentCString::char_iterator iter;
    2160                 :   PRInt32 numInts;
    2161                 :   PRInt64 expires;
    2162               0 :   bool isDomain, isHttpOnly = false;
    2163               0 :   PRUint32 originalCookieCount = mDefaultDBState->cookieCount;
    2164                 : 
    2165               0 :   PRInt64 currentTimeInUsec = PR_Now();
    2166               0 :   PRInt64 currentTime = currentTimeInUsec / PR_USEC_PER_SEC;
    2167                 :   // we use lastAccessedCounter to keep cookies in recently-used order,
    2168                 :   // so we start by initializing to currentTime (somewhat arbitrary)
    2169               0 :   PRInt64 lastAccessedCounter = currentTimeInUsec;
    2170                 : 
    2171                 :   /* file format is:
    2172                 :    *
    2173                 :    * host \t isDomain \t path \t secure \t expires \t name \t cookie
    2174                 :    *
    2175                 :    * if this format isn't respected we move onto the next line in the file.
    2176                 :    * isDomain is "TRUE" or "FALSE" (default to "FALSE")
    2177                 :    * isSecure is "TRUE" or "FALSE" (default to "TRUE")
    2178                 :    * expires is a PRInt64 integer
    2179                 :    * note 1: cookie can contain tabs.
    2180                 :    * note 2: cookies will be stored in order of lastAccessed time:
    2181                 :    *         most-recently used come first; least-recently-used come last.
    2182                 :    */
    2183                 : 
    2184                 :   /*
    2185                 :    * ...but due to bug 178933, we hide HttpOnly cookies from older code
    2186                 :    * in a comment, so they don't expose HttpOnly cookies to JS.
    2187                 :    *
    2188                 :    * The format for HttpOnly cookies is
    2189                 :    *
    2190                 :    * #HttpOnly_host \t isDomain \t path \t secure \t expires \t name \t cookie
    2191                 :    *
    2192                 :    */
    2193                 : 
    2194                 :   // We will likely be adding a bunch of cookies to the DB, so we use async
    2195                 :   // batching with storage to make this super fast.
    2196               0 :   nsCOMPtr<mozIStorageBindingParamsArray> paramsArray;
    2197               0 :   if (originalCookieCount == 0 && mDefaultDBState->dbConn) {
    2198               0 :     mDefaultDBState->stmtInsert->NewBindingParamsArray(getter_AddRefs(paramsArray));
    2199                 :   }
    2200                 : 
    2201               0 :   while (isMore && NS_SUCCEEDED(lineInputStream->ReadLine(buffer, &isMore))) {
    2202               0 :     if (StringBeginsWith(buffer, NS_LITERAL_CSTRING(kHttpOnlyPrefix))) {
    2203               0 :       isHttpOnly = true;
    2204               0 :       hostIndex = sizeof(kHttpOnlyPrefix) - 1;
    2205               0 :     } else if (buffer.IsEmpty() || buffer.First() == '#') {
    2206               0 :       continue;
    2207                 :     } else {
    2208               0 :       isHttpOnly = false;
    2209               0 :       hostIndex = 0;
    2210                 :     }
    2211                 : 
    2212                 :     // this is a cheap, cheesy way of parsing a tab-delimited line into
    2213                 :     // string indexes, which can be lopped off into substrings. just for
    2214                 :     // purposes of obfuscation, it also checks that each token was found.
    2215                 :     // todo: use iterators?
    2216               0 :     if ((isDomainIndex = buffer.FindChar('\t', hostIndex)     + 1) == 0 ||
    2217               0 :         (pathIndex     = buffer.FindChar('\t', isDomainIndex) + 1) == 0 ||
    2218               0 :         (secureIndex   = buffer.FindChar('\t', pathIndex)     + 1) == 0 ||
    2219               0 :         (expiresIndex  = buffer.FindChar('\t', secureIndex)   + 1) == 0 ||
    2220               0 :         (nameIndex     = buffer.FindChar('\t', expiresIndex)  + 1) == 0 ||
    2221               0 :         (cookieIndex   = buffer.FindChar('\t', nameIndex)     + 1) == 0) {
    2222               0 :       continue;
    2223                 :     }
    2224                 : 
    2225                 :     // check the expirytime first - if it's expired, ignore
    2226                 :     // nullstomp the trailing tab, to avoid copying the string
    2227               0 :     buffer.BeginWriting(iter);
    2228               0 :     *(iter += nameIndex - 1) = char(0);
    2229               0 :     numInts = PR_sscanf(buffer.get() + expiresIndex, "%lld", &expires);
    2230               0 :     if (numInts != 1 || expires < currentTime) {
    2231               0 :       continue;
    2232                 :     }
    2233                 : 
    2234               0 :     isDomain = Substring(buffer, isDomainIndex, pathIndex - isDomainIndex - 1).EqualsLiteral(kTrue);
    2235               0 :     const nsASingleFragmentCString &host = Substring(buffer, hostIndex, isDomainIndex - hostIndex - 1);
    2236                 :     // check for bad legacy cookies (domain not starting with a dot, or containing a port),
    2237                 :     // and discard
    2238               0 :     if ((isDomain && !host.IsEmpty() && host.First() != '.') ||
    2239               0 :         host.FindChar(':') != kNotFound) {
    2240               0 :       continue;
    2241                 :     }
    2242                 : 
    2243                 :     // compute the baseDomain from the host
    2244               0 :     rv = GetBaseDomainFromHost(host, baseDomain);
    2245               0 :     if (NS_FAILED(rv))
    2246               0 :       continue;
    2247                 : 
    2248                 :     // Create a new nsCookie and assign the data. We don't know the cookie
    2249                 :     // creation time, so just use the current time to generate a unique one.
    2250                 :     nsRefPtr<nsCookie> newCookie =
    2251               0 :       nsCookie::Create(Substring(buffer, nameIndex, cookieIndex - nameIndex - 1),
    2252               0 :                        Substring(buffer, cookieIndex, buffer.Length() - cookieIndex),
    2253                 :                        host,
    2254               0 :                        Substring(buffer, pathIndex, secureIndex - pathIndex - 1),
    2255                 :                        expires,
    2256                 :                        lastAccessedCounter,
    2257                 :                        nsCookie::GenerateUniqueCreationTime(currentTimeInUsec),
    2258                 :                        false,
    2259               0 :                        Substring(buffer, secureIndex, expiresIndex - secureIndex - 1).EqualsLiteral(kTrue),
    2260               0 :                        isHttpOnly);
    2261               0 :     if (!newCookie) {
    2262               0 :       return NS_ERROR_OUT_OF_MEMORY;
    2263                 :     }
    2264                 :     
    2265                 :     // trick: preserve the most-recently-used cookie ordering,
    2266                 :     // by successively decrementing the lastAccessed time
    2267               0 :     lastAccessedCounter--;
    2268                 : 
    2269               0 :     if (originalCookieCount == 0) {
    2270               0 :       AddCookieToList(baseDomain, newCookie, mDefaultDBState, paramsArray);
    2271                 :     }
    2272                 :     else {
    2273               0 :       AddInternal(baseDomain, newCookie, currentTimeInUsec, NULL, NULL, true);
    2274                 :     }
    2275                 :   }
    2276                 : 
    2277                 :   // If we need to write to disk, do so now.
    2278               0 :   if (paramsArray) {
    2279                 :     PRUint32 length;
    2280               0 :     paramsArray->GetLength(&length);
    2281               0 :     if (length) {
    2282               0 :       rv = mDefaultDBState->stmtInsert->BindParameters(paramsArray);
    2283               0 :       NS_ASSERT_SUCCESS(rv);
    2284               0 :       nsCOMPtr<mozIStoragePendingStatement> handle;
    2285               0 :       rv = mDefaultDBState->stmtInsert->ExecuteAsync(
    2286               0 :         mDefaultDBState->insertListener, getter_AddRefs(handle));
    2287               0 :       NS_ASSERT_SUCCESS(rv);
    2288                 :     }
    2289                 :   }
    2290                 : 
    2291                 : 
    2292               0 :   COOKIE_LOGSTRING(PR_LOG_DEBUG, ("ImportCookies(): %ld cookies imported",
    2293                 :     mDefaultDBState->cookieCount));
    2294                 : 
    2295               0 :   return NS_OK;
    2296                 : }
    2297                 : 
    2298                 : /******************************************************************************
    2299                 :  * nsCookieService impl:
    2300                 :  * private GetCookie/SetCookie helpers
    2301                 :  ******************************************************************************/
    2302                 : 
    2303                 : // helper function for GetCookieList
    2304             123 : static inline bool ispathdelimiter(char c) { return c == '/' || c == '?' || c == '#' || c == ';'; }
    2305                 : 
    2306                 : // Comparator class for sorting cookies before sending to a server.
    2307                 : class CompareCookiesForSending
    2308                 : {
    2309                 : public:
    2310              82 :   bool Equals(const nsCookie* aCookie1, const nsCookie* aCookie2) const
    2311                 :   {
    2312              82 :     return aCookie1->CreationTime() == aCookie2->CreationTime() &&
    2313             164 :            aCookie2->Path().Length() == aCookie1->Path().Length();
    2314                 :   }
    2315                 : 
    2316             181 :   bool LessThan(const nsCookie* aCookie1, const nsCookie* aCookie2) const
    2317                 :   {
    2318                 :     // compare by cookie path length in accordance with RFC2109
    2319             181 :     PRInt32 result = aCookie2->Path().Length() - aCookie1->Path().Length();
    2320             181 :     if (result != 0)
    2321              17 :       return result < 0;
    2322                 : 
    2323                 :     // when path lengths match, older cookies should be listed first.  this is
    2324                 :     // required for backwards compatibility since some websites erroneously
    2325                 :     // depend on receiving cookies in the order in which they were sent to the
    2326                 :     // browser!  see bug 236772.
    2327             164 :     return aCookie1->CreationTime() < aCookie2->CreationTime();
    2328                 :   }
    2329                 : };
    2330                 : 
    2331                 : void
    2332            3602 : nsCookieService::GetCookieStringInternal(nsIURI *aHostURI,
    2333                 :                                          bool aIsForeign,
    2334                 :                                          bool aHttpBound,
    2335                 :                                          nsCString &aCookieString)
    2336                 : {
    2337            3602 :   NS_ASSERTION(aHostURI, "null host!");
    2338                 : 
    2339            3602 :   if (!mDBState) {
    2340               3 :     NS_WARNING("No DBState! Profile already closed?");
    2341               3 :     return;
    2342                 :   }
    2343                 : 
    2344                 :   // get the base domain, host, and path from the URI.
    2345                 :   // e.g. for "www.bbc.co.uk", the base domain would be "bbc.co.uk".
    2346                 :   // file:// URI's (i.e. with an empty host) are allowed, but any other
    2347                 :   // scheme must have a non-empty host. A trailing dot in the host
    2348                 :   // is acceptable.
    2349                 :   bool requireHostMatch;
    2350            7198 :   nsCAutoString baseDomain, hostFromURI, pathFromURI;
    2351            3599 :   nsresult rv = GetBaseDomain(aHostURI, baseDomain, requireHostMatch);
    2352            3599 :   if (NS_SUCCEEDED(rv))
    2353            3597 :     rv = aHostURI->GetAsciiHost(hostFromURI);
    2354            3599 :   if (NS_SUCCEEDED(rv))
    2355            3597 :     rv = aHostURI->GetPath(pathFromURI);
    2356            3599 :   if (NS_FAILED(rv)) {
    2357               2 :     COOKIE_LOGFAILURE(GET_COOKIE, aHostURI, nsnull, "invalid host/path from URI");
    2358                 :     return;
    2359                 :   }
    2360                 : 
    2361                 :   // check default prefs
    2362                 :   CookieStatus cookieStatus = CheckPrefs(aHostURI, aIsForeign, baseDomain,
    2363            3597 :                                          requireHostMatch, nsnull);
    2364                 :   // for GetCookie(), we don't fire rejection notifications.
    2365            3597 :   switch (cookieStatus) {
    2366                 :   case STATUS_REJECTED:
    2367                 :   case STATUS_REJECTED_WITH_ERROR:
    2368                 :     return;
    2369                 :   default:
    2370                 :     break;
    2371                 :   }
    2372                 : 
    2373                 :   // check if aHostURI is using an https secure protocol.
    2374                 :   // if it isn't, then we can't send a secure cookie over the connection.
    2375                 :   // if SchemeIs fails, assume an insecure connection, to be on the safe side
    2376                 :   bool isSecure;
    2377            3597 :   if (NS_FAILED(aHostURI->SchemeIs("https", &isSecure))) {
    2378               0 :     isSecure = false;
    2379                 :   }
    2380                 : 
    2381                 :   nsCookie *cookie;
    2382            7194 :   nsAutoTArray<nsCookie*, 8> foundCookieList;
    2383            3597 :   PRInt64 currentTimeInUsec = PR_Now();
    2384            3597 :   PRInt64 currentTime = currentTimeInUsec / PR_USEC_PER_SEC;
    2385            3597 :   bool stale = false;
    2386                 : 
    2387            3597 :   EnsureReadDomain(baseDomain);
    2388                 : 
    2389                 :   // perform the hash lookup
    2390            3597 :   nsCookieEntry *entry = mDBState->hostTable.GetEntry(baseDomain);
    2391            3597 :   if (!entry)
    2392                 :     return;
    2393                 : 
    2394                 :   // iterate the cookies!
    2395              63 :   const nsCookieEntry::ArrayType &cookies = entry->GetCookies();
    2396             193 :   for (nsCookieEntry::IndexType i = 0; i < cookies.Length(); ++i) {
    2397             130 :     cookie = cookies[i];
    2398                 : 
    2399                 :     // check the host, since the base domain lookup is conservative.
    2400                 :     // first, check for an exact host or domain cookie match, e.g. "google.com"
    2401                 :     // or ".google.com"; second a subdomain match, e.g.
    2402                 :     // host = "mail.google.com", cookie domain = ".google.com".
    2403             264 :     if (cookie->RawHost() != hostFromURI &&
    2404             138 :         !(cookie->IsDomain() && StringEndsWith(hostFromURI, cookie->Host())))
    2405               0 :       continue;
    2406                 : 
    2407                 :     // if the cookie is secure and the host scheme isn't, we can't send it
    2408             130 :     if (cookie->IsSecure() && !isSecure)
    2409               0 :       continue;
    2410                 : 
    2411                 :     // if the cookie is httpOnly and it's not going directly to the HTTP
    2412                 :     // connection, don't send it
    2413             130 :     if (cookie->IsHttpOnly() && !aHttpBound)
    2414               3 :       continue;
    2415                 : 
    2416                 :     // calculate cookie path length, excluding trailing '/'
    2417             127 :     PRUint32 cookiePathLen = cookie->Path().Length();
    2418             127 :     if (cookiePathLen > 0 && cookie->Path().Last() == '/')
    2419             114 :       --cookiePathLen;
    2420                 : 
    2421                 :     // if the nsIURI path is shorter than the cookie path, don't send it back
    2422             127 :     if (!StringBeginsWith(pathFromURI, Substring(cookie->Path(), 0, cookiePathLen)))
    2423               1 :       continue;
    2424                 : 
    2425             249 :     if (pathFromURI.Length() > cookiePathLen &&
    2426             123 :         !ispathdelimiter(pathFromURI.CharAt(cookiePathLen))) {
    2427                 :       /*
    2428                 :        * |ispathdelimiter| tests four cases: '/', '?', '#', and ';'.
    2429                 :        * '/' is the "standard" case; the '?' test allows a site at host/abc?def
    2430                 :        * to receive a cookie that has a path attribute of abc.  this seems
    2431                 :        * strange but at least one major site (citibank, bug 156725) depends
    2432                 :        * on it.  The test for # and ; are put in to proactively avoid problems
    2433                 :        * with other sites - these are the only other chars allowed in the path.
    2434                 :        */
    2435               2 :       continue;
    2436                 :     }
    2437                 : 
    2438                 :     // check if the cookie has expired
    2439             124 :     if (cookie->Expiry() <= currentTime) {
    2440               0 :       continue;
    2441                 :     }
    2442                 : 
    2443                 :     // all checks passed - add to list and check if lastAccessed stamp needs updating
    2444             124 :     foundCookieList.AppendElement(cookie);
    2445             124 :     if (currentTimeInUsec - cookie->LastAccessed() > kCookieStaleThreshold)
    2446               0 :       stale = true;
    2447                 :   }
    2448                 : 
    2449              63 :   PRInt32 count = foundCookieList.Length();
    2450              63 :   if (count == 0)
    2451                 :     return;
    2452                 : 
    2453                 :   // update lastAccessed timestamps. we only do this if the timestamp is stale
    2454                 :   // by a certain amount, to avoid thrashing the db during pageload.
    2455              58 :   if (stale) {
    2456                 :     // Create an array of parameters to bind to our update statement. Batching
    2457                 :     // is OK here since we're updating cookies with no interleaved operations.
    2458               0 :     nsCOMPtr<mozIStorageBindingParamsArray> paramsArray;
    2459               0 :     mozIStorageAsyncStatement* stmt = mDBState->stmtUpdate;
    2460               0 :     if (mDBState->dbConn) {
    2461               0 :       stmt->NewBindingParamsArray(getter_AddRefs(paramsArray));
    2462                 :     }
    2463                 : 
    2464               0 :     for (PRInt32 i = 0; i < count; ++i) {
    2465               0 :       cookie = foundCookieList.ElementAt(i);
    2466                 : 
    2467               0 :       if (currentTimeInUsec - cookie->LastAccessed() > kCookieStaleThreshold)
    2468               0 :         UpdateCookieInList(cookie, currentTimeInUsec, paramsArray);
    2469                 :     }
    2470                 :     // Update the database now if necessary.
    2471               0 :     if (paramsArray) {
    2472                 :       PRUint32 length;
    2473               0 :       paramsArray->GetLength(&length);
    2474               0 :       if (length) {
    2475               0 :         nsresult rv = stmt->BindParameters(paramsArray);
    2476               0 :         NS_ASSERT_SUCCESS(rv);
    2477               0 :         nsCOMPtr<mozIStoragePendingStatement> handle;
    2478                 :         rv = stmt->ExecuteAsync(mDBState->updateListener,
    2479               0 :           getter_AddRefs(handle));
    2480               0 :         NS_ASSERT_SUCCESS(rv);
    2481                 :       }
    2482                 :     }
    2483                 :   }
    2484                 : 
    2485                 :   // return cookies in order of path length; longest to shortest.
    2486                 :   // this is required per RFC2109.  if cookies match in length,
    2487                 :   // then sort by creation time (see bug 236772).
    2488              58 :   foundCookieList.Sort(CompareCookiesForSending());
    2489                 : 
    2490             182 :   for (PRInt32 i = 0; i < count; ++i) {
    2491             124 :     cookie = foundCookieList.ElementAt(i);
    2492                 : 
    2493                 :     // check if we have anything to write
    2494             124 :     if (!cookie->Name().IsEmpty() || !cookie->Value().IsEmpty()) {
    2495                 :       // if we've already added a cookie to the return list, append a "; " so
    2496                 :       // that subsequent cookies are delimited in the final list.
    2497             122 :       if (!aCookieString.IsEmpty()) {
    2498              65 :         aCookieString.AppendLiteral("; ");
    2499                 :       }
    2500                 : 
    2501             122 :       if (!cookie->Name().IsEmpty()) {
    2502                 :         // we have a name and value - write both
    2503             117 :         aCookieString += cookie->Name() + NS_LITERAL_CSTRING("=") + cookie->Value();
    2504                 :       } else {
    2505                 :         // just write value
    2506               5 :         aCookieString += cookie->Value();
    2507                 :       }
    2508                 :     }
    2509                 :   }
    2510                 : 
    2511              58 :   if (!aCookieString.IsEmpty())
    2512              57 :     COOKIE_LOGSUCCESS(GET_COOKIE, aHostURI, aCookieString, nsnull, nsnull);
    2513                 : }
    2514                 : 
    2515                 : // processes a single cookie, and returns true if there are more cookies
    2516                 : // to be processed
    2517                 : bool
    2518           15233 : nsCookieService::SetCookieInternal(nsIURI                        *aHostURI,
    2519                 :                                    const nsCString               &aBaseDomain,
    2520                 :                                    bool                           aRequireHostMatch,
    2521                 :                                    CookieStatus                   aStatus,
    2522                 :                                    nsDependentCString            &aCookieHeader,
    2523                 :                                    PRInt64                        aServerTime,
    2524                 :                                    bool                           aFromHttp)
    2525                 : {
    2526           15233 :   NS_ASSERTION(aHostURI, "null host!");
    2527                 : 
    2528                 :   // create a stack-based nsCookieAttributes, to store all the
    2529                 :   // attributes parsed from the cookie
    2530           30466 :   nsCookieAttributes cookieAttributes;
    2531                 : 
    2532                 :   // init expiryTime such that session cookies won't prematurely expire
    2533           15233 :   cookieAttributes.expiryTime = LL_MAXINT;
    2534                 : 
    2535                 :   // aCookieHeader is an in/out param to point to the next cookie, if
    2536                 :   // there is one. Save the present value for logging purposes
    2537           30466 :   nsDependentCString savedCookieHeader(aCookieHeader);
    2538                 : 
    2539                 :   // newCookie says whether there are multiple cookies in the header;
    2540                 :   // so we can handle them separately.
    2541           15233 :   bool newCookie = ParseAttributes(aCookieHeader, cookieAttributes);
    2542                 : 
    2543           15233 :   PRInt64 currentTimeInUsec = PR_Now();
    2544                 : 
    2545                 :   // calculate expiry time of cookie.
    2546                 :   cookieAttributes.isSession = GetExpiry(cookieAttributes, aServerTime,
    2547           15233 :                                          currentTimeInUsec / PR_USEC_PER_SEC);
    2548           15233 :   if (aStatus == STATUS_ACCEPT_SESSION) {
    2549                 :     // force lifetime to session. note that the expiration time, if set above,
    2550                 :     // will still apply.
    2551               8 :     cookieAttributes.isSession = true;
    2552                 :   }
    2553                 : 
    2554                 :   // reject cookie if it's over the size limit, per RFC2109
    2555           15233 :   if ((cookieAttributes.name.Length() + cookieAttributes.value.Length()) > kMaxBytesPerCookie) {
    2556               1 :     COOKIE_LOGFAILURE(SET_COOKIE, aHostURI, savedCookieHeader, "cookie too big (> 4kb)");
    2557               1 :     return newCookie;
    2558                 :   }
    2559                 : 
    2560           15232 :   if (cookieAttributes.name.FindChar('\t') != kNotFound) {
    2561               1 :     COOKIE_LOGFAILURE(SET_COOKIE, aHostURI, savedCookieHeader, "invalid name character");
    2562               1 :     return newCookie;
    2563                 :   }
    2564                 : 
    2565                 :   // domain & path checks
    2566           15231 :   if (!CheckDomain(cookieAttributes, aHostURI, aBaseDomain, aRequireHostMatch)) {
    2567              13 :     COOKIE_LOGFAILURE(SET_COOKIE, aHostURI, savedCookieHeader, "failed the domain tests");
    2568              13 :     return newCookie;
    2569                 :   }
    2570           15218 :   if (!CheckPath(cookieAttributes, aHostURI)) {
    2571               3 :     COOKIE_LOGFAILURE(SET_COOKIE, aHostURI, savedCookieHeader, "failed the path tests");
    2572               3 :     return newCookie;
    2573                 :   }
    2574                 : 
    2575                 :   // create a new nsCookie and copy attributes
    2576                 :   nsRefPtr<nsCookie> cookie =
    2577                 :     nsCookie::Create(cookieAttributes.name,
    2578                 :                      cookieAttributes.value,
    2579                 :                      cookieAttributes.host,
    2580                 :                      cookieAttributes.path,
    2581                 :                      cookieAttributes.expiryTime,
    2582                 :                      currentTimeInUsec,
    2583                 :                      nsCookie::GenerateUniqueCreationTime(currentTimeInUsec),
    2584                 :                      cookieAttributes.isSession,
    2585                 :                      cookieAttributes.isSecure,
    2586           30430 :                      cookieAttributes.isHttpOnly);
    2587           15215 :   if (!cookie)
    2588               0 :     return newCookie;
    2589                 : 
    2590                 :   // check permissions from site permission list, or ask the user,
    2591                 :   // to determine if we can set the cookie
    2592           15215 :   if (mPermissionService) {
    2593                 :     bool permission;
    2594                 :     // Not passing an nsIChannel here means CanSetCookie will use the currently
    2595                 :     // active window to display the prompt. This isn't exactly ideal, but this
    2596                 :     // code is going away. See bug 546746.
    2597           15215 :     mPermissionService->CanSetCookie(aHostURI,
    2598                 :                                      nsnull,
    2599           15215 :                                      static_cast<nsICookie2*>(static_cast<nsCookie*>(cookie)),
    2600                 :                                      &cookieAttributes.isSession,
    2601                 :                                      &cookieAttributes.expiryTime,
    2602           30430 :                                      &permission);
    2603           15215 :     if (!permission) {
    2604               1 :       COOKIE_LOGFAILURE(SET_COOKIE, aHostURI, savedCookieHeader, "cookie rejected by permission manager");
    2605               1 :       NotifyRejected(aHostURI);
    2606               1 :       return newCookie;
    2607                 :     }
    2608                 : 
    2609                 :     // update isSession and expiry attributes, in case they changed
    2610           15214 :     cookie->SetIsSession(cookieAttributes.isSession);
    2611           15214 :     cookie->SetExpiry(cookieAttributes.expiryTime);
    2612                 :   }
    2613                 : 
    2614                 :   // add the cookie to the list. AddInternal() takes care of logging.
    2615                 :   // we get the current time again here, since it may have changed during prompting
    2616                 :   AddInternal(aBaseDomain, cookie, PR_Now(), aHostURI, savedCookieHeader.get(),
    2617           15214 :               aFromHttp);
    2618           15214 :   return newCookie;
    2619                 : }
    2620                 : 
    2621                 : // this is a backend function for adding a cookie to the list, via SetCookie.
    2622                 : // also used in the cookie manager, for profile migration from IE.
    2623                 : // it either replaces an existing cookie; or adds the cookie to the hashtable,
    2624                 : // and deletes a cookie (if maximum number of cookies has been
    2625                 : // reached). also performs list maintenance by removing expired cookies.
    2626                 : void
    2627           16231 : nsCookieService::AddInternal(const nsCString               &aBaseDomain,
    2628                 :                              nsCookie                      *aCookie,
    2629                 :                              PRInt64                        aCurrentTimeInUsec,
    2630                 :                              nsIURI                        *aHostURI,
    2631                 :                              const char                    *aCookieHeader,
    2632                 :                              bool                           aFromHttp)
    2633                 : {
    2634           16231 :   PRInt64 currentTime = aCurrentTimeInUsec / PR_USEC_PER_SEC;
    2635                 : 
    2636                 :   // if the new cookie is httponly, make sure we're not coming from script
    2637           16231 :   if (!aFromHttp && aCookie->IsHttpOnly()) {
    2638                 :     COOKIE_LOGFAILURE(SET_COOKIE, aHostURI, aCookieHeader,
    2639               2 :       "cookie is httponly; coming from script");
    2640               2 :     return;
    2641                 :   }
    2642                 : 
    2643           16229 :   nsListIter matchIter;
    2644           16229 :   bool foundCookie = FindCookie(aBaseDomain, aCookie->Host(),
    2645           32458 :     aCookie->Name(), aCookie->Path(), matchIter);
    2646                 : 
    2647           32458 :   nsRefPtr<nsCookie> oldCookie;
    2648           32458 :   nsCOMPtr<nsIArray> purgedList;
    2649           16229 :   if (foundCookie) {
    2650              42 :     oldCookie = matchIter.Cookie();
    2651                 : 
    2652                 :     // Check if the old cookie is stale (i.e. has already expired). If so, we
    2653                 :     // need to be careful about the semantics of removing it and adding the new
    2654                 :     // cookie: we want the behavior wrt adding the new cookie to be the same as
    2655                 :     // if it didn't exist, but we still want to fire a removal notification.
    2656              42 :     if (oldCookie->Expiry() <= currentTime) {
    2657               0 :       if (aCookie->Expiry() <= currentTime) {
    2658                 :         // The new cookie has expired and the old one is stale. Nothing to do.
    2659                 :         COOKIE_LOGFAILURE(SET_COOKIE, aHostURI, aCookieHeader,
    2660               0 :           "cookie has already expired");
    2661                 :         return;
    2662                 :       }
    2663                 : 
    2664                 :       // Remove the stale cookie. We save notification for later, once all list
    2665                 :       // modifications are complete.
    2666               0 :       RemoveCookieFromList(matchIter);
    2667                 :       COOKIE_LOGFAILURE(SET_COOKIE, aHostURI, aCookieHeader,
    2668               0 :         "stale cookie was purged");
    2669               0 :       purgedList = CreatePurgeList(oldCookie);
    2670                 : 
    2671                 :       // We've done all we need to wrt removing and notifying the stale cookie.
    2672                 :       // From here on out, we pretend pretend it didn't exist, so that we
    2673                 :       // preserve expected notification semantics when adding the new cookie.
    2674               0 :       foundCookie = false;
    2675                 : 
    2676                 :     } else {
    2677                 :       // If the old cookie is httponly, make sure we're not coming from script.
    2678              42 :       if (!aFromHttp && oldCookie->IsHttpOnly()) {
    2679                 :         COOKIE_LOGFAILURE(SET_COOKIE, aHostURI, aCookieHeader,
    2680               2 :           "previously stored cookie is httponly; coming from script");
    2681                 :         return;
    2682                 :       }
    2683                 : 
    2684                 :       // Remove the old cookie.
    2685              40 :       RemoveCookieFromList(matchIter);
    2686                 : 
    2687                 :       // If the new cookie has expired -- i.e. the intent was simply to delete
    2688                 :       // the old cookie -- then we're done.
    2689              40 :       if (aCookie->Expiry() <= currentTime) {
    2690                 :         COOKIE_LOGFAILURE(SET_COOKIE, aHostURI, aCookieHeader,
    2691              22 :           "previously stored cookie was deleted");
    2692              22 :         NotifyChanged(oldCookie, NS_LITERAL_STRING("deleted").get());
    2693                 :         return;
    2694                 :       }
    2695                 : 
    2696                 :       // Preserve creation time of cookie for ordering purposes.
    2697              18 :       aCookie->SetCreationTime(oldCookie->CreationTime());
    2698                 :     }
    2699                 : 
    2700                 :   } else {
    2701                 :     // check if cookie has already expired
    2702           16187 :     if (aCookie->Expiry() <= currentTime) {
    2703                 :       COOKIE_LOGFAILURE(SET_COOKIE, aHostURI, aCookieHeader,
    2704               6 :         "cookie has already expired");
    2705                 :       return;
    2706                 :     }
    2707                 : 
    2708                 :     // check if we have to delete an old cookie.
    2709           16181 :     nsCookieEntry *entry = mDBState->hostTable.GetEntry(aBaseDomain);
    2710           16181 :     if (entry && entry->GetCookies().Length() >= mMaxCookiesPerHost) {
    2711              71 :       nsListIter iter;
    2712              71 :       FindStaleCookie(entry, currentTime, iter);
    2713              71 :       oldCookie = iter.Cookie();
    2714                 : 
    2715                 :       // remove the oldest cookie from the domain
    2716              71 :       RemoveCookieFromList(iter);
    2717              71 :       COOKIE_LOGEVICTED(oldCookie, "Too many cookies for this domain");
    2718              71 :       purgedList = CreatePurgeList(oldCookie);
    2719                 : 
    2720           16110 :     } else if (mDBState->cookieCount >= ADD_TEN_PERCENT(mMaxNumberOfCookies)) {
    2721              14 :       PRInt64 maxAge = aCurrentTimeInUsec - mDBState->cookieOldestTime;
    2722              14 :       PRInt64 purgeAge = ADD_TEN_PERCENT(mCookiePurgeAge);
    2723              14 :       if (maxAge >= purgeAge) {
    2724                 :         // we're over both size and age limits by 10%; time to purge the table!
    2725                 :         // do this by:
    2726                 :         // 1) removing expired cookies;
    2727                 :         // 2) evicting the balance of old cookies until we reach the size limit.
    2728                 :         // note that the cookieOldestTime indicator can be pessimistic - if it's
    2729                 :         // older than the actual oldest cookie, we'll just purge more eagerly.
    2730               4 :         purgedList = PurgeCookies(aCurrentTimeInUsec);
    2731                 :       }
    2732                 :     }
    2733                 :   }
    2734                 : 
    2735                 :   // Add the cookie to the db. We do not supply a params array for batching
    2736                 :   // because this might result in removals and additions being out of order.
    2737           16199 :   AddCookieToList(aBaseDomain, aCookie, mDBState, NULL);
    2738           16199 :   COOKIE_LOGSUCCESS(SET_COOKIE, aHostURI, aCookieHeader, aCookie, foundCookie);
    2739                 : 
    2740                 :   // Now that list mutations are complete, notify observers. We do it here
    2741                 :   // because observers may themselves attempt to mutate the list.
    2742           16199 :   if (purgedList) {
    2743              75 :     NotifyChanged(purgedList, NS_LITERAL_STRING("batch-deleted").get());
    2744                 :   }
    2745                 : 
    2746           16217 :   NotifyChanged(aCookie, foundCookie ? NS_LITERAL_STRING("changed").get()
    2747           16217 :                                      : NS_LITERAL_STRING("added").get());
    2748                 : }
    2749                 : 
    2750                 : /******************************************************************************
    2751                 :  * nsCookieService impl:
    2752                 :  * private cookie header parsing functions
    2753                 :  ******************************************************************************/
    2754                 : 
    2755                 : // The following comment block elucidates the function of ParseAttributes.
    2756                 : /******************************************************************************
    2757                 :  ** Augmented BNF, modified from RFC2109 Section 4.2.2 and RFC2616 Section 2.1
    2758                 :  ** please note: this BNF deviates from both specifications, and reflects this
    2759                 :  ** implementation. <bnf> indicates a reference to the defined grammar "bnf".
    2760                 : 
    2761                 :  ** Differences from RFC2109/2616 and explanations:
    2762                 :     1. implied *LWS
    2763                 :          The grammar described by this specification is word-based. Except
    2764                 :          where noted otherwise, linear white space (<LWS>) can be included
    2765                 :          between any two adjacent words (token or quoted-string), and
    2766                 :          between adjacent words and separators, without changing the
    2767                 :          interpretation of a field.
    2768                 :        <LWS> according to spec is SP|HT|CR|LF, but here, we allow only SP | HT.
    2769                 : 
    2770                 :     2. We use CR | LF as cookie separators, not ',' per spec, since ',' is in
    2771                 :        common use inside values.
    2772                 : 
    2773                 :     3. tokens and values have looser restrictions on allowed characters than
    2774                 :        spec. This is also due to certain characters being in common use inside
    2775                 :        values. We allow only '=' to separate token/value pairs, and ';' to
    2776                 :        terminate tokens or values. <LWS> is allowed within tokens and values
    2777                 :        (see bug 206022).
    2778                 : 
    2779                 :     4. where appropriate, full <OCTET>s are allowed, where the spec dictates to
    2780                 :        reject control chars or non-ASCII chars. This is erring on the loose
    2781                 :        side, since there's probably no good reason to enforce this strictness.
    2782                 : 
    2783                 :     5. cookie <NAME> is optional, where spec requires it. This is a fairly
    2784                 :        trivial case, but allows the flexibility of setting only a cookie <VALUE>
    2785                 :        with a blank <NAME> and is required by some sites (see bug 169091).
    2786                 :        
    2787                 :     6. Attribute "HttpOnly", not covered in the RFCs, is supported
    2788                 :        (see bug 178993).
    2789                 : 
    2790                 :  ** Begin BNF:
    2791                 :     token         = 1*<any allowed-chars except separators>
    2792                 :     value         = 1*<any allowed-chars except value-sep>
    2793                 :     separators    = ";" | "="
    2794                 :     value-sep     = ";"
    2795                 :     cookie-sep    = CR | LF
    2796                 :     allowed-chars = <any OCTET except NUL or cookie-sep>
    2797                 :     OCTET         = <any 8-bit sequence of data>
    2798                 :     LWS           = SP | HT
    2799                 :     NUL           = <US-ASCII NUL, null control character (0)>
    2800                 :     CR            = <US-ASCII CR, carriage return (13)>
    2801                 :     LF            = <US-ASCII LF, linefeed (10)>
    2802                 :     SP            = <US-ASCII SP, space (32)>
    2803                 :     HT            = <US-ASCII HT, horizontal-tab (9)>
    2804                 : 
    2805                 :     set-cookie    = "Set-Cookie:" cookies
    2806                 :     cookies       = cookie *( cookie-sep cookie )
    2807                 :     cookie        = [NAME "="] VALUE *(";" cookie-av)    ; cookie NAME/VALUE must come first
    2808                 :     NAME          = token                                ; cookie name
    2809                 :     VALUE         = value                                ; cookie value
    2810                 :     cookie-av     = token ["=" value]
    2811                 : 
    2812                 :     valid values for cookie-av (checked post-parsing) are:
    2813                 :     cookie-av     = "Path"    "=" value
    2814                 :                   | "Domain"  "=" value
    2815                 :                   | "Expires" "=" value
    2816                 :                   | "Max-Age" "=" value
    2817                 :                   | "Comment" "=" value
    2818                 :                   | "Version" "=" value
    2819                 :                   | "Secure"
    2820                 :                   | "HttpOnly"
    2821                 : 
    2822                 : ******************************************************************************/
    2823                 : 
    2824                 : // helper functions for GetTokenValue
    2825          136549 : static inline bool iswhitespace     (char c) { return c == ' '  || c == '\t'; }
    2826          426471 : static inline bool isterminator     (char c) { return c == '\n' || c == '\r'; }
    2827          411319 : static inline bool isvalueseparator (char c) { return isterminator(c) || c == ';'; }
    2828          178068 : static inline bool istokenseparator (char c) { return isvalueseparator(c) || c == '='; }
    2829                 : 
    2830                 : // Parse a single token/value pair.
    2831                 : // Returns true if a cookie terminator is found, so caller can parse new cookie.
    2832                 : bool
    2833           30368 : nsCookieService::GetTokenValue(nsASingleFragmentCString::const_char_iterator &aIter,
    2834                 :                                nsASingleFragmentCString::const_char_iterator &aEndIter,
    2835                 :                                nsDependentCSubstring                         &aTokenString,
    2836                 :                                nsDependentCSubstring                         &aTokenValue,
    2837                 :                                bool                                          &aEqualsFound)
    2838                 : {
    2839                 :   nsASingleFragmentCString::const_char_iterator start, lastSpace;
    2840                 :   // initialize value string to clear garbage
    2841           30368 :   aTokenValue.Rebind(aIter, aIter);
    2842                 : 
    2843                 :   // find <token>, including any <LWS> between the end-of-token and the
    2844                 :   // token separator. we'll remove trailing <LWS> next
    2845           75874 :   while (aIter != aEndIter && iswhitespace(*aIter))
    2846           15138 :     ++aIter;
    2847           30368 :   start = aIter;
    2848          208445 :   while (aIter != aEndIter && !istokenseparator(*aIter))
    2849          147709 :     ++aIter;
    2850                 : 
    2851                 :   // remove trailing <LWS>; first check we're not at the beginning
    2852           30368 :   lastSpace = aIter;
    2853           30368 :   if (lastSpace != start) {
    2854           30359 :     while (--lastSpace != start && iswhitespace(*lastSpace));
    2855           30359 :     ++lastSpace;
    2856                 :   }
    2857           30368 :   aTokenString.Rebind(start, lastSpace);
    2858                 : 
    2859           30368 :   aEqualsFound = (*aIter == '=');
    2860           30368 :   if (aEqualsFound) {
    2861                 :     // find <value>
    2862           30349 :     while (++aIter != aEndIter && iswhitespace(*aIter));
    2863                 : 
    2864           30349 :     start = aIter;
    2865                 : 
    2866                 :     // process <token>
    2867                 :     // just look for ';' to terminate ('=' allowed)
    2868          278807 :     while (aIter != aEndIter && !isvalueseparator(*aIter))
    2869          218109 :       ++aIter;
    2870                 : 
    2871                 :     // remove trailing <LWS>; first check we're not at the beginning
    2872           30349 :     if (aIter != start) {
    2873           30345 :       lastSpace = aIter;
    2874           30345 :       while (--lastSpace != start && iswhitespace(*lastSpace));
    2875           30345 :       aTokenValue.Rebind(start, ++lastSpace);
    2876                 :     }
    2877                 :   }
    2878                 : 
    2879                 :   // aIter is on ';', or terminator, or EOS
    2880           30368 :   if (aIter != aEndIter) {
    2881                 :     // if on terminator, increment past & return true to process new cookie
    2882           15152 :     if (isterminator(*aIter)) {
    2883              14 :       ++aIter;
    2884              14 :       return true;
    2885                 :     }
    2886                 :     // fall-through: aIter is on ';', increment and return false
    2887           15138 :     ++aIter;
    2888                 :   }
    2889           30354 :   return false;
    2890                 : }
    2891                 : 
    2892                 : // Parses attributes from cookie header. expires/max-age attributes aren't folded into the
    2893                 : // cookie struct here, because we don't know which one to use until we've parsed the header.
    2894                 : bool
    2895           15233 : nsCookieService::ParseAttributes(nsDependentCString &aCookieHeader,
    2896                 :                                  nsCookieAttributes &aCookieAttributes)
    2897                 : {
    2898                 :   static const char kPath[]    = "path";
    2899                 :   static const char kDomain[]  = "domain";
    2900                 :   static const char kExpires[] = "expires";
    2901                 :   static const char kMaxage[]  = "max-age";
    2902                 :   static const char kSecure[]  = "secure";
    2903                 :   static const char kHttpOnly[]  = "httponly";
    2904                 : 
    2905                 :   nsASingleFragmentCString::const_char_iterator tempBegin, tempEnd;
    2906                 :   nsASingleFragmentCString::const_char_iterator cookieStart, cookieEnd;
    2907           15233 :   aCookieHeader.BeginReading(cookieStart);
    2908           15233 :   aCookieHeader.EndReading(cookieEnd);
    2909                 : 
    2910           15233 :   aCookieAttributes.isSecure = false;
    2911           15233 :   aCookieAttributes.isHttpOnly = false;
    2912                 :   
    2913           30466 :   nsDependentCSubstring tokenString(cookieStart, cookieStart);
    2914           30466 :   nsDependentCSubstring tokenValue (cookieStart, cookieStart);
    2915                 :   bool newCookie, equalsFound;
    2916                 : 
    2917                 :   // extract cookie <NAME> & <VALUE> (first attribute), and copy the strings.
    2918                 :   // if we find multiple cookies, return for processing
    2919                 :   // note: if there's no '=', we assume token is <VALUE>. this is required by
    2920                 :   //       some sites (see bug 169091).
    2921                 :   // XXX fix the parser to parse according to <VALUE> grammar for this case
    2922           15233 :   newCookie = GetTokenValue(cookieStart, cookieEnd, tokenString, tokenValue, equalsFound);
    2923           15233 :   if (equalsFound) {
    2924           15227 :     aCookieAttributes.name = tokenString;
    2925           15227 :     aCookieAttributes.value = tokenValue;
    2926                 :   } else {
    2927               6 :     aCookieAttributes.value = tokenString;
    2928                 :   }
    2929                 : 
    2930                 :   // extract remaining attributes
    2931           45601 :   while (cookieStart != cookieEnd && !newCookie) {
    2932           15135 :     newCookie = GetTokenValue(cookieStart, cookieEnd, tokenString, tokenValue, equalsFound);
    2933                 : 
    2934           15135 :     if (!tokenValue.IsEmpty()) {
    2935           15118 :       tokenValue.BeginReading(tempBegin);
    2936           15118 :       tokenValue.EndReading(tempEnd);
    2937                 :     }
    2938                 : 
    2939                 :     // decide which attribute we have, and copy the string
    2940           15135 :     if (tokenString.LowerCaseEqualsLiteral(kPath))
    2941              20 :       aCookieAttributes.path = tokenValue;
    2942                 : 
    2943           15115 :     else if (tokenString.LowerCaseEqualsLiteral(kDomain))
    2944              35 :       aCookieAttributes.host = tokenValue;
    2945                 : 
    2946           15080 :     else if (tokenString.LowerCaseEqualsLiteral(kExpires))
    2947               6 :       aCookieAttributes.expires = tokenValue;
    2948                 : 
    2949           15074 :     else if (tokenString.LowerCaseEqualsLiteral(kMaxage))
    2950           15057 :       aCookieAttributes.maxage = tokenValue;
    2951                 : 
    2952                 :     // ignore any tokenValue for isSecure; just set the boolean
    2953              17 :     else if (tokenString.LowerCaseEqualsLiteral(kSecure))
    2954               0 :       aCookieAttributes.isSecure = true;
    2955                 :       
    2956                 :     // ignore any tokenValue for isHttpOnly (see bug 178993);
    2957                 :     // just set the boolean
    2958              17 :     else if (tokenString.LowerCaseEqualsLiteral(kHttpOnly))
    2959               7 :       aCookieAttributes.isHttpOnly = true;
    2960                 :   }
    2961                 : 
    2962                 :   // rebind aCookieHeader, in case we need to process another cookie
    2963           15233 :   aCookieHeader.Rebind(cookieStart, cookieEnd);
    2964           15233 :   return newCookie;
    2965                 : }
    2966                 : 
    2967                 : /******************************************************************************
    2968                 :  * nsCookieService impl:
    2969                 :  * private domain & permission compliance enforcement functions
    2970                 :  ******************************************************************************/
    2971                 : 
    2972                 : // Get the base domain for aHostURI; e.g. for "www.bbc.co.uk", this would be
    2973                 : // "bbc.co.uk". Only properly-formed URI's are tolerated, though a trailing
    2974                 : // dot may be present. If aHostURI is an IP address, an alias such as
    2975                 : // 'localhost', an eTLD such as 'co.uk', or the empty string, aBaseDomain will
    2976                 : // be the exact host, and aRequireHostMatch will be true to indicate that
    2977                 : // substring matches should not be performed.
    2978                 : nsresult
    2979           18835 : nsCookieService::GetBaseDomain(nsIURI    *aHostURI,
    2980                 :                                nsCString &aBaseDomain,
    2981                 :                                bool      &aRequireHostMatch)
    2982                 : {
    2983                 :   // get the base domain. this will fail if the host contains a leading dot,
    2984                 :   // more than one trailing dot, or is otherwise malformed.
    2985           18835 :   nsresult rv = mTLDService->GetBaseDomain(aHostURI, 0, aBaseDomain);
    2986                 :   aRequireHostMatch = rv == NS_ERROR_HOST_IS_IP_ADDRESS ||
    2987           18835 :                       rv == NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS;
    2988           18835 :   if (aRequireHostMatch) {
    2989                 :     // aHostURI is either an IP address, an alias such as 'localhost', an eTLD
    2990                 :     // such as 'co.uk', or the empty string. use the host as a key in such
    2991                 :     // cases.
    2992            3246 :     rv = aHostURI->GetAsciiHost(aBaseDomain);
    2993                 :   }
    2994           18835 :   NS_ENSURE_SUCCESS(rv, rv);
    2995                 : 
    2996                 :   // aHost (and thus aBaseDomain) may be the string '.'. If so, fail.
    2997           18833 :   if (aBaseDomain.Length() == 1 && aBaseDomain.Last() == '.')
    2998               0 :     return NS_ERROR_INVALID_ARG;
    2999                 : 
    3000                 :   // block any URIs without a host that aren't file:// URIs.
    3001           18833 :   if (aBaseDomain.IsEmpty()) {
    3002               7 :     bool isFileURI = false;
    3003               7 :     aHostURI->SchemeIs("file", &isFileURI);
    3004               7 :     if (!isFileURI)
    3005               2 :       return NS_ERROR_INVALID_ARG;
    3006                 :   }
    3007                 : 
    3008           18831 :   return NS_OK;
    3009                 : }
    3010                 : 
    3011                 : // Get the base domain for aHost; e.g. for "www.bbc.co.uk", this would be
    3012                 : // "bbc.co.uk". This is done differently than GetBaseDomain(): it is assumed
    3013                 : // that aHost is already normalized, and it may contain a leading dot
    3014                 : // (indicating that it represents a domain). A trailing dot may be present.
    3015                 : // If aHost is an IP address, an alias such as 'localhost', an eTLD such as
    3016                 : // 'co.uk', or the empty string, aBaseDomain will be the exact host, and a
    3017                 : // leading dot will be treated as an error.
    3018                 : nsresult
    3019            7432 : nsCookieService::GetBaseDomainFromHost(const nsACString &aHost,
    3020                 :                                        nsCString        &aBaseDomain)
    3021                 : {
    3022                 :   // aHost must not be the string '.'.
    3023            7432 :   if (aHost.Length() == 1 && aHost.Last() == '.')
    3024               8 :     return NS_ERROR_INVALID_ARG;
    3025                 : 
    3026                 :   // aHost may contain a leading dot; if so, strip it now.
    3027            7424 :   bool domain = !aHost.IsEmpty() && aHost.First() == '.';
    3028                 : 
    3029                 :   // get the base domain. this will fail if the host contains a leading dot,
    3030                 :   // more than one trailing dot, or is otherwise malformed.
    3031            7424 :   nsresult rv = mTLDService->GetBaseDomainFromHost(Substring(aHost, domain), 0, aBaseDomain);
    3032            7424 :   if (rv == NS_ERROR_HOST_IS_IP_ADDRESS ||
    3033                 :       rv == NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS) {
    3034                 :     // aHost is either an IP address, an alias such as 'localhost', an eTLD
    3035                 :     // such as 'co.uk', or the empty string. use the host as a key in such
    3036                 :     // cases; however, we reject any such hosts with a leading dot, since it
    3037                 :     // doesn't make sense for them to be domain cookies.
    3038              38 :     if (domain)
    3039               6 :       return NS_ERROR_INVALID_ARG;
    3040                 : 
    3041              32 :     aBaseDomain = aHost;
    3042              32 :     return NS_OK;
    3043                 :   }
    3044            7386 :   return rv;
    3045                 : }
    3046                 : 
    3047                 : // Normalizes the given hostname, component by component. ASCII/ACE
    3048                 : // components are lower-cased, and UTF-8 components are normalized per
    3049                 : // RFC 3454 and converted to ACE.
    3050                 : nsresult
    3051            7341 : nsCookieService::NormalizeHost(nsCString &aHost)
    3052                 : {
    3053            7341 :   if (!IsASCII(aHost)) {
    3054               0 :     nsCAutoString host;
    3055               0 :     nsresult rv = mIDNService->ConvertUTF8toACE(aHost, host);
    3056               0 :     if (NS_FAILED(rv))
    3057               0 :       return rv;
    3058                 : 
    3059               0 :     aHost = host;
    3060                 :   }
    3061                 : 
    3062            7341 :   ToLowerCase(aHost);
    3063            7341 :   return NS_OK;
    3064                 : }
    3065                 : 
    3066                 : // returns true if 'a' is equal to or a subdomain of 'b',
    3067                 : // assuming no leading dots are present.
    3068              39 : static inline bool IsSubdomainOf(const nsCString &a, const nsCString &b)
    3069                 : {
    3070              39 :   if (a == b)
    3071              24 :     return true;
    3072              15 :   if (a.Length() > b.Length())
    3073              12 :     return a[a.Length() - b.Length() - 1] == '.' && StringEndsWith(a, b);
    3074               3 :   return false;
    3075                 : }
    3076                 : 
    3077                 : bool
    3078           37969 : nsCookieService::RequireThirdPartyCheck()
    3079                 : {
    3080                 :   // 'true' iff we need to perform a third party test.
    3081           37969 :   return mCookieBehavior == BEHAVIOR_REJECTFOREIGN || mThirdPartySession;
    3082                 : }
    3083                 : 
    3084                 : CookieStatus
    3085           18831 : nsCookieService::CheckPrefs(nsIURI          *aHostURI,
    3086                 :                             bool             aIsForeign,
    3087                 :                             const nsCString &aBaseDomain,
    3088                 :                             bool             aRequireHostMatch,
    3089                 :                             const char      *aCookieHeader)
    3090                 : {
    3091                 :   nsresult rv;
    3092                 : 
    3093                 :   // don't let ftp sites get/set cookies (could be a security issue)
    3094                 :   bool ftp;
    3095           18831 :   if (NS_SUCCEEDED(aHostURI->SchemeIs("ftp", &ftp)) && ftp) {
    3096               0 :     COOKIE_LOGFAILURE(aCookieHeader ? SET_COOKIE : GET_COOKIE, aHostURI, aCookieHeader, "ftp sites cannot read cookies");
    3097               0 :     return STATUS_REJECTED_WITH_ERROR;
    3098                 :   }
    3099                 : 
    3100                 :   // check the permission list first; if we find an entry, it overrides
    3101                 :   // default prefs. see bug 184059.
    3102           18831 :   if (mPermissionService) {
    3103                 :     nsCookieAccess access;
    3104                 :     // Not passing an nsIChannel here is probably OK; our implementation
    3105                 :     // doesn't do anything with it anyway.
    3106           18831 :     rv = mPermissionService->CanAccess(aHostURI, nsnull, &access);
    3107                 : 
    3108                 :     // if we found an entry, use it
    3109           18831 :     if (NS_SUCCEEDED(rv)) {
    3110           18831 :       switch (access) {
    3111                 :       case nsICookiePermission::ACCESS_DENY:
    3112               0 :         COOKIE_LOGFAILURE(aCookieHeader ? SET_COOKIE : GET_COOKIE, aHostURI, aCookieHeader, "cookies are blocked for this site");
    3113               0 :         return STATUS_REJECTED;
    3114                 : 
    3115                 :       case nsICookiePermission::ACCESS_ALLOW:
    3116               0 :         return STATUS_ACCEPTED;
    3117                 :       }
    3118                 :     }
    3119                 :   }
    3120                 : 
    3121                 :   // check default prefs
    3122           18831 :   if (mCookieBehavior == BEHAVIOR_REJECT) {
    3123               0 :     COOKIE_LOGFAILURE(aCookieHeader ? SET_COOKIE : GET_COOKIE, aHostURI, aCookieHeader, "cookies are disabled");
    3124               0 :     return STATUS_REJECTED;
    3125                 :   }
    3126                 : 
    3127           18831 :   if (RequireThirdPartyCheck() && aIsForeign) {
    3128                 :     // check if cookie is foreign
    3129              22 :     if (mCookieBehavior == BEHAVIOR_ACCEPT && mThirdPartySession)
    3130               8 :       return STATUS_ACCEPT_SESSION;
    3131                 : 
    3132              14 :     COOKIE_LOGFAILURE(aCookieHeader ? SET_COOKIE : GET_COOKIE, aHostURI, aCookieHeader, "context is third party");
    3133              14 :     return STATUS_REJECTED;
    3134                 :   }
    3135                 : 
    3136                 :   // if nothing has complained, accept cookie
    3137           18809 :   return STATUS_ACCEPTED;
    3138                 : }
    3139                 : 
    3140                 : // processes domain attribute, and returns true if host has permission to set for this domain.
    3141                 : bool
    3142           15231 : nsCookieService::CheckDomain(nsCookieAttributes &aCookieAttributes,
    3143                 :                              nsIURI             *aHostURI,
    3144                 :                              const nsCString    &aBaseDomain,
    3145                 :                              bool                aRequireHostMatch)
    3146                 : {
    3147                 :   // get host from aHostURI
    3148           30462 :   nsCAutoString hostFromURI;
    3149           15231 :   aHostURI->GetAsciiHost(hostFromURI);
    3150                 : 
    3151                 :   // if a domain is given, check the host has permission
    3152           15231 :   if (!aCookieAttributes.host.IsEmpty()) {
    3153                 :     // Tolerate leading '.' characters, but not if it's otherwise an empty host.
    3154              67 :     if (aCookieAttributes.host.Length() > 1 &&
    3155              33 :         aCookieAttributes.host.First() == '.') {
    3156              17 :       aCookieAttributes.host.Cut(0, 1);
    3157                 :     }
    3158                 : 
    3159                 :     // switch to lowercase now, to avoid case-insensitive compares everywhere
    3160              34 :     ToLowerCase(aCookieAttributes.host);
    3161                 : 
    3162                 :     // check whether the host is either an IP address, an alias such as
    3163                 :     // 'localhost', an eTLD such as 'co.uk', or the empty string. in these
    3164                 :     // cases, require an exact string match for the domain, and leave the cookie
    3165                 :     // as a non-domain one. bug 105917 originally noted the requirement to deal
    3166                 :     // with IP addresses.
    3167              34 :     if (aRequireHostMatch)
    3168              12 :       return hostFromURI.Equals(aCookieAttributes.host);
    3169                 : 
    3170                 :     // ensure the proposed domain is derived from the base domain; and also
    3171                 :     // that the host domain is derived from the proposed domain (per RFC2109).
    3172              39 :     if (IsSubdomainOf(aCookieAttributes.host, aBaseDomain) &&
    3173              17 :         IsSubdomainOf(hostFromURI, aCookieAttributes.host)) {
    3174                 :       // prepend a dot to indicate a domain cookie
    3175              15 :       aCookieAttributes.host.Insert(NS_LITERAL_CSTRING("."), 0);
    3176              15 :       return true;
    3177                 :     }
    3178                 : 
    3179                 :     /*
    3180                 :      * note: RFC2109 section 4.3.2 requires that we check the following:
    3181                 :      * that the portion of host not in domain does not contain a dot.
    3182                 :      * this prevents hosts of the form x.y.co.nz from setting cookies in the
    3183                 :      * entire .co.nz domain. however, it's only a only a partial solution and
    3184                 :      * it breaks sites (IE doesn't enforce it), so we don't perform this check.
    3185                 :      */
    3186               7 :     return false;
    3187                 :   }
    3188                 : 
    3189                 :   // no domain specified, use hostFromURI
    3190           15197 :   aCookieAttributes.host = hostFromURI;
    3191           15197 :   return true;
    3192                 : }
    3193                 : 
    3194                 : bool
    3195           15218 : nsCookieService::CheckPath(nsCookieAttributes &aCookieAttributes,
    3196                 :                            nsIURI             *aHostURI)
    3197                 : {
    3198                 :   // if a path is given, check the host has permission
    3199           15218 :   if (aCookieAttributes.path.IsEmpty() || aCookieAttributes.path.First() != '/') {
    3200                 :     // strip down everything after the last slash to get the path,
    3201                 :     // ignoring slashes in the query string part.
    3202                 :     // if we can QI to nsIURL, that'll take care of the query string portion.
    3203                 :     // otherwise, it's not an nsIURL and can't have a query string, so just find the last slash.
    3204           30400 :     nsCOMPtr<nsIURL> hostURL = do_QueryInterface(aHostURI);
    3205           15200 :     if (hostURL) {
    3206           15200 :       hostURL->GetDirectory(aCookieAttributes.path);
    3207                 :     } else {
    3208               0 :       aHostURI->GetPath(aCookieAttributes.path);
    3209               0 :       PRInt32 slash = aCookieAttributes.path.RFindChar('/');
    3210               0 :       if (slash != kNotFound) {
    3211               0 :         aCookieAttributes.path.Truncate(slash + 1);
    3212                 :       }
    3213                 :     }
    3214                 : 
    3215                 : #if 0
    3216                 :   } else {
    3217                 :     /**
    3218                 :      * The following test is part of the RFC2109 spec.  Loosely speaking, it says that a site
    3219                 :      * cannot set a cookie for a path that it is not on.  See bug 155083.  However this patch
    3220                 :      * broke several sites -- nordea (bug 155768) and citibank (bug 156725).  So this test has
    3221                 :      * been disabled, unless we can evangelize these sites.
    3222                 :      */
    3223                 :     // get path from aHostURI
    3224                 :     nsCAutoString pathFromURI;
    3225                 :     if (NS_FAILED(aHostURI->GetPath(pathFromURI)) ||
    3226                 :         !StringBeginsWith(pathFromURI, aCookieAttributes.path)) {
    3227                 :       return false;
    3228                 :     }
    3229                 : #endif
    3230                 :   }
    3231                 : 
    3232           30434 :   if (aCookieAttributes.path.Length() > kMaxBytesPerPath ||
    3233           15216 :       aCookieAttributes.path.FindChar('\t') != kNotFound )
    3234               3 :     return false;
    3235                 : 
    3236           15215 :   return true;
    3237                 : }
    3238                 : 
    3239                 : bool
    3240           15233 : nsCookieService::GetExpiry(nsCookieAttributes &aCookieAttributes,
    3241                 :                            PRInt64             aServerTime,
    3242                 :                            PRInt64             aCurrentTime)
    3243                 : {
    3244                 :   /* Determine when the cookie should expire. This is done by taking the difference between 
    3245                 :    * the server time and the time the server wants the cookie to expire, and adding that 
    3246                 :    * difference to the client time. This localizes the client time regardless of whether or
    3247                 :    * not the TZ environment variable was set on the client.
    3248                 :    *
    3249                 :    * Note: We need to consider accounting for network lag here, per RFC.
    3250                 :    */
    3251                 :   PRInt64 delta;
    3252                 : 
    3253                 :   // check for max-age attribute first; this overrides expires attribute
    3254           15233 :   if (!aCookieAttributes.maxage.IsEmpty()) {
    3255                 :     // obtain numeric value of maxageAttribute
    3256                 :     PRInt64 maxage;
    3257           15057 :     PRInt32 numInts = PR_sscanf(aCookieAttributes.maxage.get(), "%lld", &maxage);
    3258                 : 
    3259                 :     // default to session cookie if the conversion failed
    3260           15057 :     if (numInts != 1) {
    3261               0 :       return true;
    3262                 :     }
    3263                 : 
    3264           15057 :     delta = maxage;
    3265                 : 
    3266                 :   // check for expires attribute
    3267             176 :   } else if (!aCookieAttributes.expires.IsEmpty()) {
    3268                 :     PRTime expires;
    3269                 : 
    3270                 :     // parse expiry time
    3271               6 :     if (PR_ParseTimeString(aCookieAttributes.expires.get(), true, &expires) != PR_SUCCESS) {
    3272               1 :       return true;
    3273                 :     }
    3274                 : 
    3275               5 :     delta = expires / PRInt64(PR_USEC_PER_SEC) - aServerTime;
    3276                 : 
    3277                 :   // default to session cookie if no attributes found
    3278                 :   } else {
    3279             170 :     return true;
    3280                 :   }
    3281                 : 
    3282                 :   // if this addition overflows, expiryTime will be less than currentTime
    3283                 :   // and the cookie will be expired - that's okay.
    3284           15062 :   aCookieAttributes.expiryTime = aCurrentTime + delta;
    3285                 : 
    3286           15062 :   return false;
    3287                 : }
    3288                 : 
    3289                 : /******************************************************************************
    3290                 :  * nsCookieService impl:
    3291                 :  * private cookielist management functions
    3292                 :  ******************************************************************************/
    3293                 : 
    3294                 : void
    3295              44 : nsCookieService::RemoveAllFromMemory()
    3296                 : {
    3297                 :   // clearing the hashtable will call each nsCookieEntry's dtor,
    3298                 :   // which releases all their respective children.
    3299              44 :   mDBState->hostTable.Clear();
    3300              44 :   mDBState->cookieCount = 0;
    3301              44 :   mDBState->cookieOldestTime = LL_MAXINT;
    3302              44 : }
    3303                 : 
    3304                 : // stores temporary data for enumerating over the hash entries,
    3305                 : // since enumeration is done using callback functions
    3306                 : struct nsPurgeData
    3307                 : {
    3308                 :   typedef nsTArray<nsListIter> ArrayType;
    3309                 : 
    3310               4 :   nsPurgeData(PRInt64 aCurrentTime,
    3311                 :               PRInt64 aPurgeTime,
    3312                 :               ArrayType &aPurgeList,
    3313                 :               nsIMutableArray *aRemovedList,
    3314                 :               mozIStorageBindingParamsArray *aParamsArray)
    3315                 :    : currentTime(aCurrentTime)
    3316                 :    , purgeTime(aPurgeTime)
    3317                 :    , oldestTime(LL_MAXINT)
    3318                 :    , purgeList(aPurgeList)
    3319                 :    , removedList(aRemovedList)
    3320               4 :    , paramsArray(aParamsArray)
    3321                 :   {
    3322               4 :   }
    3323                 : 
    3324                 :   // the current time, in seconds
    3325                 :   PRInt64 currentTime;
    3326                 : 
    3327                 :   // lastAccessed time older than which cookies are eligible for purge
    3328                 :   PRInt64 purgeTime;
    3329                 : 
    3330                 :   // lastAccessed time of the oldest cookie found during purge, to update our indicator
    3331                 :   PRInt64 oldestTime;
    3332                 : 
    3333                 :   // list of cookies over the age limit, for purging
    3334                 :   ArrayType &purgeList;
    3335                 : 
    3336                 :   // list of all cookies we've removed, for notification
    3337                 :   nsIMutableArray *removedList;
    3338                 : 
    3339                 :   // The array of parameters to be bound to the statement for deletion later.
    3340                 :   mozIStorageBindingParamsArray *paramsArray;
    3341                 : };
    3342                 : 
    3343                 : // comparator class for lastaccessed times of cookies.
    3344                 : class CompareCookiesByAge {
    3345                 : public:
    3346             422 :   bool Equals(const nsListIter &a, const nsListIter &b) const
    3347                 :   {
    3348             422 :     return a.Cookie()->LastAccessed() == b.Cookie()->LastAccessed() &&
    3349             422 :            a.Cookie()->CreationTime() == b.Cookie()->CreationTime();
    3350                 :   }
    3351                 : 
    3352             899 :   bool LessThan(const nsListIter &a, const nsListIter &b) const
    3353                 :   {
    3354                 :     // compare by lastAccessed time, and tiebreak by creationTime.
    3355             899 :     PRInt64 result = a.Cookie()->LastAccessed() - b.Cookie()->LastAccessed();
    3356             899 :     if (result != 0)
    3357             773 :       return result < 0;
    3358                 : 
    3359             126 :     return a.Cookie()->CreationTime() < b.Cookie()->CreationTime();
    3360                 :   }
    3361                 : };
    3362                 : 
    3363                 : // comparator class for sorting cookies by entry and index.
    3364                 : class CompareCookiesByIndex {
    3365                 : public:
    3366              27 :   bool Equals(const nsListIter &a, const nsListIter &b) const
    3367                 :   {
    3368              27 :     NS_ASSERTION(a.entry != b.entry || a.index != b.index,
    3369                 :       "cookie indexes should never be equal");
    3370              27 :     return false;
    3371                 :   }
    3372                 : 
    3373              54 :   bool LessThan(const nsListIter &a, const nsListIter &b) const
    3374                 :   {
    3375                 :     // compare by entryclass pointer, then by index.
    3376              54 :     if (a.entry != b.entry)
    3377              54 :       return a.entry < b.entry;
    3378                 : 
    3379               0 :     return a.index < b.index;
    3380                 :   }
    3381                 : };
    3382                 : 
    3383                 : PLDHashOperator
    3384             440 : purgeCookiesCallback(nsCookieEntry *aEntry,
    3385                 :                      void          *aArg)
    3386                 : {
    3387             440 :   nsPurgeData &data = *static_cast<nsPurgeData*>(aArg);
    3388                 : 
    3389             440 :   const nsCookieEntry::ArrayType &cookies = aEntry->GetCookies();
    3390             440 :   mozIStorageBindingParamsArray *array = data.paramsArray;
    3391            1320 :   for (nsCookieEntry::IndexType i = 0; i < cookies.Length(); ) {
    3392             440 :     nsListIter iter(aEntry, i);
    3393             440 :     nsCookie *cookie = cookies[i];
    3394                 : 
    3395                 :     // check if the cookie has expired
    3396             440 :     if (cookie->Expiry() <= data.currentTime) {
    3397              20 :       data.removedList->AppendElement(cookie, false);
    3398              20 :       COOKIE_LOGEVICTED(cookie, "Cookie expired");
    3399                 : 
    3400                 :       // remove from list; do not increment our iterator
    3401              20 :       gCookieService->RemoveCookieFromList(iter, array);
    3402                 : 
    3403                 :     } else {
    3404                 :       // check if the cookie is over the age limit
    3405             420 :       if (cookie->LastAccessed() <= data.purgeTime) {
    3406             155 :         data.purgeList.AppendElement(iter);
    3407                 : 
    3408             265 :       } else if (cookie->LastAccessed() < data.oldestTime) {
    3409                 :         // reset our indicator
    3410              16 :         data.oldestTime = cookie->LastAccessed();
    3411                 :       }
    3412                 : 
    3413             420 :       ++i;
    3414                 :     }
    3415                 :   }
    3416             440 :   return PL_DHASH_NEXT;
    3417                 : }
    3418                 : 
    3419                 : // purges expired and old cookies in a batch operation.
    3420                 : already_AddRefed<nsIArray>
    3421               4 : nsCookieService::PurgeCookies(PRInt64 aCurrentTimeInUsec)
    3422                 : {
    3423               4 :   NS_ASSERTION(mDBState->hostTable.Count() > 0, "table is empty");
    3424               4 :   EnsureReadComplete();
    3425                 : 
    3426                 : #ifdef PR_LOGGING
    3427               4 :   PRUint32 initialCookieCount = mDBState->cookieCount;
    3428               4 :   COOKIE_LOGSTRING(PR_LOG_DEBUG,
    3429                 :     ("PurgeCookies(): beginning purge with %ld cookies and %lld oldest age",
    3430                 :      mDBState->cookieCount, aCurrentTimeInUsec - mDBState->cookieOldestTime));
    3431                 : #endif
    3432                 : 
    3433               8 :   nsAutoTArray<nsListIter, kMaxNumberOfCookies> purgeList;
    3434                 : 
    3435               8 :   nsCOMPtr<nsIMutableArray> removedList = do_CreateInstance(NS_ARRAY_CONTRACTID);
    3436                 : 
    3437                 :   // Create a params array to batch the removals. This is OK here because
    3438                 :   // all the removals are in order, and there are no interleaved additions.
    3439               4 :   mozIStorageAsyncStatement *stmt = mDBState->stmtDelete;
    3440               8 :   nsCOMPtr<mozIStorageBindingParamsArray> paramsArray;
    3441               4 :   if (mDBState->dbConn) {
    3442               4 :     stmt->NewBindingParamsArray(getter_AddRefs(paramsArray));
    3443                 :   }
    3444                 : 
    3445                 :   nsPurgeData data(aCurrentTimeInUsec / PR_USEC_PER_SEC,
    3446               4 :     aCurrentTimeInUsec - mCookiePurgeAge, purgeList, removedList, paramsArray);
    3447               4 :   mDBState->hostTable.EnumerateEntries(purgeCookiesCallback, &data);
    3448                 : 
    3449                 : #ifdef PR_LOGGING
    3450               4 :   PRUint32 postExpiryCookieCount = mDBState->cookieCount;
    3451                 : #endif
    3452                 : 
    3453                 :   // now we have a list of iterators for cookies over the age limit.
    3454                 :   // sort them by age, and then we'll see how many to remove...
    3455               4 :   purgeList.Sort(CompareCookiesByAge());
    3456                 : 
    3457                 :   // only remove old cookies until we reach the max cookie limit, no more.
    3458                 :   PRUint32 excess = mDBState->cookieCount > mMaxNumberOfCookies ?
    3459               4 :     mDBState->cookieCount - mMaxNumberOfCookies : 0;
    3460               4 :   if (purgeList.Length() > excess) {
    3461                 :     // We're not purging everything in the list, so update our indicator.
    3462               2 :     data.oldestTime = purgeList[excess].Cookie()->LastAccessed();
    3463                 : 
    3464               2 :     purgeList.SetLength(excess);
    3465                 :   }
    3466                 : 
    3467                 :   // sort the list again, this time grouping cookies with a common entryclass
    3468                 :   // together, and with ascending index. this allows us to iterate backwards
    3469                 :   // over the list removing cookies, without having to adjust indexes as we go.
    3470               4 :   purgeList.Sort(CompareCookiesByIndex());
    3471              33 :   for (nsPurgeData::ArrayType::index_type i = purgeList.Length(); i--; ) {
    3472              25 :     nsCookie *cookie = purgeList[i].Cookie();
    3473              25 :     removedList->AppendElement(cookie, false);
    3474              25 :     COOKIE_LOGEVICTED(cookie, "Cookie too old");
    3475                 : 
    3476              25 :     RemoveCookieFromList(purgeList[i], paramsArray);
    3477                 :   }
    3478                 : 
    3479                 :   // Update the database if we have entries to purge.
    3480               4 :   if (paramsArray) {
    3481                 :     PRUint32 length;
    3482               4 :     paramsArray->GetLength(&length);
    3483               4 :     if (length) {
    3484               4 :       nsresult rv = stmt->BindParameters(paramsArray);
    3485               4 :       NS_ASSERT_SUCCESS(rv);
    3486               8 :       nsCOMPtr<mozIStoragePendingStatement> handle;
    3487               4 :       rv = stmt->ExecuteAsync(mDBState->removeListener, getter_AddRefs(handle));
    3488               4 :       NS_ASSERT_SUCCESS(rv);
    3489                 :     }
    3490                 :   }
    3491                 : 
    3492                 :   // reset the oldest time indicator
    3493               4 :   mDBState->cookieOldestTime = data.oldestTime;
    3494                 : 
    3495               4 :   COOKIE_LOGSTRING(PR_LOG_DEBUG,
    3496                 :     ("PurgeCookies(): %ld expired; %ld purged; %ld remain; %lld oldest age",
    3497                 :      initialCookieCount - postExpiryCookieCount,
    3498                 :      postExpiryCookieCount - mDBState->cookieCount,
    3499                 :      mDBState->cookieCount,
    3500                 :      aCurrentTimeInUsec - mDBState->cookieOldestTime));
    3501                 : 
    3502               4 :   return removedList.forget();
    3503                 : }
    3504                 : 
    3505                 : // find whether a given cookie has been previously set. this is provided by the
    3506                 : // nsICookieManager2 interface.
    3507                 : NS_IMETHODIMP
    3508              31 : nsCookieService::CookieExists(nsICookie2 *aCookie,
    3509                 :                               bool       *aFoundCookie)
    3510                 : {
    3511              31 :   NS_ENSURE_ARG_POINTER(aCookie);
    3512                 : 
    3513              31 :   if (!mDBState) {
    3514               1 :     NS_WARNING("No DBState! Profile already closed?");
    3515               1 :     return NS_ERROR_NOT_AVAILABLE;
    3516                 :   }
    3517                 : 
    3518              60 :   nsCAutoString host, name, path;
    3519              30 :   nsresult rv = aCookie->GetHost(host);
    3520              30 :   NS_ENSURE_SUCCESS(rv, rv);
    3521              30 :   rv = aCookie->GetName(name);
    3522              30 :   NS_ENSURE_SUCCESS(rv, rv);
    3523              30 :   rv = aCookie->GetPath(path);
    3524              30 :   NS_ENSURE_SUCCESS(rv, rv);
    3525                 : 
    3526              60 :   nsCAutoString baseDomain;
    3527              30 :   rv = GetBaseDomainFromHost(host, baseDomain);
    3528              30 :   NS_ENSURE_SUCCESS(rv, rv);
    3529                 : 
    3530              30 :   nsListIter iter;
    3531              30 :   *aFoundCookie = FindCookie(baseDomain, host, name, path, iter);
    3532              30 :   return NS_OK;
    3533                 : }
    3534                 : 
    3535                 : // For a given base domain, find either an expired cookie or the oldest cookie
    3536                 : // by lastAccessed time.
    3537                 : void
    3538              71 : nsCookieService::FindStaleCookie(nsCookieEntry *aEntry,
    3539                 :                                  PRInt64 aCurrentTime,
    3540                 :                                  nsListIter &aIter)
    3541                 : {
    3542              71 :   aIter.entry = NULL;
    3543                 : 
    3544                 :   PRInt64 oldestTime;
    3545              71 :   const nsCookieEntry::ArrayType &cookies = aEntry->GetCookies();
    3546            3620 :   for (nsCookieEntry::IndexType i = 0; i < cookies.Length(); ++i) {
    3547            3550 :     nsCookie *cookie = cookies[i];
    3548                 : 
    3549                 :     // If we found an expired cookie, we're done.
    3550            3550 :     if (cookie->Expiry() <= aCurrentTime) {
    3551               1 :       aIter.entry = aEntry;
    3552               1 :       aIter.index = i;
    3553               1 :       return;
    3554                 :     }
    3555                 : 
    3556                 :     // Check if we've found the oldest cookie so far.
    3557            3549 :     if (!aIter.entry || oldestTime > cookie->LastAccessed()) {
    3558              71 :       oldestTime = cookie->LastAccessed();
    3559              71 :       aIter.entry = aEntry;
    3560              71 :       aIter.index = i;
    3561                 :     }
    3562                 :   }
    3563                 : }
    3564                 : 
    3565                 : // count the number of cookies stored by a particular host. this is provided by the
    3566                 : // nsICookieManager2 interface.
    3567                 : NS_IMETHODIMP
    3568            6014 : nsCookieService::CountCookiesFromHost(const nsACString &aHost,
    3569                 :                                       PRUint32         *aCountFromHost)
    3570                 : {
    3571            6014 :   if (!mDBState) {
    3572               1 :     NS_WARNING("No DBState! Profile already closed?");
    3573               1 :     return NS_ERROR_NOT_AVAILABLE;
    3574                 :   }
    3575                 : 
    3576                 :   // first, normalize the hostname, and fail if it contains illegal characters.
    3577           12026 :   nsCAutoString host(aHost);
    3578            6013 :   nsresult rv = NormalizeHost(host);
    3579            6013 :   NS_ENSURE_SUCCESS(rv, rv);
    3580                 : 
    3581           12026 :   nsCAutoString baseDomain;
    3582            6013 :   rv = GetBaseDomainFromHost(host, baseDomain);
    3583            6013 :   NS_ENSURE_SUCCESS(rv, rv);
    3584                 : 
    3585            6001 :   EnsureReadDomain(baseDomain);
    3586                 : 
    3587                 :   // Return a count of all cookies, including expired.
    3588            6001 :   nsCookieEntry *entry = mDBState->hostTable.GetEntry(baseDomain);
    3589            6001 :   *aCountFromHost = entry ? entry->GetCookies().Length() : 0;
    3590            6001 :   return NS_OK;
    3591                 : }
    3592                 : 
    3593                 : // get an enumerator of cookies stored by a particular host. this is provided by the
    3594                 : // nsICookieManager2 interface.
    3595                 : NS_IMETHODIMP
    3596             100 : nsCookieService::GetCookiesFromHost(const nsACString     &aHost,
    3597                 :                                     nsISimpleEnumerator **aEnumerator)
    3598                 : {
    3599             100 :   if (!mDBState) {
    3600               1 :     NS_WARNING("No DBState! Profile already closed?");
    3601               1 :     return NS_ERROR_NOT_AVAILABLE;
    3602                 :   }
    3603                 : 
    3604                 :   // first, normalize the hostname, and fail if it contains illegal characters.
    3605             198 :   nsCAutoString host(aHost);
    3606              99 :   nsresult rv = NormalizeHost(host);
    3607              99 :   NS_ENSURE_SUCCESS(rv, rv);
    3608                 : 
    3609             198 :   nsCAutoString baseDomain;
    3610              99 :   rv = GetBaseDomainFromHost(host, baseDomain);
    3611              99 :   NS_ENSURE_SUCCESS(rv, rv);
    3612                 : 
    3613              94 :   EnsureReadDomain(baseDomain);
    3614                 : 
    3615              94 :   nsCookieEntry *entry = mDBState->hostTable.GetEntry(baseDomain);
    3616              94 :   if (!entry)
    3617              47 :     return NS_NewEmptyEnumerator(aEnumerator);
    3618                 : 
    3619              94 :   nsCOMArray<nsICookie> cookieList(mMaxCookiesPerHost);
    3620              47 :   const nsCookieEntry::ArrayType &cookies = entry->GetCookies();
    3621             447 :   for (nsCookieEntry::IndexType i = 0; i < cookies.Length(); ++i) {
    3622             400 :     cookieList.AppendObject(cookies[i]);
    3623                 :   }
    3624                 : 
    3625              47 :   return NS_NewArrayEnumerator(aEnumerator, cookieList);
    3626                 : }
    3627                 : 
    3628                 : // find an exact cookie specified by host, name, and path that hasn't expired.
    3629                 : bool
    3630           16469 : nsCookieService::FindCookie(const nsCString      &aBaseDomain,
    3631                 :                             const nsAFlatCString &aHost,
    3632                 :                             const nsAFlatCString &aName,
    3633                 :                             const nsAFlatCString &aPath,
    3634                 :                             nsListIter           &aIter)
    3635                 : {
    3636           16469 :   EnsureReadDomain(aBaseDomain);
    3637                 : 
    3638           16469 :   nsCookieEntry *entry = mDBState->hostTable.GetEntry(aBaseDomain);
    3639           16469 :   if (!entry)
    3640           12784 :     return false;
    3641                 : 
    3642            3685 :   const nsCookieEntry::ArrayType &cookies = entry->GetCookies();
    3643         4483308 :   for (nsCookieEntry::IndexType i = 0; i < cookies.Length(); ++i) {
    3644         4479887 :     nsCookie *cookie = cookies[i];
    3645                 : 
    3646        26873818 :     if (aHost.Equals(cookie->Host()) &&
    3647        13436937 :         aPath.Equals(cookie->Path()) &&
    3648        13436881 :         aName.Equals(cookie->Name())) {
    3649             264 :       aIter = nsListIter(entry, i);
    3650             264 :       return true;
    3651                 :     }
    3652                 :   }
    3653                 : 
    3654            3421 :   return false;
    3655                 : }
    3656                 : 
    3657                 : // remove a cookie from the hashtable, and update the iterator state.
    3658                 : void
    3659             364 : nsCookieService::RemoveCookieFromList(const nsListIter              &aIter,
    3660                 :                                       mozIStorageBindingParamsArray *aParamsArray)
    3661                 : {
    3662                 :   // if it's a non-session cookie, remove it from the db
    3663             364 :   if (!aIter.Cookie()->IsSession() && mDBState->dbConn) {
    3664                 :     // Use the asynchronous binding methods to ensure that we do not acquire
    3665                 :     // the database lock.
    3666             249 :     mozIStorageAsyncStatement *stmt = mDBState->stmtDelete;
    3667             498 :     nsCOMPtr<mozIStorageBindingParamsArray> paramsArray(aParamsArray);
    3668             249 :     if (!paramsArray) {
    3669             204 :       stmt->NewBindingParamsArray(getter_AddRefs(paramsArray));
    3670                 :     }
    3671                 : 
    3672             498 :     nsCOMPtr<mozIStorageBindingParams> params;
    3673             249 :     paramsArray->NewBindingParams(getter_AddRefs(params));
    3674                 : 
    3675             498 :     nsresult rv = params->BindUTF8StringByName(NS_LITERAL_CSTRING("name"),
    3676             498 :                                                aIter.Cookie()->Name());
    3677             249 :     NS_ASSERT_SUCCESS(rv);
    3678                 : 
    3679             498 :     rv = params->BindUTF8StringByName(NS_LITERAL_CSTRING("host"),
    3680             498 :                                       aIter.Cookie()->Host());
    3681             249 :     NS_ASSERT_SUCCESS(rv);
    3682                 : 
    3683             498 :     rv = params->BindUTF8StringByName(NS_LITERAL_CSTRING("path"),
    3684             498 :                                       aIter.Cookie()->Path());
    3685             249 :     NS_ASSERT_SUCCESS(rv);
    3686                 : 
    3687             249 :     rv = paramsArray->AddParams(params);
    3688             249 :     NS_ASSERT_SUCCESS(rv);
    3689                 : 
    3690                 :     // If we weren't given a params array, we'll need to remove it ourselves.
    3691             249 :     if (!aParamsArray) {
    3692             204 :       rv = stmt->BindParameters(paramsArray);
    3693             204 :       NS_ASSERT_SUCCESS(rv);
    3694             408 :       nsCOMPtr<mozIStoragePendingStatement> handle;
    3695             204 :       rv = stmt->ExecuteAsync(mDBState->removeListener, getter_AddRefs(handle));
    3696             204 :       NS_ASSERT_SUCCESS(rv);
    3697                 :     }
    3698                 :   }
    3699                 : 
    3700             364 :   if (aIter.entry->GetCookies().Length() == 1) {
    3701                 :     // we're removing the last element in the array - so just remove the entry
    3702                 :     // from the hash. note that the entryclass' dtor will take care of
    3703                 :     // releasing this last element for us!
    3704             275 :     mDBState->hostTable.RawRemoveEntry(aIter.entry);
    3705                 : 
    3706                 :   } else {
    3707                 :     // just remove the element from the list
    3708              89 :     aIter.entry->GetCookies().RemoveElementAt(aIter.index);
    3709                 :   }
    3710                 : 
    3711             364 :   --mDBState->cookieCount;
    3712             364 : }
    3713                 : 
    3714                 : void
    3715           15713 : bindCookieParameters(mozIStorageBindingParamsArray *aParamsArray,
    3716                 :                      const nsCString &aBaseDomain,
    3717                 :                      const nsCookie *aCookie)
    3718                 : {
    3719           15713 :   NS_ASSERTION(aParamsArray, "Null params array passed to bindCookieParameters!");
    3720           15713 :   NS_ASSERTION(aCookie, "Null cookie passed to bindCookieParameters!");
    3721                 :   nsresult rv;
    3722                 : 
    3723                 :   // Use the asynchronous binding methods to ensure that we do not acquire the
    3724                 :   // database lock.
    3725           31426 :   nsCOMPtr<mozIStorageBindingParams> params;
    3726           15713 :   rv = aParamsArray->NewBindingParams(getter_AddRefs(params));
    3727           15713 :   NS_ASSERT_SUCCESS(rv);
    3728                 : 
    3729                 :   // Bind our values to params
    3730           31426 :   rv = params->BindUTF8StringByName(NS_LITERAL_CSTRING("baseDomain"),
    3731           15713 :                                     aBaseDomain);
    3732           15713 :   NS_ASSERT_SUCCESS(rv);
    3733                 : 
    3734           31426 :   rv = params->BindUTF8StringByName(NS_LITERAL_CSTRING("name"),
    3735           31426 :                                     aCookie->Name());
    3736           15713 :   NS_ASSERT_SUCCESS(rv);
    3737                 : 
    3738           31426 :   rv = params->BindUTF8StringByName(NS_LITERAL_CSTRING("value"),
    3739           31426 :                                     aCookie->Value());
    3740           15713 :   NS_ASSERT_SUCCESS(rv);
    3741                 : 
    3742           31426 :   rv = params->BindUTF8StringByName(NS_LITERAL_CSTRING("host"),
    3743           31426 :                                     aCookie->Host());
    3744           15713 :   NS_ASSERT_SUCCESS(rv);
    3745                 : 
    3746           31426 :   rv = params->BindUTF8StringByName(NS_LITERAL_CSTRING("path"),
    3747           31426 :                                     aCookie->Path());
    3748           15713 :   NS_ASSERT_SUCCESS(rv);
    3749                 : 
    3750           31426 :   rv = params->BindInt64ByName(NS_LITERAL_CSTRING("expiry"),
    3751           15713 :                                aCookie->Expiry());
    3752           15713 :   NS_ASSERT_SUCCESS(rv);
    3753                 : 
    3754           31426 :   rv = params->BindInt64ByName(NS_LITERAL_CSTRING("lastAccessed"),
    3755           15713 :                                aCookie->LastAccessed());
    3756           15713 :   NS_ASSERT_SUCCESS(rv);
    3757                 : 
    3758           31426 :   rv = params->BindInt64ByName(NS_LITERAL_CSTRING("creationTime"),
    3759           15713 :                                aCookie->CreationTime());
    3760           15713 :   NS_ASSERT_SUCCESS(rv);
    3761                 : 
    3762           31426 :   rv = params->BindInt32ByName(NS_LITERAL_CSTRING("isSecure"),
    3763           15713 :                                aCookie->IsSecure());
    3764           15713 :   NS_ASSERT_SUCCESS(rv);
    3765                 : 
    3766           31426 :   rv = params->BindInt32ByName(NS_LITERAL_CSTRING("isHttpOnly"),
    3767           15713 :                                aCookie->IsHttpOnly());
    3768           15713 :   NS_ASSERT_SUCCESS(rv);
    3769                 : 
    3770                 :   // Bind the params to the array.
    3771           15713 :   rv = aParamsArray->AddParams(params);
    3772           15713 :   NS_ASSERT_SUCCESS(rv);
    3773           15713 : }
    3774                 : 
    3775                 : void
    3776           28728 : nsCookieService::AddCookieToList(const nsCString               &aBaseDomain,
    3777                 :                                  nsCookie                      *aCookie,
    3778                 :                                  DBState                       *aDBState,
    3779                 :                                  mozIStorageBindingParamsArray *aParamsArray,
    3780                 :                                  bool                           aWriteToDB)
    3781                 : {
    3782           28728 :   NS_ASSERTION(!(aDBState->dbConn && !aWriteToDB && aParamsArray),
    3783                 :                "Not writing to the DB but have a params array?");
    3784           28728 :   NS_ASSERTION(!(!aDBState->dbConn && aParamsArray),
    3785                 :                "Do not have a DB connection but have a params array?");
    3786                 : 
    3787           28728 :   nsCookieEntry *entry = aDBState->hostTable.PutEntry(aBaseDomain);
    3788           28728 :   NS_ASSERTION(entry, "can't insert element into a null entry!");
    3789                 : 
    3790           28728 :   entry->GetCookies().AppendElement(aCookie);
    3791           28728 :   ++aDBState->cookieCount;
    3792                 : 
    3793                 :   // keep track of the oldest cookie, for when it comes time to purge
    3794           28728 :   if (aCookie->LastAccessed() < aDBState->cookieOldestTime)
    3795             191 :     aDBState->cookieOldestTime = aCookie->LastAccessed();
    3796                 : 
    3797                 :   // if it's a non-session cookie and hasn't just been read from the db, write it out.
    3798           28728 :   if (aWriteToDB && !aCookie->IsSession() && aDBState->dbConn) {
    3799           15696 :     mozIStorageAsyncStatement *stmt = aDBState->stmtInsert;
    3800           31392 :     nsCOMPtr<mozIStorageBindingParamsArray> paramsArray(aParamsArray);
    3801           15696 :     if (!paramsArray) {
    3802           15696 :       stmt->NewBindingParamsArray(getter_AddRefs(paramsArray));
    3803                 :     }
    3804           15696 :     bindCookieParameters(paramsArray, aBaseDomain, aCookie);
    3805                 : 
    3806                 :     // If we were supplied an array to store parameters, we shouldn't call
    3807                 :     // executeAsync - someone up the stack will do this for us.
    3808           15696 :     if (!aParamsArray) {
    3809           15696 :       nsresult rv = stmt->BindParameters(paramsArray);
    3810           15696 :       NS_ASSERT_SUCCESS(rv);
    3811           31392 :       nsCOMPtr<mozIStoragePendingStatement> handle;
    3812           15696 :       rv = stmt->ExecuteAsync(mDBState->insertListener, getter_AddRefs(handle));
    3813           15696 :       NS_ASSERT_SUCCESS(rv);
    3814                 :     }
    3815                 :   }
    3816           28728 : }
    3817                 : 
    3818                 : void
    3819               0 : nsCookieService::UpdateCookieInList(nsCookie                      *aCookie,
    3820                 :                                     PRInt64                        aLastAccessed,
    3821                 :                                     mozIStorageBindingParamsArray *aParamsArray)
    3822                 : {
    3823               0 :   NS_ASSERTION(aCookie, "Passing a null cookie to UpdateCookieInList!");
    3824                 : 
    3825                 :   // udpate the lastAccessed timestamp
    3826               0 :   aCookie->SetLastAccessed(aLastAccessed);
    3827                 : 
    3828                 :   // if it's a non-session cookie, update it in the db too
    3829               0 :   if (!aCookie->IsSession() && aParamsArray) {
    3830                 :     // Create our params holder.
    3831               0 :     nsCOMPtr<mozIStorageBindingParams> params;
    3832               0 :     aParamsArray->NewBindingParams(getter_AddRefs(params));
    3833                 : 
    3834                 :     // Bind our parameters.
    3835               0 :     nsresult rv = params->BindInt64ByName(NS_LITERAL_CSTRING("lastAccessed"),
    3836               0 :                                           aLastAccessed);
    3837               0 :     NS_ASSERT_SUCCESS(rv);
    3838                 : 
    3839               0 :     rv = params->BindUTF8StringByName(NS_LITERAL_CSTRING("name"),
    3840               0 :                                       aCookie->Name());
    3841               0 :     NS_ASSERT_SUCCESS(rv);
    3842                 : 
    3843               0 :     rv = params->BindUTF8StringByName(NS_LITERAL_CSTRING("host"),
    3844               0 :                                       aCookie->Host());
    3845               0 :     NS_ASSERT_SUCCESS(rv);
    3846                 : 
    3847               0 :     rv = params->BindUTF8StringByName(NS_LITERAL_CSTRING("path"),
    3848               0 :                                       aCookie->Path());
    3849               0 :     NS_ASSERT_SUCCESS(rv);
    3850                 : 
    3851                 :     // Add our bound parameters to the array.
    3852               0 :     rv = aParamsArray->AddParams(params);
    3853               0 :     NS_ASSERT_SUCCESS(rv);
    3854                 :   }
    3855            4392 : }
    3856                 : 

Generated by: LCOV version 1.7