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"
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
));
45 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB
));
49 PasswordChangeProcessor::~PasswordChangeProcessor() {
50 DCHECK(expected_loop_
== base::MessageLoop::current());
53 void PasswordChangeProcessor::Observe(
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_
);
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.");
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
,
86 if (result
== syncer::WriteNode::INIT_SUCCESS
) {
87 PasswordModelAssociator::WriteToSyncNode(change
->form(), &sync_node
);
88 model_associator_
->Associate(&tag
, sync_node
.GetId());
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.";
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.";
115 PasswordModelAssociator::WriteToSyncNode(change
->form(), &sync_node
);
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
,
125 LOG(ERROR
) << "Invalid sync id.";
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.";
137 PasswordModelAssociator::WriteToSyncNode(change
->form(), &sync_node
);
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!";
150 if (sync_node
.InitByIdLookup(sync_id
) !=
151 syncer::BaseNode::INIT_OK
) {
152 error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE
,
153 "Password node lookup failed.");
156 model_associator_
->Disassociate(sync_node
.GetId());
157 sync_node
.Tombstone();
165 void PasswordChangeProcessor::ApplyChangesFromSyncModel(
166 const syncer::BaseTransaction
* trans
,
168 const syncer::ImmutableChangeRecordList
& changes
) {
169 DCHECK(expected_loop_
== base::MessageLoop::current());
170 base::AutoLock
lock(disconnect_lock_
);
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.");
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
==
189 DCHECK(it
->specifics
.has_password())
190 << "Password specifics data not present on delete!";
191 DCHECK(it
->extra
.get());
192 syncer::ExtraPasswordChangeRecordData
* extra
=
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
);
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.");
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
);
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_
);
235 ScopedStopObserving
<PasswordChangeProcessor
> stop_observing(this);
237 syncer::SyncError error
= model_associator_
->WriteToPasswordStore(
240 &deleted_passwords_
);
242 error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE
,
243 "Error writing passwords");
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_
);
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(
286 chrome::NOTIFICATION_LOGINS_CHANGED
,
287 content::Source
<PasswordStore
>(password_store_
));
290 } // namespace browser_sync