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_profile_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/autofill_profile.h"
13#include "chrome/browser/autofill/personal_data_manager.h"
14#include "chrome/browser/sync/engine/syncapi.h"
15#include "chrome/browser/sync/glue/autofill_profile_model_associator.h"
16#include "chrome/browser/sync/glue/change_processor.h"
17#include "chrome/browser/sync/glue/do_optimistic_refresh_task.h"
18#include "chrome/browser/sync/unrecoverable_error_handler.h"
19#include "chrome/browser/webdata/autofill_change.h"
20#include "chrome/browser/webdata/web_database.h"
21#include "chrome/common/guid.h"
22#include "content/common/notification_registrar.h"
23#include "content/common/notification_service.h"
24#include "content/common/notification_type.h"
25
26namespace browser_sync {
27
28AutofillProfileChangeProcessor::AutofillProfileChangeProcessor(
29      AutofillProfileModelAssociator *model_associator,
30      WebDatabase* web_database,
31      PersonalDataManager* personal_data_manager,
32      UnrecoverableErrorHandler* error_handler)
33      : ChangeProcessor(error_handler),
34        model_associator_(model_associator),
35        observing_(false),
36        web_database_(web_database),
37        personal_data_(personal_data_manager) {
38  DCHECK(model_associator);
39  DCHECK(web_database);
40  DCHECK(error_handler);
41  DCHECK(personal_data_manager);
42  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
43
44  StartObserving();
45}
46
47AutofillProfileChangeProcessor::~AutofillProfileChangeProcessor() {}
48
49AutofillProfileChangeProcessor::ScopedStopObserving::ScopedStopObserving(
50    AutofillProfileChangeProcessor* processor) {
51  processor_ = processor;
52  processor_->StopObserving();
53}
54
55AutofillProfileChangeProcessor::ScopedStopObserving::~ScopedStopObserving() {
56  processor_->StartObserving();
57}
58
59void AutofillProfileChangeProcessor::ApplyChangesFromSyncModel(
60    const sync_api::BaseTransaction *write_trans,
61    const sync_api::SyncManager::ChangeRecord* changes,
62    int change_count) {
63
64  ScopedStopObserving observer(this);
65
66  sync_api::ReadNode autofill_profile_root(write_trans);
67  if (!autofill_profile_root.InitByTagLookup(kAutofillProfileTag)) {
68    error_handler()->OnUnrecoverableError(FROM_HERE,
69      "Autofill Profile root node lookup failed");
70    return;
71  }
72
73  for (int i = 0; i < change_count; ++i) {
74    if (sync_api::SyncManager::ChangeRecord::ACTION_DELETE ==
75        changes[i].action) {
76      DCHECK(changes[i].specifics.HasExtension(
77          sync_pb::autofill_profile));
78
79      const sync_pb::AutofillProfileSpecifics& specifics =
80          changes[i].specifics.GetExtension(sync_pb::autofill_profile);
81
82      autofill_changes_.push_back(AutofillProfileChangeRecord(changes[i].action,
83          changes[i].id,
84          specifics));
85      continue;
86    }
87
88    // If it is not a delete.
89    sync_api::ReadNode sync_node(write_trans);
90    if (!sync_node.InitByIdLookup(changes[i].id)) {
91      LOG(ERROR) << "Could not find the id in sync db " << changes[i].id;
92      continue;
93    }
94
95    const sync_pb::AutofillProfileSpecifics& autofill(
96        sync_node.GetAutofillProfileSpecifics());
97
98    autofill_changes_.push_back(AutofillProfileChangeRecord(changes[i].action,
99        changes[i].id,
100        autofill));
101  }
102}
103
104void AutofillProfileChangeProcessor::Observe(NotificationType type,
105    const NotificationSource& source,
106    const NotificationDetails& details) {
107  DCHECK_EQ(type.value, NotificationType::AUTOFILL_PROFILE_CHANGED);
108  WebDataService* wds = Source<WebDataService>(source).ptr();
109
110  if (!wds || wds->GetDatabase() != web_database_)
111    return;
112
113  sync_api::WriteTransaction trans(share_handle());
114  sync_api::ReadNode autofill_root(&trans);
115  if (!autofill_root.InitByTagLookup(kAutofillProfileTag)) {
116    error_handler()->OnUnrecoverableError(FROM_HERE,
117        "Server did not create a tolp level node");
118    return;
119  }
120
121  AutofillProfileChange* change = Details<AutofillProfileChange>(details).ptr();
122
123  ActOnChange(change, &trans, autofill_root);
124}
125
126void AutofillProfileChangeProcessor::ActOnChange(
127     AutofillProfileChange* change,
128     sync_api::WriteTransaction* trans,
129     sync_api::ReadNode& autofill_root) {
130  DCHECK(change->type() == AutofillProfileChange::REMOVE || change->profile());
131  switch (change->type()) {
132    case AutofillProfileChange::ADD: {
133      AddAutofillProfileSyncNode(trans, autofill_root, *(change->profile()));
134      break;
135    }
136    case AutofillProfileChange::UPDATE: {
137      int64 sync_id = model_associator_->GetSyncIdFromChromeId(change->key());
138      if (sync_api::kInvalidId == sync_id) {
139        LOG(ERROR) << "Sync id is not found for " << change->key();
140        break;
141      }
142      sync_api::WriteNode node(trans);
143      if (!node.InitByIdLookup(sync_id)) {
144        LOG(ERROR) << "Could not find sync node for id " << sync_id;
145        break;
146      }
147
148      WriteAutofillProfile(*(change->profile()), &node);
149      break;
150    }
151    case AutofillProfileChange::REMOVE: {
152      int64 sync_id = model_associator_->GetSyncIdFromChromeId(change->key());
153      if (sync_api::kInvalidId == sync_id) {
154        LOG(ERROR) << "Sync id is not found for " << change->key();
155        break;
156      }
157      sync_api::WriteNode node(trans);
158      if (!node.InitByIdLookup(sync_id)) {
159        LOG(ERROR) << "Could not find sync node for id " << sync_id;
160        break;
161      }
162      node.Remove();
163      model_associator_->Disassociate(sync_id);
164      break;
165    }
166    default:
167      NOTREACHED();
168  }
169}
170
171void AutofillProfileChangeProcessor::CommitChangesFromSyncModel() {
172  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
173
174  if (!running())
175    return;
176
177  ScopedStopObserving observer(this);
178
179  for (unsigned int i = 0;i < autofill_changes_.size(); ++i) {
180    if (sync_api::SyncManager::ChangeRecord::ACTION_DELETE ==
181        autofill_changes_[i].action_) {
182      if (!web_database_->GetAutofillTable()->RemoveAutofillProfile(
183          autofill_changes_[i].profile_specifics_.guid())) {
184        LOG(ERROR) << "could not delete the profile " <<
185           autofill_changes_[i].profile_specifics_.guid();
186        continue;
187      }
188        continue;
189    }
190
191    // Now for updates and adds.
192    ApplyAutofillProfileChange(autofill_changes_[i].action_,
193        autofill_changes_[i].profile_specifics_,
194        autofill_changes_[i].id_);
195  }
196
197  autofill_changes_.clear();
198
199  PostOptimisticRefreshTask();
200}
201
202void AutofillProfileChangeProcessor::PostOptimisticRefreshTask() {
203  BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
204      new DoOptimisticRefreshForAutofill(
205           personal_data_));
206}
207
208void AutofillProfileChangeProcessor::ApplyAutofillProfileChange(
209    sync_api::SyncManager::ChangeRecord::Action action,
210    const sync_pb::AutofillProfileSpecifics& profile_specifics,
211    int64 sync_id) {
212
213  DCHECK_NE(sync_api::SyncManager::ChangeRecord::ACTION_DELETE, action);
214  switch (action) {
215    case sync_api::SyncManager::ChangeRecord::ACTION_ADD: {
216      if (guid::IsValidGUID(profile_specifics.guid()) == false) {
217        NOTREACHED() << "Guid from the server is invalid " <<
218            profile_specifics.guid();
219        return;
220      }
221      AutofillProfile p(profile_specifics.guid());
222      AutofillProfileModelAssociator::OverwriteProfileWithServerData(&p,
223          profile_specifics);
224      if (!web_database_->GetAutofillTable()->AddAutofillProfile(p)) {
225        LOG(ERROR) << "could not add autofill profile for guid " << p.guid();
226        break;
227      }
228
229      // Now that the node has been succesfully created we can associate it.
230      std::string guid = p.guid();
231      model_associator_->Associate(&guid, sync_id);
232      break;
233    }
234    case sync_api::SyncManager::ChangeRecord::ACTION_UPDATE: {
235      AutofillProfile *p;
236      if (!web_database_->GetAutofillTable()->GetAutofillProfile(
237          profile_specifics.guid(), &p)) {
238        LOG(ERROR) << "Could not find the autofill profile to update for " <<
239            profile_specifics.guid();
240        break;
241      }
242      scoped_ptr<AutofillProfile> autofill_pointer(p);
243      AutofillProfileModelAssociator::OverwriteProfileWithServerData(
244          autofill_pointer.get(),
245          profile_specifics);
246
247      if (!web_database_->GetAutofillTable()->UpdateAutofillProfile(
248          *(autofill_pointer.get()))) {
249        LOG(ERROR) << "Could not update autofill profile for " <<
250            profile_specifics.guid();
251        break;
252      }
253      break;
254    }
255    default: {
256      NOTREACHED();
257      break;
258    }
259  }
260}
261
262void AutofillProfileChangeProcessor::RemoveSyncNode(const std::string& guid,
263    sync_api::WriteTransaction* trans) {
264  sync_api::WriteNode node(trans);
265  int64 sync_id = model_associator_->GetSyncIdFromChromeId(guid);
266  if (sync_api::kInvalidId == sync_id) {
267    LOG(ERROR) << "Could not find the node in associator " << guid;
268    return;
269  }
270
271  if (!node.InitByIdLookup(sync_id)) {
272    LOG(ERROR) << "Could not find the sync node for " << guid;
273    return;
274  }
275
276  model_associator_->Disassociate(sync_id);
277  node.Remove();
278}
279
280void AutofillProfileChangeProcessor::AddAutofillProfileSyncNode(
281    sync_api::WriteTransaction* trans,
282    sync_api::BaseNode& autofill_profile_root,
283    const AutofillProfile& profile) {
284
285  std::string guid = profile.guid();
286
287  if (guid::IsValidGUID(guid) == false) {
288    DCHECK(false) << "Guid set on the profile is invalid " << guid;
289    return;
290  }
291
292  sync_api::WriteNode node(trans);
293  if (!node.InitUniqueByCreation(syncable::AUTOFILL_PROFILE,
294      autofill_profile_root,
295      profile.guid())) {
296    LOG(ERROR) << "could not create a sync node ";
297    return;
298  }
299
300  node.SetTitle(UTF8ToWide(profile.guid()));
301
302  WriteAutofillProfile(profile, &node);
303
304  model_associator_->Associate(&guid, node.GetId());
305}
306
307void AutofillProfileChangeProcessor::StartObserving() {
308  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
309  notification_registrar_.Add(this,
310      NotificationType::AUTOFILL_PROFILE_CHANGED,
311      NotificationService::AllSources());
312}
313
314void AutofillProfileChangeProcessor::StopObserving() {
315  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
316  notification_registrar_.RemoveAll();
317}
318
319void AutofillProfileChangeProcessor::WriteAutofillProfile(
320    const AutofillProfile& profile,
321    sync_api::WriteNode* node) {
322  sync_pb::AutofillProfileSpecifics specifics;
323
324  // This would get compiled out in official builds. The caller is expected to
325  // pass in a valid profile object with valid guid.(i.e., the caller might
326  // have to a DCHECK and log before calling. Having to check in 2 places is
327  // not optimal.)
328  DCHECK(guid::IsValidGUID(profile.guid()));
329
330  specifics.set_guid(profile.guid());
331  specifics.set_name_first(UTF16ToUTF8(profile.GetInfo(NAME_FIRST)));
332  specifics.set_name_middle(UTF16ToUTF8(profile.GetInfo(NAME_MIDDLE)));
333  specifics.set_name_last(UTF16ToUTF8(profile.GetInfo(NAME_LAST)));
334  specifics.set_address_home_line1(
335      UTF16ToUTF8(profile.GetInfo(ADDRESS_HOME_LINE1)));
336  specifics.set_address_home_line2(
337      UTF16ToUTF8(profile.GetInfo(ADDRESS_HOME_LINE2)));
338  specifics.set_address_home_city(UTF16ToUTF8(profile.GetInfo(
339      ADDRESS_HOME_CITY)));
340  specifics.set_address_home_state(UTF16ToUTF8(profile.GetInfo(
341      ADDRESS_HOME_STATE)));
342  specifics.set_address_home_country(UTF16ToUTF8(profile.GetInfo(
343      ADDRESS_HOME_COUNTRY)));
344  specifics.set_address_home_zip(UTF16ToUTF8(profile.GetInfo(
345      ADDRESS_HOME_ZIP)));
346  specifics.set_email_address(UTF16ToUTF8(profile.GetInfo(EMAIL_ADDRESS)));
347  specifics.set_company_name(UTF16ToUTF8(profile.GetInfo(COMPANY_NAME)));
348  specifics.set_phone_fax_whole_number(UTF16ToUTF8(profile.GetInfo(
349      PHONE_FAX_WHOLE_NUMBER)));
350  specifics.set_phone_home_whole_number(UTF16ToUTF8(profile.GetInfo(
351      PHONE_HOME_WHOLE_NUMBER)));
352  node->SetAutofillProfileSpecifics(specifics);
353}
354
355}  // namespace browser_sync
356