summaryrefslogtreecommitdiffstats
path: root/Source/WebCore/platform/blackberry/CookieManager.cpp
blob: db012bfb40ae09ba0bfac430d06acbc7f0eb5cc0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
/*
 * Copyright (C) 2008, 2009 Julien Chaffraix <julien.chaffraix@gmail.com>
 * Copyright (C) 2010, 2011, 2012 Research In Motion Limited. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#define ENABLE_COOKIE_DEBUG 0
#define ENABLE_COOKIE_SUPER_VERBOSE_DEBUG 0
#define ENABLE_COOKIE_LIMIT_DEBUG 0

#include "config.h"
#include "CookieManager.h"

#include "CookieDatabaseBackingStore.h"
#include "CookieParser.h"
#include "FileSystem.h"
#include "Logging.h"
#include "WebSettings.h"
#include <BlackBerryPlatformExecutableMessage.h>
#include <BlackBerryPlatformMessageClient.h>
#include <BlackBerryPlatformNavigatorHandler.h>
#include <BlackBerryPlatformSettings.h>
#include <network/DomainTools.h>
#include <stdlib.h>
#include <wtf/CurrentTime.h>
#include <wtf/text/CString.h>
#include <wtf/text/StringBuilder.h>
#include <wtf/text/WTFString.h>

#if ENABLE_COOKIE_DEBUG
#include <BlackBerryPlatformLog.h>
#endif

#if ENABLE_COOKIE_SUPER_VERBOSE_DEBUG
#define CookieLog(format, ...) BlackBerry::Platform::logAlways(BlackBerry::Platform::LogLevelInfo, format, ## __VA_ARGS__)
#else
#define CookieLog(format, ...)
#endif // ENABLE_COOKIE_SUPER_VERBOSE_DEBUG

#if ENABLE_COOKIE_LIMIT_DEBUG
#define CookieLimitLog(format, ...) BlackBerry::Platform::logAlways(BlackBerry::Platform::LogLevelInfo, format, ## __VA_ARGS__)
#else
#define CookieLimitLog(format, ...)
#endif // ENABLE_COOKIE_LIMIT_DEBUG

namespace WebCore {

// Max count constants.
static const unsigned s_globalMaxCookieCount = 6000;
static const unsigned s_maxCookieCountPerHost = 60;
static const unsigned s_cookiesToDeleteWhenLimitReached = 60;
static const unsigned s_delayToStartCookieCleanup = 10;

CookieManager& cookieManager()
{
    static CookieManager *cookieManager = 0;
    if (!cookieManager) {
        // Open the cookieJar now and get the backing store cookies to fill the manager.
        cookieManager = new CookieManager;
        cookieManager->m_cookieBackingStore->open(cookieManager->cookieJar());
        cookieManager->getBackingStoreCookies();
        CookieLog("CookieManager - Backingstore load complete.\n");
    }
    return *cookieManager;
}

CookieManager::CookieManager()
    : m_count(0)
    , m_privateMode(false)
    , m_shouldDumpAllCookies(false)
    , m_cookieJarFileName(pathByAppendingComponent(BlackBerry::Platform::Settings::instance()->applicationDataDirectory().c_str(), "/cookieCollection.db"))
    , m_policy(CookieStorageAcceptPolicyAlways)
    , m_cookieBackingStore(CookieDatabaseBackingStore::create())
    , m_limitTimer(this, &CookieManager::cookieLimitCleanUp)
{
}

CookieManager::~CookieManager()
{
    removeAllCookies(DoNotRemoveFromBackingStore);
    // FIXME: m_managerMap and the top layer protocolMaps are not properly deleted.
    // Do not delete any protocol maps to avoid double-deletion of the maps that are
    // being used for both secure and non-secure protocols; this leak is OK since
    // there's nothing important in the hashtable destructors, and the memory will be reclaimed on exit

    // FIXME: CookieDatabaseBackingStore is not deleted, only flushed
    // (currently the destructor is never called since this class is a
    // singleton; on exit, the db is flushed manually. This call is only here
    // as a fallback in case this class is made a non-singleton.
    m_cookieBackingStore->sendChangesToDatabaseSynchronously();
}

// Sorting logic is based on Cookie Spec RFC6265, section 5.4.2
static bool cookieSorter(ParsedCookie* a, ParsedCookie* b)
{
    if (a->path().length() == b->path().length())
        return a->creationTime() < b->creationTime();
    return a->path().length() > b->path().length();
}

// Returns whether the protocol supports domains
static bool shouldIgnoreDomain(const String protocol)
{
    // ignore domain security for file and local
    return protocol == "file" || protocol == "local";
}

void CookieManager::setCookies(const KURL& url, const String& value, CookieFilter filter)
{
    CookieLog("CookieManager - Setting cookies");
    CookieParser parser(url);
    Vector<ParsedCookie*> cookies = parser.parse(value);

    for (size_t i = 0; i < cookies.size(); ++i) {
        BackingStoreRemovalPolicy treatment = m_privateMode ? DoNotRemoveFromBackingStore : RemoveFromBackingStore;
        checkAndTreatCookie(cookies[i], treatment, filter);
    }
}

void CookieManager::setCookies(const KURL& url, const Vector<String>& cookies, CookieFilter filter)
{
    CookieLog("CookieManager - Setting cookies");
    CookieParser parser(url);
    for (size_t i = 0; i < cookies.size(); ++i) {
        BackingStoreRemovalPolicy treatment = m_privateMode ? DoNotRemoveFromBackingStore : RemoveFromBackingStore;
        if (ParsedCookie* parsedCookie = parser.parseOneCookie(cookies[i]))
            checkAndTreatCookie(parsedCookie, treatment, filter);
    }
}

String CookieManager::getCookie(const KURL& url, CookieFilter filter) const
{
    Vector<ParsedCookie*> rawCookies;
    rawCookies.reserveInitialCapacity(s_maxCookieCountPerHost);

    // Retrieve cookies related to this url
    getRawCookies(rawCookies, url, filter);

    CookieLog("CookieManager - there are %d cookies in raw cookies\n", rawCookies.size());

    // Generate the cookie header string using the retrieved cookies
    StringBuilder cookieStringBuilder;
    cookieStringBuilder.reserveCapacity(512);
    size_t cookieSize = rawCookies.size();
    for (size_t i = 0; i < cookieSize; i++) {
        cookieStringBuilder.append(rawCookies[i]->toNameValuePair());
        if (i != cookieSize-1)
            cookieStringBuilder.append("; ");
    }

    CookieLog("CookieManager - cookieString is - %s\n", cookieStringBuilder.toString().utf8().data());

    return cookieStringBuilder.toString();
}

String CookieManager::generateHtmlFragmentForCookies()
{
    CookieLog("CookieManager - generateHtmlFragmentForCookies\n");

    Vector<ParsedCookie*> cookieCandidates;
    for (HashMap<String, CookieMap*>::iterator it = m_managerMap.begin(); it != m_managerMap.end(); ++it)
        it->second->getAllChildCookies(&cookieCandidates);

    String result;
    ParsedCookie* cookie = 0;
    result.append(String("<table style=\"word-wrap:break-word\" cellSpacing=\"0\" cellPadding=\"0\" border=\"1\"><tr><th>Domain</th><th>Path</th><th>Protocol</th><th>Name</th><th>Value</th><th>Secure</th><th>HttpOnly</th><th>Session</th></tr>"));
    for (size_t i = 0; i < cookieCandidates.size(); ++i) {
        cookie = cookieCandidates[i];
        result.append(String("<tr><td align=\"center\">"));
        result.append(cookie->domain());
        result.append(String("<td align=\"center\">"));
        result.append(cookie->path());
        result.append(String("<td align=\"center\">"));
        result.append(cookie->protocol());
        result.append(String("<td align=\"center\">"));
        result.append(cookie->name());
        result.append(String("<td align=\"center\" style= \"word-break:break-all\">"));
        result.append(cookie->value());
        result.append(String("<td align=\"center\">"));
        result.append(String(cookie->isSecure() ? "Yes" : "No"));
        result.append(String("<td align=\"center\">"));
        result.append(String(cookie->isHttpOnly() ? "Yes" : "No"));
        result.append(String("<td align=\"center\">"));
        result.append(String(cookie->isSession() ? "Yes" : "No"));
        result.append(String("</td></tr>"));
    }
    result.append(String("</table>"));
    return result;
}

void CookieManager::getRawCookies(Vector<ParsedCookie*> &stackOfCookies, const KURL& requestURL, CookieFilter filter) const
{
    CookieLog("CookieManager - getRawCookies - processing url with domain - %s & protocol: %s & path: %s\n", requestURL.host().utf8().data(), requestURL.protocol().utf8().data(), requestURL.path().utf8().data());

    bool specialCaseForLocal = (requestURL.protocolIs("local") || requestURL.protocolIs("file")) && m_shouldDumpAllCookies;
    bool isConnectionSecure = requestURL.protocolIs("https") || requestURL.protocolIs("wss") || specialCaseForLocal;

    Vector<ParsedCookie*> cookieCandidates;
    Vector<CookieMap*> protocolsToSearch;

    // Special Case: If a server sets a "secure" cookie over a non-secure channel and tries to access the cookie
    // over a secure channel, it will not succeed because the secure protocol isn't mapped to the insecure protocol yet.
    // Set the map to the non-secure version, so it'll search the mapping for a secure cookie.
    CookieMap* targetMap = m_managerMap.get(requestURL.protocol());
    if (!targetMap && isConnectionSecure) {
        CookieLog("CookieManager - special case: secure protocol are not linked yet.");
        if (requestURL.protocolIs("https"))
            targetMap = m_managerMap.get("http");
        else if (requestURL.protocolIs("wss"))
            targetMap = m_managerMap.get("ws");
    }

    if (specialCaseForLocal)
        copyValuesToVector(m_managerMap, protocolsToSearch);
    else {
        protocolsToSearch.append(targetMap);
        // FIXME: this is a hack for webworks apps; RFC 6265 says "Cookies do not provide isolation by scheme"
        // so we should not be checking protocols at all. See PR 135595
        if (m_shouldDumpAllCookies) {
            protocolsToSearch.append(m_managerMap.get("file"));
            protocolsToSearch.append(m_managerMap.get("local"));
       }
    }

    Vector<String> delimitedHost;

    // IP addresses are stored in a particular format (due to ipv6). Reduce the ip address so we can match
    // it with the one in memory.
    string canonicalIP = BlackBerry::Platform::getCanonicalIPFormat(requestURL.host().utf8().data());
    if (!canonicalIP.empty())
        delimitedHost.append(String(canonicalIP.c_str()));
    else
        requestURL.host().lower().split(".", true, delimitedHost);

    // Go through all the protocol trees that we need to search for
    // and get all cookies that are valid for this domain
    for (size_t k = 0; k < protocolsToSearch.size(); k++) {
        CookieMap* currentMap = protocolsToSearch[k];

        // if no cookies exist for this protocol, break right away
        if (!currentMap)
            continue;

        CookieLog("CookieManager - looking at protocol map %s \n", currentMap->getName().utf8().data());

        // Special case for local and files - because WebApps expect to get ALL cookies from the backing-store on local protocol
        if (specialCaseForLocal) {
            CookieLog("CookieManager - special case find in protocol map - %s\n", currentMap->getName().utf8().data());
            currentMap->getAllChildCookies(&cookieCandidates);
        } else {
            // Get cookies from the null domain map
            currentMap->getAllCookies(&cookieCandidates);

            // Get cookies from the valid domain maps
            int i = delimitedHost.size() - 1;
            while (i >= 0) {
                CookieLog("CookieManager - finding %s in currentmap\n", delimitedHost[i].utf8().data());
                currentMap = currentMap->getSubdomainMap(delimitedHost[i]);
                // if this subdomain/domain does not exist in our mapping then we simply exit
                if (!currentMap) {
                    CookieLog("CookieManager - cannot find next map exiting the while loop.\n");
                    break;
                }
                CookieLog("CookieManager - found the map, grabbing cookies from this map\n");
                currentMap->getAllCookies(&cookieCandidates);
                i--;
            }
        }
    }

    CookieLog("CookieManager - there are %d cookies in candidate\n", cookieCandidates.size());

    for (size_t i = 0; i < cookieCandidates.size(); ++i) {
        ParsedCookie* cookie = cookieCandidates[i];

        // According to the path-matches rules in RFC6265, section 5.1.4,
        // we should add a '/' at the end of cookie-path for comparison if the cookie-path is not end with '/'.
        String path = cookie->path();
        CookieLog("CookieManager - comparing cookie path %s (len %d) to request path %s (len %d)", path.utf8().data(), path.length(), requestURL.path().utf8().data(), path.length());
        if (!equalIgnoringCase(path, requestURL.path()) && !path.endsWith("/", false))
            path = path + "/";

        // Only secure connections have access to secure cookies. Unless specialCaseForLocal is true
        // Get the cookies filtering out HttpOnly cookies if requested.
        if (requestURL.path().startsWith(path, false) && (isConnectionSecure || !cookie->isSecure()) && (filter == WithHttpOnlyCookies || !cookie->isHttpOnly())) {
            CookieLog("CookieManager - cookie chosen - %s\n", cookie->toString().utf8().data());
            cookie->setLastAccessed(currentTime());
            stackOfCookies.append(cookie);
        }
    }

    std::stable_sort(stackOfCookies.begin(), stackOfCookies.end(), cookieSorter);
}

void CookieManager::removeAllCookies(BackingStoreRemovalPolicy backingStoreRemoval)
{
    HashMap<String, CookieMap*>::iterator first = m_managerMap.begin();
    HashMap<String, CookieMap*>::iterator end = m_managerMap.end();
    for (HashMap<String, CookieMap*>::iterator it = first; it != end; ++it)
        it->second->deleteAllCookiesAndDomains();

    if (backingStoreRemoval == RemoveFromBackingStore)
        m_cookieBackingStore->removeAll();
    m_count = 0;
}

void CookieManager::setCookieJar(const char* fileName)
{
    m_cookieJarFileName = String(fileName);
    m_cookieBackingStore->open(m_cookieJarFileName);
}

void CookieManager::checkAndTreatCookie(ParsedCookie* candidateCookie, BackingStoreRemovalPolicy postToBackingStore, CookieFilter filter)
{
    CookieLog("CookieManager - checkAndTreatCookie - processing url with domain - %s & protocol %s\n", candidateCookie->domain().utf8().data(), candidateCookie->protocol().utf8().data());

    // A cookie which is not from http shouldn't have a httpOnly property.
    if (filter == NoHttpOnlyCookie && candidateCookie->isHttpOnly()) {
        delete candidateCookie;
        return;
    }

    const bool ignoreDomain = shouldIgnoreDomain(candidateCookie->protocol());

    // Determine which protocol tree to add the cookie to. Create one if necessary.
    CookieMap* curMap = 0;
    if (m_managerMap.contains(candidateCookie->protocol()))
        curMap = m_managerMap.get(candidateCookie->protocol());
    else {
        // Check if it is a secure version, if it is, link it to the non-secure version
        // Link curMap to the new protocol as well as the old one if it doesn't exist
        if (candidateCookie->protocol() == "https") {
            curMap = m_managerMap.get("http");
            if (!curMap) {
                curMap = new CookieMap("http");
                m_managerMap.add("http", curMap);
            }
        } else if (candidateCookie->protocol() == "wss") {
            curMap = m_managerMap.get("ws");
            if (!curMap) {
                curMap = new CookieMap("ws");
                m_managerMap.add("ws", curMap);
            }
        } else
            curMap = new CookieMap(candidateCookie->protocol());

        CookieLog("CookieManager - adding protocol cookiemap - %s\n", curMap->getName().utf8().data());

        m_managerMap.add(candidateCookie->protocol(), curMap);
    }

    // If protocol support domain, we have to traverse the domain tree to find the right
    // cookieMap to handle with
    if (!ignoreDomain)
        curMap = findOrCreateCookieMap(curMap, *candidateCookie);

    // Now that we have the proper map for this cookie, we can modify it
    // If cookie does not exist and has expired, delete it
    // If cookie exists and it has expired, so we must remove it from the map, if not update it
    // If cookie expired and came from the BackingStore (therefore does not exist), we have to remove from database
    // If cookie does not exist & it's valid, add it to the current map

    if (candidateCookie->hasExpired() || candidateCookie->isForceExpired()) {
        // Special case for getBackingStoreCookies() to catch expired cookies
        if (postToBackingStore == BackingStoreCookieEntry)
            m_cookieBackingStore->remove(candidateCookie);
        else if (curMap) {
            // RemoveCookie will return 0 if the cookie doesn't exist.
            ParsedCookie* expired = curMap->removeCookie(candidateCookie, filter);
            // Cookie is useless, Remove the cookie from the backingstore if it exists.
            // Backup check for BackingStoreCookieEntry incase someone incorrectly uses this enum.
            if (expired && postToBackingStore != BackingStoreCookieEntry && !expired->isSession()) {
                CookieLog("CookieManager - expired cookie is nonsession, deleting from db");
                m_cookieBackingStore->remove(expired);
            }
            delete expired;

        } else
            delete candidateCookie;
    } else {
        ASSERT(curMap);
        addCookieToMap(curMap, candidateCookie, postToBackingStore, filter);
    }
}

void CookieManager::addCookieToMap(CookieMap* targetMap, ParsedCookie* candidateCookie, BackingStoreRemovalPolicy postToBackingStore, CookieFilter filter)
{
    ParsedCookie* replacedCookie = 0;

    if (!targetMap->addOrReplaceCookie(candidateCookie, &replacedCookie, filter)) {

        CookieLog("CookieManager - rejecting new cookie - %s.\n", candidateCookie->toString().utf8().data());

        delete candidateCookie;
        return;
    }

    if (replacedCookie) {
        CookieLog("CookieManager - updating new cookie - %s.\n", candidateCookie->toString().utf8().data());

        // A cookie was replaced in targetMap.
        // If old cookie is non-session and new one is, we have to delete it from backingstore
        // If new cookie is non-session and old one is, we have to add it to backingstore
        // If both sessions are non-session, then we update it in the backingstore
        bool newIsSession = candidateCookie->isSession();
        bool oldIsSession = replacedCookie->isSession();

        if (postToBackingStore == RemoveFromBackingStore) {
            if (!newIsSession && !oldIsSession)
                m_cookieBackingStore->update(candidateCookie);
            else if (newIsSession && !oldIsSession) {
                // Must manually decrease the counter because it was not counted when
                // the cookie was removed in cookieVector.
                removedCookie();
                m_cookieBackingStore->remove(replacedCookie);
            } else if (!newIsSession && oldIsSession) {
                // Must manually increase the counter because it was not counted when
                // the cookie was added in cookieVector.
                addedCookie();
                m_cookieBackingStore->insert(candidateCookie);
            }
        }
        delete replacedCookie;
        return;
    }

    CookieLog("CookieManager - adding new cookie - %s.\n", candidateCookie->toString().utf8().data());

    ParsedCookie* oldestCookie = 0;
    // Check if we have not reached the per cookie domain limit.
    // If that is not true, we check if the global limit has been reached if backingstore mode is on
    // Two points:
    // 1) We only do a global check if backingstore mode is on because the global cookie limit only
    //    counts session cookies that are saved in the database. If the user goes over the limit
    //    when they are in private mode, we know that the total cookie limit will be under the limit
    //    once the user goes back to normal mode (memory deleted and reloaded from the database)
    // 2) We use else if for this statement because if we remove a cookie in the 1st statement
    //    then it means the global count will never exceed the limit

    CookieLimitLog("CookieManager - local count: %d  global count: %d", targetMap->count(), m_count);
    if (targetMap->count() > s_maxCookieCountPerHost) {
        CookieLog("CookieManager - deleting oldest cookie from this map due to domain count.\n");
        oldestCookie = targetMap->removeOldestCookie();
    } else if (m_count > s_globalMaxCookieCount && (postToBackingStore != DoNotRemoveFromBackingStore)) {
        CookieLimitLog("CookieManager - Global limit reached, initiate cookie limit clean up.");
        initiateCookieLimitCleanUp();
    }

    // Only add non session cookie to the backing store.
    if (postToBackingStore == RemoveFromBackingStore) {
        if (oldestCookie && !oldestCookie->isSession()) {
            CookieLog("CookieManager - oldestCookie exists, deleting it from backingstore and destructing.\n");
            m_cookieBackingStore->remove(oldestCookie);
        }
        if (!candidateCookie->isSession())
            m_cookieBackingStore->insert(candidateCookie);
    }
    if (oldestCookie)
        delete oldestCookie;
}

void CookieManager::getBackingStoreCookies()
{
    // This method should be called just after having created the cookieManager
    // NEVER afterwards!
    ASSERT(!m_count);

    Vector<ParsedCookie*> cookies;
    m_cookieBackingStore->getCookiesFromDatabase(cookies);
    CookieLog("CookieManager - Backingstore has %d cookies, loading them in memory now", cookies.size());
    for (size_t i = 0; i < cookies.size(); ++i) {
        ParsedCookie* newCookie = cookies[i];
        checkAndTreatCookie(newCookie, BackingStoreCookieEntry);
    }
}

void CookieManager::setPrivateMode(bool mode)
{
    if (m_privateMode == mode)
        return;

    m_privateMode = mode;
    if (!mode) {
        removeAllCookies(DoNotRemoveFromBackingStore);
        getBackingStoreCookies();
    }
}

CookieMap* CookieManager::findOrCreateCookieMap(CookieMap* protocolMap, const ParsedCookie& candidateCookie)
{
    // Explode the domain with the '.' delimiter
    Vector<String> delimitedHost;

    // If the domain is an IP address, don't split it.
    if (candidateCookie.domainIsIPAddress())
        delimitedHost.append(candidateCookie.domain());
    else
        candidateCookie.domain().split(".", delimitedHost);

    CookieMap* curMap = protocolMap;
    size_t hostSize = delimitedHost.size();

    CookieLog("CookieManager - looking at protocol map %s \n", protocolMap->getName().utf8().data());

    // Find & create necessary CookieMaps by traversing down the domain tree
    // Each CookieMap represent a subsection of the domain, delimited by "."
    int i = hostSize - 1;
    while (i >= 0) {
        CookieLog("CookieManager - finding %s in currentmap\n", delimitedHost[i].utf8().data());
        CookieMap* nextMap = curMap->getSubdomainMap(delimitedHost[i]);
        if (!nextMap) {
            CookieLog("CookieManager - cannot find map\n");
            if (candidateCookie.hasExpired())
                return 0;
            CookieLog("CookieManager - creating %s in currentmap %s\n", delimitedHost[i].utf8().data(), curMap->getName().utf8().data());
            nextMap = new CookieMap(delimitedHost[i]);
            CookieLog("CookieManager - adding subdomain to map\n");
            curMap->addSubdomainMap(delimitedHost[i], nextMap);
        }
        curMap = nextMap;
        i--;
    }
    return curMap;
}

void CookieManager::removeCookieWithName(const KURL& url, const String& cookieName)
{
    // We get all cookies from all domains that domain matches the request domain
    // and delete any cookies with the specified name that path matches the request path
    Vector<ParsedCookie*> results;
    getRawCookies(results, url, WithHttpOnlyCookies);
    // Delete the cookies that path matches the request path
    for (size_t i = 0; i < results.size(); i++) {
        ParsedCookie* cookie = results[i];
        if (!equalIgnoringCase(cookie->name(), cookieName))
            continue;
        if (url.path().startsWith(cookie->path(), false)) {
            cookie->forceExpire();
            checkAndTreatCookie(cookie, RemoveFromBackingStore);
        }
    }
}

void CookieManager::initiateCookieLimitCleanUp()
{
    if (!m_limitTimer.isActive()) {
        CookieLog("CookieManager - Starting a timer for cookie cleanup");
        m_limitTimer.startOneShot(s_delayToStartCookieCleanup);
    } else {
#ifndef NDEBUG
        CookieLog("CookieManager - Cookie cleanup timer already running");
#endif
    }
}

void CookieManager::cookieLimitCleanUp(Timer<CookieManager>* timer)
{
    ASSERT_UNUSED(timer, timer == &m_limitTimer);

    CookieLimitLog("CookieManager - Starting cookie clean up");

    size_t numberOfCookiesOverLimit = (m_count > s_globalMaxCookieCount) ? m_count - s_globalMaxCookieCount : 0;
    size_t amountToDelete = s_cookiesToDeleteWhenLimitReached + numberOfCookiesOverLimit;

    CookieLimitLog("CookieManager - Excess: %d  Amount to Delete: %d", numberOfCookiesOverLimit, amountToDelete);

    // Call the database to delete 'amountToDelete' of cookies
    Vector<ParsedCookie*> cookiesToDelete;
    cookiesToDelete.reserveInitialCapacity(amountToDelete);

    CookieLimitLog("CookieManager - Calling database to clean up");
    m_cookieBackingStore->getCookiesFromDatabase(cookiesToDelete, amountToDelete);

    // Cookies are ordered in ASC order by lastAccessed
    for (size_t i = 0; i < amountToDelete; ++i) {
        // Expire them and call checkandtreat to delete them from memory and database
        ParsedCookie* newCookie = cookiesToDelete[i];
        CookieLimitLog("CookieManager - Expire cookie: %s and delete", newCookie->toString().utf8().data());
        newCookie->forceExpire();
        checkAndTreatCookie(newCookie, RemoveFromBackingStore);
    }

    CookieLimitLog("CookieManager - Cookie clean up complete.");
}

} // namespace WebCore