password_model_associator.cc revision 21d179b334e59e9a3bfcaed4c4430bef1bc5759d
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_model_associator.h" 6 7#include <set> 8 9#include "base/stl_util-inl.h" 10#include "base/utf_string_conversions.h" 11#include "chrome/browser/password_manager/password_store.h" 12#include "chrome/browser/sync/engine/syncapi.h" 13#include "chrome/browser/sync/profile_sync_service.h" 14#include "chrome/browser/sync/protocol/password_specifics.pb.h" 15#include "net/base/escape.h" 16#include "webkit/glue/password_form.h" 17 18namespace browser_sync { 19 20const char kPasswordTag[] = "google_chrome_passwords"; 21 22PasswordModelAssociator::PasswordModelAssociator( 23 ProfileSyncService* sync_service, 24 PasswordStore* password_store) 25 : sync_service_(sync_service), 26 password_store_(password_store), 27 password_node_id_(sync_api::kInvalidId), 28 abort_association_pending_(false), 29 expected_loop_(MessageLoop::current()) { 30 DCHECK(sync_service_); 31 DCHECK(password_store_); 32#if defined(OS_MACOSX) 33 DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI)); 34#else 35 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); 36#endif 37} 38 39PasswordModelAssociator::~PasswordModelAssociator() {} 40 41bool PasswordModelAssociator::AssociateModels() { 42 DCHECK(expected_loop_ == MessageLoop::current()); 43 { 44 AutoLock lock(abort_association_pending_lock_); 45 abort_association_pending_ = false; 46 } 47 48 sync_api::WriteTransaction trans( 49 sync_service_->backend()->GetUserShareHandle()); 50 sync_api::ReadNode password_root(&trans); 51 if (!password_root.InitByTagLookup(kPasswordTag)) { 52 LOG(ERROR) << "Server did not create the top-level password node. We " 53 << "might be running against an out-of-date server."; 54 return false; 55 } 56 57 std::vector<webkit_glue::PasswordForm*> passwords; 58 if (!password_store_->FillAutofillableLogins(&passwords) || 59 !password_store_->FillBlacklistLogins(&passwords)) { 60 STLDeleteElements(&passwords); 61 LOG(ERROR) << "Could not get the password entries."; 62 return false; 63 } 64 65 std::set<std::string> current_passwords; 66 PasswordVector new_passwords; 67 PasswordVector updated_passwords; 68 69 for (std::vector<webkit_glue::PasswordForm*>::iterator ix = passwords.begin(); 70 ix != passwords.end(); ++ix) { 71 if (IsAbortPending()) 72 return false; 73 std::string tag = MakeTag(**ix); 74 75 sync_api::ReadNode node(&trans); 76 if (node.InitByClientTagLookup(syncable::PASSWORDS, tag)) { 77 const sync_pb::PasswordSpecificsData& password = 78 node.GetPasswordSpecifics(); 79 DCHECK_EQ(tag, MakeTag(password)); 80 81 webkit_glue::PasswordForm new_password; 82 83 if (MergePasswords(password, **ix, &new_password)) { 84 sync_api::WriteNode write_node(&trans); 85 if (!write_node.InitByClientTagLookup(syncable::PASSWORDS, tag)) { 86 STLDeleteElements(&passwords); 87 LOG(ERROR) << "Failed to edit password sync node."; 88 return false; 89 } 90 WriteToSyncNode(new_password, &write_node); 91 updated_passwords.push_back(new_password); 92 } 93 94 Associate(&tag, node.GetId()); 95 } else { 96 sync_api::WriteNode node(&trans); 97 if (!node.InitUniqueByCreation(syncable::PASSWORDS, 98 password_root, tag)) { 99 STLDeleteElements(&passwords); 100 LOG(ERROR) << "Failed to create password sync node."; 101 return false; 102 } 103 104 WriteToSyncNode(**ix, &node); 105 106 Associate(&tag, node.GetId()); 107 } 108 109 current_passwords.insert(tag); 110 } 111 112 STLDeleteElements(&passwords); 113 114 int64 sync_child_id = password_root.GetFirstChildId(); 115 while (sync_child_id != sync_api::kInvalidId) { 116 sync_api::ReadNode sync_child_node(&trans); 117 if (!sync_child_node.InitByIdLookup(sync_child_id)) { 118 LOG(ERROR) << "Failed to fetch child node."; 119 return false; 120 } 121 const sync_pb::PasswordSpecificsData& password = 122 sync_child_node.GetPasswordSpecifics(); 123 std::string tag = MakeTag(password); 124 125 // The password only exists on the server. Add it to the local 126 // model. 127 if (current_passwords.find(tag) == current_passwords.end()) { 128 webkit_glue::PasswordForm new_password; 129 130 CopyPassword(password, &new_password); 131 Associate(&tag, sync_child_node.GetId()); 132 new_passwords.push_back(new_password); 133 } 134 135 sync_child_id = sync_child_node.GetSuccessorId(); 136 } 137 138 if (!WriteToPasswordStore(&new_passwords, &updated_passwords, NULL)) { 139 LOG(ERROR) << "Failed to write passwords."; 140 return false; 141 } 142 143 return true; 144} 145 146bool PasswordModelAssociator::DeleteAllNodes( 147 sync_api::WriteTransaction* trans) { 148 DCHECK(expected_loop_ == MessageLoop::current()); 149 for (PasswordToSyncIdMap::iterator node_id = id_map_.begin(); 150 node_id != id_map_.end(); ++node_id) { 151 sync_api::WriteNode sync_node(trans); 152 if (!sync_node.InitByIdLookup(node_id->second)) { 153 LOG(ERROR) << "Typed url node lookup failed."; 154 return false; 155 } 156 sync_node.Remove(); 157 } 158 159 id_map_.clear(); 160 id_map_inverse_.clear(); 161 return true; 162} 163 164bool PasswordModelAssociator::DisassociateModels() { 165 id_map_.clear(); 166 id_map_inverse_.clear(); 167 return true; 168} 169 170bool PasswordModelAssociator::SyncModelHasUserCreatedNodes(bool* has_nodes) { 171 DCHECK(has_nodes); 172 *has_nodes = false; 173 int64 password_sync_id; 174 if (!GetSyncIdForTaggedNode(kPasswordTag, &password_sync_id)) { 175 LOG(ERROR) << "Server did not create the top-level password node. We " 176 << "might be running against an out-of-date server."; 177 return false; 178 } 179 sync_api::ReadTransaction trans( 180 sync_service_->backend()->GetUserShareHandle()); 181 182 sync_api::ReadNode password_node(&trans); 183 if (!password_node.InitByIdLookup(password_sync_id)) { 184 LOG(ERROR) << "Server did not create the top-level password node. We " 185 << "might be running against an out-of-date server."; 186 return false; 187 } 188 189 // The sync model has user created nodes if the password folder has any 190 // children. 191 *has_nodes = sync_api::kInvalidId != password_node.GetFirstChildId(); 192 return true; 193} 194 195void PasswordModelAssociator::AbortAssociation() { 196 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 197 AutoLock lock(abort_association_pending_lock_); 198 abort_association_pending_ = true; 199} 200 201const std::string* PasswordModelAssociator::GetChromeNodeFromSyncId( 202 int64 sync_id) { 203 return NULL; 204} 205 206bool PasswordModelAssociator::InitSyncNodeFromChromeId( 207 const std::string& node_id, 208 sync_api::BaseNode* sync_node) { 209 return false; 210} 211 212bool PasswordModelAssociator::IsAbortPending() { 213 AutoLock lock(abort_association_pending_lock_); 214 return abort_association_pending_; 215} 216 217int64 PasswordModelAssociator::GetSyncIdFromChromeId( 218 const std::string& password) { 219 PasswordToSyncIdMap::const_iterator iter = id_map_.find(password); 220 return iter == id_map_.end() ? sync_api::kInvalidId : iter->second; 221} 222 223void PasswordModelAssociator::Associate( 224 const std::string* password, int64 sync_id) { 225 DCHECK(expected_loop_ == MessageLoop::current()); 226 DCHECK_NE(sync_api::kInvalidId, sync_id); 227 DCHECK(id_map_.find(*password) == id_map_.end()); 228 DCHECK(id_map_inverse_.find(sync_id) == id_map_inverse_.end()); 229 id_map_[*password] = sync_id; 230 id_map_inverse_[sync_id] = *password; 231} 232 233void PasswordModelAssociator::Disassociate(int64 sync_id) { 234 DCHECK(expected_loop_ == MessageLoop::current()); 235 SyncIdToPasswordMap::iterator iter = id_map_inverse_.find(sync_id); 236 if (iter == id_map_inverse_.end()) 237 return; 238 CHECK(id_map_.erase(iter->second)); 239 id_map_inverse_.erase(iter); 240} 241 242bool PasswordModelAssociator::GetSyncIdForTaggedNode(const std::string& tag, 243 int64* sync_id) { 244 sync_api::ReadTransaction trans( 245 sync_service_->backend()->GetUserShareHandle()); 246 sync_api::ReadNode sync_node(&trans); 247 if (!sync_node.InitByTagLookup(tag.c_str())) 248 return false; 249 *sync_id = sync_node.GetId(); 250 return true; 251} 252 253bool PasswordModelAssociator::WriteToPasswordStore( 254 const PasswordVector* new_passwords, 255 const PasswordVector* updated_passwords, 256 const PasswordVector* deleted_passwords) { 257 if (new_passwords) { 258 for (PasswordVector::const_iterator password = new_passwords->begin(); 259 password != new_passwords->end(); ++password) { 260 password_store_->AddLoginImpl(*password); 261 } 262 } 263 264 if (updated_passwords) { 265 for (PasswordVector::const_iterator password = updated_passwords->begin(); 266 password != updated_passwords->end(); ++password) { 267 password_store_->UpdateLoginImpl(*password); 268 } 269 } 270 271 if (deleted_passwords) { 272 for (PasswordVector::const_iterator password = deleted_passwords->begin(); 273 password != deleted_passwords->end(); ++password) { 274 password_store_->RemoveLoginImpl(*password); 275 } 276 } 277 return true; 278} 279 280// static 281void PasswordModelAssociator::CopyPassword( 282 const sync_pb::PasswordSpecificsData& password, 283 webkit_glue::PasswordForm* new_password) { 284 new_password->scheme = 285 static_cast<webkit_glue::PasswordForm::Scheme>(password.scheme()); 286 new_password->signon_realm = password.signon_realm(); 287 new_password->origin = GURL(password.origin()); 288 new_password->action = GURL(password.action()); 289 new_password->username_element = 290 UTF8ToUTF16(password.username_element()); 291 new_password->password_element = 292 UTF8ToUTF16(password.password_element()); 293 new_password->username_value = 294 UTF8ToUTF16(password.username_value()); 295 new_password->password_value = 296 UTF8ToUTF16(password.password_value()); 297 new_password->ssl_valid = password.ssl_valid(); 298 new_password->preferred = password.preferred(); 299 new_password->date_created = 300 base::Time::FromInternalValue(password.date_created()); 301 new_password->blacklisted_by_user = 302 password.blacklisted(); 303} 304 305// static 306bool PasswordModelAssociator::MergePasswords( 307 const sync_pb::PasswordSpecificsData& password, 308 const webkit_glue::PasswordForm& password_form, 309 webkit_glue::PasswordForm* new_password) { 310 DCHECK(new_password); 311 312 if (password.scheme() == password_form.scheme && 313 password_form.signon_realm == password.signon_realm() && 314 password_form.origin.spec() == password.origin() && 315 password_form.action.spec() == password.action() && 316 UTF16ToUTF8(password_form.username_element) == 317 password.username_element() && 318 UTF16ToUTF8(password_form.password_element) == 319 password.password_element() && 320 UTF16ToUTF8(password_form.username_value) == 321 password.username_value() && 322 UTF16ToUTF8(password_form.password_value) == 323 password.password_value() && 324 password.ssl_valid() == password_form.ssl_valid && 325 password.preferred() == password_form.preferred && 326 password.date_created() == password_form.date_created.ToInternalValue() && 327 password.blacklisted() == password_form.blacklisted_by_user) { 328 return false; 329 } 330 331 // If the passwords differ, we take the one that was created more recently. 332 if (base::Time::FromInternalValue(password.date_created()) <= 333 password_form.date_created) { 334 *new_password = password_form; 335 } else { 336 CopyPassword(password, new_password); 337 } 338 339 return true; 340} 341 342// static 343void PasswordModelAssociator::WriteToSyncNode( 344 const webkit_glue::PasswordForm& password_form, 345 sync_api::WriteNode* node) { 346 sync_pb::PasswordSpecificsData password; 347 password.set_scheme(password_form.scheme); 348 password.set_signon_realm(password_form.signon_realm); 349 password.set_origin(password_form.origin.spec()); 350 password.set_action(password_form.action.spec()); 351 password.set_username_element(UTF16ToUTF8(password_form.username_element)); 352 password.set_password_element(UTF16ToUTF8(password_form.password_element)); 353 password.set_username_value(UTF16ToUTF8(password_form.username_value)); 354 password.set_password_value(UTF16ToUTF8(password_form.password_value)); 355 password.set_ssl_valid(password_form.ssl_valid); 356 password.set_preferred(password_form.preferred); 357 password.set_date_created(password_form.date_created.ToInternalValue()); 358 password.set_blacklisted(password_form.blacklisted_by_user); 359 360 node->SetPasswordSpecifics(password); 361} 362 363// static 364std::string PasswordModelAssociator::MakeTag( 365 const webkit_glue::PasswordForm& password) { 366 return MakeTag(password.origin.spec(), 367 UTF16ToUTF8(password.username_element), 368 UTF16ToUTF8(password.username_value), 369 UTF16ToUTF8(password.password_element), 370 password.signon_realm); 371} 372 373// static 374std::string PasswordModelAssociator::MakeTag( 375 const sync_pb::PasswordSpecificsData& password) { 376 return MakeTag(password.origin(), 377 password.username_element(), 378 password.username_value(), 379 password.password_element(), 380 password.signon_realm()); 381} 382 383// static 384std::string PasswordModelAssociator::MakeTag( 385 const std::string& origin_url, 386 const std::string& username_element, 387 const std::string& username_value, 388 const std::string& password_element, 389 const std::string& signon_realm) { 390 return EscapePath(origin_url) + "|" + 391 EscapePath(username_element) + "|" + 392 EscapePath(username_value) + "|" + 393 EscapePath(password_element) + "|" + 394 EscapePath(signon_realm); 395} 396 397} // namespace browser_sync 398