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/password_change_processor.h"
6
7 #include <string>
8
9 #include "base/string_util.h"
10 #include "base/utf_string_conversions.h"
11 #include "chrome/browser/password_manager/password_store.h"
12 #include "chrome/browser/password_manager/password_store_change.h"
13 #include "chrome/browser/profiles/profile.h"
14 #include "chrome/browser/sync/glue/password_model_associator.h"
15 #include "chrome/browser/sync/profile_sync_service.h"
16 #include "chrome/browser/sync/protocol/password_specifics.pb.h"
17 #include "content/common/notification_details.h"
18 #include "content/common/notification_source.h"
19 #include "content/common/notification_type.h"
20 #include "webkit/glue/password_form.h"
21
22 namespace browser_sync {
23
PasswordChangeProcessor(PasswordModelAssociator * model_associator,PasswordStore * password_store,UnrecoverableErrorHandler * error_handler)24 PasswordChangeProcessor::PasswordChangeProcessor(
25 PasswordModelAssociator* model_associator,
26 PasswordStore* password_store,
27 UnrecoverableErrorHandler* error_handler)
28 : ChangeProcessor(error_handler),
29 model_associator_(model_associator),
30 password_store_(password_store),
31 observing_(false),
32 expected_loop_(MessageLoop::current()) {
33 DCHECK(model_associator);
34 DCHECK(error_handler);
35 #if defined(OS_MACOSX)
36 DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI));
37 #else
38 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
39 #endif
40 StartObserving();
41 }
42
~PasswordChangeProcessor()43 PasswordChangeProcessor::~PasswordChangeProcessor() {
44 DCHECK(expected_loop_ == MessageLoop::current());
45 }
46
Observe(NotificationType type,const NotificationSource & source,const NotificationDetails & details)47 void PasswordChangeProcessor::Observe(NotificationType type,
48 const NotificationSource& source,
49 const NotificationDetails& details) {
50 DCHECK(expected_loop_ == MessageLoop::current());
51 DCHECK(NotificationType::LOGINS_CHANGED == type);
52 if (!observing_)
53 return;
54
55 DCHECK(running());
56
57 sync_api::WriteTransaction trans(share_handle());
58
59 sync_api::ReadNode password_root(&trans);
60 if (!password_root.InitByTagLookup(kPasswordTag)) {
61 error_handler()->OnUnrecoverableError(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 PasswordStoreChangeList* changes =
68 Details<PasswordStoreChangeList>(details).ptr();
69 for (PasswordStoreChangeList::iterator change = changes->begin();
70 change != changes->end(); ++change) {
71 std::string tag = PasswordModelAssociator::MakeTag(change->form());
72 switch (change->type()) {
73 case PasswordStoreChange::ADD: {
74 sync_api::WriteNode sync_node(&trans);
75 if (!sync_node.InitUniqueByCreation(syncable::PASSWORDS,
76 password_root, tag)) {
77 error_handler()->OnUnrecoverableError(FROM_HERE,
78 "Failed to create password sync node.");
79 return;
80 }
81
82 PasswordModelAssociator::WriteToSyncNode(change->form(), &sync_node);
83 model_associator_->Associate(&tag, sync_node.GetId());
84 break;
85 }
86 case PasswordStoreChange::UPDATE: {
87 sync_api::WriteNode sync_node(&trans);
88 int64 sync_id = model_associator_->GetSyncIdFromChromeId(tag);
89 if (sync_api::kInvalidId == sync_id) {
90 error_handler()->OnUnrecoverableError(FROM_HERE,
91 "Unexpected notification for: ");
92 return;
93 } else {
94 if (!sync_node.InitByIdLookup(sync_id)) {
95 error_handler()->OnUnrecoverableError(FROM_HERE,
96 "Password node lookup failed.");
97 return;
98 }
99 }
100
101 PasswordModelAssociator::WriteToSyncNode(change->form(), &sync_node);
102 break;
103 }
104 case PasswordStoreChange::REMOVE: {
105 sync_api::WriteNode sync_node(&trans);
106 int64 sync_id = model_associator_->GetSyncIdFromChromeId(tag);
107 if (sync_api::kInvalidId == sync_id) {
108 error_handler()->OnUnrecoverableError(FROM_HERE,
109 "Unexpected notification");
110 return;
111 } else {
112 if (!sync_node.InitByIdLookup(sync_id)) {
113 error_handler()->OnUnrecoverableError(FROM_HERE,
114 "Password node lookup failed.");
115 return;
116 }
117 model_associator_->Disassociate(sync_node.GetId());
118 sync_node.Remove();
119 }
120 break;
121 }
122 }
123 }
124 }
125
ApplyChangesFromSyncModel(const sync_api::BaseTransaction * trans,const sync_api::SyncManager::ChangeRecord * changes,int change_count)126 void PasswordChangeProcessor::ApplyChangesFromSyncModel(
127 const sync_api::BaseTransaction* trans,
128 const sync_api::SyncManager::ChangeRecord* changes,
129 int change_count) {
130 DCHECK(expected_loop_ == MessageLoop::current());
131 if (!running())
132 return;
133
134 sync_api::ReadNode password_root(trans);
135 if (!password_root.InitByTagLookup(kPasswordTag)) {
136 error_handler()->OnUnrecoverableError(FROM_HERE,
137 "Password root node lookup failed.");
138 return;
139 }
140
141 DCHECK(deleted_passwords_.empty() && new_passwords_.empty() &&
142 updated_passwords_.empty());
143
144 for (int i = 0; i < change_count; ++i) {
145 if (sync_api::SyncManager::ChangeRecord::ACTION_DELETE ==
146 changes[i].action) {
147 DCHECK(changes[i].specifics.HasExtension(sync_pb::password))
148 << "Password specifics data not present on delete!";
149 DCHECK(changes[i].extra.get());
150 sync_api::SyncManager::ExtraPasswordChangeRecordData* extra =
151 changes[i].extra.get();
152 const sync_pb::PasswordSpecificsData& password = extra->unencrypted();
153 webkit_glue::PasswordForm form;
154 PasswordModelAssociator::CopyPassword(password, &form);
155 deleted_passwords_.push_back(form);
156 model_associator_->Disassociate(changes[i].id);
157 continue;
158 }
159
160 sync_api::ReadNode sync_node(trans);
161 if (!sync_node.InitByIdLookup(changes[i].id)) {
162 error_handler()->OnUnrecoverableError(FROM_HERE,
163 "Password node lookup failed.");
164 return;
165 }
166
167 // Check that the changed node is a child of the passwords folder.
168 DCHECK(password_root.GetId() == sync_node.GetParentId());
169 DCHECK(syncable::PASSWORDS == sync_node.GetModelType());
170
171 const sync_pb::PasswordSpecificsData& password_data =
172 sync_node.GetPasswordSpecifics();
173 webkit_glue::PasswordForm password;
174 PasswordModelAssociator::CopyPassword(password_data, &password);
175
176 if (sync_api::SyncManager::ChangeRecord::ACTION_ADD == changes[i].action) {
177 std::string tag(PasswordModelAssociator::MakeTag(password));
178 model_associator_->Associate(&tag, sync_node.GetId());
179 new_passwords_.push_back(password);
180 } else {
181 DCHECK(sync_api::SyncManager::ChangeRecord::ACTION_UPDATE ==
182 changes[i].action);
183 updated_passwords_.push_back(password);
184 }
185 }
186 }
187
CommitChangesFromSyncModel()188 void PasswordChangeProcessor::CommitChangesFromSyncModel() {
189 DCHECK(expected_loop_ == MessageLoop::current());
190 if (!running())
191 return;
192 StopObserving();
193
194 if (!model_associator_->WriteToPasswordStore(&new_passwords_,
195 &updated_passwords_,
196 &deleted_passwords_)) {
197 error_handler()->OnUnrecoverableError(FROM_HERE, "Error writing passwords");
198 return;
199 }
200
201 deleted_passwords_.clear();
202 new_passwords_.clear();
203 updated_passwords_.clear();
204
205 StartObserving();
206 }
207
StartImpl(Profile * profile)208 void PasswordChangeProcessor::StartImpl(Profile* profile) {
209 DCHECK(expected_loop_ == MessageLoop::current());
210 observing_ = true;
211 }
212
StopImpl()213 void PasswordChangeProcessor::StopImpl() {
214 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
215 observing_ = false;
216 }
217
218
StartObserving()219 void PasswordChangeProcessor::StartObserving() {
220 DCHECK(expected_loop_ == MessageLoop::current());
221 notification_registrar_.Add(this,
222 NotificationType::LOGINS_CHANGED,
223 Source<PasswordStore>(password_store_));
224 }
225
StopObserving()226 void PasswordChangeProcessor::StopObserving() {
227 DCHECK(expected_loop_ == MessageLoop::current());
228 notification_registrar_.Remove(this,
229 NotificationType::LOGINS_CHANGED,
230 Source<PasswordStore>(password_store_));
231 }
232
233 } // namespace browser_sync
234