Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / sync / glue / password_change_processor.cc
1 // Copyright (c) 2012 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/password_change_processor.h"
6
7 #include <string>
8
9 #include "base/location.h"
10 #include "base/strings/string_util.h"
11 #include "base/strings/utf_string_conversions.h"
12 #include "chrome/browser/profiles/profile.h"
13 #include "chrome/browser/sync/glue/password_model_associator.h"
14 #include "chrome/browser/sync/profile_sync_service.h"
15 #include "components/autofill/core/common/password_form.h"
16 #include "components/password_manager/core/browser/password_store.h"
17 #include "components/password_manager/core/browser/password_store_change.h"
18 #include "content/public/browser/browser_thread.h"
19 #include "sync/internal_api/public/change_record.h"
20 #include "sync/internal_api/public/read_node.h"
21 #include "sync/internal_api/public/write_node.h"
22 #include "sync/internal_api/public/write_transaction.h"
23 #include "sync/protocol/password_specifics.pb.h"
24
25 using content::BrowserThread;
26
27 namespace browser_sync {
28
29 PasswordChangeProcessor::PasswordChangeProcessor(
30     PasswordModelAssociator* model_associator,
31     PasswordStore* password_store,
32     DataTypeErrorHandler* error_handler)
33     : ChangeProcessor(error_handler),
34       model_associator_(model_associator),
35       password_store_(password_store),
36       expected_loop_(base::MessageLoop::current()),
37       disconnected_(false) {
38   DCHECK(model_associator);
39   DCHECK(error_handler);
40   DCHECK(password_store->GetBackgroundTaskRunner()->RunsTasksOnCurrentThread());
41 }
42
43 PasswordChangeProcessor::~PasswordChangeProcessor() {
44   base::AutoLock lock(disconnect_lock_);
45   password_store_->RemoveObserver(this);
46 }
47
48 void PasswordChangeProcessor::OnLoginsChanged(
49     const PasswordStoreChangeList& changes) {
50   DCHECK(expected_loop_ == base::MessageLoop::current());
51
52   base::AutoLock lock(disconnect_lock_);
53   if (disconnected_)
54     return;
55
56   syncer::WriteTransaction trans(FROM_HERE, share_handle());
57
58   syncer::ReadNode password_root(&trans);
59   if (password_root.InitByTagLookup(kPasswordTag) !=
60           syncer::BaseNode::INIT_OK) {
61     error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE,
62         "Server did not create the top-level password node. "
63         "We might be running against an out-of-date server.");
64     return;
65   }
66
67   for (PasswordStoreChangeList::const_iterator change = changes.begin();
68        change != changes.end(); ++change) {
69     std::string tag = PasswordModelAssociator::MakeTag(change->form());
70     switch (change->type()) {
71       case PasswordStoreChange::ADD: {
72         syncer::WriteNode sync_node(&trans);
73         syncer::WriteNode::InitUniqueByCreationResult result =
74             sync_node.InitUniqueByCreation(syncer::PASSWORDS, password_root,
75                                            tag);
76         if (result == syncer::WriteNode::INIT_SUCCESS) {
77           PasswordModelAssociator::WriteToSyncNode(change->form(), &sync_node);
78           model_associator_->Associate(&tag, sync_node.GetId());
79           break;
80         } else {
81           // Maybe this node already exists and we should update it.
82           //
83           // If the PasswordStore is told to add an entry but an entry with the
84           // same name already exists, it will overwrite it.  It will report
85           // this change as an ADD rather than an UPDATE.  Ideally, it would be
86           // able to tell us what action was actually taken, rather than what
87           // action was requested.  If it did so, we wouldn't need to fall back
88           // to trying to update an existing password node here.
89           //
90           // TODO: Remove this.  See crbug.com/87855.
91           int64 sync_id = model_associator_->GetSyncIdFromChromeId(tag);
92           if (syncer::kInvalidId == sync_id) {
93             error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE,
94                 "Unable to create or retrieve password node");
95             LOG(ERROR) << "Invalid sync id.";
96             return;
97           }
98           if (sync_node.InitByIdLookup(sync_id) !=
99                   syncer::BaseNode::INIT_OK) {
100             error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE,
101                 "Password node lookup failed.");
102             LOG(ERROR) << "Password node lookup failed.";
103             return;
104           }
105           PasswordModelAssociator::WriteToSyncNode(change->form(), &sync_node);
106           break;
107         }
108       }
109       case PasswordStoreChange::UPDATE: {
110         syncer::WriteNode sync_node(&trans);
111         int64 sync_id = model_associator_->GetSyncIdFromChromeId(tag);
112         if (syncer::kInvalidId == sync_id) {
113           error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE,
114               "Invalid sync id");
115           LOG(ERROR) << "Invalid sync id.";
116           return;
117         } else {
118           if (sync_node.InitByIdLookup(sync_id) !=
119                   syncer::BaseNode::INIT_OK) {
120             error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE,
121                 "Password node lookup failed.");
122             LOG(ERROR) << "Password node lookup failed.";
123             return;
124           }
125         }
126
127         PasswordModelAssociator::WriteToSyncNode(change->form(), &sync_node);
128         break;
129       }
130       case PasswordStoreChange::REMOVE: {
131         syncer::WriteNode sync_node(&trans);
132         int64 sync_id = model_associator_->GetSyncIdFromChromeId(tag);
133         if (syncer::kInvalidId == sync_id) {
134           // We've been asked to remove a password that we don't know about.
135           // That's weird, but apparently we were already in the requested
136           // state, so it's not really an unrecoverable error. Just return.
137           LOG(WARNING) << "Trying to delete nonexistent password sync node!";
138           return;
139         } else {
140           if (sync_node.InitByIdLookup(sync_id) !=
141                   syncer::BaseNode::INIT_OK) {
142             error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE,
143                 "Password node lookup failed.");
144             return;
145           }
146           model_associator_->Disassociate(sync_node.GetId());
147           sync_node.Tombstone();
148         }
149         break;
150       }
151     }
152   }
153 }
154
155 void PasswordChangeProcessor::ApplyChangesFromSyncModel(
156     const syncer::BaseTransaction* trans,
157     int64 model_version,
158     const syncer::ImmutableChangeRecordList& changes) {
159   DCHECK(expected_loop_ == base::MessageLoop::current());
160   base::AutoLock lock(disconnect_lock_);
161   if (disconnected_)
162     return;
163
164   syncer::ReadNode password_root(trans);
165   if (password_root.InitByTagLookup(kPasswordTag) !=
166           syncer::BaseNode::INIT_OK) {
167     error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE,
168         "Password root node lookup failed.");
169     return;
170   }
171
172   DCHECK(deleted_passwords_.empty() && new_passwords_.empty() &&
173          updated_passwords_.empty());
174
175   for (syncer::ChangeRecordList::const_iterator it =
176            changes.Get().begin(); it != changes.Get().end(); ++it) {
177     if (syncer::ChangeRecord::ACTION_DELETE ==
178         it->action) {
179       DCHECK(it->specifics.has_password())
180           << "Password specifics data not present on delete!";
181       DCHECK(it->extra.get());
182       syncer::ExtraPasswordChangeRecordData* extra =
183           it->extra.get();
184       const sync_pb::PasswordSpecificsData& password = extra->unencrypted();
185       autofill::PasswordForm form;
186       PasswordModelAssociator::CopyPassword(password, &form);
187       deleted_passwords_.push_back(form);
188       model_associator_->Disassociate(it->id);
189       continue;
190     }
191
192     syncer::ReadNode sync_node(trans);
193     if (sync_node.InitByIdLookup(it->id) != syncer::BaseNode::INIT_OK) {
194       error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE,
195           "Password node lookup failed.");
196       return;
197     }
198
199     // Check that the changed node is a child of the passwords folder.
200     DCHECK_EQ(password_root.GetId(), sync_node.GetParentId());
201     DCHECK_EQ(syncer::PASSWORDS, sync_node.GetModelType());
202
203     const sync_pb::PasswordSpecificsData& password_data =
204         sync_node.GetPasswordSpecifics();
205     autofill::PasswordForm password;
206     PasswordModelAssociator::CopyPassword(password_data, &password);
207
208     if (syncer::ChangeRecord::ACTION_ADD == it->action) {
209       std::string tag(PasswordModelAssociator::MakeTag(password));
210       model_associator_->Associate(&tag, sync_node.GetId());
211       new_passwords_.push_back(password);
212     } else {
213       DCHECK_EQ(syncer::ChangeRecord::ACTION_UPDATE, it->action);
214       updated_passwords_.push_back(password);
215     }
216   }
217 }
218
219 void PasswordChangeProcessor::CommitChangesFromSyncModel() {
220   DCHECK(expected_loop_ == base::MessageLoop::current());
221   base::AutoLock lock(disconnect_lock_);
222   if (disconnected_)
223     return;
224
225   ScopedStopObserving<PasswordChangeProcessor> stop_observing(this);
226
227   syncer::SyncError error = model_associator_->WriteToPasswordStore(
228       &new_passwords_,
229       &updated_passwords_,
230       &deleted_passwords_);
231   if (error.IsSet()) {
232     error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE,
233         "Error writing passwords");
234     return;
235   }
236
237   deleted_passwords_.clear();
238   new_passwords_.clear();
239   updated_passwords_.clear();
240 }
241
242 void PasswordChangeProcessor::Disconnect() {
243   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
244   base::AutoLock lock(disconnect_lock_);
245   disconnected_ = true;
246 }
247
248 void PasswordChangeProcessor::StartImpl(Profile* profile) {
249   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
250   password_store_->ScheduleTask(
251       base::Bind(&PasswordChangeProcessor::InitObserving,
252                  base::Unretained(this)));
253 }
254
255 void PasswordChangeProcessor::InitObserving() {
256   base::AutoLock lock(disconnect_lock_);
257   if (disconnected_)
258     return;
259   StartObserving();
260 }
261
262 void PasswordChangeProcessor::StartObserving() {
263   DCHECK(expected_loop_ == base::MessageLoop::current());
264   disconnect_lock_.AssertAcquired();
265   password_store_->AddObserver(this);
266 }
267
268 void PasswordChangeProcessor::StopObserving() {
269   DCHECK(expected_loop_ == base::MessageLoop::current());
270   disconnect_lock_.AssertAcquired();
271   password_store_->RemoveObserver(this);
272 }
273
274 }  // namespace browser_sync