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