1/* 2 * Copyright (C) 2008, 2009 Julien Chaffraix <julien.chaffraix@gmail.com> 3 * Copyright (C) 2010, 2011, 2012, 2013 Research In Motion Limited. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY 15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR 18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 21 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 22 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27#define ENABLE_COOKIE_DEBUG 0 28#define ENABLE_COOKIE_SUPER_VERBOSE_DEBUG 0 29#define ENABLE_COOKIE_LIMIT_DEBUG 0 30 31#include "config.h" 32#include "CookieManager.h" 33 34#include "CookieDatabaseBackingStore.h" 35#include "CookieParser.h" 36#include "FileSystem.h" 37#include "Logging.h" 38#include "WebSettings.h" 39#include <BlackBerryPlatformExecutableMessage.h> 40#include <BlackBerryPlatformMessageClient.h> 41#include <BlackBerryPlatformNavigatorHandler.h> 42#include <BlackBerryPlatformSettings.h> 43#include <network/DomainTools.h> 44#include <stdlib.h> 45#include <wtf/CurrentTime.h> 46#include <wtf/text/CString.h> 47#include <wtf/text/StringBuilder.h> 48#include <wtf/text/WTFString.h> 49 50#if ENABLE_COOKIE_DEBUG 51#include <BlackBerryPlatformLog.h> 52#endif 53 54#if ENABLE_COOKIE_SUPER_VERBOSE_DEBUG 55#define CookieLog(format, ...) BlackBerry::Platform::logAlways(BlackBerry::Platform::LogLevelInfo, format, ## __VA_ARGS__) 56#else 57#define CookieLog(format, ...) 58#endif // ENABLE_COOKIE_SUPER_VERBOSE_DEBUG 59 60#if ENABLE_COOKIE_LIMIT_DEBUG 61#define CookieLimitLog(format, ...) BlackBerry::Platform::logAlways(BlackBerry::Platform::LogLevelInfo, format, ## __VA_ARGS__) 62#else 63#define CookieLimitLog(format, ...) 64#endif // ENABLE_COOKIE_LIMIT_DEBUG 65 66namespace WebCore { 67 68// Max count constants. 69static const unsigned s_globalMaxCookieCount = 6000; 70static const unsigned s_maxCookieCountPerHost = 60; 71static const unsigned s_cookiesToDeleteWhenLimitReached = 60; 72static const unsigned s_delayToStartCookieCleanup = 10; 73 74CookieManager& cookieManager() 75{ 76 static CookieManager *cookieManager = 0; 77 if (!cookieManager) { 78 // Open the cookieJar now and get the backing store cookies to fill the manager. 79 cookieManager = new CookieManager; 80 cookieManager->m_cookieBackingStore->open(cookieManager->cookieJar()); 81 } 82 return *cookieManager; 83} 84 85CookieManager::CookieManager() 86 : m_count(0) 87 , m_privateMode(false) 88 , m_shouldDumpAllCookies(false) 89 , m_syncedWithDatabase(false) 90 , m_cookieJarFileName(pathByAppendingComponent(BlackBerry::Platform::Settings::instance()->applicationDataDirectory().c_str(), "/cookieCollection.db")) 91 , m_policy(CookieStorageAcceptPolicyAlways) 92 , m_cookieBackingStore(CookieDatabaseBackingStore::create()) 93 , m_limitTimer(this, &CookieManager::cookieLimitCleanUp) 94{ 95} 96 97CookieManager::~CookieManager() 98{ 99 removeAllCookies(DoNotRemoveFromBackingStore); 100 // FIXME: m_managerMap and the top layer protocolMaps are not properly deleted. 101 // Do not delete any protocol maps to avoid double-deletion of the maps that are 102 // being used for both secure and non-secure protocols; this leak is OK since 103 // there's nothing important in the hashtable destructors, and the memory will be reclaimed on exit 104 105 // FIXME: CookieDatabaseBackingStore is not deleted, only flushed 106 // (currently the destructor is never called since this class is a 107 // singleton; on exit, the db is flushed manually. This call is only here 108 // as a fallback in case this class is made a non-singleton. 109 m_cookieBackingStore->sendChangesToDatabaseSynchronously(); 110} 111 112// Sorting logic is based on Cookie Spec RFC6265, section 5.4.2 113static bool cookieSorter(PassRefPtr<ParsedCookie> a, PassRefPtr<ParsedCookie> b) 114{ 115 if (a->path().length() == b->path().length()) 116 return a->creationTime() < b->creationTime(); 117 return a->path().length() > b->path().length(); 118} 119 120// Return whether we should ignore the scheme 121static bool shouldIgnoreScheme(const String& protocol) 122{ 123 // We want to ignore file and local schemes 124 return protocol == "file" || protocol == "local"; 125} 126 127void CookieManager::setCookies(const KURL& url, const String& value, CookieFilter filter) 128{ 129 // If the database hasn't been sync-ed at this point, force a sync load 130 if (!m_syncedWithDatabase && !m_privateMode) 131 m_cookieBackingStore->openAndLoadDatabaseSynchronously(cookieJar()); 132 133 CookieLog("CookieManager - Setting cookies"); 134 CookieParser parser(url); 135 Vector<RefPtr<ParsedCookie> > cookies = parser.parse(value); 136 137 for (size_t i = 0; i < cookies.size(); ++i) { 138 BackingStoreRemovalPolicy treatment = m_privateMode ? DoNotRemoveFromBackingStore : RemoveFromBackingStore; 139 checkAndTreatCookie(cookies[i], treatment, filter); 140 } 141} 142 143void CookieManager::setCookies(const KURL& url, const Vector<String>& cookies, CookieFilter filter) 144{ 145 // If the database hasn't been sync-ed at this point, force a sync load 146 if (!m_syncedWithDatabase && !m_privateMode) 147 m_cookieBackingStore->openAndLoadDatabaseSynchronously(cookieJar()); 148 149 CookieLog("CookieManager - Setting cookies"); 150 CookieParser parser(url); 151 for (size_t i = 0; i < cookies.size(); ++i) { 152 BackingStoreRemovalPolicy treatment = m_privateMode ? DoNotRemoveFromBackingStore : RemoveFromBackingStore; 153 if (RefPtr<ParsedCookie> parsedCookie = parser.parseOneCookie(cookies[i])) 154 checkAndTreatCookie(parsedCookie, treatment, filter); 155 } 156} 157 158String CookieManager::getCookie(const KURL& url, CookieFilter filter) const 159{ 160 // If the database hasn't been sync-ed at this point, force a sync load 161 if (!m_syncedWithDatabase && !m_privateMode) 162 m_cookieBackingStore->openAndLoadDatabaseSynchronously(cookieJar()); 163 164 Vector<RefPtr<ParsedCookie> > rawCookies; 165 rawCookies.reserveInitialCapacity(s_maxCookieCountPerHost); 166 167 // Retrieve cookies related to this url 168 getRawCookies(rawCookies, url, filter); 169 170 CookieLog("CookieManager - there are %d cookies in raw cookies\n", rawCookies.size()); 171 172 // Generate the cookie header string using the retrieved cookies 173 StringBuilder cookieStringBuilder; 174 cookieStringBuilder.reserveCapacity(512); 175 size_t cookieSize = rawCookies.size(); 176 for (size_t i = 0; i < cookieSize; i++) { 177 cookieStringBuilder.append(rawCookies[i]->toNameValuePair()); 178 if (i != cookieSize-1) 179 cookieStringBuilder.append("; "); 180 } 181 182 CookieLog("CookieManager - cookieString is - %s\n", cookieStringBuilder.toString().utf8().data()); 183 184 return cookieStringBuilder.toString(); 185} 186 187String CookieManager::generateHtmlFragmentForCookies() 188{ 189 // If the database hasn't been sync-ed at this point, force a sync load 190 if (!m_syncedWithDatabase && !m_privateMode) 191 m_cookieBackingStore->openAndLoadDatabaseSynchronously(cookieJar()); 192 193 CookieLog("CookieManager - generateHtmlFragmentForCookies\n"); 194 195 Vector<RefPtr<ParsedCookie> > cookieCandidates; 196 for (HashMap<String, CookieMap*>::iterator it = m_managerMap.begin(); it != m_managerMap.end(); ++it) 197 it->value->getAllChildCookies(&cookieCandidates); 198 199 String result; 200 RefPtr<ParsedCookie> cookie = 0; 201 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>")); 202 for (size_t i = 0; i < cookieCandidates.size(); ++i) { 203 cookie = cookieCandidates[i]; 204 result.append(String("<tr><td align=\"center\">")); 205 result.append(cookie->domain()); 206 result.append(String("<td align=\"center\">")); 207 result.append(cookie->path()); 208 result.append(String("<td align=\"center\">")); 209 result.append(cookie->protocol()); 210 result.append(String("<td align=\"center\">")); 211 result.append(cookie->name()); 212 result.append(String("<td align=\"center\" style= \"word-break:break-all\">")); 213 result.append(cookie->value()); 214 result.append(String("<td align=\"center\">")); 215 result.append(String(cookie->isSecure() ? "Yes" : "No")); 216 result.append(String("<td align=\"center\">")); 217 result.append(String(cookie->isHttpOnly() ? "Yes" : "No")); 218 result.append(String("<td align=\"center\">")); 219 result.append(String(cookie->isSession() ? "Yes" : "No")); 220 result.append(String("</td></tr>")); 221 } 222 result.append(String("</table>")); 223 return result; 224} 225 226void CookieManager::getRawCookies(Vector<RefPtr<ParsedCookie> > &stackOfCookies, const KURL& requestURL, CookieFilter filter) const 227{ 228 // Force a sync load of the database 229 if (!m_syncedWithDatabase && !m_privateMode) 230 m_cookieBackingStore->openAndLoadDatabaseSynchronously(cookieJar()); 231 232 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()); 233 234 const bool invalidScheme = shouldIgnoreScheme(requestURL.protocol()); 235 const bool specialCaseForWebWorks = invalidScheme && m_shouldDumpAllCookies; 236 const bool isConnectionSecure = requestURL.protocolIs("https") || requestURL.protocolIs("wss") || specialCaseForWebWorks; 237 238 Vector<RefPtr<ParsedCookie> > cookieCandidates; 239 Vector<CookieMap*> protocolsToSearch; 240 241 // Special Case: If a server sets a "secure" cookie over a non-secure channel and tries to access the cookie 242 // over a secure channel, it will not succeed because the secure protocol isn't mapped to the insecure protocol yet. 243 // Set the map to the non-secure version, so it'll search the mapping for a secure cookie. 244 CookieMap* targetMap = m_managerMap.get(requestURL.protocol()); 245 if (!targetMap && isConnectionSecure) { 246 CookieLog("CookieManager - special case: secure protocol are not linked yet."); 247 if (requestURL.protocolIs("https")) 248 targetMap = m_managerMap.get("http"); 249 else if (requestURL.protocolIs("wss")) 250 targetMap = m_managerMap.get("ws"); 251 } 252 253 // Decide which scheme tree we should look at. 254 // Return on invalid schemes. cookies are currently disabled on file and local. 255 // We only want to enable them for WebWorks that enabled a special flag. 256 if (specialCaseForWebWorks) 257 copyValuesToVector(m_managerMap, protocolsToSearch); 258 else if (invalidScheme) 259 return; 260 else { 261 protocolsToSearch.append(targetMap); 262 // FIXME: this is a hack for webworks apps; RFC 6265 says "Cookies do not provide isolation by scheme" 263 // so we should not be checking protocols at all. See PR 135595 264 if (m_shouldDumpAllCookies) { 265 protocolsToSearch.append(m_managerMap.get("file")); 266 protocolsToSearch.append(m_managerMap.get("local")); 267 } 268 } 269 270 Vector<String> delimitedHost; 271 272 // IP addresses are stored in a particular format (due to ipv6). Reduce the ip address so we can match 273 // it with the one in memory. 274 BlackBerry::Platform::String canonicalIP = BlackBerry::Platform::getCanonicalIPFormat(requestURL.host()); 275 if (!canonicalIP.empty()) 276 delimitedHost.append(String(canonicalIP.c_str())); 277 else 278 requestURL.host().lower().split(".", true, delimitedHost); 279 280 // Go through all the protocol trees that we need to search for 281 // and get all cookies that are valid for this domain 282 for (size_t k = 0; k < protocolsToSearch.size(); k++) { 283 CookieMap* currentMap = protocolsToSearch[k]; 284 285 // if no cookies exist for this protocol, break right away 286 if (!currentMap) 287 continue; 288 289 CookieLog("CookieManager - looking at protocol map %s \n", currentMap->getName().utf8().data()); 290 291 // Special case for local and files - because WebApps expect to get ALL cookies from the backing-store on local protocol 292 if (specialCaseForWebWorks) { 293 CookieLog("CookieManager - special case find in protocol map - %s\n", currentMap->getName().utf8().data()); 294 currentMap->getAllChildCookies(&cookieCandidates); 295 } else { 296 // Get cookies from the null domain map 297 currentMap->getAllCookies(&cookieCandidates); 298 299 // Get cookies from Host-only cookies 300 if (canonicalIP.empty()) { 301 CookieLog("CookieManager - looking for host-only cookies for host - %s", requestURL.host().utf8().data()); 302 CookieMap* hostMap = currentMap->getSubdomainMap(requestURL.host()); 303 if (hostMap) 304 hostMap->getAllCookies(&cookieCandidates); 305 } 306 307 // Get cookies from the valid domain maps 308 int i = delimitedHost.size() - 1; 309 while (i >= 0) { 310 CookieLog("CookieManager - finding %s in currentmap\n", delimitedHost[i].utf8().data()); 311 currentMap = currentMap->getSubdomainMap(delimitedHost[i]); 312 // if this subdomain/domain does not exist in our mapping then we simply exit 313 if (!currentMap) { 314 CookieLog("CookieManager - cannot find next map exiting the while loop.\n"); 315 break; 316 } 317 CookieLog("CookieManager - found the map, grabbing cookies from this map\n"); 318 currentMap->getAllCookies(&cookieCandidates); 319 i--; 320 } 321 } 322 } 323 324 CookieLog("CookieManager - there are %d cookies in candidate\n", cookieCandidates.size()); 325 326 for (size_t i = 0; i < cookieCandidates.size(); ++i) { 327 RefPtr<ParsedCookie> cookie = cookieCandidates[i]; 328 329 // According to the path-matches rules in RFC6265, section 5.1.4, 330 // we should add a '/' at the end of cookie-path for comparison if the cookie-path is not end with '/'. 331 String path = cookie->path(); 332 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()); 333 if (!equalIgnoringCase(path, requestURL.path()) && !path.endsWith("/", false)) 334 path = path + "/"; 335 336 // Only secure connections have access to secure cookies. Unless specialCaseForWebWorks is true. 337 // Get the cookies filtering out HttpOnly cookies if requested. 338 if (requestURL.path().startsWith(path, false) && (isConnectionSecure || !cookie->isSecure()) && (filter == WithHttpOnlyCookies || !cookie->isHttpOnly())) { 339 CookieLog("CookieManager - cookie chosen - %s\n", cookie->toString().utf8().data()); 340 cookie->setLastAccessed(currentTime()); 341 stackOfCookies.append(cookie); 342 } 343 } 344 345 std::stable_sort(stackOfCookies.begin(), stackOfCookies.end(), cookieSorter); 346} 347 348void CookieManager::removeAllCookies(BackingStoreRemovalPolicy backingStoreRemoval) 349{ 350 HashMap<String, CookieMap*>::iterator first = m_managerMap.begin(); 351 HashMap<String, CookieMap*>::iterator end = m_managerMap.end(); 352 for (HashMap<String, CookieMap*>::iterator it = first; it != end; ++it) 353 it->value->deleteAllCookiesAndDomains(); 354 355 if (backingStoreRemoval == RemoveFromBackingStore) 356 m_cookieBackingStore->removeAll(); 357 m_count = 0; 358} 359 360void CookieManager::setCookieJar(const char* fileName) 361{ 362 m_cookieJarFileName = String(fileName); 363 m_cookieBackingStore->open(m_cookieJarFileName); 364} 365 366void CookieManager::checkAndTreatCookie(PassRefPtr<ParsedCookie> prpCandidateCookie, BackingStoreRemovalPolicy postToBackingStore, CookieFilter filter) 367{ 368 RefPtr<ParsedCookie> candidateCookie = prpCandidateCookie; 369 CookieLog("CookieManager - checkAndTreatCookie - processing url with domain - %s & protocol %s\n", candidateCookie->domain().utf8().data(), candidateCookie->protocol().utf8().data()); 370 371 // Delete invalid cookies: 372 // 1) A cookie which is not from http shouldn't have a httpOnly property. 373 // 2) Cookies coming from schemes that we do not support and the special flag isn't on 374 if ((filter == NoHttpOnlyCookie && candidateCookie->isHttpOnly()) || (shouldIgnoreScheme(candidateCookie->protocol()) && !m_shouldDumpAllCookies)) 375 return; 376 377 const bool ignoreDomain = (candidateCookie->protocol() == "file" || candidateCookie->protocol() == "local"); 378 379 // Determine which protocol tree to add the cookie to. Create one if necessary. 380 CookieMap* curMap = 0; 381 if (m_managerMap.contains(candidateCookie->protocol())) 382 curMap = m_managerMap.get(candidateCookie->protocol()); 383 else { 384 // Check if it is a secure version, if it is, link it to the non-secure version 385 // Link curMap to the new protocol as well as the old one if it doesn't exist 386 if (candidateCookie->protocol() == "https") { 387 curMap = m_managerMap.get("http"); 388 if (!curMap) { 389 curMap = new CookieMap("http"); 390 m_managerMap.add("http", curMap); 391 } 392 } else if (candidateCookie->protocol() == "wss") { 393 curMap = m_managerMap.get("ws"); 394 if (!curMap) { 395 curMap = new CookieMap("ws"); 396 m_managerMap.add("ws", curMap); 397 } 398 } else 399 curMap = new CookieMap(candidateCookie->protocol()); 400 401 CookieLog("CookieManager - adding protocol cookiemap - %s\n", curMap->getName().utf8().data()); 402 403 m_managerMap.add(candidateCookie->protocol(), curMap); 404 } 405 406 // If protocol support domain, we have to traverse the domain tree to find the right 407 // cookieMap to handle with 408 if (!ignoreDomain) 409 curMap = findOrCreateCookieMap(curMap, candidateCookie); 410 411 // Now that we have the proper map for this cookie, we can modify it 412 // If cookie does not exist and has expired, delete it 413 // If cookie exists and it has expired, so we must remove it from the map, if not update it 414 // If cookie expired and came from the BackingStore (therefore does not exist), we have to remove from database 415 // If cookie does not exist & it's valid, add it to the current map 416 417 if (candidateCookie->hasExpired() || candidateCookie->isForceExpired()) { 418 // Special case for getBackingStoreCookies() to catch expired cookies 419 if (postToBackingStore == BackingStoreCookieEntry) 420 m_cookieBackingStore->remove(candidateCookie); 421 else if (curMap) { 422 // RemoveCookie will return 0 if the cookie doesn't exist. 423 RefPtr<ParsedCookie> expired = curMap->removeCookie(candidateCookie, filter); 424 // Cookie is useless, Remove the cookie from the backingstore if it exists. 425 // Backup check for BackingStoreCookieEntry incase someone incorrectly uses this enum. 426 if (expired && postToBackingStore != BackingStoreCookieEntry && !expired->isSession()) { 427 CookieLog("CookieManager - expired cookie is nonsession, deleting from db"); 428 m_cookieBackingStore->remove(expired); 429 } 430 } 431 } else { 432 ASSERT(curMap); 433 CookieLog("CookieManager - adding cookiemap - %s\n", curMap->getName().utf8().data()); 434 addCookieToMap(curMap, candidateCookie, postToBackingStore, filter); 435 } 436} 437 438void CookieManager::addCookieToMap(CookieMap* targetMap, PassRefPtr<ParsedCookie> prpCandidateCookie, BackingStoreRemovalPolicy postToBackingStore, CookieFilter filter) 439{ 440 RefPtr<ParsedCookie> replacedCookie = 0; 441 RefPtr<ParsedCookie> candidateCookie = prpCandidateCookie; 442 443 if (!targetMap->addOrReplaceCookie(candidateCookie, replacedCookie, filter)) { 444 CookieLog("CookieManager - rejecting new cookie - %s.\n", candidateCookie->toString().utf8().data()); 445 return; 446 } 447 448 if (replacedCookie) { 449 CookieLog("CookieManager - updating new cookie - %s.\n", candidateCookie->toString().utf8().data()); 450 451 // A cookie was replaced in targetMap. 452 // If old cookie is non-session and new one is, we have to delete it from backingstore 453 // If new cookie is non-session and old one is, we have to add it to backingstore 454 // If both sessions are non-session, then we update it in the backingstore 455 bool newIsSession = candidateCookie->isSession(); 456 bool oldIsSession = replacedCookie->isSession(); 457 458 if (postToBackingStore == RemoveFromBackingStore) { 459 if (!newIsSession && !oldIsSession) 460 m_cookieBackingStore->update(candidateCookie); 461 else if (newIsSession && !oldIsSession) { 462 // Must manually decrease the counter because it was not counted when 463 // the cookie was removed in cookieVector. 464 removedCookie(); 465 m_cookieBackingStore->remove(replacedCookie); 466 } else if (!newIsSession && oldIsSession) { 467 // Must manually increase the counter because it was not counted when 468 // the cookie was added in cookieVector. 469 addedCookie(); 470 m_cookieBackingStore->insert(candidateCookie); 471 } 472 } 473 return; 474 } 475 476 CookieLog("CookieManager - adding new cookie - %s.\n", candidateCookie->toString().utf8().data()); 477 478 RefPtr<ParsedCookie> oldestCookie = 0; 479 // Check if we have not reached the per cookie domain limit. 480 // If that is not true, we check if the global limit has been reached if backingstore mode is on 481 // Two points: 482 // 1) We only do a global check if backingstore mode is on because the global cookie limit only 483 // counts session cookies that are saved in the database. If the user goes over the limit 484 // when they are in private mode, we know that the total cookie limit will be under the limit 485 // once the user goes back to normal mode (memory deleted and reloaded from the database) 486 // 2) We use else if for this statement because if we remove a cookie in the 1st statement 487 // then it means the global count will never exceed the limit 488 489 CookieLimitLog("CookieManager - local count: %d global count: %d", targetMap->count(), m_count); 490 if (targetMap->count() > s_maxCookieCountPerHost) { 491 CookieLog("CookieManager - deleting oldest cookie from this map due to domain count.\n"); 492 oldestCookie = targetMap->removeOldestCookie(); 493 } else if (m_count > s_globalMaxCookieCount && (postToBackingStore != DoNotRemoveFromBackingStore)) { 494 CookieLimitLog("CookieManager - Global limit reached, initiate cookie limit clean up."); 495 initiateCookieLimitCleanUp(); 496 } 497 498 // Only add non session cookie to the backing store. 499 if (postToBackingStore == RemoveFromBackingStore) { 500 if (oldestCookie && !oldestCookie->isSession()) { 501 CookieLog("CookieManager - oldestCookie exists, deleting it from backingstore and destructing.\n"); 502 m_cookieBackingStore->remove(oldestCookie); 503 } 504 if (!candidateCookie->isSession()) 505 m_cookieBackingStore->insert(candidateCookie); 506 } 507} 508 509void CookieManager::getBackingStoreCookies() 510{ 511 // Make sure private mode is off when the database thread calls this method 512 if (m_privateMode) 513 return; 514 515 // If there exists cookies in memory, flush them out and we'll load everything from the database again 516 if (m_count) 517 removeAllCookies(DoNotRemoveFromBackingStore); 518 519 Vector<RefPtr<ParsedCookie> > cookies; 520 m_cookieBackingStore->getCookiesFromDatabase(cookies); 521 CookieLog("CookieManager - Backingstore has %d cookies, loading them in memory now", cookies.size()); 522 for (size_t i = 0; i < cookies.size(); ++i) { 523 RefPtr<ParsedCookie> newCookie = cookies[i]; 524 525 // The IP flag is not persisted in the database. 526 if (BlackBerry::Platform::isIPAddress(newCookie->domain())) 527 newCookie->setDomain(newCookie->domain(), true); 528 529 checkAndTreatCookie(newCookie, BackingStoreCookieEntry); 530 } 531 CookieLog("CookieManager - Backingstore loading complete."); 532 533 m_syncedWithDatabase = true; 534} 535 536void CookieManager::setPrivateMode(bool privateMode) 537{ 538 if (m_privateMode == privateMode) 539 return; 540 541 m_privateMode = privateMode; 542 543 // If we switched to private mode when the database cookies haven't loaded into memory yet 544 // we can return because there's nothing in memory anyway. 545 if (m_privateMode && !m_syncedWithDatabase) 546 return; 547 548 removeAllCookies(DoNotRemoveFromBackingStore); 549 550 // If we are switching back to public mode, reload the database to memory. 551 if (!m_privateMode) 552 getBackingStoreCookies(); 553} 554 555CookieMap* CookieManager::findOrCreateCookieMap(CookieMap* protocolMap, const PassRefPtr<ParsedCookie> candidateCookie) 556{ 557 // Explode the domain with the '.' delimiter 558 Vector<String> delimitedHost; 559 560 // If the domain is an IP address or is a host-only domain, don't split it. 561 if (candidateCookie->domainIsIPAddress() || candidateCookie->isHostOnly()) 562 delimitedHost.append(candidateCookie->domain()); 563 else 564 candidateCookie->domain().split(".", delimitedHost); 565 566 CookieMap* curMap = protocolMap; 567 size_t hostSize = delimitedHost.size(); 568 569 CookieLog("CookieManager - looking at protocol map %s \n", protocolMap->getName().utf8().data()); 570 571 // Find & create necessary CookieMaps by traversing down the domain tree 572 // Each CookieMap represent a subsection of the domain, delimited by "." 573 int i = hostSize - 1; 574 while (i >= 0) { 575 CookieLog("CookieManager - finding %s in currentmap\n", delimitedHost[i].utf8().data()); 576 CookieMap* nextMap = curMap->getSubdomainMap(delimitedHost[i]); 577 if (!nextMap) { 578 CookieLog("CookieManager - cannot find map\n"); 579 if (candidateCookie->hasExpired()) 580 return 0; 581 CookieLog("CookieManager - creating %s in currentmap %s\n", delimitedHost[i].utf8().data(), curMap->getName().utf8().data()); 582 nextMap = new CookieMap(delimitedHost[i]); 583 CookieLog("CookieManager - adding subdomain to map\n"); 584 curMap->addSubdomainMap(delimitedHost[i], nextMap); 585 } 586 curMap = nextMap; 587 i--; 588 } 589 return curMap; 590} 591 592void CookieManager::removeCookieWithName(const KURL& url, const String& cookieName) 593{ 594 // Dispatch the message because the database cookies are not loaded in memory yet. 595 if (!m_syncedWithDatabase && !m_privateMode) { 596 typedef void (WebCore::CookieManager::*FunctionType)(const KURL&, const String&); 597 BlackBerry::Platform::webKitThreadMessageClient()->dispatchMessage( 598 BlackBerry::Platform::createMethodCallMessage<FunctionType, CookieManager, const KURL, const String>( 599 &CookieManager::removeCookieWithName, this, url, cookieName)); 600 return; 601 } 602 603 // We get all cookies from all domains that domain matches the request domain 604 // and delete any cookies with the specified name that path matches the request path 605 Vector<RefPtr<ParsedCookie> > results; 606 getRawCookies(results, url, WithHttpOnlyCookies); 607 // Delete the cookies that path matches the request path 608 for (size_t i = 0; i < results.size(); i++) { 609 RefPtr<ParsedCookie> cookie = results[i]; 610 if (!equalIgnoringCase(cookie->name(), cookieName)) 611 continue; 612 if (url.path().startsWith(cookie->path(), false)) { 613 cookie->forceExpire(); 614 checkAndTreatCookie(cookie, RemoveFromBackingStore); 615 } 616 } 617} 618 619void CookieManager::initiateCookieLimitCleanUp() 620{ 621 if (!m_limitTimer.isActive()) { 622 CookieLog("CookieManager - Starting a timer for cookie cleanup"); 623 m_limitTimer.startOneShot(s_delayToStartCookieCleanup); 624 } else { 625#ifndef NDEBUG 626 CookieLog("CookieManager - Cookie cleanup timer already running"); 627#endif 628 } 629} 630 631void CookieManager::cookieLimitCleanUp(Timer<CookieManager>* timer) 632{ 633 ASSERT_UNUSED(timer, timer == &m_limitTimer); 634 635 CookieLimitLog("CookieManager - Starting cookie clean up"); 636 637 size_t numberOfCookiesOverLimit = (m_count > s_globalMaxCookieCount) ? m_count - s_globalMaxCookieCount : 0; 638 size_t amountToDelete = s_cookiesToDeleteWhenLimitReached + numberOfCookiesOverLimit; 639 640 CookieLimitLog("CookieManager - Excess: %d Amount to Delete: %d", numberOfCookiesOverLimit, amountToDelete); 641 642 // Call the database to delete 'amountToDelete' of cookies 643 Vector<RefPtr<ParsedCookie> > cookiesToDelete; 644 cookiesToDelete.reserveInitialCapacity(amountToDelete); 645 646 CookieLimitLog("CookieManager - Calling database to clean up"); 647 m_cookieBackingStore->getCookiesFromDatabase(cookiesToDelete, amountToDelete); 648 649 // Cookies are ordered in ASC order by lastAccessed 650 for (size_t i = 0; i < amountToDelete; ++i) { 651 // Expire them and call checkandtreat to delete them from memory and database 652 RefPtr<ParsedCookie> newCookie = cookiesToDelete[i]; 653 CookieLimitLog("CookieManager - Expire cookie: %s and delete", newCookie->toString().utf8().data()); 654 newCookie->forceExpire(); 655 checkAndTreatCookie(newCookie, RemoveFromBackingStore); 656 } 657 658 CookieLimitLog("CookieManager - Cookie clean up complete."); 659} 660 661} // namespace WebCore 662