• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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