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