sqlite_persistent_cookie_store.cc revision 9aa5f8b435409e063e76e6e4f12f5c4e85997e0a
1// Copyright (c) 2010 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "chrome/browser/net/sqlite_persistent_cookie_store.h" 6 7#include <list> 8 9#include "app/sql/meta_table.h" 10#include "app/sql/statement.h" 11#include "app/sql/transaction.h" 12#include "base/basictypes.h" 13#include "base/file_path.h" 14#include "base/file_util.h" 15#include "base/logging.h" 16#include "base/metrics/histogram.h" 17#include "base/ref_counted.h" 18#include "base/scoped_ptr.h" 19#include "base/string_util.h" 20#include "base/threading/thread.h" 21#ifndef ANDROID 22#include "chrome/browser/browser_thread.h" 23#include "chrome/browser/diagnostics/sqlite_diagnostics.h" 24#endif 25 26#ifdef ANDROID 27base::Thread* getDbThread() 28{ 29 static base::Thread* dbThread = NULL; 30 if (dbThread && dbThread->IsRunning()) 31 return dbThread; 32 33 if (!dbThread) 34 dbThread = new base::Thread("db"); 35 36 if (!dbThread) 37 return NULL; 38 39 base::Thread::Options options; 40 options.message_loop_type = MessageLoop::TYPE_DEFAULT; 41 if (!dbThread->StartWithOptions(options)) { 42 delete dbThread; 43 dbThread = NULL; 44 } 45 return dbThread; 46} 47#endif 48 49using base::Time; 50 51// This class is designed to be shared between any calling threads and the 52// database thread. It batches operations and commits them on a timer. 53class SQLitePersistentCookieStore::Backend 54 : public base::RefCountedThreadSafe<SQLitePersistentCookieStore::Backend> { 55 public: 56 explicit Backend(const FilePath& path) 57 : path_(path), 58 db_(NULL), 59 num_pending_(0), 60 clear_local_state_on_exit_(false) 61#if defined(ANDROID) 62 , cookie_count_(0) 63#endif 64 { 65 } 66 67 // Creates or load the SQLite database. 68 bool Load(std::vector<net::CookieMonster::CanonicalCookie*>* cookies); 69 70 // Batch a cookie addition. 71 void AddCookie(const net::CookieMonster::CanonicalCookie& cc); 72 73 // Batch a cookie access time update. 74 void UpdateCookieAccessTime(const net::CookieMonster::CanonicalCookie& cc); 75 76 // Batch a cookie deletion. 77 void DeleteCookie(const net::CookieMonster::CanonicalCookie& cc); 78 79 // Commit pending operations as soon as possible. 80 void Flush(Task* completion_task); 81 82 // Commit any pending operations and close the database. This must be called 83 // before the object is destructed. 84 void Close(); 85 86 void SetClearLocalStateOnExit(bool clear_local_state); 87 88#if defined(ANDROID) 89 int get_cookie_count() const { return cookie_count_; } 90 void set_cookie_count(int count) { cookie_count_ = count; } 91#endif 92 93 private: 94 friend class base::RefCountedThreadSafe<SQLitePersistentCookieStore::Backend>; 95 96 // You should call Close() before destructing this object. 97 ~Backend() { 98 DCHECK(!db_.get()) << "Close should have already been called."; 99 DCHECK(num_pending_ == 0 && pending_.empty()); 100 } 101 102 // Database upgrade statements. 103 bool EnsureDatabaseVersion(); 104 105 class PendingOperation { 106 public: 107 typedef enum { 108 COOKIE_ADD, 109 COOKIE_UPDATEACCESS, 110 COOKIE_DELETE, 111 } OperationType; 112 113 PendingOperation(OperationType op, 114 const net::CookieMonster::CanonicalCookie& cc) 115 : op_(op), cc_(cc) { } 116 117 OperationType op() const { return op_; } 118 const net::CookieMonster::CanonicalCookie& cc() const { return cc_; } 119 120 private: 121 OperationType op_; 122 net::CookieMonster::CanonicalCookie cc_; 123 }; 124 125 private: 126 // Batch a cookie operation (add or delete) 127 void BatchOperation(PendingOperation::OperationType op, 128 const net::CookieMonster::CanonicalCookie& cc); 129 // Commit our pending operations to the database. 130#if defined(ANDROID) 131 void Commit(Task* completion_task); 132#else 133 void Commit(); 134#endif 135 // Close() executed on the background thread. 136 void InternalBackgroundClose(); 137 138 FilePath path_; 139 scoped_ptr<sql::Connection> db_; 140 sql::MetaTable meta_table_; 141 142 typedef std::list<PendingOperation*> PendingOperationsList; 143 PendingOperationsList pending_; 144 PendingOperationsList::size_type num_pending_; 145 // True if the persistent store should be deleted upon destruction. 146 bool clear_local_state_on_exit_; 147 // Guard |pending_|, |num_pending_| and |clear_local_state_on_exit_|. 148 Lock lock_; 149 150#if defined(ANDROID) 151 // Number of cookies that have actually been saved. Updated during Commit(). 152 volatile int cookie_count_; 153#endif 154 155 DISALLOW_COPY_AND_ASSIGN(Backend); 156}; 157 158// Version number of the database. In version 4, we migrated the time epoch. 159// If you open the DB with an older version on Mac or Linux, the times will 160// look wonky, but the file will likely be usable. On Windows version 3 and 4 161// are the same. 162// 163// Version 3 updated the database to include the last access time, so we can 164// expire them in decreasing order of use when we've reached the maximum 165// number of cookies. 166static const int kCurrentVersionNumber = 4; 167static const int kCompatibleVersionNumber = 3; 168 169namespace { 170 171// Initializes the cookies table, returning true on success. 172bool InitTable(sql::Connection* db) { 173 if (!db->DoesTableExist("cookies")) { 174 if (!db->Execute("CREATE TABLE cookies (" 175 "creation_utc INTEGER NOT NULL UNIQUE PRIMARY KEY," 176 "host_key TEXT NOT NULL," 177 "name TEXT NOT NULL," 178 "value TEXT NOT NULL," 179 "path TEXT NOT NULL," 180#if defined(ANDROID) 181 // On some mobile platforms, we persist session cookies 182 // because the OS can kill the browser during a session. 183 // If so, expires_utc is set to 0. When the field is read 184 // into a Time object, Time::is_null() will return true. 185#else 186 // We only store persistent, so we know it expires 187#endif 188 "expires_utc INTEGER NOT NULL," 189 "secure INTEGER NOT NULL," 190 "httponly INTEGER NOT NULL," 191 "last_access_utc INTEGER NOT NULL)")) 192 return false; 193 } 194 195 // Try to create the index every time. Older versions did not have this index, 196 // so we want those people to get it. Ignore errors, since it may exist. 197#ifdef ANDROID 198 db->Execute( 199 "CREATE INDEX IF NOT EXISTS cookie_times ON cookies (creation_utc)"); 200#else 201 db->Execute("CREATE INDEX cookie_times ON cookies (creation_utc)"); 202#endif 203 return true; 204} 205 206} // namespace 207 208bool SQLitePersistentCookieStore::Backend::Load( 209 std::vector<net::CookieMonster::CanonicalCookie*>* cookies) { 210 // This function should be called only once per instance. 211 DCHECK(!db_.get()); 212 213 db_.reset(new sql::Connection); 214 if (!db_->Open(path_)) { 215 NOTREACHED() << "Unable to open cookie DB."; 216 db_.reset(); 217 return false; 218 } 219 220#ifndef ANDROID 221 // GetErrorHandlerForCookieDb is defined in sqlite_diagnostics.h 222 // which we do not currently include on Android 223 db_->set_error_delegate(GetErrorHandlerForCookieDb()); 224#endif 225 226 if (!EnsureDatabaseVersion() || !InitTable(db_.get())) { 227 NOTREACHED() << "Unable to open cookie DB."; 228 db_.reset(); 229 return false; 230 } 231 232 db_->Preload(); 233 234 // Slurp all the cookies into the out-vector. 235 sql::Statement smt(db_->GetUniqueStatement( 236 "SELECT creation_utc, host_key, name, value, path, expires_utc, secure, " 237 "httponly, last_access_utc FROM cookies")); 238 if (!smt) { 239 NOTREACHED() << "select statement prep failed"; 240 db_.reset(); 241 return false; 242 } 243 244 while (smt.Step()) { 245#if defined(ANDROID) 246 base::Time expires = Time::FromInternalValue(smt.ColumnInt64(5)); 247#endif 248 scoped_ptr<net::CookieMonster::CanonicalCookie> cc( 249 new net::CookieMonster::CanonicalCookie( 250 smt.ColumnString(2), // name 251 smt.ColumnString(3), // value 252 smt.ColumnString(1), // domain 253 smt.ColumnString(4), // path 254 smt.ColumnInt(6) != 0, // secure 255 smt.ColumnInt(7) != 0, // httponly 256 Time::FromInternalValue(smt.ColumnInt64(0)), // creation_utc 257 Time::FromInternalValue(smt.ColumnInt64(8)), // last_access_utc 258#if defined(ANDROID) 259 !expires.is_null(), // has_expires 260 expires)); // expires_utc 261#else 262 true, // has_expires 263 Time::FromInternalValue(smt.ColumnInt64(5)))); // expires_utc 264#endif 265 DLOG_IF(WARNING, 266 cc->CreationDate() > Time::Now()) << L"CreationDate too recent"; 267 cookies->push_back(cc.release()); 268 } 269 270#ifdef ANDROID 271 set_cookie_count(cookies->size()); 272#endif 273 274 return true; 275} 276 277bool SQLitePersistentCookieStore::Backend::EnsureDatabaseVersion() { 278 // Version check. 279 if (!meta_table_.Init( 280 db_.get(), kCurrentVersionNumber, kCompatibleVersionNumber)) { 281 return false; 282 } 283 284 if (meta_table_.GetCompatibleVersionNumber() > kCurrentVersionNumber) { 285 LOG(WARNING) << "Cookie database is too new."; 286 return false; 287 } 288 289 int cur_version = meta_table_.GetVersionNumber(); 290 if (cur_version == 2) { 291 sql::Transaction transaction(db_.get()); 292 if (!transaction.Begin()) 293 return false; 294 if (!db_->Execute("ALTER TABLE cookies ADD COLUMN last_access_utc " 295 "INTEGER DEFAULT 0") || 296 !db_->Execute("UPDATE cookies SET last_access_utc = creation_utc")) { 297 LOG(WARNING) << "Unable to update cookie database to version 3."; 298 return false; 299 } 300 ++cur_version; 301 meta_table_.SetVersionNumber(cur_version); 302 meta_table_.SetCompatibleVersionNumber( 303 std::min(cur_version, kCompatibleVersionNumber)); 304 transaction.Commit(); 305 } 306 307 if (cur_version == 3) { 308 // The time epoch changed for Mac & Linux in this version to match Windows. 309 // This patch came after the main epoch change happened, so some 310 // developers have "good" times for cookies added by the more recent 311 // versions. So we have to be careful to only update times that are under 312 // the old system (which will appear to be from before 1970 in the new 313 // system). The magic number used below is 1970 in our time units. 314 sql::Transaction transaction(db_.get()); 315 transaction.Begin(); 316#if !defined(OS_WIN) 317 db_->Execute( 318 "UPDATE cookies " 319 "SET creation_utc = creation_utc + 11644473600000000 " 320 "WHERE rowid IN " 321 "(SELECT rowid FROM cookies WHERE " 322 "creation_utc > 0 AND creation_utc < 11644473600000000)"); 323 db_->Execute( 324 "UPDATE cookies " 325 "SET expires_utc = expires_utc + 11644473600000000 " 326 "WHERE rowid IN " 327 "(SELECT rowid FROM cookies WHERE " 328 "expires_utc > 0 AND expires_utc < 11644473600000000)"); 329 db_->Execute( 330 "UPDATE cookies " 331 "SET last_access_utc = last_access_utc + 11644473600000000 " 332 "WHERE rowid IN " 333 "(SELECT rowid FROM cookies WHERE " 334 "last_access_utc > 0 AND last_access_utc < 11644473600000000)"); 335#endif 336 ++cur_version; 337 meta_table_.SetVersionNumber(cur_version); 338 transaction.Commit(); 339 } 340 341 // Put future migration cases here. 342 343 // When the version is too old, we just try to continue anyway, there should 344 // not be a released product that makes a database too old for us to handle. 345 LOG_IF(WARNING, cur_version < kCurrentVersionNumber) << 346 "Cookie database version " << cur_version << " is too old to handle."; 347 348 return true; 349} 350 351void SQLitePersistentCookieStore::Backend::AddCookie( 352 const net::CookieMonster::CanonicalCookie& cc) { 353 BatchOperation(PendingOperation::COOKIE_ADD, cc); 354} 355 356void SQLitePersistentCookieStore::Backend::UpdateCookieAccessTime( 357 const net::CookieMonster::CanonicalCookie& cc) { 358 BatchOperation(PendingOperation::COOKIE_UPDATEACCESS, cc); 359} 360 361void SQLitePersistentCookieStore::Backend::DeleteCookie( 362 const net::CookieMonster::CanonicalCookie& cc) { 363 BatchOperation(PendingOperation::COOKIE_DELETE, cc); 364} 365 366void SQLitePersistentCookieStore::Backend::BatchOperation( 367 PendingOperation::OperationType op, 368 const net::CookieMonster::CanonicalCookie& cc) { 369 // Commit every 30 seconds. 370 static const int kCommitIntervalMs = 30 * 1000; 371 // Commit right away if we have more than 512 outstanding operations. 372 static const size_t kCommitAfterBatchSize = 512; 373#ifndef ANDROID 374 DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::DB)); 375#endif 376 377 // We do a full copy of the cookie here, and hopefully just here. 378 scoped_ptr<PendingOperation> po(new PendingOperation(op, cc)); 379 380 PendingOperationsList::size_type num_pending; 381 { 382 AutoLock locked(lock_); 383 pending_.push_back(po.release()); 384 num_pending = ++num_pending_; 385 } 386 387#ifdef ANDROID 388 if (!getDbThread()) 389 return; 390 MessageLoop* loop = getDbThread()->message_loop(); 391#endif 392 393 if (num_pending == 1) { 394 // We've gotten our first entry for this batch, fire off the timer. 395#ifdef ANDROID 396 loop->PostDelayedTask(FROM_HERE, NewRunnableMethod( 397 this, &Backend::Commit, static_cast<Task*>(NULL)), kCommitIntervalMs); 398#else 399 BrowserThread::PostDelayedTask( 400 BrowserThread::DB, FROM_HERE, 401 NewRunnableMethod(this, &Backend::Commit), kCommitIntervalMs); 402#endif 403 } else if (num_pending == kCommitAfterBatchSize) { 404 // We've reached a big enough batch, fire off a commit now. 405#ifdef ANDROID 406 loop->PostTask(FROM_HERE, NewRunnableMethod( 407 this, &Backend::Commit, static_cast<Task*>(NULL))); 408#else 409 BrowserThread::PostTask( 410 BrowserThread::DB, FROM_HERE, 411 NewRunnableMethod(this, &Backend::Commit)); 412#endif 413 } 414} 415 416#if defined(ANDROID) 417void SQLitePersistentCookieStore::Backend::Commit(Task* completion_task) { 418#else 419void SQLitePersistentCookieStore::Backend::Commit() { 420 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); 421#endif 422 423#if defined(ANDROID) 424 if (completion_task) { 425 // We post this task to the current thread, so it won't run until we exit. 426 MessageLoop::current()->PostTask(FROM_HERE, completion_task); 427 } 428#endif 429 430 PendingOperationsList ops; 431 { 432 AutoLock locked(lock_); 433 pending_.swap(ops); 434 num_pending_ = 0; 435 } 436 437 // Maybe an old timer fired or we are already Close()'ed. 438 if (!db_.get() || ops.empty()) 439 return; 440 441 sql::Statement add_smt(db_->GetCachedStatement(SQL_FROM_HERE, 442 "INSERT INTO cookies (creation_utc, host_key, name, value, path, " 443 "expires_utc, secure, httponly, last_access_utc) " 444 "VALUES (?,?,?,?,?,?,?,?,?)")); 445 if (!add_smt) { 446 NOTREACHED(); 447 return; 448 } 449 450 sql::Statement update_access_smt(db_->GetCachedStatement(SQL_FROM_HERE, 451 "UPDATE cookies SET last_access_utc=? WHERE creation_utc=?")); 452 if (!update_access_smt) { 453 NOTREACHED(); 454 return; 455 } 456 457 sql::Statement del_smt(db_->GetCachedStatement(SQL_FROM_HERE, 458 "DELETE FROM cookies WHERE creation_utc=?")); 459 if (!del_smt) { 460 NOTREACHED(); 461 return; 462 } 463 464 sql::Transaction transaction(db_.get()); 465 if (!transaction.Begin()) { 466 NOTREACHED(); 467 return; 468 } 469#if defined(ANDROID) 470 int cookie_delta = 0; 471#endif 472 for (PendingOperationsList::iterator it = ops.begin(); 473 it != ops.end(); ++it) { 474 // Free the cookies as we commit them to the database. 475 scoped_ptr<PendingOperation> po(*it); 476 switch (po->op()) { 477 case PendingOperation::COOKIE_ADD: 478#if defined(ANDROID) 479 ++cookie_delta; 480#endif 481 add_smt.Reset(); 482 add_smt.BindInt64(0, po->cc().CreationDate().ToInternalValue()); 483 add_smt.BindString(1, po->cc().Domain()); 484 add_smt.BindString(2, po->cc().Name()); 485 add_smt.BindString(3, po->cc().Value()); 486 add_smt.BindString(4, po->cc().Path()); 487 add_smt.BindInt64(5, po->cc().ExpiryDate().ToInternalValue()); 488 add_smt.BindInt(6, po->cc().IsSecure()); 489 add_smt.BindInt(7, po->cc().IsHttpOnly()); 490 add_smt.BindInt64(8, po->cc().LastAccessDate().ToInternalValue()); 491 if (!add_smt.Run()) 492 NOTREACHED() << "Could not add a cookie to the DB."; 493 break; 494 495 case PendingOperation::COOKIE_UPDATEACCESS: 496 update_access_smt.Reset(); 497 update_access_smt.BindInt64(0, 498 po->cc().LastAccessDate().ToInternalValue()); 499 update_access_smt.BindInt64(1, 500 po->cc().CreationDate().ToInternalValue()); 501 if (!update_access_smt.Run()) 502 NOTREACHED() << "Could not update cookie last access time in the DB."; 503 break; 504 505 case PendingOperation::COOKIE_DELETE: 506#if defined(ANDROID) 507 --cookie_delta; 508#endif 509 del_smt.Reset(); 510 del_smt.BindInt64(0, po->cc().CreationDate().ToInternalValue()); 511 if (!del_smt.Run()) 512 NOTREACHED() << "Could not delete a cookie from the DB."; 513 break; 514 515 default: 516 NOTREACHED(); 517 break; 518 } 519 } 520 bool succeeded = transaction.Commit(); 521#if defined(ANDROID) 522 if (succeeded) 523 cookie_count_ += cookie_delta; 524#endif 525 UMA_HISTOGRAM_ENUMERATION("Cookie.BackingStoreUpdateResults", 526 succeeded ? 0 : 1, 2); 527} 528 529void SQLitePersistentCookieStore::Backend::Flush(Task* completion_task) { 530#if defined(ANDROID) 531 if (!getDbThread()) { 532 if (completion_task) 533 MessageLoop::current()->PostTask(FROM_HERE, completion_task); 534 return; 535 } 536 537 MessageLoop* loop = getDbThread()->message_loop(); 538 loop->PostTask(FROM_HERE, NewRunnableMethod( 539 this, &Backend::Commit, completion_task)); 540#else 541 DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::DB)); 542 BrowserThread::PostTask( 543 BrowserThread::DB, FROM_HERE, NewRunnableMethod(this, &Backend::Commit)); 544 if (completion_task) { 545 // We want the completion task to run immediately after Commit() returns. 546 // Posting it from here means there is less chance of another task getting 547 // onto the message queue first, than if we posted it from Commit() itself. 548 BrowserThread::PostTask(BrowserThread::DB, FROM_HERE, completion_task); 549 } 550#endif 551} 552 553// Fire off a close message to the background thread. We could still have a 554// pending commit timer that will be holding a reference on us, but if/when 555// this fires we will already have been cleaned up and it will be ignored. 556void SQLitePersistentCookieStore::Backend::Close() { 557#ifndef ANDROID 558 DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::DB)); 559#endif 560 561#ifdef ANDROID 562 if (!getDbThread()) 563 return; 564 565 MessageLoop* loop = getDbThread()->message_loop(); 566 loop->PostTask(FROM_HERE, 567 NewRunnableMethod(this, &Backend::InternalBackgroundClose)); 568#else 569 // Must close the backend on the background thread. 570 BrowserThread::PostTask( 571 BrowserThread::DB, FROM_HERE, 572 NewRunnableMethod(this, &Backend::InternalBackgroundClose)); 573#endif 574} 575 576void SQLitePersistentCookieStore::Backend::InternalBackgroundClose() { 577#ifndef ANDROID 578 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); 579#endif 580 // Commit any pending operations 581#if defined(ANDROID) 582 Commit(NULL); 583#else 584 Commit(); 585#endif 586 587 db_.reset(); 588 589 if (clear_local_state_on_exit_) 590 file_util::Delete(path_, false); 591} 592 593void SQLitePersistentCookieStore::Backend::SetClearLocalStateOnExit( 594 bool clear_local_state) { 595 AutoLock locked(lock_); 596 clear_local_state_on_exit_ = clear_local_state; 597} 598SQLitePersistentCookieStore::SQLitePersistentCookieStore(const FilePath& path) 599 : backend_(new Backend(path)) { 600} 601 602SQLitePersistentCookieStore::~SQLitePersistentCookieStore() { 603 if (backend_.get()) { 604 backend_->Close(); 605 // Release our reference, it will probably still have a reference if the 606 // background thread has not run Close() yet. 607 backend_ = NULL; 608 } 609} 610 611bool SQLitePersistentCookieStore::Load( 612 std::vector<net::CookieMonster::CanonicalCookie*>* cookies) { 613 return backend_->Load(cookies); 614} 615 616void SQLitePersistentCookieStore::AddCookie( 617 const net::CookieMonster::CanonicalCookie& cc) { 618 if (backend_.get()) 619 backend_->AddCookie(cc); 620} 621 622void SQLitePersistentCookieStore::UpdateCookieAccessTime( 623 const net::CookieMonster::CanonicalCookie& cc) { 624 if (backend_.get()) 625 backend_->UpdateCookieAccessTime(cc); 626} 627 628void SQLitePersistentCookieStore::DeleteCookie( 629 const net::CookieMonster::CanonicalCookie& cc) { 630 if (backend_.get()) 631 backend_->DeleteCookie(cc); 632} 633 634void SQLitePersistentCookieStore::SetClearLocalStateOnExit( 635 bool clear_local_state) { 636 if (backend_.get()) 637 backend_->SetClearLocalStateOnExit(clear_local_state); 638} 639 640void SQLitePersistentCookieStore::Flush(Task* completion_task) { 641 if (backend_.get()) 642 backend_->Flush(completion_task); 643 else if (completion_task) 644 MessageLoop::current()->PostTask(FROM_HERE, completion_task); 645} 646 647#if defined(ANDROID) 648int SQLitePersistentCookieStore::GetCookieCount() { 649 int result = backend_ ? backend_->get_cookie_count() : 0; 650 return result; 651} 652#endif 653