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