| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2006-2008 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/common/net/cookie_monster_sqlite.h" | |
| 6 | |
| 7 #include <list> | |
| 8 | |
| 9 #include "base/basictypes.h" | |
| 10 #include "base/logging.h" | |
| 11 #include "base/ref_counted.h" | |
| 12 #include "base/string_util.h" | |
| 13 #include "base/thread.h" | |
| 14 #include "chrome/common/sqlite_compiled_statement.h" | |
| 15 #include "chrome/common/sqlite_utils.h" | |
| 16 | |
| 17 using base::Time; | |
| 18 | |
| 19 // This class is designed to be shared between any calling threads and the | |
| 20 // database thread. It batches operations and commits them on a timer. | |
| 21 class SQLitePersistentCookieStore::Backend | |
| 22 : public base::RefCountedThreadSafe<SQLitePersistentCookieStore::Backend> { | |
| 23 public: | |
| 24 // The passed database pointer must be already-initialized. This object will | |
| 25 // take ownership. | |
| 26 explicit Backend(sqlite3* db, MessageLoop* loop) | |
| 27 : db_(db), | |
| 28 background_loop_(loop), | |
| 29 cache_(new SqliteStatementCache(db)), | |
| 30 num_pending_(0) { | |
| 31 DCHECK(db_) << "Database must exist."; | |
| 32 } | |
| 33 | |
| 34 // You should call Close() before destructing this object. | |
| 35 ~Backend() { | |
| 36 DCHECK(!db_) << "Close should have already been called."; | |
| 37 DCHECK(num_pending_ == 0 && pending_.empty()); | |
| 38 } | |
| 39 | |
| 40 // Batch a cookie addition. | |
| 41 void AddCookie(const std::string& key, | |
| 42 const net::CookieMonster::CanonicalCookie& cc); | |
| 43 | |
| 44 // Batch a cookie access time update. | |
| 45 void UpdateCookieAccessTime(const net::CookieMonster::CanonicalCookie& cc); | |
| 46 | |
| 47 // Batch a cookie deletion. | |
| 48 void DeleteCookie(const net::CookieMonster::CanonicalCookie& cc); | |
| 49 | |
| 50 // Commit any pending operations and close the database. This must be called | |
| 51 // before the object is destructed. | |
| 52 void Close(); | |
| 53 | |
| 54 private: | |
| 55 class PendingOperation { | |
| 56 public: | |
| 57 typedef enum { | |
| 58 COOKIE_ADD, | |
| 59 COOKIE_UPDATEACCESS, | |
| 60 COOKIE_DELETE, | |
| 61 } OperationType; | |
| 62 | |
| 63 PendingOperation(OperationType op, | |
| 64 const std::string& key, | |
| 65 const net::CookieMonster::CanonicalCookie& cc) | |
| 66 : op_(op), key_(key), cc_(cc) { } | |
| 67 | |
| 68 OperationType op() const { return op_; } | |
| 69 const std::string& key() const { return key_; } | |
| 70 const net::CookieMonster::CanonicalCookie& cc() const { return cc_; } | |
| 71 | |
| 72 private: | |
| 73 OperationType op_; | |
| 74 std::string key_; // Only used for OP_ADD | |
| 75 net::CookieMonster::CanonicalCookie cc_; | |
| 76 }; | |
| 77 | |
| 78 private: | |
| 79 // Batch a cookie operation (add or delete) | |
| 80 void BatchOperation(PendingOperation::OperationType op, | |
| 81 const std::string& key, | |
| 82 const net::CookieMonster::CanonicalCookie& cc); | |
| 83 // Commit our pending operations to the database. | |
| 84 void Commit(); | |
| 85 // Close() executed on the background thread. | |
| 86 void InternalBackgroundClose(); | |
| 87 | |
| 88 sqlite3* db_; | |
| 89 MessageLoop* background_loop_; | |
| 90 SqliteStatementCache* cache_; | |
| 91 | |
| 92 typedef std::list<PendingOperation*> PendingOperationsList; | |
| 93 PendingOperationsList pending_; | |
| 94 PendingOperationsList::size_type num_pending_; | |
| 95 Lock pending_lock_; // Guard pending_ and num_pending_ | |
| 96 | |
| 97 DISALLOW_COPY_AND_ASSIGN(Backend); | |
| 98 }; | |
| 99 | |
| 100 void SQLitePersistentCookieStore::Backend::AddCookie( | |
| 101 const std::string& key, | |
| 102 const net::CookieMonster::CanonicalCookie& cc) { | |
| 103 BatchOperation(PendingOperation::COOKIE_ADD, key, cc); | |
| 104 } | |
| 105 | |
| 106 void SQLitePersistentCookieStore::Backend::UpdateCookieAccessTime( | |
| 107 const net::CookieMonster::CanonicalCookie& cc) { | |
| 108 BatchOperation(PendingOperation::COOKIE_UPDATEACCESS, std::string(), cc); | |
| 109 } | |
| 110 | |
| 111 void SQLitePersistentCookieStore::Backend::DeleteCookie( | |
| 112 const net::CookieMonster::CanonicalCookie& cc) { | |
| 113 BatchOperation(PendingOperation::COOKIE_DELETE, std::string(), cc); | |
| 114 } | |
| 115 | |
| 116 void SQLitePersistentCookieStore::Backend::BatchOperation( | |
| 117 PendingOperation::OperationType op, | |
| 118 const std::string& key, | |
| 119 const net::CookieMonster::CanonicalCookie& cc) { | |
| 120 // Commit every 30 seconds. | |
| 121 static const int kCommitIntervalMs = 30 * 1000; | |
| 122 // Commit right away if we have more than 512 outstanding operations. | |
| 123 static const size_t kCommitAfterBatchSize = 512; | |
| 124 DCHECK(MessageLoop::current() != background_loop_); | |
| 125 | |
| 126 // We do a full copy of the cookie here, and hopefully just here. | |
| 127 scoped_ptr<PendingOperation> po(new PendingOperation(op, key, cc)); | |
| 128 CHECK(po.get()); | |
| 129 | |
| 130 PendingOperationsList::size_type num_pending; | |
| 131 { | |
| 132 AutoLock locked(pending_lock_); | |
| 133 pending_.push_back(po.release()); | |
| 134 num_pending = ++num_pending_; | |
| 135 } | |
| 136 | |
| 137 // TODO(abarth): What if the DB thread is being destroyed on the UI thread? | |
| 138 if (num_pending == 1) { | |
| 139 // We've gotten our first entry for this batch, fire off the timer. | |
| 140 background_loop_->PostDelayedTask(FROM_HERE, | |
| 141 NewRunnableMethod(this, &Backend::Commit), kCommitIntervalMs); | |
| 142 } else if (num_pending == kCommitAfterBatchSize) { | |
| 143 // We've reached a big enough batch, fire off a commit now. | |
| 144 background_loop_->PostTask(FROM_HERE, | |
| 145 NewRunnableMethod(this, &Backend::Commit)); | |
| 146 } | |
| 147 } | |
| 148 | |
| 149 void SQLitePersistentCookieStore::Backend::Commit() { | |
| 150 DCHECK(MessageLoop::current() == background_loop_); | |
| 151 PendingOperationsList ops; | |
| 152 { | |
| 153 AutoLock locked(pending_lock_); | |
| 154 pending_.swap(ops); | |
| 155 num_pending_ = 0; | |
| 156 } | |
| 157 | |
| 158 // Maybe an old timer fired or we are already Close()'ed. | |
| 159 if (!db_ || ops.empty()) | |
| 160 return; | |
| 161 | |
| 162 SQLITE_UNIQUE_STATEMENT(add_smt, *cache_, | |
| 163 "INSERT INTO cookies (creation_utc, host_key, name, value, path, " | |
| 164 "expires_utc, secure, httponly, last_access_utc) " | |
| 165 "VALUES (?,?,?,?,?,?,?,?,?)"); | |
| 166 if (!add_smt.is_valid()) { | |
| 167 NOTREACHED(); | |
| 168 return; | |
| 169 } | |
| 170 | |
| 171 SQLITE_UNIQUE_STATEMENT(update_access_smt, *cache_, | |
| 172 "UPDATE cookies SET last_access_utc=? " | |
| 173 "WHERE creation_utc=?"); | |
| 174 if (!update_access_smt.is_valid()) { | |
| 175 NOTREACHED(); | |
| 176 return; | |
| 177 } | |
| 178 | |
| 179 SQLITE_UNIQUE_STATEMENT(del_smt, *cache_, | |
| 180 "DELETE FROM cookies WHERE creation_utc=?"); | |
| 181 if (!del_smt.is_valid()) { | |
| 182 NOTREACHED(); | |
| 183 return; | |
| 184 } | |
| 185 | |
| 186 SQLTransaction transaction(db_); | |
| 187 transaction.Begin(); | |
| 188 for (PendingOperationsList::iterator it = ops.begin(); | |
| 189 it != ops.end(); ++it) { | |
| 190 // Free the cookies as we commit them to the database. | |
| 191 scoped_ptr<PendingOperation> po(*it); | |
| 192 switch (po->op()) { | |
| 193 case PendingOperation::COOKIE_ADD: | |
| 194 add_smt->reset(); | |
| 195 add_smt->bind_int64(0, po->cc().CreationDate().ToInternalValue()); | |
| 196 add_smt->bind_string(1, po->key()); | |
| 197 add_smt->bind_string(2, po->cc().Name()); | |
| 198 add_smt->bind_string(3, po->cc().Value()); | |
| 199 add_smt->bind_string(4, po->cc().Path()); | |
| 200 add_smt->bind_int64(5, po->cc().ExpiryDate().ToInternalValue()); | |
| 201 add_smt->bind_int(6, po->cc().IsSecure()); | |
| 202 add_smt->bind_int(7, po->cc().IsHttpOnly()); | |
| 203 add_smt->bind_int64(8, po->cc().LastAccessDate().ToInternalValue()); | |
| 204 if (add_smt->step() != SQLITE_DONE) { | |
| 205 NOTREACHED() << "Could not add a cookie to the DB."; | |
| 206 } | |
| 207 break; | |
| 208 | |
| 209 case PendingOperation::COOKIE_UPDATEACCESS: | |
| 210 update_access_smt->reset(); | |
| 211 update_access_smt->bind_int64(0, | |
| 212 po->cc().LastAccessDate().ToInternalValue()); | |
| 213 update_access_smt->bind_int64(1, | |
| 214 po->cc().CreationDate().ToInternalValue()); | |
| 215 if (update_access_smt->step() != SQLITE_DONE) { | |
| 216 NOTREACHED() << "Could not update cookie last access time in the DB."; | |
| 217 } | |
| 218 break; | |
| 219 | |
| 220 case PendingOperation::COOKIE_DELETE: | |
| 221 del_smt->reset(); | |
| 222 del_smt->bind_int64(0, po->cc().CreationDate().ToInternalValue()); | |
| 223 if (del_smt->step() != SQLITE_DONE) { | |
| 224 NOTREACHED() << "Could not delete a cookie from the DB."; | |
| 225 } | |
| 226 break; | |
| 227 | |
| 228 default: | |
| 229 NOTREACHED(); | |
| 230 break; | |
| 231 } | |
| 232 } | |
| 233 transaction.Commit(); | |
| 234 } | |
| 235 | |
| 236 // Fire off a close message to the background thread. We could still have a | |
| 237 // pending commit timer that will be holding a reference on us, but if/when | |
| 238 // this fires we will already have been cleaned up and it will be ignored. | |
| 239 void SQLitePersistentCookieStore::Backend::Close() { | |
| 240 DCHECK(MessageLoop::current() != background_loop_); | |
| 241 // Must close the backend on the background thread. | |
| 242 // TODO(abarth): What if the DB thread is being destroyed on the UI thread? | |
| 243 background_loop_->PostTask(FROM_HERE, | |
| 244 NewRunnableMethod(this, &Backend::InternalBackgroundClose)); | |
| 245 } | |
| 246 | |
| 247 void SQLitePersistentCookieStore::Backend::InternalBackgroundClose() { | |
| 248 DCHECK(MessageLoop::current() == background_loop_); | |
| 249 // Commit any pending operations | |
| 250 Commit(); | |
| 251 // We must destroy the cache before closing the database. | |
| 252 delete cache_; | |
| 253 cache_ = NULL; | |
| 254 sqlite3_close(db_); | |
| 255 db_ = NULL; | |
| 256 } | |
| 257 | |
| 258 SQLitePersistentCookieStore::SQLitePersistentCookieStore( | |
| 259 const std::wstring& path, MessageLoop* background_loop) | |
| 260 : path_(path), | |
| 261 background_loop_(background_loop) { | |
| 262 DCHECK(background_loop) << "SQLitePersistentCookieStore needs a MessageLoop"; | |
| 263 } | |
| 264 | |
| 265 SQLitePersistentCookieStore::~SQLitePersistentCookieStore() { | |
| 266 if (backend_.get()) { | |
| 267 backend_->Close(); | |
| 268 // Release our reference, it will probably still have a reference if the | |
| 269 // background thread has not run Close() yet. | |
| 270 backend_ = NULL; | |
| 271 } | |
| 272 } | |
| 273 | |
| 274 // Version number of the database. In version 4, we migrated the time epoch. | |
| 275 // If you open the DB with an older version on Mac or Linux, the times will | |
| 276 // look wonky, but the file will likely be usable. On Windows version 3 and 4 | |
| 277 // are the same. | |
| 278 // | |
| 279 // Version 3 updated the database to include the last access time, so we can | |
| 280 // expire them in decreasing order of use when we've reached the maximum | |
| 281 // number of cookies. | |
| 282 static const int kCurrentVersionNumber = 4; | |
| 283 static const int kCompatibleVersionNumber = 3; | |
| 284 | |
| 285 namespace { | |
| 286 | |
| 287 // Initializes the cookies table, returning true on success. | |
| 288 bool InitTable(sqlite3* db) { | |
| 289 if (!DoesSqliteTableExist(db, "cookies")) { | |
| 290 if (sqlite3_exec(db, "CREATE TABLE cookies (" | |
| 291 "creation_utc INTEGER NOT NULL UNIQUE PRIMARY KEY," | |
| 292 "host_key TEXT NOT NULL," | |
| 293 "name TEXT NOT NULL," | |
| 294 "value TEXT NOT NULL," | |
| 295 "path TEXT NOT NULL," | |
| 296 // We only store persistent, so we know it expires | |
| 297 "expires_utc INTEGER NOT NULL," | |
| 298 "secure INTEGER NOT NULL," | |
| 299 "httponly INTEGER NOT NULL," | |
| 300 "last_access_utc INTEGER NOT NULL)", | |
| 301 NULL, NULL, NULL) != SQLITE_OK) | |
| 302 return false; | |
| 303 } | |
| 304 | |
| 305 // Try to create the index every time. Older versions did not have this index, | |
| 306 // so we want those people to get it. Ignore errors, since it may exist. | |
| 307 sqlite3_exec(db, "CREATE INDEX cookie_times ON cookies (creation_utc)", | |
| 308 NULL, NULL, NULL); | |
| 309 | |
| 310 return true; | |
| 311 } | |
| 312 | |
| 313 } // namespace | |
| 314 | |
| 315 bool SQLitePersistentCookieStore::Load( | |
| 316 std::vector<net::CookieMonster::KeyedCanonicalCookie>* cookies) { | |
| 317 DCHECK(!path_.empty()); | |
| 318 sqlite3* db; | |
| 319 if (sqlite3_open(WideToUTF8(path_).c_str(), &db) != SQLITE_OK) { | |
| 320 NOTREACHED() << "Unable to open cookie DB."; | |
| 321 return false; | |
| 322 } | |
| 323 | |
| 324 if (!EnsureDatabaseVersion(db) || !InitTable(db)) { | |
| 325 NOTREACHED() << "Unable to initialize cookie DB."; | |
| 326 sqlite3_close(db); | |
| 327 return false; | |
| 328 } | |
| 329 | |
| 330 MetaTableHelper::PrimeCache(std::string(), db); | |
| 331 | |
| 332 // Slurp all the cookies into the out-vector. | |
| 333 SQLStatement smt; | |
| 334 if (smt.prepare(db, | |
| 335 "SELECT creation_utc, host_key, name, value, path, expires_utc, secure, " | |
| 336 "httponly, last_access_utc FROM cookies") != SQLITE_OK) { | |
| 337 NOTREACHED() << "select statement prep failed"; | |
| 338 sqlite3_close(db); | |
| 339 return false; | |
| 340 } | |
| 341 | |
| 342 while (smt.step() == SQLITE_ROW) { | |
| 343 std::string key = smt.column_string(1); | |
| 344 scoped_ptr<net::CookieMonster::CanonicalCookie> cc( | |
| 345 new net::CookieMonster::CanonicalCookie( | |
| 346 smt.column_string(2), // name | |
| 347 smt.column_string(3), // value | |
| 348 smt.column_string(4), // path | |
| 349 smt.column_int(6) != 0, // secure | |
| 350 smt.column_int(7) != 0, // httponly | |
| 351 Time::FromInternalValue(smt.column_int64(0)), // creation_utc | |
| 352 Time::FromInternalValue(smt.column_int64(8)), // last_access_utc | |
| 353 true, // has_expires | |
| 354 Time::FromInternalValue(smt.column_int64(5)))); // expires_utc | |
| 355 // Memory allocation failed. | |
| 356 if (!cc.get()) | |
| 357 break; | |
| 358 | |
| 359 DLOG_IF(WARNING, | |
| 360 cc->CreationDate() > Time::Now()) << L"CreationDate too recent"; | |
| 361 cookies->push_back( | |
| 362 net::CookieMonster::KeyedCanonicalCookie(smt.column_string(1), | |
| 363 cc.release())); | |
| 364 } | |
| 365 | |
| 366 // Create the backend, this will take ownership of the db pointer. | |
| 367 backend_ = new Backend(db, background_loop_); | |
| 368 | |
| 369 return true; | |
| 370 } | |
| 371 | |
| 372 bool SQLitePersistentCookieStore::EnsureDatabaseVersion(sqlite3* db) { | |
| 373 // Version check. | |
| 374 if (!meta_table_.Init(std::string(), kCurrentVersionNumber, | |
| 375 kCompatibleVersionNumber, db)) | |
| 376 return false; | |
| 377 | |
| 378 if (meta_table_.GetCompatibleVersionNumber() > kCurrentVersionNumber) { | |
| 379 LOG(WARNING) << "Cookie database is too new."; | |
| 380 return false; | |
| 381 } | |
| 382 | |
| 383 int cur_version = meta_table_.GetVersionNumber(); | |
| 384 if (cur_version == 2) { | |
| 385 SQLTransaction transaction(db); | |
| 386 transaction.Begin(); | |
| 387 if ((sqlite3_exec(db, | |
| 388 "ALTER TABLE cookies ADD COLUMN last_access_utc " | |
| 389 "INTEGER DEFAULT 0", NULL, NULL, NULL) != SQLITE_OK) || | |
| 390 (sqlite3_exec(db, | |
| 391 "UPDATE cookies SET last_access_utc = creation_utc", | |
| 392 NULL, NULL, NULL) != SQLITE_OK)) { | |
| 393 LOG(WARNING) << "Unable to update cookie database to version 3."; | |
| 394 return false; | |
| 395 } | |
| 396 ++cur_version; | |
| 397 meta_table_.SetVersionNumber(cur_version); | |
| 398 meta_table_.SetCompatibleVersionNumber( | |
| 399 std::min(cur_version, kCompatibleVersionNumber)); | |
| 400 transaction.Commit(); | |
| 401 } | |
| 402 | |
| 403 if (cur_version == 3) { | |
| 404 // The time epoch changed for Mac & Linux in this version to match Windows. | |
| 405 // This patch came after the main epoch change happened, so some | |
| 406 // developers have "good" times for cookies added by the more recent | |
| 407 // versions. So we have to be careful to only update times that are under | |
| 408 // the old system (which will appear to be from before 1970 in the new | |
| 409 // system). The magic number used below is 1970 in our time units. | |
| 410 SQLTransaction transaction(db); | |
| 411 transaction.Begin(); | |
| 412 #if !defined(OS_WIN) | |
| 413 sqlite3_exec(db, | |
| 414 "UPDATE cookies " | |
| 415 "SET creation_utc = creation_utc + 11644473600000000 " | |
| 416 "WHERE rowid IN " | |
| 417 "(SELECT rowid FROM cookies WHERE " | |
| 418 "creation_utc > 0 AND creation_utc < 11644473600000000)", | |
| 419 NULL, NULL, NULL); | |
| 420 sqlite3_exec(db, | |
| 421 "UPDATE cookies " | |
| 422 "SET expires_utc = expires_utc + 11644473600000000 " | |
| 423 "WHERE rowid IN " | |
| 424 "(SELECT rowid FROM cookies WHERE " | |
| 425 "expires_utc > 0 AND expires_utc < 11644473600000000)", | |
| 426 NULL, NULL, NULL); | |
| 427 sqlite3_exec(db, | |
| 428 "UPDATE cookies " | |
| 429 "SET last_access_utc = last_access_utc + 11644473600000000 " | |
| 430 "WHERE rowid IN " | |
| 431 "(SELECT rowid FROM cookies WHERE " | |
| 432 "last_access_utc > 0 AND last_access_utc < 11644473600000000)", | |
| 433 NULL, NULL, NULL); | |
| 434 #endif | |
| 435 ++cur_version; | |
| 436 meta_table_.SetVersionNumber(cur_version); | |
| 437 transaction.Commit(); | |
| 438 } | |
| 439 | |
| 440 // Put future migration cases here. | |
| 441 | |
| 442 // When the version is too old, we just try to continue anyway, there should | |
| 443 // not be a released product that makes a database too old for us to handle. | |
| 444 LOG_IF(WARNING, cur_version < kCurrentVersionNumber) << | |
| 445 "Cookie database version " << cur_version << " is too old to handle."; | |
| 446 | |
| 447 return true; | |
| 448 } | |
| 449 | |
| 450 void SQLitePersistentCookieStore::AddCookie( | |
| 451 const std::string& key, | |
| 452 const net::CookieMonster::CanonicalCookie& cc) { | |
| 453 if (backend_.get()) | |
| 454 backend_->AddCookie(key, cc); | |
| 455 } | |
| 456 | |
| 457 void SQLitePersistentCookieStore::UpdateCookieAccessTime( | |
| 458 const net::CookieMonster::CanonicalCookie& cc) { | |
| 459 if (backend_.get()) | |
| 460 backend_->UpdateCookieAccessTime(cc); | |
| 461 } | |
| 462 | |
| 463 void SQLitePersistentCookieStore::DeleteCookie( | |
| 464 const net::CookieMonster::CanonicalCookie& cc) { | |
| 465 if (backend_.get()) | |
| 466 backend_->DeleteCookie(cc); | |
| 467 } | |
| OLD | NEW |