| Index: chrome/browser/sync/glue/password_model_associator.cc
|
| diff --git a/chrome/browser/sync/glue/password_model_associator.cc b/chrome/browser/sync/glue/password_model_associator.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..923ee7b16322a6b53bd0d4deca276e36a7d78075
|
| --- /dev/null
|
| +++ b/chrome/browser/sync/glue/password_model_associator.cc
|
| @@ -0,0 +1,402 @@
|
| +// Copyright (c) 2010 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +#include "chrome/browser/sync/glue/password_model_associator.h"
|
| +
|
| +#include <set>
|
| +
|
| +#include "base/stl_util-inl.h"
|
| +#include "base/utf_string_conversions.h"
|
| +#include "chrome/browser/password_manager/password_store.h"
|
| +#include "chrome/browser/profile.h"
|
| +#include "chrome/browser/sync/engine/syncapi.h"
|
| +#include "chrome/browser/sync/profile_sync_service.h"
|
| +#include "chrome/browser/sync/protocol/password_specifics.pb.h"
|
| +#include "net/base/escape.h"
|
| +#include "webkit/glue/password_form.h"
|
| +
|
| +namespace browser_sync {
|
| +
|
| +const char kPasswordTag[] = "google_chrome_passwords";
|
| +
|
| +PasswordModelAssociator::PasswordModelAssociator(
|
| + ProfileSyncService* sync_service,
|
| + PasswordStore* password_store,
|
| + UnrecoverableErrorHandler* error_handler)
|
| + : sync_service_(sync_service),
|
| + password_store_(password_store),
|
| + error_handler_(error_handler),
|
| + password_node_id_(sync_api::kInvalidId),
|
| + abort_association_pending_(false),
|
| + expected_loop_(MessageLoop::current()) {
|
| + DCHECK(sync_service_);
|
| + DCHECK(password_store_);
|
| + DCHECK(error_handler_);
|
| +#if defined(OS_MACOSX)
|
| + DCHECK(!ChromeThread::CurrentlyOn(ChromeThread::UI));
|
| +#else
|
| + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::DB));
|
| +#endif
|
| +}
|
| +
|
| +bool PasswordModelAssociator::AssociateModels() {
|
| + DCHECK(expected_loop_ == MessageLoop::current());
|
| + {
|
| + AutoLock lock(abort_association_pending_lock_);
|
| + abort_association_pending_ = false;
|
| + }
|
| +
|
| + sync_api::WriteTransaction trans(
|
| + sync_service_->backend()->GetUserShareHandle());
|
| + sync_api::ReadNode password_root(&trans);
|
| + if (!password_root.InitByTagLookup(kPasswordTag)) {
|
| + LOG(ERROR) << "Server did not create the top-level password node. We "
|
| + << "might be running against an out-of-date server.";
|
| + return false;
|
| + }
|
| +
|
| + std::vector<webkit_glue::PasswordForm*> passwords;
|
| + if (!password_store_->FillAutofillableLogins(&passwords) ||
|
| + !password_store_->FillBlacklistLogins(&passwords)) {
|
| + STLDeleteElements(&passwords);
|
| + LOG(ERROR) << "Could not get the password entries.";
|
| + return false;
|
| + }
|
| +
|
| + std::set<std::string> current_passwords;
|
| + PasswordVector new_passwords;
|
| + PasswordVector updated_passwords;
|
| +
|
| + for (std::vector<webkit_glue::PasswordForm*>::iterator ix = passwords.begin();
|
| + ix != passwords.end(); ++ix) {
|
| + if (IsAbortPending())
|
| + return false;
|
| + std::string tag = MakeTag(**ix);
|
| +
|
| + sync_api::ReadNode node(&trans);
|
| + if (node.InitByClientTagLookup(syncable::PASSWORD, tag)) {
|
| + sync_pb::PasswordSpecificsData password;
|
| + if (!node.GetPasswordSpecifics(&password)) {
|
| + STLDeleteElements(&passwords);
|
| + LOG(ERROR) << "Failed to get password specifics from sync node.";
|
| + return false;
|
| + }
|
| + DCHECK_EQ(tag, MakeTag(password));
|
| +
|
| + webkit_glue::PasswordForm new_password;
|
| +
|
| + if (MergePasswords(password, **ix, &new_password)) {
|
| + sync_api::WriteNode write_node(&trans);
|
| + if (!write_node.InitByClientTagLookup(syncable::PASSWORD, tag)) {
|
| + STLDeleteElements(&passwords);
|
| + LOG(ERROR) << "Failed to edit password sync node.";
|
| + return false;
|
| + }
|
| + WriteToSyncNode(new_password, &write_node);
|
| + updated_passwords.push_back(new_password);
|
| + }
|
| +
|
| + Associate(&tag, node.GetId());
|
| + } else {
|
| + sync_api::WriteNode node(&trans);
|
| + if (!node.InitUniqueByCreation(syncable::PASSWORD,
|
| + password_root, tag)) {
|
| + STLDeleteElements(&passwords);
|
| + LOG(ERROR) << "Failed to create password sync node.";
|
| + return false;
|
| + }
|
| +
|
| + WriteToSyncNode(**ix, &node);
|
| +
|
| + Associate(&tag, node.GetId());
|
| + }
|
| +
|
| + current_passwords.insert(tag);
|
| + }
|
| +
|
| + STLDeleteElements(&passwords);
|
| +
|
| + int64 sync_child_id = password_root.GetFirstChildId();
|
| + while (sync_child_id != sync_api::kInvalidId) {
|
| + sync_api::ReadNode sync_child_node(&trans);
|
| + if (!sync_child_node.InitByIdLookup(sync_child_id)) {
|
| + LOG(ERROR) << "Failed to fetch child node.";
|
| + return false;
|
| + }
|
| + sync_pb::PasswordSpecificsData password;
|
| + if (!sync_child_node.GetPasswordSpecifics(&password)) {
|
| + LOG(ERROR) << "Failed to get specifics from password node.";
|
| + return false;
|
| + }
|
| + std::string tag = MakeTag(password);
|
| +
|
| + // The password only exists on the server. Add it to the local
|
| + // model.
|
| + if (current_passwords.find(tag) == current_passwords.end()) {
|
| + webkit_glue::PasswordForm new_password;
|
| +
|
| + CopyPassword(password, &new_password);
|
| + Associate(&tag, sync_child_node.GetId());
|
| + new_passwords.push_back(new_password);
|
| + }
|
| +
|
| + sync_child_id = sync_child_node.GetSuccessorId();
|
| + }
|
| +
|
| + if (!WriteToPasswordStore(&new_passwords, &updated_passwords, NULL)) {
|
| + LOG(ERROR) << "Failed to write passwords.";
|
| + return false;
|
| + }
|
| +
|
| + return true;
|
| +}
|
| +
|
| +bool PasswordModelAssociator::DeleteAllNodes(
|
| + sync_api::WriteTransaction* trans) {
|
| + DCHECK(expected_loop_ == MessageLoop::current());
|
| + for (PasswordToSyncIdMap::iterator node_id = id_map_.begin();
|
| + node_id != id_map_.end(); ++node_id) {
|
| + sync_api::WriteNode sync_node(trans);
|
| + if (!sync_node.InitByIdLookup(node_id->second)) {
|
| + LOG(ERROR) << "Typed url node lookup failed.";
|
| + return false;
|
| + }
|
| + sync_node.Remove();
|
| + }
|
| +
|
| + id_map_.clear();
|
| + id_map_inverse_.clear();
|
| + return true;
|
| +}
|
| +
|
| +bool PasswordModelAssociator::DisassociateModels() {
|
| + id_map_.clear();
|
| + id_map_inverse_.clear();
|
| + return true;
|
| +}
|
| +
|
| +bool PasswordModelAssociator::SyncModelHasUserCreatedNodes(bool* has_nodes) {
|
| + DCHECK(has_nodes);
|
| + *has_nodes = false;
|
| + int64 password_sync_id;
|
| + if (!GetSyncIdForTaggedNode(kPasswordTag, &password_sync_id)) {
|
| + LOG(ERROR) << "Server did not create the top-level password node. We "
|
| + << "might be running against an out-of-date server.";
|
| + return false;
|
| + }
|
| + sync_api::ReadTransaction trans(
|
| + sync_service_->backend()->GetUserShareHandle());
|
| +
|
| + sync_api::ReadNode password_node(&trans);
|
| + if (!password_node.InitByIdLookup(password_sync_id)) {
|
| + LOG(ERROR) << "Server did not create the top-level password node. We "
|
| + << "might be running against an out-of-date server.";
|
| + return false;
|
| + }
|
| +
|
| + // The sync model has user created nodes if the password folder has any
|
| + // children.
|
| + *has_nodes = sync_api::kInvalidId != password_node.GetFirstChildId();
|
| + return true;
|
| +}
|
| +
|
| +bool PasswordModelAssociator::ChromeModelHasUserCreatedNodes(bool* has_nodes) {
|
| + DCHECK(has_nodes);
|
| + std::vector<webkit_glue::PasswordForm*> passwords;
|
| + if (!password_store_->FillAutofillableLogins(&passwords) ||
|
| + !password_store_->FillBlacklistLogins(&passwords)) {
|
| + STLDeleteElements(&passwords);
|
| + LOG(ERROR) << "Could not get the password entries.";
|
| + return false;
|
| + }
|
| +
|
| + *has_nodes = !passwords.empty();
|
| + STLDeleteElements(&passwords);
|
| + return true;
|
| +}
|
| +
|
| +void PasswordModelAssociator::AbortAssociation() {
|
| + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
|
| + AutoLock lock(abort_association_pending_lock_);
|
| + abort_association_pending_ = true;
|
| +}
|
| +
|
| +bool PasswordModelAssociator::IsAbortPending() {
|
| + AutoLock lock(abort_association_pending_lock_);
|
| + return abort_association_pending_;
|
| +}
|
| +
|
| +int64 PasswordModelAssociator::GetSyncIdFromChromeId(
|
| + const std::string password) {
|
| + PasswordToSyncIdMap::const_iterator iter = id_map_.find(password);
|
| + return iter == id_map_.end() ? sync_api::kInvalidId : iter->second;
|
| +}
|
| +
|
| +void PasswordModelAssociator::Associate(
|
| + const std::string* password, int64 sync_id) {
|
| + DCHECK(expected_loop_ == MessageLoop::current());
|
| + DCHECK_NE(sync_api::kInvalidId, sync_id);
|
| + DCHECK(id_map_.find(*password) == id_map_.end());
|
| + DCHECK(id_map_inverse_.find(sync_id) == id_map_inverse_.end());
|
| + id_map_[*password] = sync_id;
|
| + id_map_inverse_[sync_id] = *password;
|
| +}
|
| +
|
| +void PasswordModelAssociator::Disassociate(int64 sync_id) {
|
| + DCHECK(expected_loop_ == MessageLoop::current());
|
| + SyncIdToPasswordMap::iterator iter = id_map_inverse_.find(sync_id);
|
| + if (iter == id_map_inverse_.end())
|
| + return;
|
| + CHECK(id_map_.erase(iter->second));
|
| + id_map_inverse_.erase(iter);
|
| +}
|
| +
|
| +bool PasswordModelAssociator::GetSyncIdForTaggedNode(const std::string& tag,
|
| + int64* sync_id) {
|
| + sync_api::ReadTransaction trans(
|
| + sync_service_->backend()->GetUserShareHandle());
|
| + sync_api::ReadNode sync_node(&trans);
|
| + if (!sync_node.InitByTagLookup(tag.c_str()))
|
| + return false;
|
| + *sync_id = sync_node.GetId();
|
| + return true;
|
| +}
|
| +
|
| +bool PasswordModelAssociator::WriteToPasswordStore(
|
| + const PasswordVector* new_passwords,
|
| + const PasswordVector* updated_passwords,
|
| + const PasswordVector* deleted_passwords) {
|
| + if (new_passwords) {
|
| + for (PasswordVector::const_iterator password = new_passwords->begin();
|
| + password != new_passwords->end(); ++password) {
|
| + password_store_->AddLoginImpl(*password);
|
| + }
|
| + }
|
| +
|
| + if (updated_passwords) {
|
| + for (PasswordVector::const_iterator password = updated_passwords->begin();
|
| + password != updated_passwords->end(); ++password) {
|
| + password_store_->UpdateLoginImpl(*password);
|
| + }
|
| + }
|
| +
|
| + if (deleted_passwords) {
|
| + for (PasswordVector::const_iterator password = deleted_passwords->begin();
|
| + password != deleted_passwords->end(); ++password) {
|
| + password_store_->RemoveLoginImpl(*password);
|
| + }
|
| + }
|
| + return true;
|
| +}
|
| +
|
| +// static
|
| +void PasswordModelAssociator::CopyPassword(
|
| + const sync_pb::PasswordSpecificsData& password,
|
| + webkit_glue::PasswordForm* new_password) {
|
| + new_password->scheme =
|
| + static_cast<webkit_glue::PasswordForm::Scheme>(password.scheme());
|
| + new_password->signon_realm = password.signon_realm();
|
| + new_password->origin = GURL(password.origin());
|
| + new_password->action = GURL(password.action());
|
| + new_password->username_element =
|
| + UTF8ToUTF16(password.username_element());
|
| + new_password->password_element =
|
| + UTF8ToUTF16(password.password_element());
|
| + new_password->username_value =
|
| + UTF8ToUTF16(password.username_value());
|
| + new_password->password_value =
|
| + UTF8ToUTF16(password.password_value());
|
| + new_password->ssl_valid = password.ssl_valid();
|
| + new_password->preferred = password.preferred();
|
| + new_password->date_created =
|
| + base::Time::FromInternalValue(password.date_created());
|
| + new_password->blacklisted_by_user =
|
| + password.blacklisted();
|
| +}
|
| +
|
| +// static
|
| +bool PasswordModelAssociator::MergePasswords(
|
| + const sync_pb::PasswordSpecificsData& password,
|
| + const webkit_glue::PasswordForm& password_form,
|
| + webkit_glue::PasswordForm* new_password) {
|
| + DCHECK(new_password);
|
| +
|
| + if (password.scheme() == password_form.scheme &&
|
| + password_form.signon_realm == password.signon_realm() &&
|
| + password_form.origin.spec() == password.origin() &&
|
| + password_form.action.spec() == password.action() &&
|
| + UTF16ToUTF8(password_form.username_element) ==
|
| + password.username_element() &&
|
| + UTF16ToUTF8(password_form.password_element) ==
|
| + password.password_element() &&
|
| + UTF16ToUTF8(password_form.username_value) ==
|
| + password.username_value() &&
|
| + UTF16ToUTF8(password_form.password_value) ==
|
| + password.password_value() &&
|
| + password.ssl_valid() == password_form.ssl_valid &&
|
| + password.preferred() == password_form.preferred &&
|
| + password.date_created() == password_form.date_created.ToInternalValue() &&
|
| + password.blacklisted() == password_form.blacklisted_by_user) {
|
| + return false;
|
| + }
|
| +
|
| + // If the passwords differ, we take the one that was created more recently.
|
| + if (base::Time::FromInternalValue(password.date_created()) <=
|
| + password_form.date_created) {
|
| + *new_password = password_form;
|
| + } else {
|
| + CopyPassword(password, new_password);
|
| + }
|
| +
|
| + return true;
|
| +}
|
| +
|
| +// static
|
| +void PasswordModelAssociator::WriteToSyncNode(
|
| + const webkit_glue::PasswordForm& password_form,
|
| + sync_api::WriteNode* node) {
|
| + sync_pb::PasswordSpecificsData password;
|
| + password.set_scheme(password_form.scheme);
|
| + password.set_signon_realm(password_form.signon_realm);
|
| + password.set_origin(password_form.origin.spec());
|
| + password.set_action(password_form.action.spec());
|
| + password.set_username_element(UTF16ToUTF8(password_form.username_element));
|
| + password.set_password_element(UTF16ToUTF8(password_form.password_element));
|
| + password.set_username_value(UTF16ToUTF8(password_form.username_value));
|
| + password.set_password_value(UTF16ToUTF8(password_form.password_value));
|
| + password.set_ssl_valid(password_form.ssl_valid);
|
| + password.set_preferred(password_form.preferred);
|
| + password.set_date_created(password_form.date_created.ToInternalValue());
|
| + password.set_blacklisted(password_form.blacklisted_by_user);
|
| +
|
| + node->SetPasswordSpecifics(password);
|
| +}
|
| +
|
| +// static
|
| +std::string PasswordModelAssociator::MakeTag(
|
| + const webkit_glue::PasswordForm& password) {
|
| + return MakeTag(password.signon_realm,
|
| + password.origin.spec(),
|
| + password.action.spec());
|
| +}
|
| +
|
| +// static
|
| +std::string PasswordModelAssociator::MakeTag(
|
| + const sync_pb::PasswordSpecificsData& password) {
|
| + return MakeTag(password.signon_realm(),
|
| + password.origin(),
|
| + password.action());
|
| +}
|
| +
|
| +// static
|
| +std::string PasswordModelAssociator::MakeTag(
|
| + const std::string& signon_realm,
|
| + const std::string& origin,
|
| + const std::string& action) {
|
| + return EscapePath(signon_realm) + "|" +
|
| + EscapePath(origin) + "|" +
|
| + EscapePath(action);
|
| +}
|
| +
|
| +} // namespace browser_sync
|
|
|