1// Copyright (c) 2012 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/location.h"
10#include "base/metrics/histogram.h"
11#include "base/stl_util.h"
12#include "base/strings/utf_string_conversions.h"
13#include "chrome/browser/password_manager/password_store.h"
14#include "chrome/browser/sync/profile_sync_service.h"
15#include "content/public/common/password_form.h"
16#include "net/base/escape.h"
17#include "sync/api/sync_error.h"
18#include "sync/internal_api/public/read_node.h"
19#include "sync/internal_api/public/read_transaction.h"
20#include "sync/internal_api/public/write_node.h"
21#include "sync/internal_api/public/write_transaction.h"
22#include "sync/protocol/password_specifics.pb.h"
23
24using content::BrowserThread;
25
26namespace browser_sync {
27
28const char kPasswordTag[] = "google_chrome_passwords";
29
30PasswordModelAssociator::PasswordModelAssociator(
31    ProfileSyncService* sync_service,
32    PasswordStore* password_store,
33    DataTypeErrorHandler* error_handler)
34    : sync_service_(sync_service),
35      password_store_(password_store),
36      password_node_id_(syncer::kInvalidId),
37      abort_association_requested_(false),
38      expected_loop_(base::MessageLoop::current()),
39      error_handler_(error_handler) {
40  DCHECK(sync_service_);
41#if defined(OS_MACOSX)
42  DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI));
43#else
44  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
45#endif
46}
47
48PasswordModelAssociator::~PasswordModelAssociator() {
49  DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI));
50}
51
52syncer::SyncError PasswordModelAssociator::AssociateModels(
53    syncer::SyncMergeResult* local_merge_result,
54    syncer::SyncMergeResult* syncer_merge_result) {
55  DCHECK(expected_loop_ == base::MessageLoop::current());
56
57  // We must not be holding a transaction when we interact with the password
58  // store, as it can post tasks to the UI thread which can itself be blocked
59  // on our transaction, resulting in deadlock. (http://crbug.com/70658)
60  std::vector<content::PasswordForm*> passwords;
61  if (!password_store_->FillAutofillableLogins(&passwords) ||
62      !password_store_->FillBlacklistLogins(&passwords)) {
63    STLDeleteElements(&passwords);
64
65    // Password store often fails to load passwords. Track failures with UMA.
66    // (http://crbug.com/249000)
67    UMA_HISTOGRAM_ENUMERATION("Sync.LocalDataFailedToLoad",
68                              ModelTypeToHistogramInt(syncer::PASSWORDS),
69                              syncer::MODEL_TYPE_COUNT);
70    return syncer::SyncError(FROM_HERE,
71                             syncer::SyncError::DATATYPE_ERROR,
72                             "Could not get the password entries.",
73                             model_type());
74  }
75
76  PasswordVector new_passwords;
77  PasswordVector updated_passwords;
78  {
79    base::AutoLock lock(association_lock_);
80    if (abort_association_requested_)
81      return syncer::SyncError();
82
83    std::set<std::string> current_passwords;
84    syncer::WriteTransaction trans(FROM_HERE, sync_service_->GetUserShare());
85    syncer::ReadNode password_root(&trans);
86    if (password_root.InitByTagLookup(kPasswordTag) !=
87            syncer::BaseNode::INIT_OK) {
88      return error_handler_->CreateAndUploadError(
89          FROM_HERE,
90          "Server did not create the top-level password node. We "
91          "might be running against an out-of-date server.",
92          model_type());
93    }
94
95    for (std::vector<content::PasswordForm*>::iterator ix =
96             passwords.begin();
97         ix != passwords.end(); ++ix) {
98      std::string tag = MakeTag(**ix);
99
100      syncer::ReadNode node(&trans);
101      if (node.InitByClientTagLookup(syncer::PASSWORDS, tag) ==
102              syncer::BaseNode::INIT_OK) {
103        const sync_pb::PasswordSpecificsData& password =
104            node.GetPasswordSpecifics();
105        DCHECK_EQ(tag, MakeTag(password));
106
107        content::PasswordForm new_password;
108
109        if (MergePasswords(password, **ix, &new_password)) {
110          syncer::WriteNode write_node(&trans);
111          if (write_node.InitByClientTagLookup(syncer::PASSWORDS, tag) !=
112                  syncer::BaseNode::INIT_OK) {
113            STLDeleteElements(&passwords);
114            return error_handler_->CreateAndUploadError(
115                FROM_HERE,
116                "Failed to edit password sync node.",
117                model_type());
118          }
119          WriteToSyncNode(new_password, &write_node);
120          updated_passwords.push_back(new_password);
121        }
122
123        Associate(&tag, node.GetId());
124      } else {
125        syncer::WriteNode node(&trans);
126        syncer::WriteNode::InitUniqueByCreationResult result =
127            node.InitUniqueByCreation(syncer::PASSWORDS, password_root, tag);
128        if (result != syncer::WriteNode::INIT_SUCCESS) {
129          STLDeleteElements(&passwords);
130          return error_handler_->CreateAndUploadError(
131              FROM_HERE,
132              "Failed to create password sync node.",
133              model_type());
134        }
135
136        WriteToSyncNode(**ix, &node);
137
138        Associate(&tag, node.GetId());
139      }
140
141      current_passwords.insert(tag);
142    }
143
144    STLDeleteElements(&passwords);
145
146    int64 sync_child_id = password_root.GetFirstChildId();
147    while (sync_child_id != syncer::kInvalidId) {
148      syncer::ReadNode sync_child_node(&trans);
149      if (sync_child_node.InitByIdLookup(sync_child_id) !=
150              syncer::BaseNode::INIT_OK) {
151        return error_handler_->CreateAndUploadError(
152            FROM_HERE,
153            "Failed to fetch child node.",
154            model_type());
155      }
156      const sync_pb::PasswordSpecificsData& password =
157          sync_child_node.GetPasswordSpecifics();
158      std::string tag = MakeTag(password);
159
160      // The password only exists on the server.  Add it to the local
161      // model.
162      if (current_passwords.find(tag) == current_passwords.end()) {
163        content::PasswordForm new_password;
164
165        CopyPassword(password, &new_password);
166        Associate(&tag, sync_child_node.GetId());
167        new_passwords.push_back(new_password);
168      }
169
170      sync_child_id = sync_child_node.GetSuccessorId();
171    }
172  }
173
174  // We must not be holding a transaction when we interact with the password
175  // store, as it can post tasks to the UI thread which can itself be blocked
176  // on our transaction, resulting in deadlock. (http://crbug.com/70658)
177  return WriteToPasswordStore(&new_passwords,
178                              &updated_passwords,
179                              NULL);
180}
181
182bool PasswordModelAssociator::DeleteAllNodes(
183    syncer::WriteTransaction* trans) {
184  DCHECK(expected_loop_ == base::MessageLoop::current());
185  for (PasswordToSyncIdMap::iterator node_id = id_map_.begin();
186       node_id != id_map_.end(); ++node_id) {
187    syncer::WriteNode sync_node(trans);
188    if (sync_node.InitByIdLookup(node_id->second) !=
189            syncer::BaseNode::INIT_OK) {
190      LOG(ERROR) << "Typed url node lookup failed.";
191      return false;
192    }
193    sync_node.Tombstone();
194  }
195
196  id_map_.clear();
197  id_map_inverse_.clear();
198  return true;
199}
200
201syncer::SyncError PasswordModelAssociator::DisassociateModels() {
202  id_map_.clear();
203  id_map_inverse_.clear();
204  return syncer::SyncError();
205}
206
207bool PasswordModelAssociator::SyncModelHasUserCreatedNodes(bool* has_nodes) {
208  DCHECK(has_nodes);
209  *has_nodes = false;
210  int64 password_sync_id;
211  if (!GetSyncIdForTaggedNode(kPasswordTag, &password_sync_id)) {
212    LOG(ERROR) << "Server did not create the top-level password node. We "
213               << "might be running against an out-of-date server.";
214    return false;
215  }
216  syncer::ReadTransaction trans(FROM_HERE, sync_service_->GetUserShare());
217
218  syncer::ReadNode password_node(&trans);
219  if (password_node.InitByIdLookup(password_sync_id) !=
220          syncer::BaseNode::INIT_OK) {
221    LOG(ERROR) << "Server did not create the top-level password node. We "
222               << "might be running against an out-of-date server.";
223    return false;
224  }
225
226  // The sync model has user created nodes if the password folder has any
227  // children.
228  *has_nodes = password_node.HasChildren();
229  return true;
230}
231
232void PasswordModelAssociator::AbortAssociation() {
233  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
234  base::AutoLock lock(association_lock_);
235  abort_association_requested_ = true;
236}
237
238bool PasswordModelAssociator::CryptoReadyIfNecessary() {
239  // We only access the cryptographer while holding a transaction.
240  syncer::ReadTransaction trans(FROM_HERE, sync_service_->GetUserShare());
241  // We always encrypt passwords, so no need to check if encryption is enabled.
242  return sync_service_->IsCryptographerReady(&trans);
243}
244
245const std::string* PasswordModelAssociator::GetChromeNodeFromSyncId(
246    int64 sync_id) {
247  return NULL;
248}
249
250bool PasswordModelAssociator::InitSyncNodeFromChromeId(
251    const std::string& node_id,
252    syncer::BaseNode* sync_node) {
253  return false;
254}
255
256int64 PasswordModelAssociator::GetSyncIdFromChromeId(
257    const std::string& password) {
258  PasswordToSyncIdMap::const_iterator iter = id_map_.find(password);
259  return iter == id_map_.end() ? syncer::kInvalidId : iter->second;
260}
261
262void PasswordModelAssociator::Associate(
263    const std::string* password, int64 sync_id) {
264  DCHECK(expected_loop_ == base::MessageLoop::current());
265  DCHECK_NE(syncer::kInvalidId, sync_id);
266  DCHECK(id_map_.find(*password) == id_map_.end());
267  DCHECK(id_map_inverse_.find(sync_id) == id_map_inverse_.end());
268  id_map_[*password] = sync_id;
269  id_map_inverse_[sync_id] = *password;
270}
271
272void PasswordModelAssociator::Disassociate(int64 sync_id) {
273  DCHECK(expected_loop_ == base::MessageLoop::current());
274  SyncIdToPasswordMap::iterator iter = id_map_inverse_.find(sync_id);
275  if (iter == id_map_inverse_.end())
276    return;
277  CHECK(id_map_.erase(iter->second));
278  id_map_inverse_.erase(iter);
279}
280
281bool PasswordModelAssociator::GetSyncIdForTaggedNode(const std::string& tag,
282                                                     int64* sync_id) {
283  syncer::ReadTransaction trans(FROM_HERE, sync_service_->GetUserShare());
284  syncer::ReadNode sync_node(&trans);
285  if (sync_node.InitByTagLookup(tag.c_str()) != syncer::BaseNode::INIT_OK)
286    return false;
287  *sync_id = sync_node.GetId();
288  return true;
289}
290
291syncer::SyncError PasswordModelAssociator::WriteToPasswordStore(
292    const PasswordVector* new_passwords,
293    const PasswordVector* updated_passwords,
294    const PasswordVector* deleted_passwords) {
295  if (new_passwords) {
296    for (PasswordVector::const_iterator password = new_passwords->begin();
297         password != new_passwords->end(); ++password) {
298      password_store_->AddLoginImpl(*password);
299    }
300  }
301
302  if (updated_passwords) {
303    for (PasswordVector::const_iterator password = updated_passwords->begin();
304         password != updated_passwords->end(); ++password) {
305      password_store_->UpdateLoginImpl(*password);
306    }
307  }
308
309  if (deleted_passwords) {
310    for (PasswordVector::const_iterator password = deleted_passwords->begin();
311         password != deleted_passwords->end(); ++password) {
312      password_store_->RemoveLoginImpl(*password);
313    }
314  }
315
316  if (new_passwords || updated_passwords || deleted_passwords) {
317    // We have to notify password store observers of the change by hand since
318    // we use internal password store interfaces to make changes synchronously.
319    password_store_->PostNotifyLoginsChanged();
320  }
321  return syncer::SyncError();
322}
323
324// static
325void PasswordModelAssociator::CopyPassword(
326        const sync_pb::PasswordSpecificsData& password,
327        content::PasswordForm* new_password) {
328  new_password->scheme =
329      static_cast<content::PasswordForm::Scheme>(password.scheme());
330  new_password->signon_realm = password.signon_realm();
331  new_password->origin = GURL(password.origin());
332  new_password->action = GURL(password.action());
333  new_password->username_element =
334      UTF8ToUTF16(password.username_element());
335  new_password->password_element =
336      UTF8ToUTF16(password.password_element());
337  new_password->username_value =
338      UTF8ToUTF16(password.username_value());
339  new_password->password_value =
340      UTF8ToUTF16(password.password_value());
341  new_password->ssl_valid = password.ssl_valid();
342  new_password->preferred = password.preferred();
343  new_password->date_created =
344      base::Time::FromInternalValue(password.date_created());
345  new_password->blacklisted_by_user =
346      password.blacklisted();
347}
348
349// static
350bool PasswordModelAssociator::MergePasswords(
351        const sync_pb::PasswordSpecificsData& password,
352        const content::PasswordForm& password_form,
353        content::PasswordForm* new_password) {
354  DCHECK(new_password);
355
356  if (password.scheme() == password_form.scheme &&
357      password_form.signon_realm == password.signon_realm() &&
358      password_form.origin.spec() == password.origin() &&
359      password_form.action.spec() == password.action() &&
360      UTF16ToUTF8(password_form.username_element) ==
361          password.username_element() &&
362      UTF16ToUTF8(password_form.password_element) ==
363          password.password_element() &&
364      UTF16ToUTF8(password_form.username_value) ==
365          password.username_value() &&
366      UTF16ToUTF8(password_form.password_value) ==
367          password.password_value() &&
368      password.ssl_valid() == password_form.ssl_valid &&
369      password.preferred() == password_form.preferred &&
370      password.date_created() == password_form.date_created.ToInternalValue() &&
371      password.blacklisted() == password_form.blacklisted_by_user) {
372    return false;
373  }
374
375  // If the passwords differ, we take the one that was created more recently.
376  if (base::Time::FromInternalValue(password.date_created()) <=
377      password_form.date_created) {
378    *new_password = password_form;
379  } else {
380    CopyPassword(password, new_password);
381  }
382
383  return true;
384}
385
386// static
387void PasswordModelAssociator::WriteToSyncNode(
388         const content::PasswordForm& password_form,
389         syncer::WriteNode* node) {
390  sync_pb::PasswordSpecificsData password;
391  password.set_scheme(password_form.scheme);
392  password.set_signon_realm(password_form.signon_realm);
393  password.set_origin(password_form.origin.spec());
394  password.set_action(password_form.action.spec());
395  password.set_username_element(UTF16ToUTF8(password_form.username_element));
396  password.set_password_element(UTF16ToUTF8(password_form.password_element));
397  password.set_username_value(UTF16ToUTF8(password_form.username_value));
398  password.set_password_value(UTF16ToUTF8(password_form.password_value));
399  password.set_ssl_valid(password_form.ssl_valid);
400  password.set_preferred(password_form.preferred);
401  password.set_date_created(password_form.date_created.ToInternalValue());
402  password.set_blacklisted(password_form.blacklisted_by_user);
403
404  node->SetPasswordSpecifics(password);
405}
406
407// static
408std::string PasswordModelAssociator::MakeTag(
409                const content::PasswordForm& password) {
410  return MakeTag(password.origin.spec(),
411                 UTF16ToUTF8(password.username_element),
412                 UTF16ToUTF8(password.username_value),
413                 UTF16ToUTF8(password.password_element),
414                 password.signon_realm);
415}
416
417// static
418std::string PasswordModelAssociator::MakeTag(
419                const sync_pb::PasswordSpecificsData& password) {
420  return MakeTag(password.origin(),
421                 password.username_element(),
422                 password.username_value(),
423                 password.password_element(),
424                 password.signon_realm());
425}
426
427// static
428std::string PasswordModelAssociator::MakeTag(
429    const std::string& origin_url,
430    const std::string& username_element,
431    const std::string& username_value,
432    const std::string& password_element,
433    const std::string& signon_realm) {
434  return net::EscapePath(origin_url) + "|" +
435         net::EscapePath(username_element) + "|" +
436         net::EscapePath(username_value) + "|" +
437         net::EscapePath(password_element) + "|" +
438         net::EscapePath(signon_realm);
439}
440
441}  // namespace browser_sync
442