Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "components/password_manager/core/browser/login_database.h" | 5 #include "components/password_manager/core/browser/login_database.h" |
| 6 | 6 |
| 7 #include <stddef.h> | 7 #include <stddef.h> |
| 8 #include <stdint.h> | 8 #include <stdint.h> |
| 9 #include <algorithm> | 9 #include <algorithm> |
| 10 #include <limits> | 10 #include <limits> |
| 11 #include <map> | 11 #include <map> |
| 12 #include <utility> | 12 #include <utility> |
| 13 | 13 |
| 14 #include "base/bind.h" | 14 #include "base/bind.h" |
| 15 #include "base/files/file_path.h" | 15 #include "base/files/file_path.h" |
| 16 #include "base/logging.h" | 16 #include "base/logging.h" |
| 17 #include "base/macros.h" | 17 #include "base/macros.h" |
| 18 #include "base/metrics/histogram_macros.h" | 18 #include "base/metrics/histogram_macros.h" |
| 19 #include "base/metrics/sparse_histogram.h" | 19 #include "base/metrics/sparse_histogram.h" |
| 20 #include "base/numerics/safe_conversions.h" | |
| 20 #include "base/pickle.h" | 21 #include "base/pickle.h" |
| 21 #include "base/stl_util.h" | 22 #include "base/stl_util.h" |
| 22 #include "base/strings/string_util.h" | 23 #include "base/strings/string_util.h" |
| 23 #include "base/strings/stringprintf.h" | 24 #include "base/strings/stringprintf.h" |
| 24 #include "base/time/time.h" | 25 #include "base/time/time.h" |
| 25 #include "build/build_config.h" | 26 #include "build/build_config.h" |
| 26 #include "components/autofill/core/common/password_form.h" | 27 #include "components/autofill/core/common/password_form.h" |
| 27 #include "components/password_manager/core/browser/affiliation_utils.h" | 28 #include "components/password_manager/core/browser/affiliation_utils.h" |
| 28 #include "components/password_manager/core/browser/password_manager_client.h" | 29 #include "components/password_manager/core/browser/password_manager_client.h" |
| 29 #include "components/password_manager/core/browser/password_manager_metrics_util .h" | 30 #include "components/password_manager/core/browser/password_manager_metrics_util .h" |
| 31 #include "components/password_manager/core/browser/sql_table_builder.h" | |
| 30 #include "google_apis/gaia/gaia_auth_util.h" | 32 #include "google_apis/gaia/gaia_auth_util.h" |
| 31 #include "google_apis/gaia/gaia_urls.h" | 33 #include "google_apis/gaia/gaia_urls.h" |
| 32 #include "sql/connection.h" | 34 #include "sql/connection.h" |
| 33 #include "sql/statement.h" | 35 #include "sql/statement.h" |
| 34 #include "sql/transaction.h" | 36 #include "sql/transaction.h" |
| 35 #include "url/origin.h" | 37 #include "url/origin.h" |
| 36 #include "url/url_constants.h" | 38 #include "url/url_constants.h" |
| 37 | 39 |
| 38 using autofill::PasswordForm; | 40 using autofill::PasswordForm; |
| 39 | 41 |
| (...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 84 COLUMN_PASSWORD_TYPE, | 86 COLUMN_PASSWORD_TYPE, |
| 85 COLUMN_POSSIBLE_USERNAMES, | 87 COLUMN_POSSIBLE_USERNAMES, |
| 86 COLUMN_TIMES_USED, | 88 COLUMN_TIMES_USED, |
| 87 COLUMN_FORM_DATA, | 89 COLUMN_FORM_DATA, |
| 88 COLUMN_DATE_SYNCED, | 90 COLUMN_DATE_SYNCED, |
| 89 COLUMN_DISPLAY_NAME, | 91 COLUMN_DISPLAY_NAME, |
| 90 COLUMN_ICON_URL, | 92 COLUMN_ICON_URL, |
| 91 COLUMN_FEDERATION_URL, | 93 COLUMN_FEDERATION_URL, |
| 92 COLUMN_SKIP_ZERO_CLICK, | 94 COLUMN_SKIP_ZERO_CLICK, |
| 93 COLUMN_GENERATION_UPLOAD_STATUS, | 95 COLUMN_GENERATION_UPLOAD_STATUS, |
| 96 COLUMN_NUM // Keep this last. | |
| 94 }; | 97 }; |
| 95 | 98 |
| 96 enum class HistogramSize { SMALL, LARGE }; | 99 enum class HistogramSize { SMALL, LARGE }; |
| 97 | 100 |
| 98 // An enum for UMA reporting. Add values to the end only. | 101 // An enum for UMA reporting. Add values to the end only. |
| 99 enum DatabaseInitError { | 102 enum DatabaseInitError { |
| 100 INIT_OK, | 103 INIT_OK, |
| 101 OPEN_FILE_ERROR, | 104 OPEN_FILE_ERROR, |
| 102 START_TRANSACTION_ERROR, | 105 START_TRANSACTION_ERROR, |
| 103 META_TABLE_INIT_ERROR, | 106 META_TABLE_INIT_ERROR, |
| (...skipping 224 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 328 LogNumberOfAccountsReusingPassword( | 331 LogNumberOfAccountsReusingPassword( |
| 329 source_realm_kind + ".OnAnyRealmWithDifferentHost", | 332 source_realm_kind + ".OnAnyRealmWithDifferentHost", |
| 330 num_accounts_for_different_domain[SCHEME_HTTP] + | 333 num_accounts_for_different_domain[SCHEME_HTTP] + |
| 331 num_accounts_for_different_domain[SCHEME_HTTPS], | 334 num_accounts_for_different_domain[SCHEME_HTTPS], |
| 332 HistogramSize::LARGE); | 335 HistogramSize::LARGE); |
| 333 } | 336 } |
| 334 } | 337 } |
| 335 } | 338 } |
| 336 } | 339 } |
| 337 | 340 |
| 338 // Creates a table named |table_name| using our current schema. | 341 // Teaches |builder| about the different DB schemes in different versions. |
| 339 bool CreateNewTable(sql::Connection* db, | 342 void InitializeBuilder(SQLTableBuilder* builder) { |
| 340 const char* table_name, | 343 // Versions 0 and 1, which are the same. |
| 341 const char* extra_columns) { | 344 builder->AddColumnToUniqueKey("origin_url", "VARCHAR NOT NULL"); |
| 342 std::string query = base::StringPrintf( | 345 builder->AddColumn("action_url", "VARCHAR"); |
| 343 "CREATE TABLE %s (" | 346 builder->AddColumnToUniqueKey("username_element", "VARCHAR"); |
| 344 "origin_url VARCHAR NOT NULL, " | 347 builder->AddColumnToUniqueKey("username_value", "VARCHAR"); |
| 345 "action_url VARCHAR, " | 348 builder->AddColumnToUniqueKey("password_element", "VARCHAR"); |
| 346 "username_element VARCHAR, " | 349 builder->AddColumn("password_value", "BLOB"); |
| 347 "username_value VARCHAR, " | 350 builder->AddColumn("submit_element", "VARCHAR"); |
| 348 "password_element VARCHAR, " | 351 builder->AddColumnToUniqueKey("signon_realm", "VARCHAR NOT NULL"); |
| 349 "password_value BLOB, " | 352 builder->AddColumn("ssl_valid", "INTEGER NOT NULL"); |
| 350 "submit_element VARCHAR, " | 353 builder->AddColumn("preferred", "INTEGER NOT NULL"); |
| 351 "signon_realm VARCHAR NOT NULL," | 354 builder->AddColumn("date_created", "INTEGER NOT NULL"); |
| 352 "ssl_valid INTEGER NOT NULL," | 355 builder->AddColumn("blacklisted_by_user", "INTEGER NOT NULL"); |
| 353 "preferred INTEGER NOT NULL," | 356 builder->AddColumn("scheme", "INTEGER NOT NULL"); |
| 354 "date_created INTEGER NOT NULL," | 357 builder->SealVersion(); |
| 355 "blacklisted_by_user INTEGER NOT NULL," | 358 unsigned version = builder->SealVersion(); |
| 356 "scheme INTEGER NOT NULL," | 359 DCHECK_EQ(1u, version); |
| 357 "password_type INTEGER," | 360 |
| 358 "possible_usernames BLOB," | 361 // Version 2. |
| 359 "times_used INTEGER," | 362 builder->AddColumn("password_type", "INTEGER"); |
| 360 "form_data BLOB," | 363 builder->AddColumn("possible_usernames", "BLOB"); |
| 361 "date_synced INTEGER," | 364 version = builder->SealVersion(); |
| 362 "display_name VARCHAR," | 365 DCHECK_EQ(2u, version); |
| 363 "icon_url VARCHAR," | 366 |
| 364 "federation_url VARCHAR," | 367 // Version 3. |
| 365 "skip_zero_click INTEGER," | 368 builder->AddColumn("times_used", "INTEGER"); |
| 366 "%s" | 369 version = builder->SealVersion(); |
| 367 "UNIQUE (origin_url, username_element, username_value, " | 370 DCHECK_EQ(3u, version); |
| 368 "password_element, signon_realm))", | 371 |
| 369 table_name, extra_columns); | 372 // Version 4. |
| 370 return db->Execute(query.c_str()); | 373 builder->AddColumn("form_data", "BLOB"); |
| 374 version = builder->SealVersion(); | |
| 375 DCHECK_EQ(4u, version); | |
| 376 | |
| 377 // Version 5. | |
| 378 builder->AddColumn("use_additional_auth", "INTEGER"); | |
| 379 version = builder->SealVersion(); | |
| 380 DCHECK_EQ(5u, version); | |
| 381 | |
| 382 // Version 6. | |
| 383 builder->AddColumn("date_synced", "INTEGER"); | |
| 384 version = builder->SealVersion(); | |
| 385 DCHECK_EQ(6u, version); | |
| 386 | |
| 387 // Version 7. | |
| 388 builder->AddColumn("display_name", "VARCHAR"); | |
| 389 builder->AddColumn("avatar_url", "VARCHAR"); | |
| 390 builder->AddColumn("federation_url", "VARCHAR"); | |
| 391 builder->AddColumn("is_zero_click", "INTEGER"); | |
| 392 version = builder->SealVersion(); | |
| 393 DCHECK_EQ(7u, version); | |
| 394 | |
| 395 // Version 8. | |
| 396 builder->SealVersion(); | |
| 397 // Version 9. | |
| 398 version = builder->SealVersion(); | |
| 399 // Version 10. | |
| 400 builder->DropColumn("use_additional_auth"); | |
| 401 version = builder->SealVersion(); | |
| 402 DCHECK_EQ(10u, version); | |
| 403 | |
| 404 // Version 11. | |
| 405 builder->RenameColumn("is_zero_click", "skip_zero_click"); | |
| 406 version = builder->SealVersion(); | |
| 407 DCHECK_EQ(11u, version); | |
| 408 | |
| 409 // Version 12. | |
| 410 builder->AddColumn("generation_upload_status", "INTEGER"); | |
| 411 version = builder->SealVersion(); | |
| 412 DCHECK_EQ(12u, version); | |
| 413 | |
| 414 // Version 13. | |
| 415 builder->SealVersion(); | |
| 416 // Version 14. | |
| 417 builder->RenameColumn("avatar_url", "icon_url"); | |
| 418 version = builder->SealVersion(); | |
| 419 DCHECK_EQ(14u, version); | |
| 420 | |
| 421 // Version 15. | |
| 422 builder->SealVersion(); | |
| 423 // Version 16. | |
| 424 builder->SealVersion(); | |
| 425 // Version 17. | |
| 426 version = builder->SealVersion(); | |
| 427 DCHECK_EQ(17u, version); | |
| 428 | |
| 429 DCHECK_EQ(static_cast<size_t>(COLUMN_NUM), builder->NumberOfColumns()) | |
| 430 << "Adjust LoginTableColumns if you change column definitions here."; | |
| 371 } | 431 } |
| 372 | 432 |
| 373 bool CreateIndexOnSignonRealm(sql::Connection* db, const char* table_name) { | 433 // Call this after having called InitializeBuilder, to migrate the database from |
| 374 std::string query = base::StringPrintf( | 434 // the current version to kCurrentVersionNumber. |
| 375 "CREATE INDEX logins_signon ON %s (signon_realm)", table_name); | 435 bool MigrateLogins(unsigned current_version, |
| 376 return db->Execute(query.c_str()); | 436 SQLTableBuilder* builder, |
| 437 sql::Connection* db) { | |
| 438 if (!builder->MigrateFrom(current_version, db)) | |
| 439 return false; | |
| 440 | |
| 441 // Data changes, not covered by the schema migration above. | |
| 442 if (current_version <= 8) { | |
| 443 sql::Statement fix_time_format; | |
| 444 fix_time_format.Assign(db->GetCachedStatement( | |
| 445 SQL_FROM_HERE, | |
| 446 "UPDATE logins SET date_created = (date_created * ?) + ?")); | |
| 447 fix_time_format.BindInt64(0, base::Time::kMicrosecondsPerSecond); | |
| 448 fix_time_format.BindInt64(1, base::Time::kTimeTToMicrosecondsOffset); | |
| 449 if (!fix_time_format.Run()) | |
| 450 return false; | |
| 451 } | |
| 452 | |
| 453 if (current_version <= 16) { | |
| 454 sql::Statement reset_zero_click; | |
| 455 reset_zero_click.Assign(db->GetCachedStatement( | |
| 456 SQL_FROM_HERE, "UPDATE logins SET skip_zero_click = 1")); | |
| 457 if (!reset_zero_click.Run()) | |
| 458 return false; | |
| 459 } | |
| 460 | |
| 461 return true; | |
| 462 } | |
| 463 | |
| 464 // Because of https://crbug.com/295851, some early version numbers might be | |
| 465 // wrong. This function detects that and fixes the version. | |
| 466 bool FixVersionIfNeeded(sql::Connection* db, int* current_version) { | |
| 467 if (*current_version == 1) { | |
| 468 int extra_columns = 0; | |
| 469 if (db->DoesColumnExist("logins", "password_type")) | |
| 470 ++extra_columns; | |
| 471 if (db->DoesColumnExist("logins", "possible_usernames")) | |
| 472 ++extra_columns; | |
| 473 if (extra_columns == 2) { | |
| 474 *current_version = 2; | |
| 475 } else if (extra_columns == 1) { | |
| 476 // If this is https://crbug.com/295851 then either both columns exist | |
| 477 // or none. | |
| 478 return false; | |
| 479 } | |
| 480 } | |
| 481 if (*current_version == 2) { | |
| 482 if (db->DoesColumnExist("logins", "times_used")) | |
| 483 *current_version = 3; | |
| 484 } | |
| 485 if (*current_version == 3) { | |
| 486 if (db->DoesColumnExist("logins", "form_data")) | |
| 487 *current_version = 4; | |
| 488 } | |
| 489 return true; | |
| 490 } | |
| 491 | |
| 492 // Generates the string "(?,?,...,?)" with |count| repetitions of "?". | |
| 493 std::string GeneratePlaceholders(size_t count) { | |
| 494 std::string result(2 * count + 1, ','); | |
| 495 result.front() = '('; | |
| 496 result.back() = ')'; | |
| 497 for (size_t i = 1; i < 2 * count + 1; i += 2) { | |
| 498 result[i] = '?'; | |
| 499 } | |
| 500 return result; | |
| 377 } | 501 } |
| 378 | 502 |
| 379 } // namespace | 503 } // namespace |
| 380 | 504 |
| 381 LoginDatabase::LoginDatabase(const base::FilePath& db_path) | 505 LoginDatabase::LoginDatabase(const base::FilePath& db_path) |
| 382 : db_path_(db_path), clear_password_values_(false) { | 506 : db_path_(db_path), clear_password_values_(false) { |
| 383 } | 507 } |
| 384 | 508 |
| 385 LoginDatabase::~LoginDatabase() { | 509 LoginDatabase::~LoginDatabase() { |
| 386 } | 510 } |
| (...skipping 30 matching lines...) Expand all Loading... | |
| 417 } | 541 } |
| 418 if (meta_table_.GetCompatibleVersionNumber() > kCurrentVersionNumber) { | 542 if (meta_table_.GetCompatibleVersionNumber() > kCurrentVersionNumber) { |
| 419 LogDatabaseInitError(INCOMPATIBLE_VERSION); | 543 LogDatabaseInitError(INCOMPATIBLE_VERSION); |
| 420 LOG(ERROR) << "Password store database is too new, kCurrentVersionNumber=" | 544 LOG(ERROR) << "Password store database is too new, kCurrentVersionNumber=" |
| 421 << kCurrentVersionNumber << ", GetCompatibleVersionNumber=" | 545 << kCurrentVersionNumber << ", GetCompatibleVersionNumber=" |
| 422 << meta_table_.GetCompatibleVersionNumber(); | 546 << meta_table_.GetCompatibleVersionNumber(); |
| 423 db_.Close(); | 547 db_.Close(); |
| 424 return false; | 548 return false; |
| 425 } | 549 } |
| 426 | 550 |
| 427 // Initialize the tables. | 551 SQLTableBuilder builder; |
| 428 if (!InitLoginsTable()) { | 552 InitializeBuilder(&builder); |
| 429 LogDatabaseInitError(INIT_LOGINS_ERROR); | 553 |
| 430 LOG(ERROR) << "Unable to initialize the logins table."; | 554 // Initialize the cached strings. |
|
dvadym
2016/07/12 14:48:58
Nit: it looks like all next strings can be moved i
vabr (Chromium)
2016/07/13 09:12:13
Done.
| |
| 431 db_.Close(); | 555 std::string all_column_names = builder.ListAllNames(); |
| 432 return false; | 556 std::string right_amount_of_placeholders = |
| 557 GeneratePlaceholders(builder.NumberOfColumns()); | |
| 558 std::string all_unique_key_column_names = builder.ListAllUniqueKeyNames(); | |
| 559 std::string all_nonunique_key_column_names = | |
| 560 builder.ListAllNonuniqueKeyNames(); | |
| 561 | |
| 562 // This method may be called multiple times, if Chrome switches backends and | |
| 563 // LoginDatabase::DeleteAndRecreateDatabaseFile ends up being called. In those | |
| 564 // case do not recompute the SQL statements, because they would end up the | |
| 565 // same. | |
| 566 if (add_statement_.empty()) { | |
|
dvadym
2016/07/12 14:48:58
Nit: I'd prefer to extract initialization *stateme
vabr (Chromium)
2016/07/13 09:12:13
Done.
| |
| 567 add_statement_ = "INSERT INTO logins (" + all_column_names + ") VALUES " + | |
| 568 right_amount_of_placeholders; | |
| 569 DCHECK(add_replace_statement_.empty()); | |
| 570 add_replace_statement_ = "INSERT OR REPLACE INTO logins (" + | |
| 571 all_column_names + ") VALUES " + | |
| 572 right_amount_of_placeholders; | |
| 573 DCHECK(update_statement_.empty()); | |
| 574 update_statement_ = "UPDATE OR REPLACE logins SET " + | |
| 575 all_nonunique_key_column_names + " WHERE " + | |
| 576 all_unique_key_column_names; | |
| 577 DCHECK(delete_statement_.empty()); | |
| 578 delete_statement_ = | |
| 579 "DELETE FROM logins WHERE " + all_unique_key_column_names; | |
| 580 DCHECK(autosignin_statement_.empty()); | |
| 581 autosignin_statement_ = "SELECT " + all_column_names + | |
| 582 " FROM logins " | |
| 583 "WHERE skip_zero_click = 0 ORDER BY origin_url"; | |
| 584 DCHECK(get_statement_.empty()); | |
| 585 get_statement_ = "SELECT " + all_column_names + | |
| 586 " FROM logins " | |
| 587 "WHERE signon_realm == ?"; | |
| 588 std::string psl_statement = "OR signon_realm REGEXP ? "; | |
| 589 std::string federated_statement = | |
| 590 "OR (signon_realm LIKE ? AND password_type == 2) "; | |
| 591 DCHECK(get_statement_psl_.empty()); | |
| 592 get_statement_psl_ = get_statement_ + psl_statement; | |
| 593 DCHECK(get_statement_federated_.empty()); | |
| 594 get_statement_federated_ = get_statement_ + federated_statement; | |
| 595 DCHECK(get_statement_psl_federated_.empty()); | |
| 596 get_statement_psl_federated_ = | |
| 597 get_statement_ + psl_statement + federated_statement; | |
| 598 DCHECK(created_statement_.empty()); | |
| 599 created_statement_ = | |
| 600 "SELECT " + all_column_names + | |
| 601 " FROM logins WHERE date_created >= ? AND date_created < " | |
| 602 "? ORDER BY origin_url"; | |
| 603 DCHECK(synced_statement_.empty()); | |
| 604 synced_statement_ = "SELECT " + all_column_names + | |
| 605 " FROM logins WHERE date_synced >= ? AND date_synced < " | |
| 606 "? ORDER BY origin_url"; | |
| 607 DCHECK(blacklisted_statement_.empty()); | |
| 608 blacklisted_statement_ = | |
| 609 "SELECT " + all_column_names + | |
| 610 " FROM logins WHERE blacklisted_by_user == ? ORDER BY origin_url"; | |
| 611 DCHECK(encrypted_statement_.empty()); | |
| 612 encrypted_statement_ = "SELECT password_value FROM logins WHERE " + | |
| 613 all_unique_key_column_names; | |
| 433 } | 614 } |
| 615 | |
| 616 if (!db_.DoesTableExist("logins")) { | |
| 617 if (!builder.CreateTable(&db_)) { | |
| 618 VLOG(0) << "Failed to create the 'logins' table"; | |
| 619 db_.Close(); | |
| 620 return false; | |
| 621 } | |
| 622 } | |
| 623 | |
| 434 stats_table_.Init(&db_); | 624 stats_table_.Init(&db_); |
| 435 | 625 |
| 626 int current_version = meta_table_.GetVersionNumber(); | |
| 627 bool migration_success = FixVersionIfNeeded(&db_, ¤t_version); | |
| 628 DCHECK_LE(current_version, kCurrentVersionNumber); | |
| 629 | |
| 436 // If the file on disk is an older database version, bring it up to date. | 630 // If the file on disk is an older database version, bring it up to date. |
| 437 if (meta_table_.GetVersionNumber() < kCurrentVersionNumber && | 631 if (migration_success && current_version < kCurrentVersionNumber) { |
| 438 !MigrateOldVersionsAsNeeded()) { | 632 migration_success = MigrateLogins( |
| 633 base::checked_cast<unsigned>(current_version), &builder, &db_); | |
| 634 } | |
| 635 if (migration_success && current_version <= 15) { | |
| 636 migration_success = stats_table_.MigrateToVersion(16); | |
| 637 } | |
| 638 if (migration_success) { | |
| 639 meta_table_.SetCompatibleVersionNumber(kCompatibleVersionNumber); | |
| 640 meta_table_.SetVersionNumber(kCurrentVersionNumber); | |
| 641 } else { | |
| 439 LogDatabaseInitError(MIGRATION_ERROR); | 642 LogDatabaseInitError(MIGRATION_ERROR); |
| 440 UMA_HISTOGRAM_SPARSE_SLOWLY("PasswordManager.LoginDatabaseFailedVersion", | 643 UMA_HISTOGRAM_SPARSE_SLOWLY("PasswordManager.LoginDatabaseFailedVersion", |
| 441 meta_table_.GetVersionNumber()); | 644 meta_table_.GetVersionNumber()); |
| 442 LOG(ERROR) << "Unable to migrate database from " | 645 LOG(ERROR) << "Unable to migrate database from " |
| 443 << meta_table_.GetVersionNumber() << " to " | 646 << meta_table_.GetVersionNumber() << " to " |
| 444 << kCurrentVersionNumber; | 647 << kCurrentVersionNumber; |
| 445 db_.Close(); | 648 db_.Close(); |
| 446 return false; | 649 return false; |
| 447 } | 650 } |
| 448 | 651 |
| 449 if (!stats_table_.CreateTableIfNecessary()) { | 652 if (!stats_table_.CreateTableIfNecessary()) { |
| 450 LogDatabaseInitError(INIT_STATS_ERROR); | 653 LogDatabaseInitError(INIT_STATS_ERROR); |
| 451 LOG(ERROR) << "Unable to create the stats table."; | 654 LOG(ERROR) << "Unable to create the stats table."; |
| 452 db_.Close(); | 655 db_.Close(); |
| 453 return false; | 656 return false; |
| 454 } | 657 } |
| 455 | 658 |
| 456 if (!transaction.Commit()) { | 659 if (!transaction.Commit()) { |
| 457 LogDatabaseInitError(COMMIT_TRANSACTION_ERROR); | 660 LogDatabaseInitError(COMMIT_TRANSACTION_ERROR); |
| 458 LOG(ERROR) << "Unable to commit a transaction."; | 661 LOG(ERROR) << "Unable to commit a transaction."; |
| 459 db_.Close(); | 662 db_.Close(); |
| 460 return false; | 663 return false; |
| 461 } | 664 } |
| 462 | 665 |
| 463 LogDatabaseInitError(INIT_OK); | 666 LogDatabaseInitError(INIT_OK); |
| 464 return true; | 667 return true; |
| 465 } | 668 } |
| 466 | 669 |
| 467 bool LoginDatabase::MigrateOldVersionsAsNeeded() { | |
| 468 const int original_version = meta_table_.GetVersionNumber(); | |
| 469 switch (original_version) { | |
| 470 case 1: | |
| 471 // Column could exist because of https://crbug.com/295851 | |
| 472 if (!db_.DoesColumnExist("logins", "password_type") && | |
| 473 !db_.Execute("ALTER TABLE logins " | |
| 474 "ADD COLUMN password_type INTEGER")) { | |
| 475 return false; | |
| 476 } | |
| 477 if (!db_.DoesColumnExist("logins", "possible_usernames") && | |
| 478 !db_.Execute("ALTER TABLE logins " | |
| 479 "ADD COLUMN possible_usernames BLOB")) { | |
| 480 return false; | |
| 481 } | |
| 482 // Fall through. | |
| 483 case 2: | |
| 484 // Column could exist because of https://crbug.com/295851 | |
| 485 if (!db_.DoesColumnExist("logins", "times_used") && | |
| 486 !db_.Execute("ALTER TABLE logins ADD COLUMN times_used INTEGER")) { | |
| 487 return false; | |
| 488 } | |
| 489 // Fall through. | |
| 490 case 3: | |
| 491 // Column could exist because of https://crbug.com/295851 | |
| 492 if (!db_.DoesColumnExist("logins", "form_data") && | |
| 493 !db_.Execute("ALTER TABLE logins ADD COLUMN form_data BLOB")) { | |
| 494 return false; | |
| 495 } | |
| 496 // Fall through. | |
| 497 case 4: | |
| 498 if (!db_.Execute( | |
| 499 "ALTER TABLE logins ADD COLUMN use_additional_auth INTEGER")) { | |
| 500 return false; | |
| 501 } | |
| 502 // Fall through. | |
| 503 case 5: | |
| 504 if (!db_.Execute("ALTER TABLE logins ADD COLUMN date_synced INTEGER")) { | |
| 505 return false; | |
| 506 } | |
| 507 // Fall through. | |
| 508 case 6: | |
| 509 if (!db_.Execute("ALTER TABLE logins ADD COLUMN display_name VARCHAR") || | |
| 510 !db_.Execute("ALTER TABLE logins ADD COLUMN avatar_url VARCHAR") || | |
| 511 !db_.Execute("ALTER TABLE logins " | |
| 512 "ADD COLUMN federation_url VARCHAR") || | |
| 513 !db_.Execute("ALTER TABLE logins ADD COLUMN is_zero_click INTEGER")) { | |
| 514 return false; | |
| 515 } | |
| 516 // Fall through. | |
| 517 case 7: { | |
| 518 // Keep version 8 around even though no changes are made. See | |
| 519 // crbug.com/423716 for context. | |
| 520 // Fall through. | |
| 521 } | |
| 522 case 8: { | |
| 523 sql::Statement s; | |
| 524 s.Assign(db_.GetCachedStatement(SQL_FROM_HERE, | |
| 525 "UPDATE logins SET " | |
| 526 "date_created = " | |
| 527 "(date_created * ?) + ?")); | |
| 528 s.BindInt64(0, base::Time::kMicrosecondsPerSecond); | |
| 529 s.BindInt64(1, base::Time::kTimeTToMicrosecondsOffset); | |
| 530 if (!s.Run()) | |
| 531 return false; | |
| 532 // Fall through. | |
| 533 } | |
| 534 case 9: { | |
| 535 // Remove use_additional_auth column from database schema | |
| 536 // crbug.com/423716 for context. | |
| 537 std::string fields_to_copy = | |
| 538 "origin_url, action_url, username_element, username_value, " | |
| 539 "password_element, password_value, submit_element, " | |
| 540 "signon_realm, ssl_valid, preferred, date_created, " | |
| 541 "blacklisted_by_user, scheme, password_type, possible_usernames, " | |
| 542 "times_used, form_data, date_synced, display_name, avatar_url, " | |
| 543 "federation_url, is_zero_click"; | |
| 544 auto copy_data_query = | |
| 545 [&fields_to_copy](const std::string& from, const std::string& to) { | |
| 546 return "INSERT INTO " + to + " SELECT " + fields_to_copy + " FROM " + | |
| 547 from; | |
| 548 }; | |
| 549 | |
| 550 if (!db_.Execute(("CREATE TEMPORARY TABLE logins_data(" + fields_to_copy + | |
| 551 ")").c_str()) || | |
| 552 !db_.Execute(copy_data_query("logins", "logins_data").c_str()) || | |
| 553 !db_.Execute("DROP TABLE logins") || | |
| 554 !db_.Execute( | |
| 555 ("CREATE TABLE logins(" + fields_to_copy + ")").c_str()) || | |
| 556 !db_.Execute(copy_data_query("logins_data", "logins").c_str()) || | |
| 557 !db_.Execute("DROP TABLE logins_data") || | |
| 558 !CreateIndexOnSignonRealm(&db_, "logins")) { | |
| 559 return false; | |
| 560 } | |
| 561 // Fall through. | |
| 562 } | |
| 563 case 10: { | |
| 564 // Rename is_zero_click -> skip_zero_click. Note that previous versions | |
| 565 // may have incorrectly used a 6-column key (origin_url, username_element, | |
| 566 // username_value, password_element, signon_realm, submit_element). | |
| 567 // In that case, this step also restores the correct 5-column key; | |
| 568 // that is, the above without "submit_element". | |
| 569 const char copy_query[] = "INSERT OR REPLACE INTO logins_new SELECT " | |
| 570 "origin_url, action_url, username_element, username_value, " | |
| 571 "password_element, password_value, submit_element, signon_realm, " | |
| 572 "ssl_valid, preferred, date_created, blacklisted_by_user, scheme, " | |
| 573 "password_type, possible_usernames, times_used, form_data, " | |
| 574 "date_synced, display_name, avatar_url, federation_url, is_zero_click" | |
| 575 " FROM logins"; | |
| 576 if (!CreateNewTable(&db_, "logins_new", "") || | |
| 577 !db_.Execute(copy_query) || | |
| 578 !db_.Execute("DROP TABLE logins") || | |
| 579 !db_.Execute("ALTER TABLE logins_new RENAME TO logins") || | |
| 580 !CreateIndexOnSignonRealm(&db_, "logins")) { | |
| 581 return false; | |
| 582 } | |
| 583 // Fall through. | |
| 584 } | |
| 585 case 11: | |
| 586 if (!db_.Execute( | |
| 587 "ALTER TABLE logins ADD COLUMN " | |
| 588 "generation_upload_status INTEGER")) | |
| 589 return false; | |
| 590 // Fall through. | |
| 591 case 12: | |
| 592 // The stats table was added. Nothing to do really. | |
| 593 // Fall through. | |
| 594 case 13: { | |
| 595 // Rename avatar_url -> icon_url. Note that if the original version was | |
| 596 // at most 10, this renaming would have already happened in step 10, | |
| 597 // as |CreateNewTable| would create a table with the new column name. | |
| 598 if (original_version > 10) { | |
| 599 const char copy_query[] = "INSERT OR REPLACE INTO logins_new SELECT " | |
| 600 "origin_url, action_url, username_element, username_value, " | |
| 601 "password_element, password_value, submit_element, signon_realm, " | |
| 602 "ssl_valid, preferred, date_created, blacklisted_by_user, scheme, " | |
| 603 "password_type, possible_usernames, times_used, form_data, " | |
| 604 "date_synced, display_name, avatar_url, federation_url, " | |
| 605 "skip_zero_click, generation_upload_status FROM logins"; | |
| 606 if (!CreateNewTable( | |
| 607 &db_, "logins_new", "generation_upload_status INTEGER,") || | |
| 608 !db_.Execute(copy_query) || | |
| 609 !db_.Execute("DROP TABLE logins") || | |
| 610 !db_.Execute("ALTER TABLE logins_new RENAME TO logins") || | |
| 611 !CreateIndexOnSignonRealm(&db_, "logins")) { | |
| 612 return false; | |
| 613 } | |
| 614 } | |
| 615 // Fall through. | |
| 616 } | |
| 617 case 14: | |
| 618 // No change of schema. Version 15 was introduced to force all databases | |
| 619 // through an otherwise no-op migration process that will, however, now | |
| 620 // correctly set the 'compatible version number'. Previously, it was | |
| 621 // always being set to (and forever left at) version 1. | |
| 622 meta_table_.SetCompatibleVersionNumber(kCompatibleVersionNumber); | |
| 623 case 15: | |
| 624 // Recreate the statistics. | |
| 625 if (!stats_table_.MigrateToVersion(16)) | |
| 626 return false; | |
| 627 case 16: { | |
| 628 // No change in scheme: just disable auto sign-in by default in | |
| 629 // preparation to launch the credential management API. | |
| 630 if (!db_.Execute("UPDATE logins SET skip_zero_click = 1")) | |
| 631 return false; | |
| 632 // Fall through. | |
| 633 } | |
| 634 | |
| 635 // ------------------------------------------------------------------------- | |
| 636 // DO NOT FORGET to update |kCompatibleVersionNumber| if you add a migration | |
| 637 // step that is a breaking change. This is needed so that an older version | |
| 638 // of the browser can fail with a meaningful error when opening a newer | |
| 639 // database, as opposed to failing on the first database operation. | |
| 640 // ------------------------------------------------------------------------- | |
| 641 case kCurrentVersionNumber: | |
| 642 // Already up to date. | |
| 643 meta_table_.SetVersionNumber(kCurrentVersionNumber); | |
| 644 return true; | |
| 645 default: | |
| 646 NOTREACHED(); | |
| 647 return false; | |
| 648 } | |
| 649 } | |
| 650 | |
| 651 bool LoginDatabase::InitLoginsTable() { | |
| 652 if (!db_.DoesTableExist("logins")) { | |
| 653 if (!CreateNewTable(&db_, "logins", "generation_upload_status INTEGER,")) { | |
| 654 NOTREACHED(); | |
| 655 return false; | |
| 656 } | |
| 657 if (!CreateIndexOnSignonRealm(&db_, "logins")) { | |
| 658 NOTREACHED(); | |
| 659 return false; | |
| 660 } | |
| 661 } | |
| 662 return true; | |
| 663 } | |
| 664 | |
| 665 void LoginDatabase::ReportMetrics(const std::string& sync_username, | 670 void LoginDatabase::ReportMetrics(const std::string& sync_username, |
| 666 bool custom_passphrase_sync_enabled) { | 671 bool custom_passphrase_sync_enabled) { |
| 667 sql::Statement s(db_.GetCachedStatement( | 672 sql::Statement s(db_.GetCachedStatement( |
| 668 SQL_FROM_HERE, | 673 SQL_FROM_HERE, |
| 669 "SELECT signon_realm, password_type, blacklisted_by_user," | 674 "SELECT signon_realm, password_type, blacklisted_by_user," |
| 670 "COUNT(username_value) FROM logins GROUP BY " | 675 "COUNT(username_value) FROM logins GROUP BY " |
| 671 "signon_realm, password_type, blacklisted_by_user")); | 676 "signon_realm, password_type, blacklisted_by_user")); |
| 672 | 677 |
| 673 if (!s.is_valid()) | 678 if (!s.is_valid()) |
| 674 return; | 679 return; |
| (...skipping 174 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 849 PasswordStoreChangeList LoginDatabase::AddLogin(const PasswordForm& form) { | 854 PasswordStoreChangeList LoginDatabase::AddLogin(const PasswordForm& form) { |
| 850 PasswordStoreChangeList list; | 855 PasswordStoreChangeList list; |
| 851 if (!DoesMatchConstraints(form)) | 856 if (!DoesMatchConstraints(form)) |
| 852 return list; | 857 return list; |
| 853 std::string encrypted_password; | 858 std::string encrypted_password; |
| 854 if (EncryptedString( | 859 if (EncryptedString( |
| 855 clear_password_values_ ? base::string16() : form.password_value, | 860 clear_password_values_ ? base::string16() : form.password_value, |
| 856 &encrypted_password) != ENCRYPTION_RESULT_SUCCESS) | 861 &encrypted_password) != ENCRYPTION_RESULT_SUCCESS) |
| 857 return list; | 862 return list; |
| 858 | 863 |
| 859 // You *must* change LoginTableColumns if this query changes. | 864 DCHECK(!add_statement_.empty()); |
| 860 sql::Statement s(db_.GetCachedStatement( | 865 sql::Statement s( |
| 861 SQL_FROM_HERE, | 866 db_.GetCachedStatement(SQL_FROM_HERE, add_statement_.c_str())); |
| 862 "INSERT INTO logins " | |
| 863 "(origin_url, action_url, username_element, username_value, " | |
| 864 " password_element, password_value, submit_element, " | |
| 865 " signon_realm, ssl_valid, preferred, date_created, blacklisted_by_user, " | |
| 866 " scheme, password_type, possible_usernames, times_used, form_data, " | |
| 867 " date_synced, display_name, icon_url," | |
| 868 " federation_url, skip_zero_click, generation_upload_status) VALUES " | |
| 869 "(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)")); | |
| 870 BindAddStatement(form, encrypted_password, &s); | 867 BindAddStatement(form, encrypted_password, &s); |
| 871 db_.set_error_callback(base::Bind(&AddCallback)); | 868 db_.set_error_callback(base::Bind(&AddCallback)); |
| 872 const bool success = s.Run(); | 869 const bool success = s.Run(); |
| 873 db_.reset_error_callback(); | 870 db_.reset_error_callback(); |
| 874 if (success) { | 871 if (success) { |
| 875 list.push_back(PasswordStoreChange(PasswordStoreChange::ADD, form)); | 872 list.push_back(PasswordStoreChange(PasswordStoreChange::ADD, form)); |
| 876 return list; | 873 return list; |
| 877 } | 874 } |
| 878 // Repeat the same statement but with REPLACE semantic. | 875 // Repeat the same statement but with REPLACE semantic. |
| 879 s.Assign(db_.GetCachedStatement( | 876 DCHECK(!add_replace_statement_.empty()); |
| 880 SQL_FROM_HERE, | 877 s.Assign( |
| 881 "INSERT OR REPLACE INTO logins " | 878 db_.GetCachedStatement(SQL_FROM_HERE, add_replace_statement_.c_str())); |
| 882 "(origin_url, action_url, username_element, username_value, " | |
| 883 " password_element, password_value, submit_element, " | |
| 884 " signon_realm, ssl_valid, preferred, date_created, blacklisted_by_user, " | |
| 885 " scheme, password_type, possible_usernames, times_used, form_data, " | |
| 886 " date_synced, display_name, icon_url," | |
| 887 " federation_url, skip_zero_click, generation_upload_status) VALUES " | |
| 888 "(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)")); | |
| 889 BindAddStatement(form, encrypted_password, &s); | 879 BindAddStatement(form, encrypted_password, &s); |
| 890 if (s.Run()) { | 880 if (s.Run()) { |
| 891 list.push_back(PasswordStoreChange(PasswordStoreChange::REMOVE, form)); | 881 list.push_back(PasswordStoreChange(PasswordStoreChange::REMOVE, form)); |
| 892 list.push_back(PasswordStoreChange(PasswordStoreChange::ADD, form)); | 882 list.push_back(PasswordStoreChange(PasswordStoreChange::ADD, form)); |
| 893 } | 883 } |
| 894 return list; | 884 return list; |
| 895 } | 885 } |
| 896 | 886 |
| 897 PasswordStoreChangeList LoginDatabase::UpdateLogin(const PasswordForm& form) { | 887 PasswordStoreChangeList LoginDatabase::UpdateLogin(const PasswordForm& form) { |
| 898 std::string encrypted_password; | 888 std::string encrypted_password; |
| 899 if (EncryptedString( | 889 if (EncryptedString( |
| 900 clear_password_values_ ? base::string16() : form.password_value, | 890 clear_password_values_ ? base::string16() : form.password_value, |
| 901 &encrypted_password) != ENCRYPTION_RESULT_SUCCESS) | 891 &encrypted_password) != ENCRYPTION_RESULT_SUCCESS) |
| 902 return PasswordStoreChangeList(); | 892 return PasswordStoreChangeList(); |
| 903 | 893 |
| 904 #if defined(OS_IOS) | 894 #if defined(OS_IOS) |
| 905 DeleteEncryptedPassword(form); | 895 DeleteEncryptedPassword(form); |
| 906 #endif | 896 #endif |
| 907 // Replacement is necessary to deal with updating imported credentials. See | 897 // Replacement is necessary to deal with updating imported credentials. See |
| 908 // crbug.com/349138 for details. | 898 // crbug.com/349138 for details. |
| 909 sql::Statement s(db_.GetCachedStatement(SQL_FROM_HERE, | 899 DCHECK(!update_statement_.empty()); |
| 910 "UPDATE OR REPLACE logins SET " | 900 sql::Statement s( |
| 911 "action_url = ?, " | 901 db_.GetCachedStatement(SQL_FROM_HERE, update_statement_.c_str())); |
| 912 "password_value = ?, " | |
| 913 "ssl_valid = ?, " | |
| 914 "preferred = ?, " | |
| 915 "possible_usernames = ?, " | |
| 916 "times_used = ?, " | |
| 917 "submit_element = ?, " | |
| 918 "date_synced = ?, " | |
| 919 "date_created = ?, " | |
| 920 "blacklisted_by_user = ?, " | |
| 921 "scheme = ?, " | |
| 922 "password_type = ?, " | |
| 923 "display_name = ?, " | |
| 924 "icon_url = ?, " | |
| 925 "federation_url = ?, " | |
| 926 "skip_zero_click = ?, " | |
| 927 "generation_upload_status = ? " | |
| 928 "WHERE origin_url = ? AND " | |
| 929 "username_element = ? AND " | |
| 930 "username_value = ? AND " | |
| 931 "password_element = ? AND " | |
| 932 "signon_realm = ?")); | |
| 933 s.BindString(0, form.action.spec()); | 902 s.BindString(0, form.action.spec()); |
| 934 s.BindBlob(1, encrypted_password.data(), | 903 s.BindBlob(1, encrypted_password.data(), |
| 935 static_cast<int>(encrypted_password.length())); | 904 static_cast<int>(encrypted_password.length())); |
| 936 s.BindInt(2, form.ssl_valid); | 905 s.BindString16(2, form.submit_element); |
| 937 s.BindInt(3, form.preferred); | 906 s.BindInt(3, form.ssl_valid); |
| 907 s.BindInt(4, form.preferred); | |
| 908 s.BindInt64(5, form.date_created.ToInternalValue()); | |
| 909 s.BindInt(6, form.blacklisted_by_user); | |
| 910 s.BindInt(7, form.scheme); | |
| 911 s.BindInt(8, form.type); | |
| 938 base::Pickle pickle = SerializeVector(form.other_possible_usernames); | 912 base::Pickle pickle = SerializeVector(form.other_possible_usernames); |
| 939 s.BindBlob(4, pickle.data(), pickle.size()); | 913 s.BindBlob(9, pickle.data(), pickle.size()); |
| 940 s.BindInt(5, form.times_used); | 914 s.BindInt(10, form.times_used); |
| 941 s.BindString16(6, form.submit_element); | 915 base::Pickle form_data_pickle; |
| 942 s.BindInt64(7, form.date_synced.ToInternalValue()); | 916 autofill::SerializeFormData(form.form_data, &form_data_pickle); |
| 943 s.BindInt64(8, form.date_created.ToInternalValue()); | 917 s.BindBlob(11, form_data_pickle.data(), form_data_pickle.size()); |
| 944 s.BindInt(9, form.blacklisted_by_user); | 918 s.BindInt64(12, form.date_synced.ToInternalValue()); |
| 945 s.BindInt(10, form.scheme); | 919 s.BindString16(13, form.display_name); |
| 946 s.BindInt(11, form.type); | 920 s.BindString(14, form.icon_url.spec()); |
| 947 s.BindString16(12, form.display_name); | |
| 948 s.BindString(13, form.icon_url.spec()); | |
| 949 // An empty Origin serializes as "null" which would be strange to store here. | 921 // An empty Origin serializes as "null" which would be strange to store here. |
| 950 s.BindString(14, form.federation_origin.unique() | 922 s.BindString(15, form.federation_origin.unique() |
| 951 ? std::string() | 923 ? std::string() |
| 952 : form.federation_origin.Serialize()); | 924 : form.federation_origin.Serialize()); |
| 953 s.BindInt(15, form.skip_zero_click); | 925 s.BindInt(16, form.skip_zero_click); |
| 954 s.BindInt(16, form.generation_upload_status); | 926 s.BindInt(17, form.generation_upload_status); |
| 955 | 927 |
| 956 // WHERE starts here. | 928 // WHERE starts here. |
| 957 s.BindString(17, form.origin.spec()); | 929 s.BindString(18, form.origin.spec()); |
| 958 s.BindString16(18, form.username_element); | 930 s.BindString16(19, form.username_element); |
| 959 s.BindString16(19, form.username_value); | 931 s.BindString16(20, form.username_value); |
| 960 s.BindString16(20, form.password_element); | 932 s.BindString16(21, form.password_element); |
| 961 s.BindString(21, form.signon_realm); | 933 s.BindString(22, form.signon_realm); |
| 962 | 934 |
| 963 if (!s.Run()) | 935 if (!s.Run()) |
| 964 return PasswordStoreChangeList(); | 936 return PasswordStoreChangeList(); |
| 965 | 937 |
| 966 PasswordStoreChangeList list; | 938 PasswordStoreChangeList list; |
| 967 if (db_.GetLastChangeCount()) | 939 if (db_.GetLastChangeCount()) |
| 968 list.push_back(PasswordStoreChange(PasswordStoreChange::UPDATE, form)); | 940 list.push_back(PasswordStoreChange(PasswordStoreChange::UPDATE, form)); |
| 969 | 941 |
| 970 return list; | 942 return list; |
| 971 } | 943 } |
| 972 | 944 |
| 973 bool LoginDatabase::RemoveLogin(const PasswordForm& form) { | 945 bool LoginDatabase::RemoveLogin(const PasswordForm& form) { |
| 974 if (form.is_public_suffix_match) { | 946 if (form.is_public_suffix_match) { |
| 975 // TODO(dvadym): Discuss whether we should allow to remove PSL matched | 947 // TODO(dvadym): Discuss whether we should allow to remove PSL matched |
| 976 // credentials. | 948 // credentials. |
| 977 return false; | 949 return false; |
| 978 } | 950 } |
| 979 #if defined(OS_IOS) | 951 #if defined(OS_IOS) |
| 980 DeleteEncryptedPassword(form); | 952 DeleteEncryptedPassword(form); |
| 981 #endif | 953 #endif |
| 982 // Remove a login by UNIQUE-constrained fields. | 954 // Remove a login by UNIQUE-constrained fields. |
| 983 sql::Statement s(db_.GetCachedStatement(SQL_FROM_HERE, | 955 DCHECK(!delete_statement_.empty()); |
| 984 "DELETE FROM logins WHERE " | 956 sql::Statement s( |
| 985 "origin_url = ? AND " | 957 db_.GetCachedStatement(SQL_FROM_HERE, delete_statement_.c_str())); |
| 986 "username_element = ? AND " | |
| 987 "username_value = ? AND " | |
| 988 "password_element = ? AND " | |
| 989 "submit_element = ? AND " | |
| 990 "signon_realm = ? ")); | |
| 991 s.BindString(0, form.origin.spec()); | 958 s.BindString(0, form.origin.spec()); |
| 992 s.BindString16(1, form.username_element); | 959 s.BindString16(1, form.username_element); |
| 993 s.BindString16(2, form.username_value); | 960 s.BindString16(2, form.username_value); |
| 994 s.BindString16(3, form.password_element); | 961 s.BindString16(3, form.password_element); |
| 995 s.BindString16(4, form.submit_element); | 962 s.BindString(4, form.signon_realm); |
| 996 s.BindString(5, form.signon_realm); | |
| 997 | 963 |
| 998 return s.Run() && db_.GetLastChangeCount() > 0; | 964 return s.Run() && db_.GetLastChangeCount() > 0; |
| 999 } | 965 } |
| 1000 | 966 |
| 1001 bool LoginDatabase::RemoveLoginsCreatedBetween(base::Time delete_begin, | 967 bool LoginDatabase::RemoveLoginsCreatedBetween(base::Time delete_begin, |
| 1002 base::Time delete_end) { | 968 base::Time delete_end) { |
| 1003 #if defined(OS_IOS) | 969 #if defined(OS_IOS) |
| 1004 ScopedVector<autofill::PasswordForm> forms; | 970 ScopedVector<autofill::PasswordForm> forms; |
| 1005 if (GetLoginsCreatedBetween(delete_begin, delete_end, &forms)) { | 971 if (GetLoginsCreatedBetween(delete_begin, delete_end, &forms)) { |
| 1006 for (size_t i = 0; i < forms.size(); i++) { | 972 for (size_t i = 0; i < forms.size(); i++) { |
| (...skipping 21 matching lines...) Expand all Loading... | |
| 1028 s.BindInt64(1, | 994 s.BindInt64(1, |
| 1029 delete_end.is_null() ? base::Time::Max().ToInternalValue() | 995 delete_end.is_null() ? base::Time::Max().ToInternalValue() |
| 1030 : delete_end.ToInternalValue()); | 996 : delete_end.ToInternalValue()); |
| 1031 | 997 |
| 1032 return s.Run(); | 998 return s.Run(); |
| 1033 } | 999 } |
| 1034 | 1000 |
| 1035 bool LoginDatabase::GetAutoSignInLogins( | 1001 bool LoginDatabase::GetAutoSignInLogins( |
| 1036 ScopedVector<autofill::PasswordForm>* forms) const { | 1002 ScopedVector<autofill::PasswordForm>* forms) const { |
| 1037 DCHECK(forms); | 1003 DCHECK(forms); |
| 1038 sql::Statement s(db_.GetCachedStatement( | 1004 DCHECK(!autosignin_statement_.empty()); |
| 1039 SQL_FROM_HERE, | 1005 sql::Statement s( |
| 1040 "SELECT origin_url, action_url, username_element, username_value, " | 1006 db_.GetCachedStatement(SQL_FROM_HERE, autosignin_statement_.c_str())); |
| 1041 "password_element, password_value, submit_element, signon_realm, " | |
| 1042 "ssl_valid, preferred, date_created, blacklisted_by_user, " | |
| 1043 "scheme, password_type, possible_usernames, times_used, form_data, " | |
| 1044 "date_synced, display_name, icon_url, " | |
| 1045 "federation_url, skip_zero_click, generation_upload_status FROM logins " | |
| 1046 "WHERE skip_zero_click = 0 ORDER BY origin_url")); | |
| 1047 | 1007 |
| 1048 return StatementToForms(&s, nullptr, forms); | 1008 return StatementToForms(&s, nullptr, forms); |
| 1049 } | 1009 } |
| 1050 | 1010 |
| 1051 bool LoginDatabase::DisableAutoSignInForAllLogins() { | 1011 bool LoginDatabase::DisableAutoSignInForAllLogins() { |
| 1052 sql::Statement s(db_.GetCachedStatement( | 1012 sql::Statement s(db_.GetCachedStatement( |
| 1053 SQL_FROM_HERE, "UPDATE logins SET skip_zero_click = 1;")); | 1013 SQL_FROM_HERE, "UPDATE logins SET skip_zero_click = 1;")); |
| 1054 | 1014 |
| 1055 return s.Run(); | 1015 return s.Run(); |
| 1056 } | 1016 } |
| (...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1126 form->generation_upload_status = | 1086 form->generation_upload_status = |
| 1127 static_cast<PasswordForm::GenerationUploadStatus>( | 1087 static_cast<PasswordForm::GenerationUploadStatus>( |
| 1128 generation_upload_status_int); | 1088 generation_upload_status_int); |
| 1129 return ENCRYPTION_RESULT_SUCCESS; | 1089 return ENCRYPTION_RESULT_SUCCESS; |
| 1130 } | 1090 } |
| 1131 | 1091 |
| 1132 bool LoginDatabase::GetLogins( | 1092 bool LoginDatabase::GetLogins( |
| 1133 const PasswordForm& form, | 1093 const PasswordForm& form, |
| 1134 ScopedVector<autofill::PasswordForm>* forms) const { | 1094 ScopedVector<autofill::PasswordForm>* forms) const { |
| 1135 DCHECK(forms); | 1095 DCHECK(forms); |
| 1136 // You *must* change LoginTableColumns if this query changes. | |
| 1137 std::string sql_query = | |
| 1138 "SELECT origin_url, action_url, " | |
| 1139 "username_element, username_value, " | |
| 1140 "password_element, password_value, submit_element, " | |
| 1141 "signon_realm, ssl_valid, preferred, date_created, blacklisted_by_user, " | |
| 1142 "scheme, password_type, possible_usernames, times_used, form_data, " | |
| 1143 "date_synced, display_name, icon_url, " | |
| 1144 "federation_url, skip_zero_click, generation_upload_status " | |
| 1145 "FROM logins WHERE signon_realm == ? "; | |
| 1146 const GURL signon_realm(form.signon_realm); | 1096 const GURL signon_realm(form.signon_realm); |
| 1147 std::string registered_domain = GetRegistryControlledDomain(signon_realm); | 1097 std::string registered_domain = GetRegistryControlledDomain(signon_realm); |
| 1148 const bool should_PSL_matching_apply = | 1098 const bool should_PSL_matching_apply = |
| 1149 form.scheme == PasswordForm::SCHEME_HTML && | 1099 form.scheme == PasswordForm::SCHEME_HTML && |
| 1150 ShouldPSLDomainMatchingApply(registered_domain); | 1100 ShouldPSLDomainMatchingApply(registered_domain); |
| 1151 const bool should_federated_apply = form.scheme == PasswordForm::SCHEME_HTML; | 1101 const bool should_federated_apply = form.scheme == PasswordForm::SCHEME_HTML; |
| 1152 if (should_PSL_matching_apply) | 1102 DCHECK(!get_statement_.empty()); |
| 1153 sql_query += "OR signon_realm REGEXP ? "; | 1103 DCHECK(!get_statement_psl_.empty()); |
| 1154 if (should_federated_apply) | 1104 DCHECK(!get_statement_federated_.empty()); |
| 1155 sql_query += "OR (signon_realm LIKE ? AND password_type == 2) "; | 1105 DCHECK(!get_statement_psl_federated_.empty()); |
| 1106 const std::string* sql_query = &get_statement_; | |
| 1107 if (should_PSL_matching_apply && should_federated_apply) | |
| 1108 sql_query = &get_statement_psl_federated_; | |
| 1109 else if (should_PSL_matching_apply) | |
| 1110 sql_query = &get_statement_psl_; | |
| 1111 else if (should_federated_apply) | |
| 1112 sql_query = &get_statement_federated_; | |
| 1156 | 1113 |
| 1157 // TODO(nyquist) Consider usage of GetCachedStatement when | 1114 // TODO(nyquist) Consider usage of GetCachedStatement when |
| 1158 // http://crbug.com/248608 is fixed. | 1115 // http://crbug.com/248608 is fixed. |
| 1159 sql::Statement s(db_.GetUniqueStatement(sql_query.c_str())); | 1116 sql::Statement s(db_.GetUniqueStatement(sql_query->c_str())); |
| 1160 s.BindString(0, form.signon_realm); | 1117 s.BindString(0, form.signon_realm); |
| 1161 int placeholder = 1; | 1118 int placeholder = 1; |
| 1162 | 1119 |
| 1163 // PSL matching only applies to HTML forms. | 1120 // PSL matching only applies to HTML forms. |
| 1164 if (should_PSL_matching_apply) { | 1121 if (should_PSL_matching_apply) { |
| 1165 // We are extending the original SQL query with one that includes more | 1122 // We are extending the original SQL query with one that includes more |
| 1166 // possible matches based on public suffix domain matching. Using a regexp | 1123 // possible matches based on public suffix domain matching. Using a regexp |
| 1167 // here is just an optimization to not have to parse all the stored entries | 1124 // here is just an optimization to not have to parse all the stored entries |
| 1168 // in the |logins| table. The result (scheme, domain and port) is verified | 1125 // in the |logins| table. The result (scheme, domain and port) is verified |
| 1169 // further down using GURL. See the functions SchemeMatches, | 1126 // further down using GURL. See the functions SchemeMatches, |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1201 return StatementToForms( | 1158 return StatementToForms( |
| 1202 &s, should_PSL_matching_apply || should_federated_apply ? &form : nullptr, | 1159 &s, should_PSL_matching_apply || should_federated_apply ? &form : nullptr, |
| 1203 forms); | 1160 forms); |
| 1204 } | 1161 } |
| 1205 | 1162 |
| 1206 bool LoginDatabase::GetLoginsCreatedBetween( | 1163 bool LoginDatabase::GetLoginsCreatedBetween( |
| 1207 const base::Time begin, | 1164 const base::Time begin, |
| 1208 const base::Time end, | 1165 const base::Time end, |
| 1209 ScopedVector<autofill::PasswordForm>* forms) const { | 1166 ScopedVector<autofill::PasswordForm>* forms) const { |
| 1210 DCHECK(forms); | 1167 DCHECK(forms); |
| 1211 sql::Statement s(db_.GetCachedStatement( | 1168 DCHECK(!created_statement_.empty()); |
| 1212 SQL_FROM_HERE, | 1169 sql::Statement s( |
| 1213 "SELECT origin_url, action_url, " | 1170 db_.GetCachedStatement(SQL_FROM_HERE, created_statement_.c_str())); |
| 1214 "username_element, username_value, " | |
| 1215 "password_element, password_value, submit_element, " | |
| 1216 "signon_realm, ssl_valid, preferred, date_created, blacklisted_by_user, " | |
| 1217 "scheme, password_type, possible_usernames, times_used, form_data, " | |
| 1218 "date_synced, display_name, icon_url, " | |
| 1219 "federation_url, skip_zero_click, generation_upload_status FROM logins " | |
| 1220 "WHERE date_created >= ? AND date_created < ?" | |
| 1221 "ORDER BY origin_url")); | |
| 1222 s.BindInt64(0, begin.ToInternalValue()); | 1171 s.BindInt64(0, begin.ToInternalValue()); |
| 1223 s.BindInt64(1, end.is_null() ? std::numeric_limits<int64_t>::max() | 1172 s.BindInt64(1, end.is_null() ? std::numeric_limits<int64_t>::max() |
| 1224 : end.ToInternalValue()); | 1173 : end.ToInternalValue()); |
| 1225 | 1174 |
| 1226 return StatementToForms(&s, nullptr, forms); | 1175 return StatementToForms(&s, nullptr, forms); |
| 1227 } | 1176 } |
| 1228 | 1177 |
| 1229 bool LoginDatabase::GetLoginsSyncedBetween( | 1178 bool LoginDatabase::GetLoginsSyncedBetween( |
| 1230 const base::Time begin, | 1179 const base::Time begin, |
| 1231 const base::Time end, | 1180 const base::Time end, |
| 1232 ScopedVector<autofill::PasswordForm>* forms) const { | 1181 ScopedVector<autofill::PasswordForm>* forms) const { |
| 1233 DCHECK(forms); | 1182 DCHECK(forms); |
| 1234 sql::Statement s(db_.GetCachedStatement( | 1183 DCHECK(!synced_statement_.empty()); |
| 1235 SQL_FROM_HERE, | 1184 sql::Statement s( |
| 1236 "SELECT origin_url, action_url, username_element, username_value, " | 1185 db_.GetCachedStatement(SQL_FROM_HERE, synced_statement_.c_str())); |
| 1237 "password_element, password_value, submit_element, signon_realm, " | |
| 1238 "ssl_valid, preferred, date_created, blacklisted_by_user, " | |
| 1239 "scheme, password_type, possible_usernames, times_used, form_data, " | |
| 1240 "date_synced, display_name, icon_url, " | |
| 1241 "federation_url, skip_zero_click, generation_upload_status FROM logins " | |
| 1242 "WHERE date_synced >= ? AND date_synced < ?" | |
| 1243 "ORDER BY origin_url")); | |
| 1244 s.BindInt64(0, begin.ToInternalValue()); | 1186 s.BindInt64(0, begin.ToInternalValue()); |
| 1245 s.BindInt64(1, | 1187 s.BindInt64(1, |
| 1246 end.is_null() ? base::Time::Max().ToInternalValue() | 1188 end.is_null() ? base::Time::Max().ToInternalValue() |
| 1247 : end.ToInternalValue()); | 1189 : end.ToInternalValue()); |
| 1248 | 1190 |
| 1249 return StatementToForms(&s, nullptr, forms); | 1191 return StatementToForms(&s, nullptr, forms); |
| 1250 } | 1192 } |
| 1251 | 1193 |
| 1252 bool LoginDatabase::GetAutofillableLogins( | 1194 bool LoginDatabase::GetAutofillableLogins( |
| 1253 ScopedVector<autofill::PasswordForm>* forms) const { | 1195 ScopedVector<autofill::PasswordForm>* forms) const { |
| 1254 return GetAllLoginsWithBlacklistSetting(false, forms); | 1196 return GetAllLoginsWithBlacklistSetting(false, forms); |
| 1255 } | 1197 } |
| 1256 | 1198 |
| 1257 bool LoginDatabase::GetBlacklistLogins( | 1199 bool LoginDatabase::GetBlacklistLogins( |
| 1258 ScopedVector<autofill::PasswordForm>* forms) const { | 1200 ScopedVector<autofill::PasswordForm>* forms) const { |
| 1259 return GetAllLoginsWithBlacklistSetting(true, forms); | 1201 return GetAllLoginsWithBlacklistSetting(true, forms); |
| 1260 } | 1202 } |
| 1261 | 1203 |
| 1262 bool LoginDatabase::GetAllLoginsWithBlacklistSetting( | 1204 bool LoginDatabase::GetAllLoginsWithBlacklistSetting( |
| 1263 bool blacklisted, | 1205 bool blacklisted, |
| 1264 ScopedVector<autofill::PasswordForm>* forms) const { | 1206 ScopedVector<autofill::PasswordForm>* forms) const { |
| 1265 DCHECK(forms); | 1207 DCHECK(forms); |
| 1266 // You *must* change LoginTableColumns if this query changes. | 1208 DCHECK(!blacklisted_statement_.empty()); |
| 1267 sql::Statement s(db_.GetCachedStatement( | 1209 sql::Statement s( |
| 1268 SQL_FROM_HERE, | 1210 db_.GetCachedStatement(SQL_FROM_HERE, blacklisted_statement_.c_str())); |
| 1269 "SELECT origin_url, action_url, " | |
| 1270 "username_element, username_value, " | |
| 1271 "password_element, password_value, submit_element, " | |
| 1272 "signon_realm, ssl_valid, preferred, date_created, blacklisted_by_user, " | |
| 1273 "scheme, password_type, possible_usernames, times_used, form_data, " | |
| 1274 "date_synced, display_name, icon_url, " | |
| 1275 "federation_url, skip_zero_click, generation_upload_status FROM logins " | |
| 1276 "WHERE blacklisted_by_user == ? ORDER BY origin_url")); | |
| 1277 s.BindInt(0, blacklisted ? 1 : 0); | 1211 s.BindInt(0, blacklisted ? 1 : 0); |
| 1278 | 1212 |
| 1279 return StatementToForms(&s, nullptr, forms); | 1213 return StatementToForms(&s, nullptr, forms); |
| 1280 } | 1214 } |
| 1281 | 1215 |
| 1282 bool LoginDatabase::DeleteAndRecreateDatabaseFile() { | 1216 bool LoginDatabase::DeleteAndRecreateDatabaseFile() { |
| 1283 DCHECK(db_.is_open()); | 1217 DCHECK(db_.is_open()); |
| 1284 meta_table_.Reset(); | 1218 meta_table_.Reset(); |
| 1285 db_.Close(); | 1219 db_.Close(); |
| 1286 sql::Connection::Delete(db_path_); | 1220 sql::Connection::Delete(db_path_); |
| 1287 return Init(); | 1221 return Init(); |
| 1288 } | 1222 } |
| 1289 | 1223 |
| 1290 std::string LoginDatabase::GetEncryptedPassword( | 1224 std::string LoginDatabase::GetEncryptedPassword( |
| 1291 const autofill::PasswordForm& form) const { | 1225 const autofill::PasswordForm& form) const { |
| 1226 DCHECK(!encrypted_statement_.empty()); | |
| 1292 sql::Statement s( | 1227 sql::Statement s( |
| 1293 db_.GetCachedStatement(SQL_FROM_HERE, | 1228 db_.GetCachedStatement(SQL_FROM_HERE, encrypted_statement_.c_str())); |
| 1294 "SELECT password_value FROM logins WHERE " | |
| 1295 "origin_url = ? AND " | |
| 1296 "username_element = ? AND " | |
| 1297 "username_value = ? AND " | |
| 1298 "password_element = ? AND " | |
| 1299 "submit_element = ? AND " | |
| 1300 "signon_realm = ? ")); | |
| 1301 | 1229 |
| 1302 s.BindString(0, form.origin.spec()); | 1230 s.BindString(0, form.origin.spec()); |
| 1303 s.BindString16(1, form.username_element); | 1231 s.BindString16(1, form.username_element); |
| 1304 s.BindString16(2, form.username_value); | 1232 s.BindString16(2, form.username_value); |
| 1305 s.BindString16(3, form.password_element); | 1233 s.BindString16(3, form.password_element); |
| 1306 s.BindString16(4, form.submit_element); | 1234 s.BindString(4, form.signon_realm); |
| 1307 s.BindString(5, form.signon_realm); | |
| 1308 | 1235 |
| 1309 std::string encrypted_password; | 1236 std::string encrypted_password; |
| 1310 if (s.Step()) { | 1237 if (s.Step()) { |
| 1311 s.ColumnBlobAsString(0, &encrypted_password); | 1238 s.ColumnBlobAsString(0, &encrypted_password); |
| 1312 } | 1239 } |
| 1313 return encrypted_password; | 1240 return encrypted_password; |
| 1314 } | 1241 } |
| 1315 | 1242 |
| 1316 // static | 1243 // static |
| 1317 bool LoginDatabase::StatementToForms( | 1244 bool LoginDatabase::StatementToForms( |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1352 UMA_HISTOGRAM_ENUMERATION("PasswordManager.PslDomainMatchTriggering", | 1279 UMA_HISTOGRAM_ENUMERATION("PasswordManager.PslDomainMatchTriggering", |
| 1353 psl_domain_match_metric, PSL_DOMAIN_MATCH_COUNT); | 1280 psl_domain_match_metric, PSL_DOMAIN_MATCH_COUNT); |
| 1354 } | 1281 } |
| 1355 | 1282 |
| 1356 if (!statement->Succeeded()) | 1283 if (!statement->Succeeded()) |
| 1357 return false; | 1284 return false; |
| 1358 return true; | 1285 return true; |
| 1359 } | 1286 } |
| 1360 | 1287 |
| 1361 } // namespace password_manager | 1288 } // namespace password_manager |
| OLD | NEW |