webrtc_identity_store_backend.cc revision 4e180b6a0b4720a9b8e9e959a882386f690f08ff
1ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch// Copyright 2013 The Chromium Authors. All rights reserved.
2ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch// Use of this source code is governed by a BSD-style license that can be
3ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch// found in the LICENSE file.
4ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
5ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch#include "content/browser/media/webrtc_identity_store_backend.h"
6ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
7ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch#include "base/file_util.h"
8ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch#include "base/files/file_path.h"
9ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch#include "content/public/browser/browser_thread.h"
10ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch#include "net/base/net_errors.h"
11ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch#include "sql/error_delegate_util.h"
12ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch#include "sql/statement.h"
13ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch#include "sql/transaction.h"
14ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch#include "url/gurl.h"
15ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch#include "webkit/browser/quota/special_storage_policy.h"
16ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
17ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdochnamespace content {
18ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
19ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdochstatic const char* kWebRTCIdentityStoreDBName = "webrtc_identity_store";
20ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
21ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdochstatic const base::FilePath::CharType kWebRTCIdentityStoreDirectory[] =
22ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    FILE_PATH_LITERAL("WebRTCIdentityStore");
23ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
24ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch// Initializes the identity table, returning true on success.
25ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdochstatic bool InitDB(sql::Connection* db) {
26ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  if (db->DoesTableExist(kWebRTCIdentityStoreDBName)) {
27ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    if (db->DoesColumnExist(kWebRTCIdentityStoreDBName, "origin") &&
28ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch        db->DoesColumnExist(kWebRTCIdentityStoreDBName, "identity_name") &&
29ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch        db->DoesColumnExist(kWebRTCIdentityStoreDBName, "common_name") &&
30ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch        db->DoesColumnExist(kWebRTCIdentityStoreDBName, "certificate") &&
31ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch        db->DoesColumnExist(kWebRTCIdentityStoreDBName, "private_key") &&
32ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch        db->DoesColumnExist(kWebRTCIdentityStoreDBName, "creation_time"))
33ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch      return true;
34ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    if (!db->Execute("DROP TABLE webrtc_identity_store"))
35ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch      return false;
36ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  }
37ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
38ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  return db->Execute(
39ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch      "CREATE TABLE webrtc_identity_store"
40ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch      " ("
41ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch      "origin TEXT NOT NULL,"
42ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch      "identity_name TEXT NOT NULL,"
43ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch      "common_name TEXT NOT NULL,"
44ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch      "certificate BLOB NOT NULL,"
45ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch      "private_key BLOB NOT NULL,"
46ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch      "creation_time INTEGER)");
47ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch}
48ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
49ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdochstruct WebRTCIdentityStoreBackend::IdentityKey {
50ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  IdentityKey(const GURL& origin, const std::string& identity_name)
51ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch      : origin(origin), identity_name(identity_name) {}
52ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
53ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  bool operator<(const IdentityKey& other) const {
54ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    return origin < other.origin ||
55ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch           (origin == other.origin && identity_name < other.identity_name);
56ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  }
57ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
58ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  GURL origin;
59ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  std::string identity_name;
60ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch};
61ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
62ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdochstruct WebRTCIdentityStoreBackend::Identity {
63ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  Identity(const std::string& common_name,
64ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch           const std::string& certificate,
65ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch           const std::string& private_key)
66ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch      : common_name(common_name),
67ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch        certificate(certificate),
68ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch        private_key(private_key),
69ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch        creation_time(base::Time::Now().ToInternalValue()) {}
70ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
71ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  Identity(const std::string& common_name,
72ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch           const std::string& certificate,
73ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch           const std::string& private_key,
74ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch           int64 creation_time)
75ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch      : common_name(common_name),
76ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch        certificate(certificate),
77ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch        private_key(private_key),
78ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch        creation_time(creation_time) {}
79ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
80ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  std::string common_name;
81ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  std::string certificate;
82ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  std::string private_key;
83ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  int64 creation_time;
84ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch};
85ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
86ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdochstruct WebRTCIdentityStoreBackend::PendingFindRequest {
87ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  PendingFindRequest(const GURL& origin,
88ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch                     const std::string& identity_name,
89ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch                     const std::string& common_name,
90ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch                     const FindIdentityCallback& callback)
91ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch      : origin(origin),
92ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch        identity_name(identity_name),
93ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch        common_name(common_name),
94ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch        callback(callback) {}
95ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
96ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  ~PendingFindRequest() {}
97ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
98ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  GURL origin;
99ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  std::string identity_name;
100ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  std::string common_name;
101ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  FindIdentityCallback callback;
102ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch};
103ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
104ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch// The class encapsulates the database operations. All members except ctor and
105ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch// dtor should be accessed on the DB thread.
106ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch// It can be created/destroyed on any thread.
107ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdochclass WebRTCIdentityStoreBackend::SqlLiteStorage
108ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    : public base::RefCountedThreadSafe<SqlLiteStorage> {
109ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch public:
1104e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  SqlLiteStorage(base::TimeDelta validity_period,
1114e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)                 const base::FilePath& path,
112ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch                 quota::SpecialStoragePolicy* policy)
1134e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      : validity_period_(validity_period), special_storage_policy_(policy) {
114ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    if (!path.empty())
115ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch      path_ = path.Append(kWebRTCIdentityStoreDirectory);
116ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  }
117ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
118ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  void Load(IdentityMap* out_map);
119ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  void Close();
120ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  void AddIdentity(const GURL& origin,
121ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch                   const std::string& identity_name,
122ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch                   const Identity& identity);
123ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  void DeleteIdentity(const GURL& origin,
124ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch                      const std::string& identity_name,
125ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch                      const Identity& identity);
1264e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  void DeleteBetween(base::Time delete_begin, base::Time delete_end);
1274e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
1284e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  void SetValidityPeriodForTesting(base::TimeDelta validity_period) {
1294e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
1304e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    DCHECK(!db_.get());
1314e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    validity_period_ = validity_period;
1324e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  }
133ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
134ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch private:
135ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  friend class base::RefCountedThreadSafe<SqlLiteStorage>;
136ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
137ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  enum OperationType {
138ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    ADD_IDENTITY,
139ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    DELETE_IDENTITY
140ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  };
141ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  struct PendingOperation {
142ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    PendingOperation(OperationType type,
143ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch                     const GURL& origin,
144ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch                     const std::string& identity_name,
145ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch                     const Identity& identity)
146ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch        : type(type),
147ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch          origin(origin),
148ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch          identity_name(identity_name),
149ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch          identity(identity) {}
150ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
151ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    OperationType type;
152ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    GURL origin;
153ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    std::string identity_name;
154ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    Identity identity;
155ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  };
156ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  typedef std::vector<PendingOperation*> PendingOperationList;
157ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
158ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  virtual ~SqlLiteStorage() {}
159ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  void OnDatabaseError(int error, sql::Statement* stmt);
160ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  void BatchOperation(OperationType type,
161ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch                      const GURL& origin,
162ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch                      const std::string& identity_name,
163ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch                      const Identity& identity);
164ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  void Commit();
165ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
1664e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  base::TimeDelta validity_period_;
167ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  // The file path of the DB. Empty if temporary.
168ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  base::FilePath path_;
169ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  scoped_refptr<quota::SpecialStoragePolicy> special_storage_policy_;
170ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  scoped_ptr<sql::Connection> db_;
171ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  // Batched DB operations pending to commit.
172ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  PendingOperationList pending_operations_;
173ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
174ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  DISALLOW_COPY_AND_ASSIGN(SqlLiteStorage);
175ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch};
176ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
177ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben MurdochWebRTCIdentityStoreBackend::WebRTCIdentityStoreBackend(
178ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    const base::FilePath& path,
1794e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    quota::SpecialStoragePolicy* policy,
1804e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    base::TimeDelta validity_period)
1814e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    : validity_period_(validity_period),
1824e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      state_(NOT_STARTED),
1834e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      sql_lite_storage_(new SqlLiteStorage(validity_period, path, policy)) {}
184ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
185ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdochbool WebRTCIdentityStoreBackend::FindIdentity(
186ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    const GURL& origin,
187ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    const std::string& identity_name,
188ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    const std::string& common_name,
189ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    const FindIdentityCallback& callback) {
190ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
191ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  if (state_ == CLOSED)
192ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    return false;
193ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
194ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  if (state_ != LOADED) {
195ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    // Queues the request to wait for the DB to load.
196ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    pending_find_requests_.push_back(
197ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch        new PendingFindRequest(origin, identity_name, common_name, callback));
198ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    if (state_ == LOADING)
199ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch      return true;
200ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
201ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    DCHECK_EQ(state_, NOT_STARTED);
202ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
203ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    // Kick off loading the DB.
204ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    scoped_ptr<IdentityMap> out_map(new IdentityMap());
2053551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    base::Closure task(
2063551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)        base::Bind(&SqlLiteStorage::Load, sql_lite_storage_, out_map.get()));
2073551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    // |out_map| will be NULL after this call.
208ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    if (BrowserThread::PostTaskAndReply(
209ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch            BrowserThread::DB,
210ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch            FROM_HERE,
2113551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)            task,
212ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch            base::Bind(&WebRTCIdentityStoreBackend::OnLoaded,
213ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch                       this,
214ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch                       base::Passed(&out_map)))) {
215ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch      state_ = LOADING;
216ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch      return true;
217ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    }
218ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    // If it fails to post task, falls back to ERR_FILE_NOT_FOUND.
219ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  }
220ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
221ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  IdentityKey key(origin, identity_name);
222ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  IdentityMap::iterator iter = identities_.find(key);
223ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  if (iter != identities_.end() && iter->second.common_name == common_name) {
2244e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    base::TimeDelta age = base::Time::Now() - base::Time::FromInternalValue(
2254e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)                                                  iter->second.creation_time);
2264e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    if (age < validity_period_) {
2274e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      // Identity found.
2284e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      return BrowserThread::PostTask(BrowserThread::IO,
2294e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)                                     FROM_HERE,
2304e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)                                     base::Bind(callback,
2314e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)                                                net::OK,
2324e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)                                                iter->second.certificate,
2334e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)                                                iter->second.private_key));
2344e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    }
2354e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    // Removes the expired identity from the in-memory cache. The copy in the
2364e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    // database will be removed on the next load.
2374e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    identities_.erase(iter);
238ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  }
239ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
240ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  return BrowserThread::PostTask(
241ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch      BrowserThread::IO,
242ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch      FROM_HERE,
243ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch      base::Bind(callback, net::ERR_FILE_NOT_FOUND, "", ""));
244ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch}
245ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
246ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdochvoid WebRTCIdentityStoreBackend::AddIdentity(const GURL& origin,
247ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch                                             const std::string& identity_name,
248ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch                                             const std::string& common_name,
249ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch                                             const std::string& certificate,
250ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch                                             const std::string& private_key) {
251ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
252ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  if (state_ == CLOSED)
253ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    return;
254ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
255ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  // If there is an existing identity for the same origin and identity_name,
256ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  // delete it.
257ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  IdentityKey key(origin, identity_name);
258ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  Identity identity(common_name, certificate, private_key);
259ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
260ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  if (identities_.find(key) != identities_.end()) {
261ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    if (!BrowserThread::PostTask(BrowserThread::DB,
262ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch                                 FROM_HERE,
263ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch                                 base::Bind(&SqlLiteStorage::DeleteIdentity,
264ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch                                            sql_lite_storage_,
265ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch                                            origin,
266ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch                                            identity_name,
267ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch                                            identities_.find(key)->second)))
268ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch      return;
269ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  }
270ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  identities_.insert(std::pair<IdentityKey, Identity>(key, identity));
271ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
272ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  BrowserThread::PostTask(BrowserThread::DB,
273ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch                          FROM_HERE,
274ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch                          base::Bind(&SqlLiteStorage::AddIdentity,
275ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch                                     sql_lite_storage_,
276ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch                                     origin,
277ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch                                     identity_name,
278ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch                                     identity));
279ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch}
280ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
281ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdochvoid WebRTCIdentityStoreBackend::Close() {
282ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) {
283ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    BrowserThread::PostTask(
284ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch        BrowserThread::IO,
285ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch        FROM_HERE,
286ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch        base::Bind(&WebRTCIdentityStoreBackend::Close, this));
287ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    return;
288ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  }
289ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
290ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  if (state_ == CLOSED)
291ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    return;
292ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
293ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  state_ = CLOSED;
294ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  BrowserThread::PostTask(
295ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch      BrowserThread::DB,
296ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch      FROM_HERE,
297ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch      base::Bind(&SqlLiteStorage::Close, sql_lite_storage_));
298ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch}
299ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
300ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdochvoid WebRTCIdentityStoreBackend::DeleteBetween(base::Time delete_begin,
301ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch                                               base::Time delete_end,
302ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch                                               const base::Closure& callback) {
303ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
3044e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  if (state_ == CLOSED)
3054e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    return;
3064e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
307ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  // Delete the in-memory cache.
308ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  IdentityMap::iterator it = identities_.begin();
309ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  while (it != identities_.end()) {
310ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    if (it->second.creation_time >= delete_begin.ToInternalValue() &&
3114e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        it->second.creation_time <= delete_end.ToInternalValue()) {
312ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch      identities_.erase(it++);
3134e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    } else {
3144e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      ++it;
3154e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    }
316ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  }
317ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  BrowserThread::PostTaskAndReply(BrowserThread::DB,
318ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch                                  FROM_HERE,
319ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch                                  base::Bind(&SqlLiteStorage::DeleteBetween,
320ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch                                             sql_lite_storage_,
321ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch                                             delete_begin,
3224e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)                                             delete_end),
323ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch                                  callback);
324ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch}
325ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
3264e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)void WebRTCIdentityStoreBackend::SetValidityPeriodForTesting(
3274e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    base::TimeDelta validity_period) {
3284e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
3294e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  validity_period_ = validity_period;
3304e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  BrowserThread::PostTask(
3314e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      BrowserThread::DB,
3324e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      FROM_HERE,
3334e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      base::Bind(&SqlLiteStorage::SetValidityPeriodForTesting,
3344e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)                 sql_lite_storage_,
3354e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)                 validity_period));
3364e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)}
3374e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
338ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben MurdochWebRTCIdentityStoreBackend::~WebRTCIdentityStoreBackend() {}
339ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
340ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdochvoid WebRTCIdentityStoreBackend::OnLoaded(scoped_ptr<IdentityMap> out_map) {
341ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
3424e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
3434e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  if (state_ != LOADING)
3444e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    return;
3454e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
3464e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  DVLOG(2) << "WebRTC identity store has loaded.";
3474e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
348ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  state_ = LOADED;
349ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  identities_.swap(*out_map);
350ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
351ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  for (size_t i = 0; i < pending_find_requests_.size(); ++i) {
352ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    FindIdentity(pending_find_requests_[i]->origin,
353ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch                 pending_find_requests_[i]->identity_name,
354ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch                 pending_find_requests_[i]->common_name,
355ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch                 pending_find_requests_[i]->callback);
356ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    delete pending_find_requests_[i];
357ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  }
358ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  pending_find_requests_.clear();
359ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch}
360ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
361ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch//
362ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch// Implementation of SqlLiteStorage.
363ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch//
364ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
365ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdochvoid WebRTCIdentityStoreBackend::SqlLiteStorage::Load(IdentityMap* out_map) {
366ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
367ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  DCHECK(!db_.get());
368ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
369ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  // Ensure the parent directory for storing certs is created before reading
370ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  // from it.
371ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  const base::FilePath dir = path_.DirName();
372ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  if (!base::PathExists(dir) && !file_util::CreateDirectory(dir)) {
373ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    DLOG(ERROR) << "Unable to open DB file path.";
374ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    return;
375ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  }
376ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
377ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  db_.reset(new sql::Connection());
378ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
379ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  db_->set_error_callback(base::Bind(&SqlLiteStorage::OnDatabaseError, this));
380ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
381ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  if (!db_->Open(path_)) {
382ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    DLOG(ERROR) << "Unable to open DB.";
383ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    db_.reset();
384ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    return;
385ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  }
386ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
387ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  if (!InitDB(db_.get())) {
388ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    DLOG(ERROR) << "Unable to init DB.";
389ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    db_.reset();
390ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    return;
391ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  }
392ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
393ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  db_->Preload();
394ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
3954e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  // Delete expired identities.
3964e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  DeleteBetween(base::Time(), base::Time::Now() - validity_period_);
3974e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
398ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  // Slurp all the identities into the out_map.
399ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  sql::Statement stmt(db_->GetUniqueStatement(
400ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch      "SELECT origin, identity_name, common_name, "
401ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch      "certificate, private_key, creation_time "
402ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch      "FROM webrtc_identity_store"));
403ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  CHECK(stmt.is_valid());
404ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
405ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  while (stmt.Step()) {
406ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    IdentityKey key(GURL(stmt.ColumnString(0)), stmt.ColumnString(1));
407ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    std::string common_name(stmt.ColumnString(2));
408ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    std::string cert, private_key;
409ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    stmt.ColumnBlobAsString(3, &cert);
410ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    stmt.ColumnBlobAsString(4, &private_key);
411ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    int64 creation_time = stmt.ColumnInt64(5);
412ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    std::pair<IdentityMap::iterator, bool> result =
413ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch        out_map->insert(std::pair<IdentityKey, Identity>(
414ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch            key, Identity(common_name, cert, private_key, creation_time)));
415ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    DCHECK(result.second);
416ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  }
417ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch}
418ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
419ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdochvoid WebRTCIdentityStoreBackend::SqlLiteStorage::Close() {
420ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
421ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  Commit();
422ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  db_.reset();
423ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch}
424ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
425ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdochvoid WebRTCIdentityStoreBackend::SqlLiteStorage::AddIdentity(
426ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    const GURL& origin,
427ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    const std::string& identity_name,
428ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    const Identity& identity) {
429ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
430ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  if (!db_.get())
431ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    return;
432ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
433ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  // Do not add for session only origins.
434ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  if (special_storage_policy_.get() &&
435ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch      !special_storage_policy_->IsStorageProtected(origin) &&
436ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch      special_storage_policy_->IsStorageSessionOnly(origin)) {
437ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    return;
438ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  }
439ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  BatchOperation(ADD_IDENTITY, origin, identity_name, identity);
440ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch}
441ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
442ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdochvoid WebRTCIdentityStoreBackend::SqlLiteStorage::DeleteIdentity(
443ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    const GURL& origin,
444ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    const std::string& identity_name,
445ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    const Identity& identity) {
446ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
447ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  if (!db_.get())
448ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    return;
449ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  BatchOperation(DELETE_IDENTITY, origin, identity_name, identity);
450ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch}
451ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
4524e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)void WebRTCIdentityStoreBackend::SqlLiteStorage::DeleteBetween(
4534e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    base::Time delete_begin,
4544e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    base::Time delete_end) {
4554e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
4564e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  if (!db_.get())
4574e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    return;
4584e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
4594e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  // Commit pending operations first.
4604e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  Commit();
4614e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
4624e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  sql::Statement del_stmt(db_->GetCachedStatement(
4634e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      SQL_FROM_HERE,
4644e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      "DELETE FROM webrtc_identity_store"
4654e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      " WHERE creation_time >= ? AND creation_time <= ?"));
4664e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  CHECK(del_stmt.is_valid());
4674e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
4684e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  del_stmt.BindInt64(0, delete_begin.ToInternalValue());
4694e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  del_stmt.BindInt64(1, delete_end.ToInternalValue());
4704e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
4714e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  sql::Transaction transaction(db_.get());
4724e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  if (!transaction.Begin()) {
4734e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    DLOG(ERROR) << "Failed to begin the transaction.";
4744e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    return;
4754e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  }
4764e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
4774e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  CHECK(del_stmt.Run());
4784e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  transaction.Commit();
4794e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)}
4804e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
481ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdochvoid WebRTCIdentityStoreBackend::SqlLiteStorage::OnDatabaseError(
482ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    int error,
483ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    sql::Statement* stmt) {
484ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
485ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  if (!sql::IsErrorCatastrophic(error))
486ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    return;
487ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  db_->RazeAndClose();
488ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch}
489ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
490ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdochvoid WebRTCIdentityStoreBackend::SqlLiteStorage::BatchOperation(
491ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    OperationType type,
492ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    const GURL& origin,
493ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    const std::string& identity_name,
494ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    const Identity& identity) {
495ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
496ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  // Commit every 30 seconds.
497ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  static const base::TimeDelta kCommitInterval(
498ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch      base::TimeDelta::FromSeconds(30));
499ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  // Commit right away if we have more than 512 outstanding operations.
500ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  static const size_t kCommitAfterBatchSize = 512;
501ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
502ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  // We do a full copy of the cert here, and hopefully just here.
503ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  scoped_ptr<PendingOperation> operation(
504ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch      new PendingOperation(type, origin, identity_name, identity));
505ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
506ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  pending_operations_.push_back(operation.release());
507ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
508ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  if (pending_operations_.size() == 1) {
509ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    // We've gotten our first entry for this batch, fire off the timer.
510ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    BrowserThread::PostDelayedTask(BrowserThread::DB,
511ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch                                   FROM_HERE,
512ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch                                   base::Bind(&SqlLiteStorage::Commit, this),
513ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch                                   kCommitInterval);
514ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  } else if (pending_operations_.size() >= kCommitAfterBatchSize) {
515ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    // We've reached a big enough batch, fire off a commit now.
516ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    BrowserThread::PostTask(BrowserThread::DB,
517ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch                            FROM_HERE,
518ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch                            base::Bind(&SqlLiteStorage::Commit, this));
519ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  }
520ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch}
521ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
522ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdochvoid WebRTCIdentityStoreBackend::SqlLiteStorage::Commit() {
523ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
524ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  // Maybe an old timer fired or we are already Close()'ed.
525ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  if (!db_.get() || pending_operations_.empty())
526ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    return;
527ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
528ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  sql::Statement add_stmt(db_->GetCachedStatement(
529ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch      SQL_FROM_HERE,
530ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch      "INSERT INTO webrtc_identity_store "
531ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch      "(origin, identity_name, common_name, certificate,"
532ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch      " private_key, creation_time) VALUES"
533ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch      " (?,?,?,?,?,?)"));
534ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
535ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  CHECK(add_stmt.is_valid());
536ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
537ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  sql::Statement del_stmt(db_->GetCachedStatement(
538ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch      SQL_FROM_HERE,
539ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch      "DELETE FROM webrtc_identity_store WHERE origin=? AND identity_name=?"));
540ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
541ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  CHECK(del_stmt.is_valid());
542ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
543ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  sql::Transaction transaction(db_.get());
544ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  if (!transaction.Begin()) {
545ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    DLOG(ERROR) << "Failed to begin the transaction.";
546ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    return;
547ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  }
548ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
549ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  for (PendingOperationList::iterator it = pending_operations_.begin();
550ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch       it != pending_operations_.end();
551ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch       ++it) {
552ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    scoped_ptr<PendingOperation> po(*it);
553ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    switch (po->type) {
554ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch      case ADD_IDENTITY: {
555ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch        add_stmt.Reset(true);
556ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch        add_stmt.BindString(0, po->origin.spec());
557ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch        add_stmt.BindString(1, po->identity_name);
558ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch        add_stmt.BindString(2, po->identity.common_name);
559ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch        const std::string& cert = po->identity.certificate;
560ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch        add_stmt.BindBlob(3, cert.data(), cert.size());
561ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch        const std::string& private_key = po->identity.private_key;
562ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch        add_stmt.BindBlob(4, private_key.data(), private_key.size());
563ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch        add_stmt.BindInt64(5, po->identity.creation_time);
564ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch        CHECK(add_stmt.Run());
565ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch        break;
566ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch      }
567ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch      case DELETE_IDENTITY:
568ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch        del_stmt.Reset(true);
569ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch        del_stmt.BindString(0, po->origin.spec());
570ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch        add_stmt.BindString(1, po->identity_name);
571ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch        CHECK(del_stmt.Run());
572ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch        break;
573ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
574ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch      default:
575ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch        NOTREACHED();
576ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch        break;
577ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    }
578ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  }
579ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  transaction.Commit();
580ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  pending_operations_.clear();
581ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch}
582ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
583ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch}  // namespace content
584