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