Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1762)

Side by Side Diff: components/password_manager/core/browser/login_database.cc

Issue 2126713006: Refactor LoginDatabase migration (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Comments addressed Created 4 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
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
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
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 InitializeStatementStrings(builder);
430 LOG(ERROR) << "Unable to initialize the logins table."; 554
431 db_.Close(); 555 if (!db_.DoesTableExist("logins")) {
432 return false; 556 if (!builder.CreateTable(&db_)) {
557 VLOG(0) << "Failed to create the 'logins' table";
558 db_.Close();
559 return false;
560 }
433 } 561 }
562
434 stats_table_.Init(&db_); 563 stats_table_.Init(&db_);
435 564
565 int current_version = meta_table_.GetVersionNumber();
566 bool migration_success = FixVersionIfNeeded(&db_, &current_version);
567 DCHECK_LE(current_version, kCurrentVersionNumber);
568
436 // If the file on disk is an older database version, bring it up to date. 569 // If the file on disk is an older database version, bring it up to date.
437 if (meta_table_.GetVersionNumber() < kCurrentVersionNumber && 570 if (migration_success && current_version < kCurrentVersionNumber) {
438 !MigrateOldVersionsAsNeeded()) { 571 migration_success = MigrateLogins(
572 base::checked_cast<unsigned>(current_version), &builder, &db_);
573 }
574 if (migration_success && current_version <= 15) {
575 migration_success = stats_table_.MigrateToVersion(16);
576 }
577 if (migration_success) {
578 meta_table_.SetCompatibleVersionNumber(kCompatibleVersionNumber);
579 meta_table_.SetVersionNumber(kCurrentVersionNumber);
580 } else {
439 LogDatabaseInitError(MIGRATION_ERROR); 581 LogDatabaseInitError(MIGRATION_ERROR);
440 UMA_HISTOGRAM_SPARSE_SLOWLY("PasswordManager.LoginDatabaseFailedVersion", 582 UMA_HISTOGRAM_SPARSE_SLOWLY("PasswordManager.LoginDatabaseFailedVersion",
441 meta_table_.GetVersionNumber()); 583 meta_table_.GetVersionNumber());
442 LOG(ERROR) << "Unable to migrate database from " 584 LOG(ERROR) << "Unable to migrate database from "
443 << meta_table_.GetVersionNumber() << " to " 585 << meta_table_.GetVersionNumber() << " to "
444 << kCurrentVersionNumber; 586 << kCurrentVersionNumber;
445 db_.Close(); 587 db_.Close();
446 return false; 588 return false;
447 } 589 }
448 590
449 if (!stats_table_.CreateTableIfNecessary()) { 591 if (!stats_table_.CreateTableIfNecessary()) {
450 LogDatabaseInitError(INIT_STATS_ERROR); 592 LogDatabaseInitError(INIT_STATS_ERROR);
451 LOG(ERROR) << "Unable to create the stats table."; 593 LOG(ERROR) << "Unable to create the stats table.";
452 db_.Close(); 594 db_.Close();
453 return false; 595 return false;
454 } 596 }
455 597
456 if (!transaction.Commit()) { 598 if (!transaction.Commit()) {
457 LogDatabaseInitError(COMMIT_TRANSACTION_ERROR); 599 LogDatabaseInitError(COMMIT_TRANSACTION_ERROR);
458 LOG(ERROR) << "Unable to commit a transaction."; 600 LOG(ERROR) << "Unable to commit a transaction.";
459 db_.Close(); 601 db_.Close();
460 return false; 602 return false;
461 } 603 }
462 604
463 LogDatabaseInitError(INIT_OK); 605 LogDatabaseInitError(INIT_OK);
464 return true; 606 return true;
465 } 607 }
466 608
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, 609 void LoginDatabase::ReportMetrics(const std::string& sync_username,
666 bool custom_passphrase_sync_enabled) { 610 bool custom_passphrase_sync_enabled) {
667 sql::Statement s(db_.GetCachedStatement( 611 sql::Statement s(db_.GetCachedStatement(
668 SQL_FROM_HERE, 612 SQL_FROM_HERE,
669 "SELECT signon_realm, password_type, blacklisted_by_user," 613 "SELECT signon_realm, password_type, blacklisted_by_user,"
670 "COUNT(username_value) FROM logins GROUP BY " 614 "COUNT(username_value) FROM logins GROUP BY "
671 "signon_realm, password_type, blacklisted_by_user")); 615 "signon_realm, password_type, blacklisted_by_user"));
672 616
673 if (!s.is_valid()) 617 if (!s.is_valid())
674 return; 618 return;
(...skipping 174 matching lines...) Expand 10 before | Expand all | Expand 10 after
849 PasswordStoreChangeList LoginDatabase::AddLogin(const PasswordForm& form) { 793 PasswordStoreChangeList LoginDatabase::AddLogin(const PasswordForm& form) {
850 PasswordStoreChangeList list; 794 PasswordStoreChangeList list;
851 if (!DoesMatchConstraints(form)) 795 if (!DoesMatchConstraints(form))
852 return list; 796 return list;
853 std::string encrypted_password; 797 std::string encrypted_password;
854 if (EncryptedString( 798 if (EncryptedString(
855 clear_password_values_ ? base::string16() : form.password_value, 799 clear_password_values_ ? base::string16() : form.password_value,
856 &encrypted_password) != ENCRYPTION_RESULT_SUCCESS) 800 &encrypted_password) != ENCRYPTION_RESULT_SUCCESS)
857 return list; 801 return list;
858 802
859 // You *must* change LoginTableColumns if this query changes. 803 DCHECK(!add_statement_.empty());
860 sql::Statement s(db_.GetCachedStatement( 804 sql::Statement s(
861 SQL_FROM_HERE, 805 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); 806 BindAddStatement(form, encrypted_password, &s);
871 db_.set_error_callback(base::Bind(&AddCallback)); 807 db_.set_error_callback(base::Bind(&AddCallback));
872 const bool success = s.Run(); 808 const bool success = s.Run();
873 db_.reset_error_callback(); 809 db_.reset_error_callback();
874 if (success) { 810 if (success) {
875 list.push_back(PasswordStoreChange(PasswordStoreChange::ADD, form)); 811 list.push_back(PasswordStoreChange(PasswordStoreChange::ADD, form));
876 return list; 812 return list;
877 } 813 }
878 // Repeat the same statement but with REPLACE semantic. 814 // Repeat the same statement but with REPLACE semantic.
879 s.Assign(db_.GetCachedStatement( 815 DCHECK(!add_replace_statement_.empty());
880 SQL_FROM_HERE, 816 s.Assign(
881 "INSERT OR REPLACE INTO logins " 817 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); 818 BindAddStatement(form, encrypted_password, &s);
890 if (s.Run()) { 819 if (s.Run()) {
891 list.push_back(PasswordStoreChange(PasswordStoreChange::REMOVE, form)); 820 list.push_back(PasswordStoreChange(PasswordStoreChange::REMOVE, form));
892 list.push_back(PasswordStoreChange(PasswordStoreChange::ADD, form)); 821 list.push_back(PasswordStoreChange(PasswordStoreChange::ADD, form));
893 } 822 }
894 return list; 823 return list;
895 } 824 }
896 825
897 PasswordStoreChangeList LoginDatabase::UpdateLogin(const PasswordForm& form) { 826 PasswordStoreChangeList LoginDatabase::UpdateLogin(const PasswordForm& form) {
898 std::string encrypted_password; 827 std::string encrypted_password;
899 if (EncryptedString( 828 if (EncryptedString(
900 clear_password_values_ ? base::string16() : form.password_value, 829 clear_password_values_ ? base::string16() : form.password_value,
901 &encrypted_password) != ENCRYPTION_RESULT_SUCCESS) 830 &encrypted_password) != ENCRYPTION_RESULT_SUCCESS)
902 return PasswordStoreChangeList(); 831 return PasswordStoreChangeList();
903 832
904 #if defined(OS_IOS) 833 #if defined(OS_IOS)
905 DeleteEncryptedPassword(form); 834 DeleteEncryptedPassword(form);
906 #endif 835 #endif
907 // Replacement is necessary to deal with updating imported credentials. See 836 // Replacement is necessary to deal with updating imported credentials. See
908 // crbug.com/349138 for details. 837 // crbug.com/349138 for details.
909 sql::Statement s(db_.GetCachedStatement(SQL_FROM_HERE, 838 DCHECK(!update_statement_.empty());
910 "UPDATE OR REPLACE logins SET " 839 sql::Statement s(
911 "action_url = ?, " 840 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()); 841 s.BindString(0, form.action.spec());
934 s.BindBlob(1, encrypted_password.data(), 842 s.BindBlob(1, encrypted_password.data(),
935 static_cast<int>(encrypted_password.length())); 843 static_cast<int>(encrypted_password.length()));
936 s.BindInt(2, form.ssl_valid); 844 s.BindString16(2, form.submit_element);
937 s.BindInt(3, form.preferred); 845 s.BindInt(3, form.ssl_valid);
846 s.BindInt(4, form.preferred);
847 s.BindInt64(5, form.date_created.ToInternalValue());
848 s.BindInt(6, form.blacklisted_by_user);
849 s.BindInt(7, form.scheme);
850 s.BindInt(8, form.type);
938 base::Pickle pickle = SerializeVector(form.other_possible_usernames); 851 base::Pickle pickle = SerializeVector(form.other_possible_usernames);
939 s.BindBlob(4, pickle.data(), pickle.size()); 852 s.BindBlob(9, pickle.data(), pickle.size());
940 s.BindInt(5, form.times_used); 853 s.BindInt(10, form.times_used);
941 s.BindString16(6, form.submit_element); 854 base::Pickle form_data_pickle;
942 s.BindInt64(7, form.date_synced.ToInternalValue()); 855 autofill::SerializeFormData(form.form_data, &form_data_pickle);
943 s.BindInt64(8, form.date_created.ToInternalValue()); 856 s.BindBlob(11, form_data_pickle.data(), form_data_pickle.size());
944 s.BindInt(9, form.blacklisted_by_user); 857 s.BindInt64(12, form.date_synced.ToInternalValue());
945 s.BindInt(10, form.scheme); 858 s.BindString16(13, form.display_name);
946 s.BindInt(11, form.type); 859 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. 860 // An empty Origin serializes as "null" which would be strange to store here.
950 s.BindString(14, form.federation_origin.unique() 861 s.BindString(15, form.federation_origin.unique()
951 ? std::string() 862 ? std::string()
952 : form.federation_origin.Serialize()); 863 : form.federation_origin.Serialize());
953 s.BindInt(15, form.skip_zero_click); 864 s.BindInt(16, form.skip_zero_click);
954 s.BindInt(16, form.generation_upload_status); 865 s.BindInt(17, form.generation_upload_status);
955 866
956 // WHERE starts here. 867 // WHERE starts here.
957 s.BindString(17, form.origin.spec()); 868 s.BindString(18, form.origin.spec());
958 s.BindString16(18, form.username_element); 869 s.BindString16(19, form.username_element);
959 s.BindString16(19, form.username_value); 870 s.BindString16(20, form.username_value);
960 s.BindString16(20, form.password_element); 871 s.BindString16(21, form.password_element);
961 s.BindString(21, form.signon_realm); 872 s.BindString(22, form.signon_realm);
962 873
963 if (!s.Run()) 874 if (!s.Run())
964 return PasswordStoreChangeList(); 875 return PasswordStoreChangeList();
965 876
966 PasswordStoreChangeList list; 877 PasswordStoreChangeList list;
967 if (db_.GetLastChangeCount()) 878 if (db_.GetLastChangeCount())
968 list.push_back(PasswordStoreChange(PasswordStoreChange::UPDATE, form)); 879 list.push_back(PasswordStoreChange(PasswordStoreChange::UPDATE, form));
969 880
970 return list; 881 return list;
971 } 882 }
972 883
973 bool LoginDatabase::RemoveLogin(const PasswordForm& form) { 884 bool LoginDatabase::RemoveLogin(const PasswordForm& form) {
974 if (form.is_public_suffix_match) { 885 if (form.is_public_suffix_match) {
975 // TODO(dvadym): Discuss whether we should allow to remove PSL matched 886 // TODO(dvadym): Discuss whether we should allow to remove PSL matched
976 // credentials. 887 // credentials.
977 return false; 888 return false;
978 } 889 }
979 #if defined(OS_IOS) 890 #if defined(OS_IOS)
980 DeleteEncryptedPassword(form); 891 DeleteEncryptedPassword(form);
981 #endif 892 #endif
982 // Remove a login by UNIQUE-constrained fields. 893 // Remove a login by UNIQUE-constrained fields.
983 sql::Statement s(db_.GetCachedStatement(SQL_FROM_HERE, 894 DCHECK(!delete_statement_.empty());
984 "DELETE FROM logins WHERE " 895 sql::Statement s(
985 "origin_url = ? AND " 896 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()); 897 s.BindString(0, form.origin.spec());
992 s.BindString16(1, form.username_element); 898 s.BindString16(1, form.username_element);
993 s.BindString16(2, form.username_value); 899 s.BindString16(2, form.username_value);
994 s.BindString16(3, form.password_element); 900 s.BindString16(3, form.password_element);
995 s.BindString16(4, form.submit_element); 901 s.BindString(4, form.signon_realm);
996 s.BindString(5, form.signon_realm);
997 902
998 return s.Run() && db_.GetLastChangeCount() > 0; 903 return s.Run() && db_.GetLastChangeCount() > 0;
999 } 904 }
1000 905
1001 bool LoginDatabase::RemoveLoginsCreatedBetween(base::Time delete_begin, 906 bool LoginDatabase::RemoveLoginsCreatedBetween(base::Time delete_begin,
1002 base::Time delete_end) { 907 base::Time delete_end) {
1003 #if defined(OS_IOS) 908 #if defined(OS_IOS)
1004 ScopedVector<autofill::PasswordForm> forms; 909 ScopedVector<autofill::PasswordForm> forms;
1005 if (GetLoginsCreatedBetween(delete_begin, delete_end, &forms)) { 910 if (GetLoginsCreatedBetween(delete_begin, delete_end, &forms)) {
1006 for (size_t i = 0; i < forms.size(); i++) { 911 for (size_t i = 0; i < forms.size(); i++) {
(...skipping 21 matching lines...) Expand all
1028 s.BindInt64(1, 933 s.BindInt64(1,
1029 delete_end.is_null() ? base::Time::Max().ToInternalValue() 934 delete_end.is_null() ? base::Time::Max().ToInternalValue()
1030 : delete_end.ToInternalValue()); 935 : delete_end.ToInternalValue());
1031 936
1032 return s.Run(); 937 return s.Run();
1033 } 938 }
1034 939
1035 bool LoginDatabase::GetAutoSignInLogins( 940 bool LoginDatabase::GetAutoSignInLogins(
1036 ScopedVector<autofill::PasswordForm>* forms) const { 941 ScopedVector<autofill::PasswordForm>* forms) const {
1037 DCHECK(forms); 942 DCHECK(forms);
1038 sql::Statement s(db_.GetCachedStatement( 943 DCHECK(!autosignin_statement_.empty());
1039 SQL_FROM_HERE, 944 sql::Statement s(
1040 "SELECT origin_url, action_url, username_element, username_value, " 945 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 946
1048 return StatementToForms(&s, nullptr, forms); 947 return StatementToForms(&s, nullptr, forms);
1049 } 948 }
1050 949
1051 bool LoginDatabase::DisableAutoSignInForOrigin(const GURL& origin) { 950 bool LoginDatabase::DisableAutoSignInForOrigin(const GURL& origin) {
1052 sql::Statement s(db_.GetCachedStatement( 951 sql::Statement s(db_.GetCachedStatement(
1053 SQL_FROM_HERE, 952 SQL_FROM_HERE,
1054 "UPDATE logins SET skip_zero_click = 1 WHERE origin_url = ?;")); 953 "UPDATE logins SET skip_zero_click = 1 WHERE origin_url = ?;"));
1055 s.BindString(0, origin.spec()); 954 s.BindString(0, origin.spec());
1056 955
(...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after
1128 form->generation_upload_status = 1027 form->generation_upload_status =
1129 static_cast<PasswordForm::GenerationUploadStatus>( 1028 static_cast<PasswordForm::GenerationUploadStatus>(
1130 generation_upload_status_int); 1029 generation_upload_status_int);
1131 return ENCRYPTION_RESULT_SUCCESS; 1030 return ENCRYPTION_RESULT_SUCCESS;
1132 } 1031 }
1133 1032
1134 bool LoginDatabase::GetLogins( 1033 bool LoginDatabase::GetLogins(
1135 const PasswordForm& form, 1034 const PasswordForm& form,
1136 ScopedVector<autofill::PasswordForm>* forms) const { 1035 ScopedVector<autofill::PasswordForm>* forms) const {
1137 DCHECK(forms); 1036 DCHECK(forms);
1138 // You *must* change LoginTableColumns if this query changes.
1139 std::string sql_query =
1140 "SELECT origin_url, action_url, "
1141 "username_element, username_value, "
1142 "password_element, password_value, submit_element, "
1143 "signon_realm, ssl_valid, preferred, date_created, blacklisted_by_user, "
1144 "scheme, password_type, possible_usernames, times_used, form_data, "
1145 "date_synced, display_name, icon_url, "
1146 "federation_url, skip_zero_click, generation_upload_status "
1147 "FROM logins WHERE signon_realm == ? ";
1148 const GURL signon_realm(form.signon_realm); 1037 const GURL signon_realm(form.signon_realm);
1149 std::string registered_domain = GetRegistryControlledDomain(signon_realm); 1038 std::string registered_domain = GetRegistryControlledDomain(signon_realm);
1150 const bool should_PSL_matching_apply = 1039 const bool should_PSL_matching_apply =
1151 form.scheme == PasswordForm::SCHEME_HTML && 1040 form.scheme == PasswordForm::SCHEME_HTML &&
1152 ShouldPSLDomainMatchingApply(registered_domain); 1041 ShouldPSLDomainMatchingApply(registered_domain);
1153 const bool should_federated_apply = form.scheme == PasswordForm::SCHEME_HTML; 1042 const bool should_federated_apply = form.scheme == PasswordForm::SCHEME_HTML;
1154 if (should_PSL_matching_apply) 1043 DCHECK(!get_statement_.empty());
1155 sql_query += "OR signon_realm REGEXP ? "; 1044 DCHECK(!get_statement_psl_.empty());
1156 if (should_federated_apply) 1045 DCHECK(!get_statement_federated_.empty());
1157 sql_query += "OR (signon_realm LIKE ? AND password_type == 2) "; 1046 DCHECK(!get_statement_psl_federated_.empty());
1047 const std::string* sql_query = &get_statement_;
1048 if (should_PSL_matching_apply && should_federated_apply)
1049 sql_query = &get_statement_psl_federated_;
1050 else if (should_PSL_matching_apply)
1051 sql_query = &get_statement_psl_;
1052 else if (should_federated_apply)
1053 sql_query = &get_statement_federated_;
1158 1054
1159 // TODO(nyquist) Consider usage of GetCachedStatement when 1055 // TODO(nyquist) Consider usage of GetCachedStatement when
1160 // http://crbug.com/248608 is fixed. 1056 // http://crbug.com/248608 is fixed.
1161 sql::Statement s(db_.GetUniqueStatement(sql_query.c_str())); 1057 sql::Statement s(db_.GetUniqueStatement(sql_query->c_str()));
1162 s.BindString(0, form.signon_realm); 1058 s.BindString(0, form.signon_realm);
1163 int placeholder = 1; 1059 int placeholder = 1;
1164 1060
1165 // PSL matching only applies to HTML forms. 1061 // PSL matching only applies to HTML forms.
1166 if (should_PSL_matching_apply) { 1062 if (should_PSL_matching_apply) {
1167 // We are extending the original SQL query with one that includes more 1063 // We are extending the original SQL query with one that includes more
1168 // possible matches based on public suffix domain matching. Using a regexp 1064 // possible matches based on public suffix domain matching. Using a regexp
1169 // here is just an optimization to not have to parse all the stored entries 1065 // here is just an optimization to not have to parse all the stored entries
1170 // in the |logins| table. The result (scheme, domain and port) is verified 1066 // in the |logins| table. The result (scheme, domain and port) is verified
1171 // further down using GURL. See the functions SchemeMatches, 1067 // further down using GURL. See the functions SchemeMatches,
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
1203 return StatementToForms( 1099 return StatementToForms(
1204 &s, should_PSL_matching_apply || should_federated_apply ? &form : nullptr, 1100 &s, should_PSL_matching_apply || should_federated_apply ? &form : nullptr,
1205 forms); 1101 forms);
1206 } 1102 }
1207 1103
1208 bool LoginDatabase::GetLoginsCreatedBetween( 1104 bool LoginDatabase::GetLoginsCreatedBetween(
1209 const base::Time begin, 1105 const base::Time begin,
1210 const base::Time end, 1106 const base::Time end,
1211 ScopedVector<autofill::PasswordForm>* forms) const { 1107 ScopedVector<autofill::PasswordForm>* forms) const {
1212 DCHECK(forms); 1108 DCHECK(forms);
1213 sql::Statement s(db_.GetCachedStatement( 1109 DCHECK(!created_statement_.empty());
1214 SQL_FROM_HERE, 1110 sql::Statement s(
1215 "SELECT origin_url, action_url, " 1111 db_.GetCachedStatement(SQL_FROM_HERE, created_statement_.c_str()));
1216 "username_element, username_value, "
1217 "password_element, password_value, submit_element, "
1218 "signon_realm, ssl_valid, preferred, date_created, blacklisted_by_user, "
1219 "scheme, password_type, possible_usernames, times_used, form_data, "
1220 "date_synced, display_name, icon_url, "
1221 "federation_url, skip_zero_click, generation_upload_status FROM logins "
1222 "WHERE date_created >= ? AND date_created < ?"
1223 "ORDER BY origin_url"));
1224 s.BindInt64(0, begin.ToInternalValue()); 1112 s.BindInt64(0, begin.ToInternalValue());
1225 s.BindInt64(1, end.is_null() ? std::numeric_limits<int64_t>::max() 1113 s.BindInt64(1, end.is_null() ? std::numeric_limits<int64_t>::max()
1226 : end.ToInternalValue()); 1114 : end.ToInternalValue());
1227 1115
1228 return StatementToForms(&s, nullptr, forms); 1116 return StatementToForms(&s, nullptr, forms);
1229 } 1117 }
1230 1118
1231 bool LoginDatabase::GetLoginsSyncedBetween( 1119 bool LoginDatabase::GetLoginsSyncedBetween(
1232 const base::Time begin, 1120 const base::Time begin,
1233 const base::Time end, 1121 const base::Time end,
1234 ScopedVector<autofill::PasswordForm>* forms) const { 1122 ScopedVector<autofill::PasswordForm>* forms) const {
1235 DCHECK(forms); 1123 DCHECK(forms);
1236 sql::Statement s(db_.GetCachedStatement( 1124 DCHECK(!synced_statement_.empty());
1237 SQL_FROM_HERE, 1125 sql::Statement s(
1238 "SELECT origin_url, action_url, username_element, username_value, " 1126 db_.GetCachedStatement(SQL_FROM_HERE, synced_statement_.c_str()));
1239 "password_element, password_value, submit_element, signon_realm, "
1240 "ssl_valid, preferred, date_created, blacklisted_by_user, "
1241 "scheme, password_type, possible_usernames, times_used, form_data, "
1242 "date_synced, display_name, icon_url, "
1243 "federation_url, skip_zero_click, generation_upload_status FROM logins "
1244 "WHERE date_synced >= ? AND date_synced < ?"
1245 "ORDER BY origin_url"));
1246 s.BindInt64(0, begin.ToInternalValue()); 1127 s.BindInt64(0, begin.ToInternalValue());
1247 s.BindInt64(1, 1128 s.BindInt64(1,
1248 end.is_null() ? base::Time::Max().ToInternalValue() 1129 end.is_null() ? base::Time::Max().ToInternalValue()
1249 : end.ToInternalValue()); 1130 : end.ToInternalValue());
1250 1131
1251 return StatementToForms(&s, nullptr, forms); 1132 return StatementToForms(&s, nullptr, forms);
1252 } 1133 }
1253 1134
1254 bool LoginDatabase::GetAutofillableLogins( 1135 bool LoginDatabase::GetAutofillableLogins(
1255 ScopedVector<autofill::PasswordForm>* forms) const { 1136 ScopedVector<autofill::PasswordForm>* forms) const {
1256 return GetAllLoginsWithBlacklistSetting(false, forms); 1137 return GetAllLoginsWithBlacklistSetting(false, forms);
1257 } 1138 }
1258 1139
1259 bool LoginDatabase::GetBlacklistLogins( 1140 bool LoginDatabase::GetBlacklistLogins(
1260 ScopedVector<autofill::PasswordForm>* forms) const { 1141 ScopedVector<autofill::PasswordForm>* forms) const {
1261 return GetAllLoginsWithBlacklistSetting(true, forms); 1142 return GetAllLoginsWithBlacklistSetting(true, forms);
1262 } 1143 }
1263 1144
1264 bool LoginDatabase::GetAllLoginsWithBlacklistSetting( 1145 bool LoginDatabase::GetAllLoginsWithBlacklistSetting(
1265 bool blacklisted, 1146 bool blacklisted,
1266 ScopedVector<autofill::PasswordForm>* forms) const { 1147 ScopedVector<autofill::PasswordForm>* forms) const {
1267 DCHECK(forms); 1148 DCHECK(forms);
1268 // You *must* change LoginTableColumns if this query changes. 1149 DCHECK(!blacklisted_statement_.empty());
1269 sql::Statement s(db_.GetCachedStatement( 1150 sql::Statement s(
1270 SQL_FROM_HERE, 1151 db_.GetCachedStatement(SQL_FROM_HERE, blacklisted_statement_.c_str()));
1271 "SELECT origin_url, action_url, "
1272 "username_element, username_value, "
1273 "password_element, password_value, submit_element, "
1274 "signon_realm, ssl_valid, preferred, date_created, blacklisted_by_user, "
1275 "scheme, password_type, possible_usernames, times_used, form_data, "
1276 "date_synced, display_name, icon_url, "
1277 "federation_url, skip_zero_click, generation_upload_status FROM logins "
1278 "WHERE blacklisted_by_user == ? ORDER BY origin_url"));
1279 s.BindInt(0, blacklisted ? 1 : 0); 1152 s.BindInt(0, blacklisted ? 1 : 0);
1280 1153
1281 return StatementToForms(&s, nullptr, forms); 1154 return StatementToForms(&s, nullptr, forms);
1282 } 1155 }
1283 1156
1284 bool LoginDatabase::DeleteAndRecreateDatabaseFile() { 1157 bool LoginDatabase::DeleteAndRecreateDatabaseFile() {
1285 DCHECK(db_.is_open()); 1158 DCHECK(db_.is_open());
1286 meta_table_.Reset(); 1159 meta_table_.Reset();
1287 db_.Close(); 1160 db_.Close();
1288 sql::Connection::Delete(db_path_); 1161 sql::Connection::Delete(db_path_);
1289 return Init(); 1162 return Init();
1290 } 1163 }
1291 1164
1292 std::string LoginDatabase::GetEncryptedPassword( 1165 std::string LoginDatabase::GetEncryptedPassword(
1293 const autofill::PasswordForm& form) const { 1166 const autofill::PasswordForm& form) const {
1167 DCHECK(!encrypted_statement_.empty());
1294 sql::Statement s( 1168 sql::Statement s(
1295 db_.GetCachedStatement(SQL_FROM_HERE, 1169 db_.GetCachedStatement(SQL_FROM_HERE, encrypted_statement_.c_str()));
1296 "SELECT password_value FROM logins WHERE "
1297 "origin_url = ? AND "
1298 "username_element = ? AND "
1299 "username_value = ? AND "
1300 "password_element = ? AND "
1301 "submit_element = ? AND "
1302 "signon_realm = ? "));
1303 1170
1304 s.BindString(0, form.origin.spec()); 1171 s.BindString(0, form.origin.spec());
1305 s.BindString16(1, form.username_element); 1172 s.BindString16(1, form.username_element);
1306 s.BindString16(2, form.username_value); 1173 s.BindString16(2, form.username_value);
1307 s.BindString16(3, form.password_element); 1174 s.BindString16(3, form.password_element);
1308 s.BindString16(4, form.submit_element); 1175 s.BindString(4, form.signon_realm);
1309 s.BindString(5, form.signon_realm);
1310 1176
1311 std::string encrypted_password; 1177 std::string encrypted_password;
1312 if (s.Step()) { 1178 if (s.Step()) {
1313 s.ColumnBlobAsString(0, &encrypted_password); 1179 s.ColumnBlobAsString(0, &encrypted_password);
1314 } 1180 }
1315 return encrypted_password; 1181 return encrypted_password;
1316 } 1182 }
1317 1183
1318 // static 1184 // static
1319 bool LoginDatabase::StatementToForms( 1185 bool LoginDatabase::StatementToForms(
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
1353 if (matched_form) { 1219 if (matched_form) {
1354 UMA_HISTOGRAM_ENUMERATION("PasswordManager.PslDomainMatchTriggering", 1220 UMA_HISTOGRAM_ENUMERATION("PasswordManager.PslDomainMatchTriggering",
1355 psl_domain_match_metric, PSL_DOMAIN_MATCH_COUNT); 1221 psl_domain_match_metric, PSL_DOMAIN_MATCH_COUNT);
1356 } 1222 }
1357 1223
1358 if (!statement->Succeeded()) 1224 if (!statement->Succeeded())
1359 return false; 1225 return false;
1360 return true; 1226 return true;
1361 } 1227 }
1362 1228
1229 void LoginDatabase::InitializeStatementStrings(const SQLTableBuilder& builder) {
1230 // This method may be called multiple times, if Chrome switches backends and
1231 // LoginDatabase::DeleteAndRecreateDatabaseFile ends up being called. In those
1232 // case do not recompute the SQL statements, because they would end up the
1233 // same.
1234 if (!add_statement_.empty())
1235 return;
1236
1237 // Initialize the cached strings.
1238 std::string all_column_names = builder.ListAllColumnNames();
1239 std::string right_amount_of_placeholders =
1240 GeneratePlaceholders(builder.NumberOfColumns());
1241 std::string all_unique_key_column_names = builder.ListAllUniqueKeyNames();
1242 std::string all_nonunique_key_column_names =
1243 builder.ListAllNonuniqueKeyNames();
1244
1245 add_statement_ = "INSERT INTO logins (" + all_column_names + ") VALUES " +
1246 right_amount_of_placeholders;
1247 DCHECK(add_replace_statement_.empty());
1248 add_replace_statement_ = "INSERT OR REPLACE INTO logins (" +
1249 all_column_names + ") VALUES " +
1250 right_amount_of_placeholders;
1251 DCHECK(update_statement_.empty());
1252 update_statement_ = "UPDATE OR REPLACE logins SET " +
1253 all_nonunique_key_column_names + " WHERE " +
1254 all_unique_key_column_names;
1255 DCHECK(delete_statement_.empty());
1256 delete_statement_ = "DELETE FROM logins WHERE " + all_unique_key_column_names;
1257 DCHECK(autosignin_statement_.empty());
1258 autosignin_statement_ = "SELECT " + all_column_names +
1259 " FROM logins "
1260 "WHERE skip_zero_click = 0 ORDER BY origin_url";
1261 DCHECK(get_statement_.empty());
1262 get_statement_ = "SELECT " + all_column_names +
1263 " FROM logins "
1264 "WHERE signon_realm == ?";
1265 std::string psl_statement = "OR signon_realm REGEXP ? ";
1266 std::string federated_statement =
1267 "OR (signon_realm LIKE ? AND password_type == 2) ";
1268 DCHECK(get_statement_psl_.empty());
1269 get_statement_psl_ = get_statement_ + psl_statement;
1270 DCHECK(get_statement_federated_.empty());
1271 get_statement_federated_ = get_statement_ + federated_statement;
1272 DCHECK(get_statement_psl_federated_.empty());
1273 get_statement_psl_federated_ =
1274 get_statement_ + psl_statement + federated_statement;
1275 DCHECK(created_statement_.empty());
1276 created_statement_ =
1277 "SELECT " + all_column_names +
1278 " FROM logins WHERE date_created >= ? AND date_created < "
1279 "? ORDER BY origin_url";
1280 DCHECK(synced_statement_.empty());
1281 synced_statement_ = "SELECT " + all_column_names +
1282 " FROM logins WHERE date_synced >= ? AND date_synced < "
1283 "? ORDER BY origin_url";
1284 DCHECK(blacklisted_statement_.empty());
1285 blacklisted_statement_ =
1286 "SELECT " + all_column_names +
1287 " FROM logins WHERE blacklisted_by_user == ? ORDER BY origin_url";
1288 DCHECK(encrypted_statement_.empty());
1289 encrypted_statement_ =
1290 "SELECT password_value FROM logins WHERE " + all_unique_key_column_names;
1291 }
1292
1363 } // namespace password_manager 1293 } // namespace password_manager
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698