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