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