login_database.cc revision f8ee788a64d60abd8f2d742a5fdedde054ecd910
1// Copyright 2014 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 "components/password_manager/core/browser/login_database.h"
6
7#include <algorithm>
8#include <limits>
9
10#include "base/bind.h"
11#include "base/files/file_path.h"
12#include "base/logging.h"
13#include "base/metrics/histogram.h"
14#include "base/pickle.h"
15#include "base/strings/string_util.h"
16#include "base/time/time.h"
17#include "components/autofill/core/common/password_form.h"
18#include "sql/connection.h"
19#include "sql/statement.h"
20#include "sql/transaction.h"
21
22using autofill::PasswordForm;
23
24namespace password_manager {
25
26static const int kCurrentVersionNumber = 6;
27static const int kCompatibleVersionNumber = 1;
28
29Pickle SerializeVector(const std::vector<base::string16>& vec) {
30  Pickle p;
31  for (size_t i = 0; i < vec.size(); ++i) {
32    p.WriteString16(vec[i]);
33  }
34  return p;
35}
36
37std::vector<base::string16> DeserializeVector(const Pickle& p) {
38  std::vector<base::string16> ret;
39  base::string16 str;
40
41  PickleIterator iterator(p);
42  while (iterator.ReadString16(&str)) {
43    ret.push_back(str);
44  }
45  return ret;
46}
47
48namespace {
49
50// Convenience enum for interacting with SQL queries that use all the columns.
51enum LoginTableColumns {
52  COLUMN_ORIGIN_URL = 0,
53  COLUMN_ACTION_URL,
54  COLUMN_USERNAME_ELEMENT,
55  COLUMN_USERNAME_VALUE,
56  COLUMN_PASSWORD_ELEMENT,
57  COLUMN_PASSWORD_VALUE,
58  COLUMN_SUBMIT_ELEMENT,
59  COLUMN_SIGNON_REALM,
60  COLUMN_SSL_VALID,
61  COLUMN_PREFERRED,
62  COLUMN_DATE_CREATED,
63  COLUMN_BLACKLISTED_BY_USER,
64  COLUMN_SCHEME,
65  COLUMN_PASSWORD_TYPE,
66  COLUMN_POSSIBLE_USERNAMES,
67  COLUMN_TIMES_USED,
68  COLUMN_FORM_DATA,
69  COLUMN_USE_ADDITIONAL_AUTH,
70  COLUMN_DATE_SYNCED
71};
72
73void BindAddStatement(const PasswordForm& form,
74                      const std::string& encrypted_password,
75                      sql::Statement* s) {
76  s->BindString(COLUMN_ORIGIN_URL, form.origin.spec());
77  s->BindString(COLUMN_ACTION_URL, form.action.spec());
78  s->BindString16(COLUMN_USERNAME_ELEMENT, form.username_element);
79  s->BindString16(COLUMN_USERNAME_VALUE, form.username_value);
80  s->BindString16(COLUMN_PASSWORD_ELEMENT, form.password_element);
81  s->BindBlob(COLUMN_PASSWORD_VALUE, encrypted_password.data(),
82              static_cast<int>(encrypted_password.length()));
83  s->BindString16(COLUMN_SUBMIT_ELEMENT, form.submit_element);
84  s->BindString(COLUMN_SIGNON_REALM, form.signon_realm);
85  s->BindInt(COLUMN_SSL_VALID, form.ssl_valid);
86  s->BindInt(COLUMN_PREFERRED, form.preferred);
87  s->BindInt64(COLUMN_DATE_CREATED, form.date_created.ToTimeT());
88  s->BindInt(COLUMN_BLACKLISTED_BY_USER, form.blacklisted_by_user);
89  s->BindInt(COLUMN_SCHEME, form.scheme);
90  s->BindInt(COLUMN_PASSWORD_TYPE, form.type);
91  Pickle usernames_pickle = SerializeVector(form.other_possible_usernames);
92  s->BindBlob(COLUMN_POSSIBLE_USERNAMES,
93              usernames_pickle.data(),
94              usernames_pickle.size());
95  s->BindInt(COLUMN_TIMES_USED, form.times_used);
96  Pickle form_data_pickle;
97  autofill::SerializeFormData(form.form_data, &form_data_pickle);
98  s->BindBlob(COLUMN_FORM_DATA,
99              form_data_pickle.data(),
100              form_data_pickle.size());
101  s->BindInt(COLUMN_USE_ADDITIONAL_AUTH, form.use_additional_authentication);
102  s->BindInt64(COLUMN_DATE_SYNCED, form.date_synced.ToInternalValue());
103}
104
105void AddCallback(int err, sql::Statement* /*stmt*/) {
106  if (err == 19 /*SQLITE_CONSTRAINT*/)
107    DLOG(WARNING) << "LoginDatabase::AddLogin updated an existing form";
108}
109
110}  // namespace
111
112LoginDatabase::LoginDatabase() {
113}
114
115LoginDatabase::~LoginDatabase() {
116}
117
118bool LoginDatabase::Init(const base::FilePath& db_path) {
119  // Set pragmas for a small, private database (based on WebDatabase).
120  db_.set_page_size(2048);
121  db_.set_cache_size(32);
122  db_.set_exclusive_locking();
123  db_.set_restrict_to_user();
124
125  if (!db_.Open(db_path)) {
126    LOG(WARNING) << "Unable to open the password store database.";
127    return false;
128  }
129
130  sql::Transaction transaction(&db_);
131  transaction.Begin();
132
133  // Check the database version.
134  if (!meta_table_.Init(&db_, kCurrentVersionNumber,
135                        kCompatibleVersionNumber)) {
136    db_.Close();
137    return false;
138  }
139  if (meta_table_.GetCompatibleVersionNumber() > kCurrentVersionNumber) {
140    LOG(WARNING) << "Password store database is too new.";
141    db_.Close();
142    return false;
143  }
144
145  // Initialize the tables.
146  if (!InitLoginsTable()) {
147    LOG(WARNING) << "Unable to initialize the password store database.";
148    db_.Close();
149    return false;
150  }
151
152  // Save the path for DeleteDatabaseFile().
153  db_path_ = db_path;
154
155  // If the file on disk is an older database version, bring it up to date.
156  if (!MigrateOldVersionsAsNeeded()) {
157    LOG(WARNING) << "Unable to migrate database";
158    db_.Close();
159    return false;
160  }
161
162  if (!transaction.Commit()) {
163    db_.Close();
164    return false;
165  }
166
167  return true;
168}
169
170bool LoginDatabase::MigrateOldVersionsAsNeeded() {
171  switch (meta_table_.GetVersionNumber()) {
172    case 1:
173      if (!db_.Execute("ALTER TABLE logins "
174                       "ADD COLUMN password_type INTEGER") ||
175          !db_.Execute("ALTER TABLE logins "
176                       "ADD COLUMN possible_usernames BLOB")) {
177        return false;
178      }
179      meta_table_.SetVersionNumber(2);
180      // Fall through.
181    case 2:
182      if (!db_.Execute("ALTER TABLE logins ADD COLUMN times_used INTEGER")) {
183        return false;
184      }
185      meta_table_.SetVersionNumber(3);
186      // Fall through.
187    case 3:
188      // We need to check if the column exists because of
189      // https://crbug.com/295851
190      if (!db_.DoesColumnExist("logins", "form_data") &&
191          !db_.Execute("ALTER TABLE logins ADD COLUMN form_data BLOB")) {
192        return false;
193      }
194      meta_table_.SetVersionNumber(4);
195      // Fall through.
196    case 4:
197      if (!db_.Execute(
198          "ALTER TABLE logins ADD COLUMN use_additional_auth INTEGER")) {
199        return false;
200      }
201      meta_table_.SetVersionNumber(5);
202      // Fall through.
203    case 5:
204      if (!db_.Execute(
205          "ALTER TABLE logins ADD COLUMN date_synced INTEGER")) {
206        return false;
207      }
208      meta_table_.SetVersionNumber(6);
209      // Fall through.
210    case kCurrentVersionNumber:
211      // Already up to date
212      return true;
213    default:
214      NOTREACHED();
215      return false;
216  }
217}
218
219bool LoginDatabase::InitLoginsTable() {
220  if (!db_.DoesTableExist("logins")) {
221    if (!db_.Execute("CREATE TABLE logins ("
222                     "origin_url VARCHAR NOT NULL, "
223                     "action_url VARCHAR, "
224                     "username_element VARCHAR, "
225                     "username_value VARCHAR, "
226                     "password_element VARCHAR, "
227                     "password_value BLOB, "
228                     "submit_element VARCHAR, "
229                     "signon_realm VARCHAR NOT NULL,"
230                     "ssl_valid INTEGER NOT NULL,"
231                     "preferred INTEGER NOT NULL,"
232                     "date_created INTEGER NOT NULL,"
233                     "blacklisted_by_user INTEGER NOT NULL,"
234                     "scheme INTEGER NOT NULL,"
235                     "password_type INTEGER,"
236                     "possible_usernames BLOB,"
237                     "times_used INTEGER,"
238                     "form_data BLOB,"
239                     "use_additional_auth INTEGER,"
240                     "date_synced INTEGER,"
241                     "UNIQUE "
242                     "(origin_url, username_element, "
243                     "username_value, password_element, "
244                     "submit_element, signon_realm))")) {
245      NOTREACHED();
246      return false;
247    }
248    if (!db_.Execute("CREATE INDEX logins_signon ON "
249                     "logins (signon_realm)")) {
250      NOTREACHED();
251      return false;
252    }
253  }
254  return true;
255}
256
257void LoginDatabase::ReportMetrics() {
258  sql::Statement s(db_.GetCachedStatement(
259      SQL_FROM_HERE,
260      "SELECT signon_realm, blacklisted_by_user, COUNT(username_value) "
261      "FROM logins GROUP BY signon_realm, blacklisted_by_user"));
262
263  if (!s.is_valid())
264    return;
265
266  int total_accounts = 0;
267  int blacklisted_sites = 0;
268  while (s.Step()) {
269    int blacklisted = s.ColumnInt(1);
270    int accounts_per_site = s.ColumnInt(2);
271    if (blacklisted) {
272      ++blacklisted_sites;
273    } else {
274      total_accounts += accounts_per_site;
275      UMA_HISTOGRAM_CUSTOM_COUNTS("PasswordManager.AccountsPerSite",
276                                  accounts_per_site, 0, 32, 6);
277    }
278  }
279  UMA_HISTOGRAM_CUSTOM_COUNTS("PasswordManager.TotalAccounts",
280                              total_accounts, 0, 32, 6);
281  UMA_HISTOGRAM_CUSTOM_COUNTS("PasswordManager.BlacklistedSites",
282                              blacklisted_sites, 0, 32, 6);
283
284  sql::Statement usage_statement(db_.GetCachedStatement(
285      SQL_FROM_HERE,
286      "SELECT password_type, times_used FROM logins"));
287
288  if (!usage_statement.is_valid())
289    return;
290
291  while (usage_statement.Step()) {
292    PasswordForm::Type type = static_cast<PasswordForm::Type>(
293        usage_statement.ColumnInt(0));
294
295    if (type == PasswordForm::TYPE_GENERATED) {
296      UMA_HISTOGRAM_CUSTOM_COUNTS(
297          "PasswordManager.TimesGeneratedPasswordUsed",
298          usage_statement.ColumnInt(1), 0, 100, 10);
299    } else {
300      UMA_HISTOGRAM_CUSTOM_COUNTS(
301          "PasswordManager.TimesPasswordUsed",
302          usage_statement.ColumnInt(1), 0, 100, 10);
303    }
304  }
305}
306
307PasswordStoreChangeList LoginDatabase::AddLogin(const PasswordForm& form) {
308  PasswordStoreChangeList list;
309  std::string encrypted_password;
310  if (EncryptedString(form.password_value, &encrypted_password) !=
311          ENCRYPTION_RESULT_SUCCESS)
312    return list;
313
314  // You *must* change LoginTableColumns if this query changes.
315  sql::Statement s(db_.GetCachedStatement(SQL_FROM_HERE,
316      "INSERT INTO logins "
317      "(origin_url, action_url, username_element, username_value, "
318      " password_element, password_value, submit_element, "
319      " signon_realm, ssl_valid, preferred, date_created, blacklisted_by_user, "
320      " scheme, password_type, possible_usernames, times_used, form_data, "
321      " use_additional_auth, date_synced) VALUES "
322      "(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"));
323  BindAddStatement(form, encrypted_password, &s);
324  db_.set_error_callback(base::Bind(&AddCallback));
325  const bool success = s.Run();
326  db_.reset_error_callback();
327  if (success) {
328    list.push_back(PasswordStoreChange(PasswordStoreChange::ADD, form));
329    return list;
330  }
331  // Repeat the same statement but with REPLACE semantic.
332  s.Assign(db_.GetCachedStatement(SQL_FROM_HERE,
333      "INSERT OR REPLACE INTO logins "
334      "(origin_url, action_url, username_element, username_value, "
335      " password_element, password_value, submit_element, "
336      " signon_realm, ssl_valid, preferred, date_created, blacklisted_by_user, "
337      " scheme, password_type, possible_usernames, times_used, form_data, "
338      " use_additional_auth, date_synced) VALUES "
339      "(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"));
340  BindAddStatement(form, encrypted_password, &s);
341  if (s.Run()) {
342    list.push_back(PasswordStoreChange(PasswordStoreChange::REMOVE, form));
343    list.push_back(PasswordStoreChange(PasswordStoreChange::ADD, form));
344  }
345  return list;
346}
347
348PasswordStoreChangeList LoginDatabase::UpdateLogin(const PasswordForm& form) {
349  std::string encrypted_password;
350  if (EncryptedString(form.password_value, &encrypted_password) !=
351          ENCRYPTION_RESULT_SUCCESS)
352    return PasswordStoreChangeList();
353
354  // Replacement is necessary to deal with updating imported credentials. See
355  // crbug.com/349138 for details.
356  sql::Statement s(db_.GetCachedStatement(SQL_FROM_HERE,
357                                          "UPDATE OR REPLACE logins SET "
358                                          "action_url = ?, "
359                                          "password_value = ?, "
360                                          "ssl_valid = ?, "
361                                          "preferred = ?, "
362                                          "possible_usernames = ?, "
363                                          "times_used = ?, "
364                                          "submit_element = ?, "
365                                          "date_synced = ? "
366                                          "WHERE origin_url = ? AND "
367                                          "username_element = ? AND "
368                                          "username_value = ? AND "
369                                          "password_element = ? AND "
370                                          "signon_realm = ?"));
371  s.BindString(0, form.action.spec());
372  s.BindBlob(1, encrypted_password.data(),
373             static_cast<int>(encrypted_password.length()));
374  s.BindInt(2, form.ssl_valid);
375  s.BindInt(3, form.preferred);
376  Pickle pickle = SerializeVector(form.other_possible_usernames);
377  s.BindBlob(4, pickle.data(), pickle.size());
378  s.BindInt(5, form.times_used);
379  s.BindString16(6, form.submit_element);
380  s.BindInt64(7, form.date_synced.ToInternalValue());
381
382  s.BindString(8, form.origin.spec());
383  s.BindString16(9, form.username_element);
384  s.BindString16(10, form.username_value);
385  s.BindString16(11, form.password_element);
386  s.BindString(12, form.signon_realm);
387
388  if (!s.Run())
389    return PasswordStoreChangeList();
390
391  PasswordStoreChangeList list;
392  if (db_.GetLastChangeCount())
393    list.push_back(PasswordStoreChange(PasswordStoreChange::UPDATE, form));
394
395  return list;
396}
397
398bool LoginDatabase::RemoveLogin(const PasswordForm& form) {
399  // Remove a login by UNIQUE-constrained fields.
400  sql::Statement s(db_.GetCachedStatement(SQL_FROM_HERE,
401      "DELETE FROM logins WHERE "
402      "origin_url = ? AND "
403      "username_element = ? AND "
404      "username_value = ? AND "
405      "password_element = ? AND "
406      "submit_element = ? AND "
407      "signon_realm = ? "));
408  s.BindString(0, form.origin.spec());
409  s.BindString16(1, form.username_element);
410  s.BindString16(2, form.username_value);
411  s.BindString16(3, form.password_element);
412  s.BindString16(4, form.submit_element);
413  s.BindString(5, form.signon_realm);
414
415  return s.Run();
416}
417
418bool LoginDatabase::RemoveLoginsCreatedBetween(const base::Time delete_begin,
419                                               const base::Time delete_end) {
420  sql::Statement s(db_.GetCachedStatement(SQL_FROM_HERE,
421      "DELETE FROM logins WHERE "
422      "date_created >= ? AND date_created < ?"));
423  s.BindInt64(0, delete_begin.ToTimeT());
424  s.BindInt64(1, delete_end.is_null() ? std::numeric_limits<int64>::max()
425                                      : delete_end.ToTimeT());
426
427  return s.Run();
428}
429
430bool LoginDatabase::RemoveLoginsSyncedBetween(base::Time delete_begin,
431                                              base::Time delete_end) {
432  sql::Statement s(db_.GetCachedStatement(
433      SQL_FROM_HERE,
434      "DELETE FROM logins WHERE date_synced >= ? AND date_synced < ?"));
435  s.BindInt64(0, delete_begin.ToInternalValue());
436  s.BindInt64(1,
437              delete_end.is_null() ? base::Time::Max().ToInternalValue()
438                                   : delete_end.ToInternalValue());
439
440  return s.Run();
441}
442
443LoginDatabase::EncryptionResult LoginDatabase::InitPasswordFormFromStatement(
444    PasswordForm* form,
445    sql::Statement& s) const {
446  std::string encrypted_password;
447  s.ColumnBlobAsString(COLUMN_PASSWORD_VALUE, &encrypted_password);
448  base::string16 decrypted_password;
449  EncryptionResult encryption_result =
450      DecryptedString(encrypted_password, &decrypted_password);
451  if (encryption_result != ENCRYPTION_RESULT_SUCCESS)
452    return encryption_result;
453
454  std::string tmp = s.ColumnString(COLUMN_ORIGIN_URL);
455  form->origin = GURL(tmp);
456  tmp = s.ColumnString(COLUMN_ACTION_URL);
457  form->action = GURL(tmp);
458  form->username_element = s.ColumnString16(COLUMN_USERNAME_ELEMENT);
459  form->username_value = s.ColumnString16(COLUMN_USERNAME_VALUE);
460  form->password_element = s.ColumnString16(COLUMN_PASSWORD_ELEMENT);
461  form->password_value = decrypted_password;
462  form->submit_element = s.ColumnString16(COLUMN_SUBMIT_ELEMENT);
463  tmp = s.ColumnString(COLUMN_SIGNON_REALM);
464  form->signon_realm = tmp;
465  form->ssl_valid = (s.ColumnInt(COLUMN_SSL_VALID) > 0);
466  form->preferred = (s.ColumnInt(COLUMN_PREFERRED) > 0);
467  form->date_created = base::Time::FromTimeT(
468      s.ColumnInt64(COLUMN_DATE_CREATED));
469  form->blacklisted_by_user = (s.ColumnInt(COLUMN_BLACKLISTED_BY_USER) > 0);
470  int scheme_int = s.ColumnInt(COLUMN_SCHEME);
471  DCHECK((scheme_int >= 0) && (scheme_int <= PasswordForm::SCHEME_OTHER));
472  form->scheme = static_cast<PasswordForm::Scheme>(scheme_int);
473  int type_int = s.ColumnInt(COLUMN_PASSWORD_TYPE);
474  DCHECK(type_int >= 0 && type_int <= PasswordForm::TYPE_GENERATED);
475  form->type = static_cast<PasswordForm::Type>(type_int);
476  if (s.ColumnByteLength(COLUMN_POSSIBLE_USERNAMES)) {
477    Pickle pickle(
478        static_cast<const char*>(s.ColumnBlob(COLUMN_POSSIBLE_USERNAMES)),
479        s.ColumnByteLength(COLUMN_POSSIBLE_USERNAMES));
480    form->other_possible_usernames = DeserializeVector(pickle);
481  }
482  form->times_used = s.ColumnInt(COLUMN_TIMES_USED);
483  if (s.ColumnByteLength(COLUMN_FORM_DATA)) {
484    Pickle form_data_pickle(
485        static_cast<const char*>(s.ColumnBlob(COLUMN_FORM_DATA)),
486        s.ColumnByteLength(COLUMN_FORM_DATA));
487    PickleIterator form_data_iter(form_data_pickle);
488    autofill::DeserializeFormData(&form_data_iter, &form->form_data);
489  }
490  form->use_additional_authentication =
491      (s.ColumnInt(COLUMN_USE_ADDITIONAL_AUTH) > 0);
492  form->date_synced = base::Time::FromInternalValue(
493      s.ColumnInt64(COLUMN_DATE_SYNCED));
494  return ENCRYPTION_RESULT_SUCCESS;
495}
496
497bool LoginDatabase::GetLogins(const PasswordForm& form,
498                              std::vector<PasswordForm*>* forms) const {
499  DCHECK(forms);
500  // You *must* change LoginTableColumns if this query changes.
501  const std::string sql_query = "SELECT origin_url, action_url, "
502      "username_element, username_value, "
503      "password_element, password_value, submit_element, "
504      "signon_realm, ssl_valid, preferred, date_created, blacklisted_by_user, "
505      "scheme, password_type, possible_usernames, times_used, form_data, "
506      "use_additional_auth, date_synced FROM logins WHERE signon_realm == ? ";
507  sql::Statement s;
508  const GURL signon_realm(form.signon_realm);
509  std::string registered_domain =
510      PSLMatchingHelper::GetRegistryControlledDomain(signon_realm);
511  PSLMatchingHelper::PSLDomainMatchMetric psl_domain_match_metric =
512      PSLMatchingHelper::PSL_DOMAIN_MATCH_NONE;
513  // PSL matching only applies to HTML forms.
514  if (form.scheme == PasswordForm::SCHEME_HTML &&
515      psl_helper_.ShouldPSLDomainMatchingApply(registered_domain)) {
516    // We are extending the original SQL query with one that includes more
517    // possible matches based on public suffix domain matching. Using a regexp
518    // here is just an optimization to not have to parse all the stored entries
519    // in the |logins| table. The result (scheme, domain and port) is verified
520    // further down using GURL. See the functions SchemeMatches,
521    // RegistryControlledDomainMatches and PortMatches.
522    const std::string extended_sql_query =
523        sql_query + "OR signon_realm REGEXP ? ";
524    // TODO(nyquist) Re-enable usage of GetCachedStatement when
525    // http://crbug.com/248608 is fixed.
526    s.Assign(db_.GetUniqueStatement(extended_sql_query.c_str()));
527    // We need to escape . in the domain. Since the domain has already been
528    // sanitized using GURL, we do not need to escape any other characters.
529    base::ReplaceChars(registered_domain, ".", "\\.", &registered_domain);
530    std::string scheme = signon_realm.scheme();
531    // We need to escape . in the scheme. Since the scheme has already been
532    // sanitized using GURL, we do not need to escape any other characters.
533    // The scheme soap.beep is an example with '.'.
534    base::ReplaceChars(scheme, ".", "\\.", &scheme);
535    const std::string port = signon_realm.port();
536    // For a signon realm such as http://foo.bar/, this regexp will match
537    // domains on the form http://foo.bar/, http://www.foo.bar/,
538    // http://www.mobile.foo.bar/. It will not match http://notfoo.bar/.
539    // The scheme and port has to be the same as the observed form.
540    std::string regexp = "^(" + scheme + ":\\/\\/)([\\w-]+\\.)*" +
541                         registered_domain + "(:" + port + ")?\\/$";
542    s.BindString(0, form.signon_realm);
543    s.BindString(1, regexp);
544  } else {
545    psl_domain_match_metric = PSLMatchingHelper::PSL_DOMAIN_MATCH_DISABLED;
546    s.Assign(db_.GetCachedStatement(SQL_FROM_HERE, sql_query.c_str()));
547    s.BindString(0, form.signon_realm);
548  }
549
550  while (s.Step()) {
551    scoped_ptr<PasswordForm> new_form(new PasswordForm());
552    EncryptionResult result = InitPasswordFormFromStatement(new_form.get(), s);
553    if (result == ENCRYPTION_RESULT_SERVICE_FAILURE)
554      return false;
555    if (result == ENCRYPTION_RESULT_ITEM_FAILURE)
556      continue;
557    DCHECK(result == ENCRYPTION_RESULT_SUCCESS);
558    if (psl_helper_.IsMatchingEnabled()) {
559      if (!PSLMatchingHelper::IsPublicSuffixDomainMatch(new_form->signon_realm,
560                                                        form.signon_realm)) {
561        // The database returned results that should not match. Skipping result.
562        continue;
563      }
564      if (form.signon_realm != new_form->signon_realm) {
565        // Ignore non-HTML matches.
566        if (new_form->scheme != PasswordForm::SCHEME_HTML)
567          continue;
568
569        psl_domain_match_metric = PSLMatchingHelper::PSL_DOMAIN_MATCH_FOUND;
570        // This is not a perfect match, so we need to create a new valid result.
571        // We do this by copying over origin, signon realm and action from the
572        // observed form and setting the original signon realm to what we found
573        // in the database. We use the fact that |original_signon_realm| is
574        // non-empty to communicate that this match was found using public
575        // suffix matching.
576        new_form->original_signon_realm = new_form->signon_realm;
577        new_form->origin = form.origin;
578        new_form->signon_realm = form.signon_realm;
579        new_form->action = form.action;
580      }
581    }
582    forms->push_back(new_form.release());
583  }
584  UMA_HISTOGRAM_ENUMERATION("PasswordManager.PslDomainMatchTriggering",
585                            psl_domain_match_metric,
586                            PSLMatchingHelper::PSL_DOMAIN_MATCH_COUNT);
587  return s.Succeeded();
588}
589
590bool LoginDatabase::GetLoginsCreatedBetween(
591    const base::Time begin,
592    const base::Time end,
593    std::vector<autofill::PasswordForm*>* forms) const {
594  DCHECK(forms);
595  sql::Statement s(db_.GetCachedStatement(SQL_FROM_HERE,
596      "SELECT origin_url, action_url, "
597      "username_element, username_value, "
598      "password_element, password_value, submit_element, "
599      "signon_realm, ssl_valid, preferred, date_created, blacklisted_by_user, "
600      "scheme, password_type, possible_usernames, times_used, form_data, "
601      "use_additional_auth, date_synced FROM logins "
602      "WHERE date_created >= ? AND date_created < ?"
603      "ORDER BY origin_url"));
604  s.BindInt64(0, begin.ToTimeT());
605  s.BindInt64(1, end.is_null() ? std::numeric_limits<int64>::max()
606                               : end.ToTimeT());
607
608  while (s.Step()) {
609    scoped_ptr<PasswordForm> new_form(new PasswordForm());
610    EncryptionResult result = InitPasswordFormFromStatement(new_form.get(), s);
611    if (result == ENCRYPTION_RESULT_SERVICE_FAILURE)
612      return false;
613    if (result == ENCRYPTION_RESULT_ITEM_FAILURE)
614      continue;
615    DCHECK(result == ENCRYPTION_RESULT_SUCCESS);
616    forms->push_back(new_form.release());
617  }
618  return s.Succeeded();
619}
620
621bool LoginDatabase::GetLoginsSyncedBetween(
622    const base::Time begin,
623    const base::Time end,
624    std::vector<autofill::PasswordForm*>* forms) const {
625  DCHECK(forms);
626  sql::Statement s(db_.GetCachedStatement(
627      SQL_FROM_HERE,
628      "SELECT origin_url, action_url, username_element, username_value, "
629      "password_element, password_value, submit_element, signon_realm, "
630      "ssl_valid, preferred, date_created, blacklisted_by_user, "
631      "scheme, password_type, possible_usernames, times_used, form_data, "
632      "use_additional_auth, date_synced FROM logins "
633      "WHERE date_synced >= ? AND date_synced < ?"
634      "ORDER BY origin_url"));
635  s.BindInt64(0, begin.ToInternalValue());
636  s.BindInt64(1,
637              end.is_null() ? base::Time::Max().ToInternalValue()
638                            : end.ToInternalValue());
639
640  while (s.Step()) {
641    scoped_ptr<PasswordForm> new_form(new PasswordForm());
642    EncryptionResult result = InitPasswordFormFromStatement(new_form.get(), s);
643    if (result == ENCRYPTION_RESULT_SERVICE_FAILURE)
644      return false;
645    if (result == ENCRYPTION_RESULT_ITEM_FAILURE)
646      continue;
647    DCHECK(result == ENCRYPTION_RESULT_SUCCESS);
648    forms->push_back(new_form.release());
649  }
650  return s.Succeeded();
651}
652
653bool LoginDatabase::GetAutofillableLogins(
654    std::vector<PasswordForm*>* forms) const {
655  return GetAllLoginsWithBlacklistSetting(false, forms);
656}
657
658bool LoginDatabase::GetBlacklistLogins(
659    std::vector<PasswordForm*>* forms) const {
660  return GetAllLoginsWithBlacklistSetting(true, forms);
661}
662
663bool LoginDatabase::GetAllLoginsWithBlacklistSetting(
664    bool blacklisted, std::vector<PasswordForm*>* forms) const {
665  DCHECK(forms);
666  // You *must* change LoginTableColumns if this query changes.
667  sql::Statement s(db_.GetCachedStatement(SQL_FROM_HERE,
668      "SELECT origin_url, action_url, "
669      "username_element, username_value, "
670      "password_element, password_value, submit_element, "
671      "signon_realm, ssl_valid, preferred, date_created, blacklisted_by_user, "
672      "scheme, password_type, possible_usernames, times_used, form_data, "
673      "use_additional_auth, date_synced FROM logins "
674      "WHERE blacklisted_by_user == ? ORDER BY origin_url"));
675  s.BindInt(0, blacklisted ? 1 : 0);
676
677  while (s.Step()) {
678    scoped_ptr<PasswordForm> new_form(new PasswordForm());
679    EncryptionResult result = InitPasswordFormFromStatement(new_form.get(), s);
680    if (result == ENCRYPTION_RESULT_SERVICE_FAILURE)
681      return false;
682    if (result == ENCRYPTION_RESULT_ITEM_FAILURE)
683      continue;
684    DCHECK(result == ENCRYPTION_RESULT_SUCCESS);
685    forms->push_back(new_form.release());
686  }
687  return s.Succeeded();
688}
689
690bool LoginDatabase::DeleteAndRecreateDatabaseFile() {
691  DCHECK(db_.is_open());
692  meta_table_.Reset();
693  db_.Close();
694  sql::Connection::Delete(db_path_);
695  return Init(db_path_);
696}
697
698}  // namespace password_manager
699