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 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661
|
/*
* Copyright (C) 2008, 2009 Julien Chaffraix <julien.chaffraix@gmail.com>
* Copyright (C) 2010, 2011, 2012, 2013 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());
}
return *cookieManager;
}
CookieManager::CookieManager()
: m_count(0)
, m_privateMode(false)
, m_shouldDumpAllCookies(false)
, m_syncedWithDatabase(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(PassRefPtr<ParsedCookie> a, PassRefPtr<ParsedCookie> b)
{
if (a->path().length() == b->path().length())
return a->creationTime() < b->creationTime();
return a->path().length() > b->path().length();
}
// Return whether we should ignore the scheme
static bool shouldIgnoreScheme(const String& protocol)
{
// We want to ignore file and local schemes
return protocol == "file" || protocol == "local";
}
void CookieManager::setCookies(const KURL& url, const String& value, CookieFilter filter)
{
// If the database hasn't been sync-ed at this point, force a sync load
if (!m_syncedWithDatabase && !m_privateMode)
m_cookieBackingStore->openAndLoadDatabaseSynchronously(cookieJar());
CookieLog("CookieManager - Setting cookies");
CookieParser parser(url);
Vector<RefPtr<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)
{
// If the database hasn't been sync-ed at this point, force a sync load
if (!m_syncedWithDatabase && !m_privateMode)
m_cookieBackingStore->openAndLoadDatabaseSynchronously(cookieJar());
CookieLog("CookieManager - Setting cookies");
CookieParser parser(url);
for (size_t i = 0; i < cookies.size(); ++i) {
BackingStoreRemovalPolicy treatment = m_privateMode ? DoNotRemoveFromBackingStore : RemoveFromBackingStore;
if (RefPtr<ParsedCookie> parsedCookie = parser.parseOneCookie(cookies[i]))
checkAndTreatCookie(parsedCookie, treatment, filter);
}
}
String CookieManager::getCookie(const KURL& url, CookieFilter filter) const
{
// If the database hasn't been sync-ed at this point, force a sync load
if (!m_syncedWithDatabase && !m_privateMode)
m_cookieBackingStore->openAndLoadDatabaseSynchronously(cookieJar());
Vector<RefPtr<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()
{
// If the database hasn't been sync-ed at this point, force a sync load
if (!m_syncedWithDatabase && !m_privateMode)
m_cookieBackingStore->openAndLoadDatabaseSynchronously(cookieJar());
CookieLog("CookieManager - generateHtmlFragmentForCookies\n");
Vector<RefPtr<ParsedCookie> > cookieCandidates;
for (HashMap<String, CookieMap*>::iterator it = m_managerMap.begin(); it != m_managerMap.end(); ++it)
it->value->getAllChildCookies(&cookieCandidates);
String result;
RefPtr<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<RefPtr<ParsedCookie> > &stackOfCookies, const KURL& requestURL, CookieFilter filter) const
{
// Force a sync load of the database
if (!m_syncedWithDatabase && !m_privateMode)
m_cookieBackingStore->openAndLoadDatabaseSynchronously(cookieJar());
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());
const bool invalidScheme = shouldIgnoreScheme(requestURL.protocol());
const bool specialCaseForWebWorks = invalidScheme && m_shouldDumpAllCookies;
const bool isConnectionSecure = requestURL.protocolIs("https") || requestURL.protocolIs("wss") || specialCaseForWebWorks;
Vector<RefPtr<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");
}
// Decide which scheme tree we should look at.
// Return on invalid schemes. cookies are currently disabled on file and local.
// We only want to enable them for WebWorks that enabled a special flag.
if (specialCaseForWebWorks)
copyValuesToVector(m_managerMap, protocolsToSearch);
else if (invalidScheme)
return;
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.
BlackBerry::Platform::String canonicalIP = BlackBerry::Platform::getCanonicalIPFormat(requestURL.host());
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 (specialCaseForWebWorks) {
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 Host-only cookies
if (canonicalIP.empty()) {
CookieLog("CookieManager - looking for host-only cookies for host - %s", requestURL.host().utf8().data());
CookieMap* hostMap = currentMap->getSubdomainMap(requestURL.host());
if (hostMap)
hostMap->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) {
RefPtr<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 specialCaseForWebWorks 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->value->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(PassRefPtr<ParsedCookie> prpCandidateCookie, BackingStoreRemovalPolicy postToBackingStore, CookieFilter filter)
{
RefPtr<ParsedCookie> candidateCookie = prpCandidateCookie;
CookieLog("CookieManager - checkAndTreatCookie - processing url with domain - %s & protocol %s\n", candidateCookie->domain().utf8().data(), candidateCookie->protocol().utf8().data());
// Delete invalid cookies:
// 1) A cookie which is not from http shouldn't have a httpOnly property.
// 2) Cookies coming from schemes that we do not support and the special flag isn't on
if ((filter == NoHttpOnlyCookie && candidateCookie->isHttpOnly()) || (shouldIgnoreScheme(candidateCookie->protocol()) && !m_shouldDumpAllCookies))
return;
const bool ignoreDomain = (candidateCookie->protocol() == "file" || candidateCookie->protocol() == "local");
// 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.
RefPtr<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);
}
}
} else {
ASSERT(curMap);
CookieLog("CookieManager - adding cookiemap - %s\n", curMap->getName().utf8().data());
addCookieToMap(curMap, candidateCookie, postToBackingStore, filter);
}
}
void CookieManager::addCookieToMap(CookieMap* targetMap, PassRefPtr<ParsedCookie> prpCandidateCookie, BackingStoreRemovalPolicy postToBackingStore, CookieFilter filter)
{
RefPtr<ParsedCookie> replacedCookie = 0;
RefPtr<ParsedCookie> candidateCookie = prpCandidateCookie;
if (!targetMap->addOrReplaceCookie(candidateCookie, replacedCookie, filter)) {
CookieLog("CookieManager - rejecting new cookie - %s.\n", candidateCookie->toString().utf8().data());
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);
}
}
return;
}
CookieLog("CookieManager - adding new cookie - %s.\n", candidateCookie->toString().utf8().data());
RefPtr<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);
}
}
void CookieManager::getBackingStoreCookies()
{
// Make sure private mode is off when the database thread calls this method
if (m_privateMode)
return;
// If there exists cookies in memory, flush them out and we'll load everything from the database again
if (m_count)
removeAllCookies(DoNotRemoveFromBackingStore);
Vector<RefPtr<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) {
RefPtr<ParsedCookie> newCookie = cookies[i];
// The IP flag is not persisted in the database.
if (BlackBerry::Platform::isIPAddress(newCookie->domain()))
newCookie->setDomain(newCookie->domain(), true);
checkAndTreatCookie(newCookie, BackingStoreCookieEntry);
}
CookieLog("CookieManager - Backingstore loading complete.");
m_syncedWithDatabase = true;
}
void CookieManager::setPrivateMode(bool privateMode)
{
if (m_privateMode == privateMode)
return;
m_privateMode = privateMode;
// If we switched to private mode when the database cookies haven't loaded into memory yet
// we can return because there's nothing in memory anyway.
if (m_privateMode && !m_syncedWithDatabase)
return;
removeAllCookies(DoNotRemoveFromBackingStore);
// If we are switching back to public mode, reload the database to memory.
if (!m_privateMode)
getBackingStoreCookies();
}
CookieMap* CookieManager::findOrCreateCookieMap(CookieMap* protocolMap, const PassRefPtr<ParsedCookie> candidateCookie)
{
// Explode the domain with the '.' delimiter
Vector<String> delimitedHost;
// If the domain is an IP address or is a host-only domain, don't split it.
if (candidateCookie->domainIsIPAddress() || candidateCookie->isHostOnly())
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)
{
// Dispatch the message because the database cookies are not loaded in memory yet.
if (!m_syncedWithDatabase && !m_privateMode) {
typedef void (WebCore::CookieManager::*FunctionType)(const KURL&, const String&);
BlackBerry::Platform::webKitThreadMessageClient()->dispatchMessage(
BlackBerry::Platform::createMethodCallMessage<FunctionType, CookieManager, const KURL, const String>(
&CookieManager::removeCookieWithName, this, url, cookieName));
return;
}
// 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<RefPtr<ParsedCookie> > results;
getRawCookies(results, url, WithHttpOnlyCookies);
// Delete the cookies that path matches the request path
for (size_t i = 0; i < results.size(); i++) {
RefPtr<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<RefPtr<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
RefPtr<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
|