1// Copyright 2014 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 "google_apis/gcm/engine/gcm_store_impl.h"
6
7#include "base/basictypes.h"
8#include "base/bind.h"
9#include "base/callback.h"
10#include "base/files/file_path.h"
11#include "base/files/file_util.h"
12#include "base/logging.h"
13#include "base/message_loop/message_loop_proxy.h"
14#include "base/metrics/histogram.h"
15#include "base/sequenced_task_runner.h"
16#include "base/stl_util.h"
17#include "base/strings/string_number_conversions.h"
18#include "base/strings/string_piece.h"
19#include "base/strings/string_tokenizer.h"
20#include "base/time/time.h"
21#include "base/tracked_objects.h"
22#include "google_apis/gcm/base/encryptor.h"
23#include "google_apis/gcm/base/mcs_message.h"
24#include "google_apis/gcm/base/mcs_util.h"
25#include "google_apis/gcm/protocol/mcs.pb.h"
26#include "third_party/leveldatabase/src/include/leveldb/db.h"
27#include "third_party/leveldatabase/src/include/leveldb/write_batch.h"
28
29namespace gcm {
30
31namespace {
32
33// Limit to the number of outstanding messages per app.
34const int kMessagesPerAppLimit = 20;
35
36// ---- LevelDB keys. ----
37// Key for this device's android id.
38const char kDeviceAIDKey[] = "device_aid_key";
39// Key for this device's android security token.
40const char kDeviceTokenKey[] = "device_token_key";
41// Lowest lexicographically ordered app ids.
42// Used for prefixing app id.
43const char kRegistrationKeyStart[] = "reg1-";
44// Key guaranteed to be higher than all app ids.
45// Used for limiting iteration.
46const char kRegistrationKeyEnd[] = "reg2-";
47// Lowest lexicographically ordered incoming message key.
48// Used for prefixing messages.
49const char kIncomingMsgKeyStart[] = "incoming1-";
50// Key guaranteed to be higher than all incoming message keys.
51// Used for limiting iteration.
52const char kIncomingMsgKeyEnd[] = "incoming2-";
53// Lowest lexicographically ordered outgoing message key.
54// Used for prefixing outgoing messages.
55const char kOutgoingMsgKeyStart[] = "outgoing1-";
56// Key guaranteed to be higher than all outgoing message keys.
57// Used for limiting iteration.
58const char kOutgoingMsgKeyEnd[] = "outgoing2-";
59// Lowest lexicographically ordered G-service settings key.
60// Used for prefixing G-services settings.
61const char kGServiceSettingKeyStart[] = "gservice1-";
62// Key guaranteed to be higher than all G-services settings keys.
63// Used for limiting iteration.
64const char kGServiceSettingKeyEnd[] = "gservice2-";
65// Key for digest of the last G-services settings update.
66const char kGServiceSettingsDigestKey[] = "gservices_digest";
67// Key used to indicate how many accounts were last checked in with this device.
68const char kLastCheckinAccountsKey[] = "last_checkin_accounts_count";
69// Key used to timestamp last checkin (marked with G services settings update).
70const char kLastCheckinTimeKey[] = "last_checkin_time";
71// Lowest lexicographically ordered account key.
72// Used for prefixing account information.
73const char kAccountKeyStart[] = "account1-";
74// Key guaranteed to be higher than all account keys.
75// Used for limiting iteration.
76const char kAccountKeyEnd[] = "account2-";
77
78std::string MakeRegistrationKey(const std::string& app_id) {
79  return kRegistrationKeyStart + app_id;
80}
81
82std::string ParseRegistrationKey(const std::string& key) {
83  return key.substr(arraysize(kRegistrationKeyStart) - 1);
84}
85
86std::string MakeIncomingKey(const std::string& persistent_id) {
87  return kIncomingMsgKeyStart + persistent_id;
88}
89
90std::string MakeOutgoingKey(const std::string& persistent_id) {
91  return kOutgoingMsgKeyStart + persistent_id;
92}
93
94std::string ParseOutgoingKey(const std::string& key) {
95  return key.substr(arraysize(kOutgoingMsgKeyStart) - 1);
96}
97
98std::string MakeGServiceSettingKey(const std::string& setting_name) {
99  return kGServiceSettingKeyStart + setting_name;
100}
101
102std::string ParseGServiceSettingKey(const std::string& key) {
103  return key.substr(arraysize(kGServiceSettingKeyStart) - 1);
104}
105
106std::string MakeAccountKey(const std::string& account_id) {
107  return kAccountKeyStart + account_id;
108}
109
110std::string ParseAccountKey(const std::string& key) {
111  return key.substr(arraysize(kAccountKeyStart) - 1);
112}
113
114// Note: leveldb::Slice keeps a pointer to the data in |s|, which must therefore
115// outlive the slice.
116// For example: MakeSlice(MakeOutgoingKey(x)) is invalid.
117leveldb::Slice MakeSlice(const base::StringPiece& s) {
118  return leveldb::Slice(s.begin(), s.size());
119}
120
121}  // namespace
122
123class GCMStoreImpl::Backend
124    : public base::RefCountedThreadSafe<GCMStoreImpl::Backend> {
125 public:
126  Backend(const base::FilePath& path,
127          scoped_refptr<base::SequencedTaskRunner> foreground_runner,
128          scoped_ptr<Encryptor> encryptor);
129
130  // Blocking implementations of GCMStoreImpl methods.
131  void Load(const LoadCallback& callback);
132  void Close();
133  void Destroy(const UpdateCallback& callback);
134  void SetDeviceCredentials(uint64 device_android_id,
135                            uint64 device_security_token,
136                            const UpdateCallback& callback);
137  void AddRegistration(const std::string& app_id,
138                       const linked_ptr<RegistrationInfo>& registration,
139                       const UpdateCallback& callback);
140  void RemoveRegistration(const std::string& app_id,
141                          const UpdateCallback& callback);
142  void AddIncomingMessage(const std::string& persistent_id,
143                          const UpdateCallback& callback);
144  void RemoveIncomingMessages(const PersistentIdList& persistent_ids,
145                              const UpdateCallback& callback);
146  void AddOutgoingMessage(const std::string& persistent_id,
147                          const MCSMessage& message,
148                          const UpdateCallback& callback);
149  void RemoveOutgoingMessages(
150      const PersistentIdList& persistent_ids,
151      const base::Callback<void(bool, const AppIdToMessageCountMap&)>
152          callback);
153  void AddUserSerialNumber(const std::string& username,
154                           int64 serial_number,
155                           const UpdateCallback& callback);
156  void RemoveUserSerialNumber(const std::string& username,
157                              const UpdateCallback& callback);
158  void SetLastCheckinInfo(const base::Time& time,
159                          const std::set<std::string>& accounts,
160                          const UpdateCallback& callback);
161  void SetGServicesSettings(
162      const std::map<std::string, std::string>& settings,
163      const std::string& digest,
164      const UpdateCallback& callback);
165  void AddAccountMapping(const AccountMapping& account_mapping,
166                         const UpdateCallback& callback);
167  void RemoveAccountMapping(const std::string& account_id,
168                            const UpdateCallback& callback);
169
170 private:
171  friend class base::RefCountedThreadSafe<Backend>;
172  ~Backend();
173
174  bool LoadDeviceCredentials(uint64* android_id, uint64* security_token);
175  bool LoadRegistrations(RegistrationInfoMap* registrations);
176  bool LoadIncomingMessages(std::vector<std::string>* incoming_messages);
177  bool LoadOutgoingMessages(OutgoingMessageMap* outgoing_messages);
178  bool LoadLastCheckinInfo(base::Time* last_checkin_time,
179                           std::set<std::string>* accounts);
180  bool LoadGServicesSettings(std::map<std::string, std::string>* settings,
181                             std::string* digest);
182  bool LoadAccountMappingInfo(AccountMappings* account_mappings);
183
184  const base::FilePath path_;
185  scoped_refptr<base::SequencedTaskRunner> foreground_task_runner_;
186  scoped_ptr<Encryptor> encryptor_;
187
188  scoped_ptr<leveldb::DB> db_;
189};
190
191GCMStoreImpl::Backend::Backend(
192    const base::FilePath& path,
193    scoped_refptr<base::SequencedTaskRunner> foreground_task_runner,
194    scoped_ptr<Encryptor> encryptor)
195    : path_(path),
196      foreground_task_runner_(foreground_task_runner),
197      encryptor_(encryptor.Pass()) {
198}
199
200GCMStoreImpl::Backend::~Backend() {}
201
202void GCMStoreImpl::Backend::Load(const LoadCallback& callback) {
203  scoped_ptr<LoadResult> result(new LoadResult());
204  if (db_.get()) {
205    LOG(ERROR) << "Attempting to reload open database.";
206    foreground_task_runner_->PostTask(FROM_HERE,
207                                      base::Bind(callback,
208                                                 base::Passed(&result)));
209    return;
210  }
211
212  leveldb::Options options;
213  options.create_if_missing = true;
214  leveldb::DB* db;
215  leveldb::Status status =
216      leveldb::DB::Open(options, path_.AsUTF8Unsafe(), &db);
217  UMA_HISTOGRAM_BOOLEAN("GCM.LoadSucceeded", status.ok());
218  if (!status.ok()) {
219    LOG(ERROR) << "Failed to open database " << path_.value() << ": "
220               << status.ToString();
221    foreground_task_runner_->PostTask(FROM_HERE,
222                                      base::Bind(callback,
223                                                 base::Passed(&result)));
224    return;
225  }
226  db_.reset(db);
227
228  if (!LoadDeviceCredentials(&result->device_android_id,
229                             &result->device_security_token) ||
230      !LoadRegistrations(&result->registrations) ||
231      !LoadIncomingMessages(&result->incoming_messages) ||
232      !LoadOutgoingMessages(&result->outgoing_messages) ||
233      !LoadLastCheckinInfo(&result->last_checkin_time,
234                           &result->last_checkin_accounts) ||
235      !LoadGServicesSettings(&result->gservices_settings,
236                             &result->gservices_digest) ||
237      !LoadAccountMappingInfo(&result->account_mappings)) {
238    result->Reset();
239    foreground_task_runner_->PostTask(FROM_HERE,
240                                      base::Bind(callback,
241                                                 base::Passed(&result)));
242    return;
243  }
244
245  // Only record histograms if GCM had already been set up for this device.
246  if (result->device_android_id != 0 && result->device_security_token != 0) {
247    int64 file_size = 0;
248    if (base::GetFileSize(path_, &file_size)) {
249      UMA_HISTOGRAM_COUNTS("GCM.StoreSizeKB",
250                           static_cast<int>(file_size / 1024));
251    }
252    UMA_HISTOGRAM_COUNTS("GCM.RestoredRegistrations",
253                         result->registrations.size());
254    UMA_HISTOGRAM_COUNTS("GCM.RestoredOutgoingMessages",
255                         result->outgoing_messages.size());
256    UMA_HISTOGRAM_COUNTS("GCM.RestoredIncomingMessages",
257                         result->incoming_messages.size());
258  }
259
260  DVLOG(1) << "Succeeded in loading " << result->registrations.size()
261           << " registrations, "
262           << result->incoming_messages.size()
263           << " unacknowledged incoming messages and "
264           << result->outgoing_messages.size()
265           << " unacknowledged outgoing messages.";
266  result->success = true;
267  foreground_task_runner_->PostTask(FROM_HERE,
268                                    base::Bind(callback,
269                                               base::Passed(&result)));
270  return;
271}
272
273void GCMStoreImpl::Backend::Close() {
274  DVLOG(1) << "Closing GCM store.";
275  db_.reset();
276}
277
278void GCMStoreImpl::Backend::Destroy(const UpdateCallback& callback) {
279  DVLOG(1) << "Destroying GCM store.";
280  db_.reset();
281  const leveldb::Status s =
282      leveldb::DestroyDB(path_.AsUTF8Unsafe(), leveldb::Options());
283  if (s.ok()) {
284    foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, true));
285    return;
286  }
287  LOG(ERROR) << "Destroy failed: " << s.ToString();
288  foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
289}
290
291void GCMStoreImpl::Backend::SetDeviceCredentials(
292    uint64 device_android_id,
293    uint64 device_security_token,
294    const UpdateCallback& callback) {
295  DVLOG(1) << "Saving device credentials with AID " << device_android_id;
296  if (!db_.get()) {
297    LOG(ERROR) << "GCMStore db doesn't exist.";
298    foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
299    return;
300  }
301
302  leveldb::WriteOptions write_options;
303  write_options.sync = true;
304
305  std::string encrypted_token;
306  encryptor_->EncryptString(base::Uint64ToString(device_security_token),
307                            &encrypted_token);
308  std::string android_id_str = base::Uint64ToString(device_android_id);
309  leveldb::Status s =
310      db_->Put(write_options,
311               MakeSlice(kDeviceAIDKey),
312               MakeSlice(android_id_str));
313  if (s.ok()) {
314    s = db_->Put(
315        write_options, MakeSlice(kDeviceTokenKey), MakeSlice(encrypted_token));
316  }
317  if (s.ok()) {
318    foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, true));
319    return;
320  }
321  LOG(ERROR) << "LevelDB put failed: " << s.ToString();
322  foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
323}
324
325void GCMStoreImpl::Backend::AddRegistration(
326    const std::string& app_id,
327    const linked_ptr<RegistrationInfo>& registration,
328    const UpdateCallback& callback) {
329  DVLOG(1) << "Saving registration info for app: " << app_id;
330  if (!db_.get()) {
331    LOG(ERROR) << "GCMStore db doesn't exist.";
332    foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
333    return;
334  }
335  leveldb::WriteOptions write_options;
336  write_options.sync = true;
337
338  std::string key = MakeRegistrationKey(app_id);
339  std::string value = registration->SerializeAsString();
340  const leveldb::Status status = db_->Put(write_options,
341                                          MakeSlice(key),
342                                          MakeSlice(value));
343  if (status.ok()) {
344    foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, true));
345    return;
346  }
347  LOG(ERROR) << "LevelDB put failed: " << status.ToString();
348  foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
349}
350
351void GCMStoreImpl::Backend::RemoveRegistration(const std::string& app_id,
352                                               const UpdateCallback& callback) {
353  if (!db_.get()) {
354    LOG(ERROR) << "GCMStore db doesn't exist.";
355    foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
356    return;
357  }
358  leveldb::WriteOptions write_options;
359  write_options.sync = true;
360
361  leveldb::Status status =
362      db_->Delete(write_options, MakeSlice(MakeRegistrationKey(app_id)));
363  if (status.ok()) {
364    foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, true));
365    return;
366  }
367  LOG(ERROR) << "LevelDB remove failed: " << status.ToString();
368  foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
369}
370
371void GCMStoreImpl::Backend::AddIncomingMessage(const std::string& persistent_id,
372                                               const UpdateCallback& callback) {
373  DVLOG(1) << "Saving incoming message with id " << persistent_id;
374  if (!db_.get()) {
375    LOG(ERROR) << "GCMStore db doesn't exist.";
376    foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
377    return;
378  }
379
380  leveldb::WriteOptions write_options;
381  write_options.sync = true;
382
383  std::string key = MakeIncomingKey(persistent_id);
384  const leveldb::Status s = db_->Put(write_options,
385                                     MakeSlice(key),
386                                     MakeSlice(persistent_id));
387  if (s.ok()) {
388    foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, true));
389    return;
390  }
391  LOG(ERROR) << "LevelDB put failed: " << s.ToString();
392  foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
393}
394
395void GCMStoreImpl::Backend::RemoveIncomingMessages(
396    const PersistentIdList& persistent_ids,
397    const UpdateCallback& callback) {
398  if (!db_.get()) {
399    LOG(ERROR) << "GCMStore db doesn't exist.";
400    foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
401    return;
402  }
403  leveldb::WriteOptions write_options;
404  write_options.sync = true;
405
406  leveldb::Status s;
407  for (PersistentIdList::const_iterator iter = persistent_ids.begin();
408       iter != persistent_ids.end();
409       ++iter) {
410    DVLOG(1) << "Removing incoming message with id " << *iter;
411    std::string key = MakeIncomingKey(*iter);
412    s = db_->Delete(write_options, MakeSlice(key));
413    if (!s.ok())
414      break;
415  }
416  if (s.ok()) {
417    foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, true));
418    return;
419  }
420  LOG(ERROR) << "LevelDB remove failed: " << s.ToString();
421  foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
422}
423
424void GCMStoreImpl::Backend::AddOutgoingMessage(const std::string& persistent_id,
425                                               const MCSMessage& message,
426                                               const UpdateCallback& callback) {
427  DVLOG(1) << "Saving outgoing message with id " << persistent_id;
428  if (!db_.get()) {
429    LOG(ERROR) << "GCMStore db doesn't exist.";
430    foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
431    return;
432  }
433  leveldb::WriteOptions write_options;
434  write_options.sync = true;
435
436  std::string data =
437      static_cast<char>(message.tag()) + message.SerializeAsString();
438  std::string key = MakeOutgoingKey(persistent_id);
439  const leveldb::Status s = db_->Put(write_options,
440                                     MakeSlice(key),
441                                     MakeSlice(data));
442  if (s.ok()) {
443    foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, true));
444    return;
445  }
446  LOG(ERROR) << "LevelDB put failed: " << s.ToString();
447  foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
448}
449
450void GCMStoreImpl::Backend::RemoveOutgoingMessages(
451    const PersistentIdList& persistent_ids,
452    const base::Callback<void(bool, const AppIdToMessageCountMap&)>
453        callback) {
454  if (!db_.get()) {
455    LOG(ERROR) << "GCMStore db doesn't exist.";
456    foreground_task_runner_->PostTask(FROM_HERE,
457                                      base::Bind(callback,
458                                                 false,
459                                                 AppIdToMessageCountMap()));
460    return;
461  }
462  leveldb::ReadOptions read_options;
463  leveldb::WriteOptions write_options;
464  write_options.sync = true;
465
466  AppIdToMessageCountMap removed_message_counts;
467
468  leveldb::Status s;
469  for (PersistentIdList::const_iterator iter = persistent_ids.begin();
470       iter != persistent_ids.end();
471       ++iter) {
472    DVLOG(1) << "Removing outgoing message with id " << *iter;
473    std::string outgoing_message;
474    std::string key = MakeOutgoingKey(*iter);
475    s = db_->Get(read_options,
476                 MakeSlice(key),
477                 &outgoing_message);
478    if (!s.ok())
479      break;
480    mcs_proto::DataMessageStanza data_message;
481    // Skip the initial tag byte and parse the rest to extract the message.
482    if (data_message.ParseFromString(outgoing_message.substr(1))) {
483      DCHECK(!data_message.category().empty());
484      if (removed_message_counts.count(data_message.category()) != 0)
485        removed_message_counts[data_message.category()]++;
486      else
487        removed_message_counts[data_message.category()] = 1;
488    }
489    DVLOG(1) << "Removing outgoing message with id " << *iter;
490    s = db_->Delete(write_options, MakeSlice(key));
491    if (!s.ok())
492      break;
493  }
494  if (s.ok()) {
495    foreground_task_runner_->PostTask(FROM_HERE,
496                                      base::Bind(callback,
497                                                 true,
498                                                 removed_message_counts));
499    return;
500  }
501  LOG(ERROR) << "LevelDB remove failed: " << s.ToString();
502  foreground_task_runner_->PostTask(FROM_HERE,
503                                    base::Bind(callback,
504                                               false,
505                                               AppIdToMessageCountMap()));
506}
507
508void GCMStoreImpl::Backend::SetLastCheckinInfo(
509    const base::Time& time,
510    const std::set<std::string>& accounts,
511    const UpdateCallback& callback) {
512  leveldb::WriteBatch write_batch;
513
514  int64 last_checkin_time_internal = time.ToInternalValue();
515  write_batch.Put(MakeSlice(kLastCheckinTimeKey),
516                  MakeSlice(base::Int64ToString(last_checkin_time_internal)));
517
518  std::string serialized_accounts;
519  for (std::set<std::string>::iterator iter = accounts.begin();
520       iter != accounts.end();
521       ++iter) {
522    serialized_accounts += *iter;
523    serialized_accounts += ",";
524  }
525  if (!serialized_accounts.empty())
526    serialized_accounts.erase(serialized_accounts.length() - 1);
527
528  write_batch.Put(MakeSlice(kLastCheckinAccountsKey),
529                  MakeSlice(serialized_accounts));
530
531  leveldb::WriteOptions write_options;
532  write_options.sync = true;
533  const leveldb::Status s = db_->Write(write_options, &write_batch);
534
535  if (!s.ok())
536    LOG(ERROR) << "LevelDB set last checkin info failed: " << s.ToString();
537  foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, s.ok()));
538}
539
540void GCMStoreImpl::Backend::SetGServicesSettings(
541    const std::map<std::string, std::string>& settings,
542    const std::string& settings_digest,
543    const UpdateCallback& callback) {
544  leveldb::WriteBatch write_batch;
545
546  // Remove all existing settings.
547  leveldb::ReadOptions read_options;
548  read_options.verify_checksums = true;
549  scoped_ptr<leveldb::Iterator> iter(db_->NewIterator(read_options));
550  for (iter->Seek(MakeSlice(kGServiceSettingKeyStart));
551       iter->Valid() && iter->key().ToString() < kGServiceSettingKeyEnd;
552       iter->Next()) {
553    write_batch.Delete(iter->key());
554  }
555
556  // Add the new settings.
557  for (std::map<std::string, std::string>::const_iterator iter =
558           settings.begin();
559       iter != settings.end(); ++iter) {
560    write_batch.Put(MakeSlice(MakeGServiceSettingKey(iter->first)),
561                    MakeSlice(iter->second));
562  }
563
564  // Update the settings digest.
565  write_batch.Put(MakeSlice(kGServiceSettingsDigestKey),
566                  MakeSlice(settings_digest));
567
568  // Write it all in a batch.
569  leveldb::WriteOptions write_options;
570  write_options.sync = true;
571
572  leveldb::Status s = db_->Write(write_options, &write_batch);
573  if (!s.ok())
574    LOG(ERROR) << "LevelDB GService Settings update failed: " << s.ToString();
575  foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, s.ok()));
576}
577
578void GCMStoreImpl::Backend::AddAccountMapping(
579    const AccountMapping& account_mapping,
580    const UpdateCallback& callback) {
581  DVLOG(1) << "Saving account info for account with email: "
582           << account_mapping.email;
583  if (!db_.get()) {
584    LOG(ERROR) << "GCMStore db doesn't exist.";
585    foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
586    return;
587  }
588
589  leveldb::WriteOptions write_options;
590  write_options.sync = true;
591
592  std::string data = account_mapping.SerializeAsString();
593  std::string key = MakeAccountKey(account_mapping.account_id);
594  const leveldb::Status s =
595      db_->Put(write_options, MakeSlice(key), MakeSlice(data));
596  if (!s.ok())
597    LOG(ERROR) << "LevelDB adding account mapping failed: " << s.ToString();
598  foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, s.ok()));
599}
600
601void GCMStoreImpl::Backend::RemoveAccountMapping(
602    const std::string& account_id,
603    const UpdateCallback& callback) {
604  if (!db_.get()) {
605    LOG(ERROR) << "GCMStore db doesn't exist.";
606    foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
607    return;
608  }
609
610  leveldb::WriteOptions write_options;
611  write_options.sync = true;
612
613  leveldb::Status s =
614      db_->Delete(write_options, MakeSlice(MakeAccountKey(account_id)));
615
616  if (!s.ok())
617    LOG(ERROR) << "LevelDB removal of account mapping failed: " << s.ToString();
618  foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, s.ok()));
619}
620
621bool GCMStoreImpl::Backend::LoadDeviceCredentials(uint64* android_id,
622                                                  uint64* security_token) {
623  leveldb::ReadOptions read_options;
624  read_options.verify_checksums = true;
625
626  std::string result;
627  leveldb::Status s = db_->Get(read_options, MakeSlice(kDeviceAIDKey), &result);
628  if (s.ok()) {
629    if (!base::StringToUint64(result, android_id)) {
630      LOG(ERROR) << "Failed to restore device id.";
631      return false;
632    }
633    result.clear();
634    s = db_->Get(read_options, MakeSlice(kDeviceTokenKey), &result);
635  }
636  if (s.ok()) {
637    std::string decrypted_token;
638    encryptor_->DecryptString(result, &decrypted_token);
639    if (!base::StringToUint64(decrypted_token, security_token)) {
640      LOG(ERROR) << "Failed to restore security token.";
641      return false;
642    }
643    return true;
644  }
645
646  if (s.IsNotFound()) {
647    DVLOG(1) << "No credentials found.";
648    return true;
649  }
650
651  LOG(ERROR) << "Error reading credentials from store.";
652  return false;
653}
654
655bool GCMStoreImpl::Backend::LoadRegistrations(
656    RegistrationInfoMap* registrations) {
657  leveldb::ReadOptions read_options;
658  read_options.verify_checksums = true;
659
660  scoped_ptr<leveldb::Iterator> iter(db_->NewIterator(read_options));
661  for (iter->Seek(MakeSlice(kRegistrationKeyStart));
662       iter->Valid() && iter->key().ToString() < kRegistrationKeyEnd;
663       iter->Next()) {
664    leveldb::Slice s = iter->value();
665    if (s.size() <= 1) {
666      LOG(ERROR) << "Error reading registration with key " << s.ToString();
667      return false;
668    }
669    std::string app_id = ParseRegistrationKey(iter->key().ToString());
670    linked_ptr<RegistrationInfo> registration(new RegistrationInfo);
671    if (!registration->ParseFromString(iter->value().ToString())) {
672      LOG(ERROR) << "Failed to parse registration with app id " << app_id;
673      return false;
674    }
675    DVLOG(1) << "Found registration with app id " << app_id;
676    (*registrations)[app_id] = registration;
677  }
678
679  return true;
680}
681
682bool GCMStoreImpl::Backend::LoadIncomingMessages(
683    std::vector<std::string>* incoming_messages) {
684  leveldb::ReadOptions read_options;
685  read_options.verify_checksums = true;
686
687  scoped_ptr<leveldb::Iterator> iter(db_->NewIterator(read_options));
688  for (iter->Seek(MakeSlice(kIncomingMsgKeyStart));
689       iter->Valid() && iter->key().ToString() < kIncomingMsgKeyEnd;
690       iter->Next()) {
691    leveldb::Slice s = iter->value();
692    if (s.empty()) {
693      LOG(ERROR) << "Error reading incoming message with key "
694                 << iter->key().ToString();
695      return false;
696    }
697    DVLOG(1) << "Found incoming message with id " << s.ToString();
698    incoming_messages->push_back(s.ToString());
699  }
700
701  return true;
702}
703
704bool GCMStoreImpl::Backend::LoadOutgoingMessages(
705    OutgoingMessageMap* outgoing_messages) {
706  leveldb::ReadOptions read_options;
707  read_options.verify_checksums = true;
708
709  scoped_ptr<leveldb::Iterator> iter(db_->NewIterator(read_options));
710  for (iter->Seek(MakeSlice(kOutgoingMsgKeyStart));
711       iter->Valid() && iter->key().ToString() < kOutgoingMsgKeyEnd;
712       iter->Next()) {
713    leveldb::Slice s = iter->value();
714    if (s.size() <= 1) {
715      LOG(ERROR) << "Error reading incoming message with key " << s.ToString();
716      return false;
717    }
718    uint8 tag = iter->value().data()[0];
719    std::string id = ParseOutgoingKey(iter->key().ToString());
720    scoped_ptr<google::protobuf::MessageLite> message(
721        BuildProtobufFromTag(tag));
722    if (!message.get() ||
723        !message->ParseFromString(iter->value().ToString().substr(1))) {
724      LOG(ERROR) << "Failed to parse outgoing message with id " << id
725                 << " and tag " << tag;
726      return false;
727    }
728    DVLOG(1) << "Found outgoing message with id " << id << " of type "
729             << base::IntToString(tag);
730    (*outgoing_messages)[id] = make_linked_ptr(message.release());
731  }
732
733  return true;
734}
735
736bool GCMStoreImpl::Backend::LoadLastCheckinInfo(
737    base::Time* last_checkin_time,
738    std::set<std::string>* accounts) {
739  leveldb::ReadOptions read_options;
740  read_options.verify_checksums = true;
741
742  std::string result;
743  leveldb::Status s = db_->Get(read_options,
744                               MakeSlice(kLastCheckinTimeKey),
745                               &result);
746  int64 time_internal = 0LL;
747  if (s.ok() && !base::StringToInt64(result, &time_internal))
748    LOG(ERROR) << "Failed to restore last checkin time. Using default = 0.";
749
750  // In case we cannot read last checkin time, we default it to 0, as we don't
751  // want that situation to cause the whole load to fail.
752  *last_checkin_time = base::Time::FromInternalValue(time_internal);
753
754  accounts->clear();
755  s = db_->Get(read_options, MakeSlice(kLastCheckinAccountsKey), &result);
756  if (!s.ok())
757    DVLOG(1) << "No accounts where stored during last run.";
758
759  base::StringTokenizer t(result, ",");
760  while (t.GetNext())
761    accounts->insert(t.token());
762
763  return true;
764}
765
766bool GCMStoreImpl::Backend::LoadGServicesSettings(
767    std::map<std::string, std::string>* settings,
768    std::string* digest) {
769  leveldb::ReadOptions read_options;
770  read_options.verify_checksums = true;
771
772  // Load all of the GServices settings.
773  scoped_ptr<leveldb::Iterator> iter(db_->NewIterator(read_options));
774  for (iter->Seek(MakeSlice(kGServiceSettingKeyStart));
775       iter->Valid() && iter->key().ToString() < kGServiceSettingKeyEnd;
776       iter->Next()) {
777    std::string value = iter->value().ToString();
778    if (value.empty()) {
779      LOG(ERROR) << "Error reading GService Settings " << value;
780      return false;
781    }
782    std::string id = ParseGServiceSettingKey(iter->key().ToString());
783    (*settings)[id] = value;
784    DVLOG(1) << "Found G Service setting with key: " << id
785             << ", and value: " << value;
786  }
787
788  // Load the settings digest. It's ok if it is empty.
789  db_->Get(read_options, MakeSlice(kGServiceSettingsDigestKey), digest);
790
791  return true;
792}
793
794bool GCMStoreImpl::Backend::LoadAccountMappingInfo(
795    AccountMappings* account_mappings) {
796  leveldb::ReadOptions read_options;
797  read_options.verify_checksums = true;
798
799  scoped_ptr<leveldb::Iterator> iter(db_->NewIterator(read_options));
800  for (iter->Seek(MakeSlice(kAccountKeyStart));
801       iter->Valid() && iter->key().ToString() < kAccountKeyEnd;
802       iter->Next()) {
803    AccountMapping account_mapping;
804    account_mapping.account_id = ParseAccountKey(iter->key().ToString());
805    if (!account_mapping.ParseFromString(iter->value().ToString())) {
806      DVLOG(1) << "Failed to parse account info with ID: "
807               << account_mapping.account_id;
808      return false;
809    }
810    DVLOG(1) << "Found account mapping with ID: " << account_mapping.account_id;
811    account_mappings->push_back(account_mapping);
812  }
813
814  return true;
815}
816
817GCMStoreImpl::GCMStoreImpl(
818    const base::FilePath& path,
819    scoped_refptr<base::SequencedTaskRunner> blocking_task_runner,
820    scoped_ptr<Encryptor> encryptor)
821    : backend_(new Backend(path,
822                           base::MessageLoopProxy::current(),
823                           encryptor.Pass())),
824      blocking_task_runner_(blocking_task_runner),
825      weak_ptr_factory_(this) {
826}
827
828GCMStoreImpl::~GCMStoreImpl() {}
829
830void GCMStoreImpl::Load(const LoadCallback& callback) {
831  blocking_task_runner_->PostTask(
832      FROM_HERE,
833      base::Bind(&GCMStoreImpl::Backend::Load,
834                 backend_,
835                 base::Bind(&GCMStoreImpl::LoadContinuation,
836                            weak_ptr_factory_.GetWeakPtr(),
837                            callback)));
838}
839
840void GCMStoreImpl::Close() {
841  weak_ptr_factory_.InvalidateWeakPtrs();
842  app_message_counts_.clear();
843  blocking_task_runner_->PostTask(
844      FROM_HERE,
845      base::Bind(&GCMStoreImpl::Backend::Close, backend_));
846}
847
848void GCMStoreImpl::Destroy(const UpdateCallback& callback) {
849  blocking_task_runner_->PostTask(
850      FROM_HERE,
851      base::Bind(&GCMStoreImpl::Backend::Destroy, backend_, callback));
852}
853
854void GCMStoreImpl::SetDeviceCredentials(uint64 device_android_id,
855                                        uint64 device_security_token,
856                                        const UpdateCallback& callback) {
857  blocking_task_runner_->PostTask(
858      FROM_HERE,
859      base::Bind(&GCMStoreImpl::Backend::SetDeviceCredentials,
860                 backend_,
861                 device_android_id,
862                 device_security_token,
863                 callback));
864}
865
866void GCMStoreImpl::AddRegistration(
867    const std::string& app_id,
868    const linked_ptr<RegistrationInfo>& registration,
869    const UpdateCallback& callback) {
870  blocking_task_runner_->PostTask(
871      FROM_HERE,
872      base::Bind(&GCMStoreImpl::Backend::AddRegistration,
873                 backend_,
874                 app_id,
875                 registration,
876                 callback));
877}
878
879void GCMStoreImpl::RemoveRegistration(const std::string& app_id,
880                                          const UpdateCallback& callback) {
881  blocking_task_runner_->PostTask(
882      FROM_HERE,
883      base::Bind(&GCMStoreImpl::Backend::RemoveRegistration,
884                 backend_,
885                 app_id,
886                 callback));
887}
888
889void GCMStoreImpl::AddIncomingMessage(const std::string& persistent_id,
890                                      const UpdateCallback& callback) {
891  blocking_task_runner_->PostTask(
892      FROM_HERE,
893      base::Bind(&GCMStoreImpl::Backend::AddIncomingMessage,
894                 backend_,
895                 persistent_id,
896                 callback));
897}
898
899void GCMStoreImpl::RemoveIncomingMessage(const std::string& persistent_id,
900                                         const UpdateCallback& callback) {
901  blocking_task_runner_->PostTask(
902      FROM_HERE,
903      base::Bind(&GCMStoreImpl::Backend::RemoveIncomingMessages,
904                 backend_,
905                 PersistentIdList(1, persistent_id),
906                 callback));
907}
908
909void GCMStoreImpl::RemoveIncomingMessages(
910    const PersistentIdList& persistent_ids,
911    const UpdateCallback& callback) {
912  blocking_task_runner_->PostTask(
913      FROM_HERE,
914      base::Bind(&GCMStoreImpl::Backend::RemoveIncomingMessages,
915                 backend_,
916                 persistent_ids,
917                 callback));
918}
919
920bool GCMStoreImpl::AddOutgoingMessage(const std::string& persistent_id,
921                                      const MCSMessage& message,
922                                      const UpdateCallback& callback) {
923  DCHECK_EQ(message.tag(), kDataMessageStanzaTag);
924  std::string app_id = reinterpret_cast<const mcs_proto::DataMessageStanza*>(
925                           &message.GetProtobuf())->category();
926  DCHECK(!app_id.empty());
927  if (app_message_counts_.count(app_id) == 0)
928    app_message_counts_[app_id] = 0;
929  if (app_message_counts_[app_id] < kMessagesPerAppLimit) {
930    app_message_counts_[app_id]++;
931
932    blocking_task_runner_->PostTask(
933        FROM_HERE,
934        base::Bind(&GCMStoreImpl::Backend::AddOutgoingMessage,
935                   backend_,
936                   persistent_id,
937                   message,
938                   base::Bind(&GCMStoreImpl::AddOutgoingMessageContinuation,
939                              weak_ptr_factory_.GetWeakPtr(),
940                              callback,
941                              app_id)));
942    return true;
943  }
944  return false;
945}
946
947void GCMStoreImpl::OverwriteOutgoingMessage(const std::string& persistent_id,
948                                            const MCSMessage& message,
949                                            const UpdateCallback& callback) {
950  DCHECK_EQ(message.tag(), kDataMessageStanzaTag);
951  std::string app_id = reinterpret_cast<const mcs_proto::DataMessageStanza*>(
952                           &message.GetProtobuf())->category();
953  DCHECK(!app_id.empty());
954  // There should already be pending messages for this app.
955  DCHECK(app_message_counts_.count(app_id));
956  // TODO(zea): consider verifying the specific message already exists.
957  blocking_task_runner_->PostTask(
958      FROM_HERE,
959      base::Bind(&GCMStoreImpl::Backend::AddOutgoingMessage,
960                 backend_,
961                 persistent_id,
962                 message,
963                 callback));
964}
965
966void GCMStoreImpl::RemoveOutgoingMessage(const std::string& persistent_id,
967                                         const UpdateCallback& callback) {
968  blocking_task_runner_->PostTask(
969      FROM_HERE,
970      base::Bind(&GCMStoreImpl::Backend::RemoveOutgoingMessages,
971                 backend_,
972                 PersistentIdList(1, persistent_id),
973                 base::Bind(&GCMStoreImpl::RemoveOutgoingMessagesContinuation,
974                            weak_ptr_factory_.GetWeakPtr(),
975                            callback)));
976}
977
978void GCMStoreImpl::RemoveOutgoingMessages(
979    const PersistentIdList& persistent_ids,
980    const UpdateCallback& callback) {
981  blocking_task_runner_->PostTask(
982      FROM_HERE,
983      base::Bind(&GCMStoreImpl::Backend::RemoveOutgoingMessages,
984                 backend_,
985                 persistent_ids,
986                 base::Bind(&GCMStoreImpl::RemoveOutgoingMessagesContinuation,
987                            weak_ptr_factory_.GetWeakPtr(),
988                            callback)));
989}
990
991void GCMStoreImpl::SetLastCheckinInfo(const base::Time& time,
992                                      const std::set<std::string>& accounts,
993                                      const UpdateCallback& callback) {
994  blocking_task_runner_->PostTask(
995      FROM_HERE,
996      base::Bind(&GCMStoreImpl::Backend::SetLastCheckinInfo,
997                 backend_,
998                 time,
999                 accounts,
1000                 callback));
1001}
1002
1003void GCMStoreImpl::SetGServicesSettings(
1004    const std::map<std::string, std::string>& settings,
1005    const std::string& digest,
1006    const UpdateCallback& callback) {
1007  blocking_task_runner_->PostTask(
1008      FROM_HERE,
1009      base::Bind(&GCMStoreImpl::Backend::SetGServicesSettings,
1010                 backend_,
1011                 settings,
1012                 digest,
1013                 callback));
1014}
1015
1016void GCMStoreImpl::AddAccountMapping(const AccountMapping& account_mapping,
1017                                     const UpdateCallback& callback) {
1018  blocking_task_runner_->PostTask(
1019      FROM_HERE,
1020      base::Bind(&GCMStoreImpl::Backend::AddAccountMapping,
1021                 backend_,
1022                 account_mapping,
1023                 callback));
1024}
1025
1026void GCMStoreImpl::RemoveAccountMapping(const std::string& account_id,
1027                                        const UpdateCallback& callback) {
1028  blocking_task_runner_->PostTask(
1029      FROM_HERE,
1030      base::Bind(&GCMStoreImpl::Backend::RemoveAccountMapping,
1031                 backend_,
1032                 account_id,
1033                 callback));
1034}
1035
1036void GCMStoreImpl::LoadContinuation(const LoadCallback& callback,
1037                                    scoped_ptr<LoadResult> result) {
1038  if (!result->success) {
1039    callback.Run(result.Pass());
1040    return;
1041  }
1042  int num_throttled_apps = 0;
1043  for (OutgoingMessageMap::const_iterator
1044           iter = result->outgoing_messages.begin();
1045       iter != result->outgoing_messages.end(); ++iter) {
1046    const mcs_proto::DataMessageStanza* data_message =
1047        reinterpret_cast<mcs_proto::DataMessageStanza*>(iter->second.get());
1048    DCHECK(!data_message->category().empty());
1049    if (app_message_counts_.count(data_message->category()) == 0)
1050      app_message_counts_[data_message->category()] = 1;
1051    else
1052      app_message_counts_[data_message->category()]++;
1053    if (app_message_counts_[data_message->category()] == kMessagesPerAppLimit)
1054      num_throttled_apps++;
1055  }
1056  UMA_HISTOGRAM_COUNTS("GCM.NumThrottledApps", num_throttled_apps);
1057  callback.Run(result.Pass());
1058}
1059
1060void GCMStoreImpl::AddOutgoingMessageContinuation(
1061    const UpdateCallback& callback,
1062    const std::string& app_id,
1063    bool success) {
1064  if (!success) {
1065    DCHECK(app_message_counts_[app_id] > 0);
1066    app_message_counts_[app_id]--;
1067  }
1068  callback.Run(success);
1069}
1070
1071void GCMStoreImpl::RemoveOutgoingMessagesContinuation(
1072    const UpdateCallback& callback,
1073    bool success,
1074    const AppIdToMessageCountMap& removed_message_counts) {
1075  if (!success) {
1076    callback.Run(false);
1077    return;
1078  }
1079  for (AppIdToMessageCountMap::const_iterator iter =
1080           removed_message_counts.begin();
1081       iter != removed_message_counts.end(); ++iter) {
1082    DCHECK_NE(app_message_counts_.count(iter->first), 0U);
1083    app_message_counts_[iter->first] -= iter->second;
1084    DCHECK_GE(app_message_counts_[iter->first], 0);
1085  }
1086  callback.Run(true);
1087}
1088
1089}  // namespace gcm
1090