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