1// Copyright (c) 2011 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/sync/glue/autofill_change_processor.h"
6
7#include <string>
8#include <vector>
9
10#include "base/string_util.h"
11#include "base/utf_string_conversions.h"
12#include "chrome/browser/autofill/personal_data_manager.h"
13#include "chrome/browser/profiles/profile.h"
14#include "chrome/browser/sync/glue/autofill_model_associator.h"
15#include "chrome/browser/sync/glue/autofill_profile_model_associator.h"
16#include "chrome/browser/sync/glue/do_optimistic_refresh_task.h"
17#include "chrome/browser/sync/profile_sync_service.h"
18#include "chrome/browser/webdata/autofill_change.h"
19#include "chrome/browser/webdata/web_data_service.h"
20#include "chrome/browser/webdata/web_database.h"
21#include "chrome/common/guid.h"
22#include "content/common/notification_service.h"
23
24namespace browser_sync {
25
26struct AutofillChangeProcessor::AutofillChangeRecord {
27  sync_api::SyncManager::ChangeRecord::Action action_;
28  int64 id_;
29  sync_pb::AutofillSpecifics autofill_;
30  AutofillChangeRecord(sync_api::SyncManager::ChangeRecord::Action action,
31                       int64 id, const sync_pb::AutofillSpecifics& autofill)
32      : action_(action),
33        id_(id),
34        autofill_(autofill) { }
35};
36
37AutofillChangeProcessor::AutofillChangeProcessor(
38    AutofillModelAssociator* model_associator,
39    WebDatabase* web_database,
40    PersonalDataManager* personal_data,
41    UnrecoverableErrorHandler* error_handler)
42    : ChangeProcessor(error_handler),
43      model_associator_(model_associator),
44      web_database_(web_database),
45      personal_data_(personal_data),
46      observing_(false) {
47  DCHECK(model_associator);
48  DCHECK(web_database);
49  DCHECK(error_handler);
50  DCHECK(personal_data);
51  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
52  StartObserving();
53}
54
55AutofillChangeProcessor::~AutofillChangeProcessor() {}
56
57void AutofillChangeProcessor::Observe(NotificationType type,
58                                      const NotificationSource& source,
59                                      const NotificationDetails& details) {
60  // Ensure this notification came from our web database.
61  WebDataService* wds = Source<WebDataService>(source).ptr();
62  if (!wds || wds->GetDatabase() != web_database_)
63    return;
64
65  DCHECK(running());
66  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
67  if (!observing_)
68    return;
69
70  sync_api::WriteTransaction trans(share_handle());
71  sync_api::ReadNode autofill_root(&trans);
72  if (!autofill_root.InitByTagLookup(kAutofillTag)) {
73    error_handler()->OnUnrecoverableError(FROM_HERE,
74        "Server did not create the top-level autofill node. "
75        "We might be running against an out-of-date server.");
76    return;
77  }
78
79  DCHECK(type.value == NotificationType::AUTOFILL_ENTRIES_CHANGED);
80
81  AutofillChangeList* changes = Details<AutofillChangeList>(details).ptr();
82  ObserveAutofillEntriesChanged(changes, &trans, autofill_root);
83}
84
85void AutofillChangeProcessor::PostOptimisticRefreshTask() {
86  BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
87      new DoOptimisticRefreshForAutofill(
88           personal_data_));
89}
90
91void AutofillChangeProcessor::ObserveAutofillEntriesChanged(
92    AutofillChangeList* changes, sync_api::WriteTransaction* trans,
93    const sync_api::ReadNode& autofill_root) {
94  for (AutofillChangeList::iterator change = changes->begin();
95       change != changes->end(); ++change) {
96    switch (change->type()) {
97      case AutofillChange::ADD:
98        {
99          sync_api::WriteNode sync_node(trans);
100          std::string tag =
101              AutofillModelAssociator::KeyToTag(change->key().name(),
102                                                change->key().value());
103          if (!sync_node.InitUniqueByCreation(syncable::AUTOFILL,
104                                              autofill_root, tag)) {
105            error_handler()->OnUnrecoverableError(FROM_HERE,
106                "Failed to create autofill sync node.");
107            return;
108          }
109
110          std::vector<base::Time> timestamps;
111          if (!web_database_->GetAutofillTable()->GetAutofillTimestamps(
112                  change->key().name(),
113                  change->key().value(),
114                  &timestamps)) {
115            error_handler()->OnUnrecoverableError(FROM_HERE,
116                "Failed to get timestamps.");
117            return;
118          }
119
120          sync_node.SetTitle(UTF8ToWide(tag));
121
122          WriteAutofillEntry(AutofillEntry(change->key(), timestamps),
123                             &sync_node);
124          model_associator_->Associate(&tag, sync_node.GetId());
125        }
126        break;
127
128      case AutofillChange::UPDATE:
129        {
130          sync_api::WriteNode sync_node(trans);
131          std::string tag = AutofillModelAssociator::KeyToTag(
132              change->key().name(), change->key().value());
133          int64 sync_id = model_associator_->GetSyncIdFromChromeId(tag);
134          if (sync_api::kInvalidId == sync_id) {
135            std::string err = "Unexpected notification for: " +
136                UTF16ToUTF8(change->key().name());
137            error_handler()->OnUnrecoverableError(FROM_HERE, err);
138            return;
139          } else {
140            if (!sync_node.InitByIdLookup(sync_id)) {
141              error_handler()->OnUnrecoverableError(FROM_HERE,
142                  "Autofill node lookup failed.");
143              return;
144            }
145          }
146
147          std::vector<base::Time> timestamps;
148          if (!web_database_->GetAutofillTable()->GetAutofillTimestamps(
149                   change->key().name(),
150                   change->key().value(),
151                   &timestamps)) {
152            error_handler()->OnUnrecoverableError(FROM_HERE,
153                "Failed to get timestamps.");
154            return;
155          }
156
157          WriteAutofillEntry(AutofillEntry(change->key(), timestamps),
158                             &sync_node);
159        }
160        break;
161      case AutofillChange::REMOVE: {
162        std::string tag = AutofillModelAssociator::KeyToTag(
163            change->key().name(), change->key().value());
164        RemoveSyncNode(tag, trans);
165        }
166        break;
167    }
168  }
169}
170
171void AutofillChangeProcessor::RemoveSyncNode(const std::string& tag,
172    sync_api::WriteTransaction* trans) {
173  sync_api::WriteNode sync_node(trans);
174  int64 sync_id = model_associator_->GetSyncIdFromChromeId(tag);
175  if (sync_api::kInvalidId == sync_id) {
176    // This could happen because web db might have duplicates and when an entry
177    // and its duplicate is deleted.
178    LOG(WARNING) <<
179        "Bogus delete notification generate for autofill entry " + tag;
180    return;
181  } else {
182    if (!sync_node.InitByIdLookup(sync_id)) {
183      error_handler()->OnUnrecoverableError(FROM_HERE,
184          "Autofill node lookup failed.");
185      return;
186    }
187    model_associator_->Disassociate(sync_node.GetId());
188    sync_node.Remove();
189  }
190}
191
192void AutofillChangeProcessor::ApplyChangesFromSyncModel(
193    const sync_api::BaseTransaction* trans,
194    const sync_api::SyncManager::ChangeRecord* changes,
195    int change_count) {
196  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
197  if (!running())
198    return;
199  StopObserving();
200
201  bool autofill_profile_not_migrated = HasNotMigratedYet(trans);
202
203  sync_api::ReadNode autofill_root(trans);
204  if (!autofill_root.InitByTagLookup(kAutofillTag)) {
205    error_handler()->OnUnrecoverableError(FROM_HERE,
206        "Autofill root node lookup failed.");
207    return;
208  }
209
210  for (int i = 0; i < change_count; ++i) {
211    sync_api::SyncManager::ChangeRecord::Action action(changes[i].action);
212    if (sync_api::SyncManager::ChangeRecord::ACTION_DELETE == action) {
213      DCHECK(changes[i].specifics.HasExtension(sync_pb::autofill))
214          << "Autofill specifics data not present on delete!";
215      const sync_pb::AutofillSpecifics& autofill =
216          changes[i].specifics.GetExtension(sync_pb::autofill);
217      if (autofill.has_value() ||
218        (autofill_profile_not_migrated && autofill.has_profile())) {
219        autofill_changes_.push_back(AutofillChangeRecord(changes[i].action,
220                                                         changes[i].id,
221                                                         autofill));
222      } else {
223        NOTREACHED() << "Autofill specifics has no data!";
224      }
225      continue;
226    }
227
228    // Handle an update or add.
229    sync_api::ReadNode sync_node(trans);
230    if (!sync_node.InitByIdLookup(changes[i].id)) {
231      error_handler()->OnUnrecoverableError(FROM_HERE,
232          "Autofill node lookup failed.");
233      return;
234    }
235
236    // Check that the changed node is a child of the autofills folder.
237    DCHECK(autofill_root.GetId() == sync_node.GetParentId());
238    DCHECK(syncable::AUTOFILL == sync_node.GetModelType());
239
240    const sync_pb::AutofillSpecifics& autofill(
241        sync_node.GetAutofillSpecifics());
242    int64 sync_id = sync_node.GetId();
243    if (autofill.has_value() ||
244      (autofill_profile_not_migrated && autofill.has_profile())) {
245      autofill_changes_.push_back(AutofillChangeRecord(changes[i].action,
246                                                       sync_id, autofill));
247    } else {
248      NOTREACHED() << "Autofill specifics has no data!";
249    }
250  }
251
252  StartObserving();
253}
254
255void AutofillChangeProcessor::CommitChangesFromSyncModel() {
256  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
257  if (!running())
258    return;
259  StopObserving();
260
261  std::vector<AutofillEntry> new_entries;
262  for (unsigned int i = 0; i < autofill_changes_.size(); i++) {
263    // Handle deletions.
264    if (sync_api::SyncManager::ChangeRecord::ACTION_DELETE ==
265        autofill_changes_[i].action_) {
266      if (autofill_changes_[i].autofill_.has_value()) {
267        ApplySyncAutofillEntryDelete(autofill_changes_[i].autofill_);
268      } else if (autofill_changes_[i].autofill_.has_profile()) {
269        ApplySyncAutofillProfileDelete(autofill_changes_[i].id_);
270      } else {
271        NOTREACHED() << "Autofill's CommitChanges received change with no"
272            " data!";
273      }
274      continue;
275    }
276
277    // Handle update/adds.
278    if (autofill_changes_[i].autofill_.has_value()) {
279      ApplySyncAutofillEntryChange(autofill_changes_[i].action_,
280                                   autofill_changes_[i].autofill_, &new_entries,
281                                   autofill_changes_[i].id_);
282    } else if (autofill_changes_[i].autofill_.has_profile()) {
283      ApplySyncAutofillProfileChange(autofill_changes_[i].action_,
284                                     autofill_changes_[i].autofill_.profile(),
285                                     autofill_changes_[i].id_);
286    } else {
287      NOTREACHED() << "Autofill's CommitChanges received change with no data!";
288    }
289  }
290  autofill_changes_.clear();
291
292  // Make changes
293  if (!web_database_->GetAutofillTable()->UpdateAutofillEntries(new_entries)) {
294    error_handler()->OnUnrecoverableError(FROM_HERE,
295                                          "Could not update autofill entries.");
296    return;
297  }
298
299  PostOptimisticRefreshTask();
300
301  StartObserving();
302}
303
304void AutofillChangeProcessor::ApplySyncAutofillEntryDelete(
305      const sync_pb::AutofillSpecifics& autofill) {
306  if (!web_database_->GetAutofillTable()->RemoveFormElement(
307      UTF8ToUTF16(autofill.name()), UTF8ToUTF16(autofill.value()))) {
308    error_handler()->OnUnrecoverableError(FROM_HERE,
309        "Could not remove autofill node.");
310    return;
311  }
312}
313
314void AutofillChangeProcessor::ApplySyncAutofillEntryChange(
315      sync_api::SyncManager::ChangeRecord::Action action,
316      const sync_pb::AutofillSpecifics& autofill,
317      std::vector<AutofillEntry>* new_entries,
318      int64 sync_id) {
319  DCHECK_NE(sync_api::SyncManager::ChangeRecord::ACTION_DELETE, action);
320
321  std::vector<base::Time> timestamps;
322  size_t timestamps_size = autofill.usage_timestamp_size();
323  for (size_t c = 0; c < timestamps_size; ++c) {
324    timestamps.push_back(
325        base::Time::FromInternalValue(autofill.usage_timestamp(c)));
326  }
327  AutofillKey k(UTF8ToUTF16(autofill.name()), UTF8ToUTF16(autofill.value()));
328  AutofillEntry new_entry(k, timestamps);
329
330  new_entries->push_back(new_entry);
331  std::string tag(AutofillModelAssociator::KeyToTag(k.name(), k.value()));
332  if (action == sync_api::SyncManager::ChangeRecord::ACTION_ADD)
333    model_associator_->Associate(&tag, sync_id);
334}
335
336void AutofillChangeProcessor::ApplySyncAutofillProfileChange(
337    sync_api::SyncManager::ChangeRecord::Action action,
338    const sync_pb::AutofillProfileSpecifics& profile,
339    int64 sync_id) {
340  DCHECK_NE(sync_api::SyncManager::ChangeRecord::ACTION_DELETE, action);
341
342  switch (action) {
343    case sync_api::SyncManager::ChangeRecord::ACTION_ADD: {
344      std::string guid(guid::GenerateGUID());
345      if (guid::IsValidGUID(guid) == false) {
346        DCHECK(false) << "Guid generated is invalid " << guid;
347        return;
348      }
349      scoped_ptr<AutofillProfile> p(new AutofillProfile);
350      p->set_guid(guid);
351      AutofillModelAssociator::FillProfileWithServerData(p.get(),
352                                                              profile);
353      if (!web_database_->GetAutofillTable()->AddAutofillProfile(*p.get())) {
354        NOTREACHED() << "Couldn't add autofill profile: " << guid;
355        return;
356      }
357      model_associator_->Associate(&guid, sync_id);
358      break;
359    }
360    case sync_api::SyncManager::ChangeRecord::ACTION_UPDATE: {
361      const std::string* guid = model_associator_->GetChromeNodeFromSyncId(
362          sync_id);
363      if (guid == NULL) {
364        LOG(ERROR) << " Model association has not happened for " << sync_id;
365        error_handler()->OnUnrecoverableError(FROM_HERE,
366            "model association has not happened");
367        return;
368      }
369      AutofillProfile *temp_ptr;
370      if (!web_database_->GetAutofillTable()->GetAutofillProfile(
371          *guid, &temp_ptr)) {
372        LOG(ERROR) << "Autofill profile not found for " << *guid;
373        return;
374      }
375
376      scoped_ptr<AutofillProfile> p(temp_ptr);
377
378      AutofillModelAssociator::FillProfileWithServerData(p.get(), profile);
379      if (!web_database_->GetAutofillTable()->UpdateAutofillProfile(
380          *(p.get()))) {
381        LOG(ERROR) << "Couldn't update autofill profile: " << guid;
382        return;
383      }
384      break;
385    }
386    default:
387      NOTREACHED();
388  }
389}
390
391void AutofillChangeProcessor::ApplySyncAutofillProfileDelete(
392    int64 sync_id) {
393
394  const std::string *guid = model_associator_->GetChromeNodeFromSyncId(sync_id);
395  if (guid == NULL) {
396    LOG(ERROR)<< "The profile is not associated";
397    return;
398  }
399
400  if (!web_database_->GetAutofillTable()->RemoveAutofillProfile(*guid)) {
401    LOG(ERROR) << "Could not remove the profile";
402    return;
403  }
404
405  model_associator_->Disassociate(sync_id);
406}
407
408void AutofillChangeProcessor::StartImpl(Profile* profile) {
409  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
410  observing_ = true;
411}
412
413void AutofillChangeProcessor::StopImpl() {
414  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
415  observing_ = false;
416}
417
418
419void AutofillChangeProcessor::StartObserving() {
420  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
421  notification_registrar_.Add(this, NotificationType::AUTOFILL_ENTRIES_CHANGED,
422                              NotificationService::AllSources());
423}
424
425void AutofillChangeProcessor::StopObserving() {
426  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
427  notification_registrar_.RemoveAll();
428}
429
430// static
431void AutofillChangeProcessor::WriteAutofillEntry(
432    const AutofillEntry& entry,
433    sync_api::WriteNode* node) {
434  sync_pb::AutofillSpecifics autofill;
435  autofill.set_name(UTF16ToUTF8(entry.key().name()));
436  autofill.set_value(UTF16ToUTF8(entry.key().value()));
437  const std::vector<base::Time>& ts(entry.timestamps());
438  for (std::vector<base::Time>::const_iterator timestamp = ts.begin();
439       timestamp != ts.end(); ++timestamp) {
440    autofill.add_usage_timestamp(timestamp->ToInternalValue());
441  }
442  node->SetAutofillSpecifics(autofill);
443}
444
445bool AutofillChangeProcessor::HasNotMigratedYet(
446    const sync_api::BaseTransaction* trans) {
447  return model_associator_->HasNotMigratedYet(trans);
448}
449
450}  // namespace browser_sync
451