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, ".", "\\.", ®istered_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