| // Copyright 2013 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "components/autofill/core/browser/webdata/autofill_table.h" |
| |
| #include <stdint.h> |
| |
| #include <algorithm> |
| #include <limits> |
| #include <map> |
| #include <memory> |
| #include <set> |
| #include <utility> |
| |
| #include "base/command_line.h" |
| #include "base/guid.h" |
| #include "base/i18n/case_conversion.h" |
| #include "base/logging.h" |
| #include "base/numerics/safe_conversions.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "base/strings/string_util.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/time/time.h" |
| #include "components/autofill/core/browser/autofill_country.h" |
| #include "components/autofill/core/browser/autofill_profile.h" |
| #include "components/autofill/core/browser/autofill_type.h" |
| #include "components/autofill/core/browser/credit_card.h" |
| #include "components/autofill/core/browser/personal_data_manager.h" |
| #include "components/autofill/core/browser/webdata/autofill_change.h" |
| #include "components/autofill/core/browser/webdata/autofill_entry.h" |
| #include "components/autofill/core/browser/webdata/autofill_table_encryptor.h" |
| #include "components/autofill/core/browser/webdata/autofill_table_encryptor_factory.h" |
| #include "components/autofill/core/common/autofill_clock.h" |
| #include "components/autofill/core/common/autofill_switches.h" |
| #include "components/autofill/core/common/autofill_util.h" |
| #include "components/autofill/core/common/form_field_data.h" |
| #include "components/sync/base/model_type.h" |
| #include "components/sync/protocol/entity_metadata.pb.h" |
| #include "components/sync/protocol/model_type_state.pb.h" |
| #include "components/webdata/common/web_database.h" |
| #include "sql/statement.h" |
| #include "sql/transaction.h" |
| #include "ui/base/l10n/l10n_util.h" |
| #include "ui/gfx/geometry/safe_integer_conversions.h" |
| #include "url/gurl.h" |
| |
| namespace autofill { |
| namespace { |
| |
| // The period after which autocomplete entries should expire in days. |
| const int64_t kExpirationPeriodInDays = 60; |
| |
| // Helper struct for AutofillTable::RemoveFormElementsAddedBetween(). |
| // Contains all the necessary fields to update a row in the 'autofill' table. |
| struct AutofillUpdate { |
| base::string16 name; |
| base::string16 value; |
| time_t date_created; |
| time_t date_last_used; |
| int count; |
| }; |
| |
| // Returns the |data_model|'s value corresponding to the |type|, trimmed to the |
| // maximum length that can be stored in a column of the Autofill database. |
| base::string16 GetInfo(const AutofillDataModel& data_model, |
| ServerFieldType type) { |
| base::string16 data = data_model.GetRawInfo(type); |
| if (data.size() > AutofillTable::kMaxDataLength) |
| return data.substr(0, AutofillTable::kMaxDataLength); |
| |
| return data; |
| } |
| |
| void BindAutofillProfileToStatement(const AutofillProfile& profile, |
| const base::Time& modification_date, |
| sql::Statement* s) { |
| DCHECK(base::IsValidGUID(profile.guid())); |
| int index = 0; |
| s->BindString(index++, profile.guid()); |
| |
| s->BindString16(index++, GetInfo(profile, COMPANY_NAME)); |
| s->BindString16(index++, GetInfo(profile, ADDRESS_HOME_STREET_ADDRESS)); |
| s->BindString16(index++, GetInfo(profile, ADDRESS_HOME_DEPENDENT_LOCALITY)); |
| s->BindString16(index++, GetInfo(profile, ADDRESS_HOME_CITY)); |
| s->BindString16(index++, GetInfo(profile, ADDRESS_HOME_STATE)); |
| s->BindString16(index++, GetInfo(profile, ADDRESS_HOME_ZIP)); |
| s->BindString16(index++, GetInfo(profile, ADDRESS_HOME_SORTING_CODE)); |
| s->BindString16(index++, GetInfo(profile, ADDRESS_HOME_COUNTRY)); |
| s->BindInt64(index++, profile.use_count()); |
| s->BindInt64(index++, profile.use_date().ToTimeT()); |
| s->BindInt64(index++, modification_date.ToTimeT()); |
| s->BindString(index++, profile.origin()); |
| s->BindString(index++, profile.language_code()); |
| s->BindInt64(index++, profile.GetValidityBitfieldValue()); |
| } |
| |
| std::unique_ptr<AutofillProfile> AutofillProfileFromStatement( |
| const sql::Statement& s) { |
| std::unique_ptr<AutofillProfile> profile(new AutofillProfile); |
| int index = 0; |
| profile->set_guid(s.ColumnString(index++)); |
| DCHECK(base::IsValidGUID(profile->guid())); |
| |
| profile->SetRawInfo(COMPANY_NAME, s.ColumnString16(index++)); |
| profile->SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, s.ColumnString16(index++)); |
| profile->SetRawInfo(ADDRESS_HOME_DEPENDENT_LOCALITY, |
| s.ColumnString16(index++)); |
| profile->SetRawInfo(ADDRESS_HOME_CITY, s.ColumnString16(index++)); |
| profile->SetRawInfo(ADDRESS_HOME_STATE, s.ColumnString16(index++)); |
| profile->SetRawInfo(ADDRESS_HOME_ZIP, s.ColumnString16(index++)); |
| profile->SetRawInfo(ADDRESS_HOME_SORTING_CODE, s.ColumnString16(index++)); |
| profile->SetRawInfo(ADDRESS_HOME_COUNTRY, s.ColumnString16(index++)); |
| profile->set_use_count(s.ColumnInt64(index++)); |
| profile->set_use_date(base::Time::FromTimeT(s.ColumnInt64(index++))); |
| profile->set_modification_date(base::Time::FromTimeT(s.ColumnInt64(index++))); |
| profile->set_origin(s.ColumnString(index++)); |
| profile->set_language_code(s.ColumnString(index++)); |
| profile->SetValidityFromBitfieldValue(s.ColumnInt64(index++)); |
| |
| return profile; |
| } |
| |
| void BindEncryptedCardToColumn(sql::Statement* s, |
| int column_index, |
| const base::string16& number, |
| const AutofillTableEncryptor& encryptor) { |
| std::string encrypted_data; |
| encryptor.EncryptString16(number, &encrypted_data); |
| s->BindBlob(column_index, encrypted_data.data(), |
| static_cast<int>(encrypted_data.length())); |
| } |
| |
| void BindCreditCardToStatement(const CreditCard& credit_card, |
| const base::Time& modification_date, |
| sql::Statement* s, |
| const AutofillTableEncryptor& encryptor) { |
| DCHECK(base::IsValidGUID(credit_card.guid())); |
| int index = 0; |
| s->BindString(index++, credit_card.guid()); |
| |
| s->BindString16(index++, GetInfo(credit_card, CREDIT_CARD_NAME_FULL)); |
| s->BindString16(index++, GetInfo(credit_card, CREDIT_CARD_EXP_MONTH)); |
| s->BindString16(index++, GetInfo(credit_card, CREDIT_CARD_EXP_4_DIGIT_YEAR)); |
| BindEncryptedCardToColumn( |
| s, index++, credit_card.GetRawInfo(CREDIT_CARD_NUMBER), encryptor); |
| |
| s->BindInt64(index++, credit_card.use_count()); |
| s->BindInt64(index++, credit_card.use_date().ToTimeT()); |
| s->BindInt64(index++, modification_date.ToTimeT()); |
| s->BindString(index++, credit_card.origin()); |
| s->BindString(index++, credit_card.billing_address_id()); |
| } |
| |
| base::string16 UnencryptedCardFromColumn( |
| const sql::Statement& s, |
| int column_index, |
| const AutofillTableEncryptor& encryptor) { |
| base::string16 credit_card_number; |
| int encrypted_number_len = s.ColumnByteLength(column_index); |
| if (encrypted_number_len) { |
| std::string encrypted_number; |
| encrypted_number.resize(encrypted_number_len); |
| memcpy(&encrypted_number[0], s.ColumnBlob(column_index), |
| encrypted_number_len); |
| encryptor.DecryptString16(encrypted_number, &credit_card_number); |
| } |
| return credit_card_number; |
| } |
| |
| std::unique_ptr<CreditCard> CreditCardFromStatement( |
| const sql::Statement& s, |
| const AutofillTableEncryptor& encryptor) { |
| std::unique_ptr<CreditCard> credit_card(new CreditCard); |
| |
| int index = 0; |
| credit_card->set_guid(s.ColumnString(index++)); |
| DCHECK(base::IsValidGUID(credit_card->guid())); |
| |
| credit_card->SetRawInfo(CREDIT_CARD_NAME_FULL, s.ColumnString16(index++)); |
| credit_card->SetRawInfo(CREDIT_CARD_EXP_MONTH, s.ColumnString16(index++)); |
| credit_card->SetRawInfo(CREDIT_CARD_EXP_4_DIGIT_YEAR, |
| s.ColumnString16(index++)); |
| credit_card->SetRawInfo(CREDIT_CARD_NUMBER, |
| UnencryptedCardFromColumn(s, index++, encryptor)); |
| credit_card->set_use_count(s.ColumnInt64(index++)); |
| credit_card->set_use_date(base::Time::FromTimeT(s.ColumnInt64(index++))); |
| credit_card->set_modification_date( |
| base::Time::FromTimeT(s.ColumnInt64(index++))); |
| credit_card->set_origin(s.ColumnString(index++)); |
| credit_card->set_billing_address_id(s.ColumnString(index++)); |
| credit_card->set_bank_name(s.ColumnString(index++)); |
| |
| return credit_card; |
| } |
| |
| bool AddAutofillProfileNamesToProfile(sql::Database* db, |
| AutofillProfile* profile) { |
| // TODO(estade): update schema so that multiple names are not associated per |
| // unique profile guid. Please refer https://crbug.com/497934. |
| sql::Statement s(db->GetUniqueStatement( |
| "SELECT guid, first_name, middle_name, last_name, full_name " |
| "FROM autofill_profile_names " |
| "WHERE guid=?" |
| "LIMIT 1")); |
| s.BindString(0, profile->guid()); |
| |
| if (!s.is_valid()) |
| return false; |
| |
| if (s.Step()) { |
| DCHECK_EQ(profile->guid(), s.ColumnString(0)); |
| profile->SetRawInfo(NAME_FIRST, s.ColumnString16(1)); |
| profile->SetRawInfo(NAME_MIDDLE, s.ColumnString16(2)); |
| profile->SetRawInfo(NAME_LAST, s.ColumnString16(3)); |
| profile->SetRawInfo(NAME_FULL, s.ColumnString16(4)); |
| } |
| return s.Succeeded(); |
| } |
| |
| bool AddAutofillProfileEmailsToProfile(sql::Database* db, |
| AutofillProfile* profile) { |
| // TODO(estade): update schema so that multiple emails are not associated per |
| // unique profile guid. Please refer https://crbug.com/497934. |
| sql::Statement s(db->GetUniqueStatement( |
| "SELECT guid, email FROM autofill_profile_emails WHERE guid=? LIMIT 1")); |
| s.BindString(0, profile->guid()); |
| |
| if (!s.is_valid()) |
| return false; |
| |
| if (s.Step()) { |
| DCHECK_EQ(profile->guid(), s.ColumnString(0)); |
| profile->SetRawInfo(EMAIL_ADDRESS, s.ColumnString16(1)); |
| } |
| return s.Succeeded(); |
| } |
| |
| bool AddAutofillProfilePhonesToProfile(sql::Database* db, |
| AutofillProfile* profile) { |
| // TODO(estade): update schema so that multiple phone numbers are not |
| // associated per unique profile guid. Please refer https://crbug.com/497934. |
| sql::Statement s(db->GetUniqueStatement( |
| "SELECT guid, number FROM autofill_profile_phones WHERE guid=? LIMIT 1")); |
| s.BindString(0, profile->guid()); |
| |
| if (!s.is_valid()) |
| return false; |
| |
| if (s.Step()) { |
| DCHECK_EQ(profile->guid(), s.ColumnString(0)); |
| profile->SetRawInfo(PHONE_HOME_WHOLE_NUMBER, s.ColumnString16(1)); |
| } |
| return s.Succeeded(); |
| } |
| |
| bool AddAutofillProfileNames(const AutofillProfile& profile, |
| sql::Database* db) { |
| // Add the new name. |
| sql::Statement s(db->GetUniqueStatement( |
| "INSERT INTO autofill_profile_names" |
| " (guid, first_name, middle_name, last_name, full_name) " |
| "VALUES (?,?,?,?,?)")); |
| s.BindString(0, profile.guid()); |
| s.BindString16(1, profile.GetRawInfo(NAME_FIRST)); |
| s.BindString16(2, profile.GetRawInfo(NAME_MIDDLE)); |
| s.BindString16(3, profile.GetRawInfo(NAME_LAST)); |
| s.BindString16(4, profile.GetRawInfo(NAME_FULL)); |
| |
| return s.Run(); |
| } |
| |
| bool AddAutofillProfileEmails(const AutofillProfile& profile, |
| sql::Database* db) { |
| // Add the new email. |
| sql::Statement s(db->GetUniqueStatement( |
| "INSERT INTO autofill_profile_emails (guid, email) VALUES (?,?)")); |
| s.BindString(0, profile.guid()); |
| s.BindString16(1, profile.GetRawInfo(EMAIL_ADDRESS)); |
| |
| return s.Run(); |
| } |
| |
| bool AddAutofillProfilePhones(const AutofillProfile& profile, |
| sql::Database* db) { |
| // Add the new number. |
| sql::Statement s(db->GetUniqueStatement( |
| "INSERT INTO autofill_profile_phones (guid, number) VALUES (?,?)")); |
| s.BindString(0, profile.guid()); |
| s.BindString16(1, profile.GetRawInfo(PHONE_HOME_WHOLE_NUMBER)); |
| |
| return s.Run(); |
| } |
| |
| bool AddAutofillProfilePieces(const AutofillProfile& profile, |
| sql::Database* db) { |
| if (!AddAutofillProfileNames(profile, db)) |
| return false; |
| |
| if (!AddAutofillProfileEmails(profile, db)) |
| return false; |
| |
| if (!AddAutofillProfilePhones(profile, db)) |
| return false; |
| |
| return true; |
| } |
| |
| bool RemoveAutofillProfilePieces(const std::string& guid, sql::Database* db) { |
| sql::Statement s1(db->GetUniqueStatement( |
| "DELETE FROM autofill_profile_names WHERE guid = ?")); |
| s1.BindString(0, guid); |
| |
| if (!s1.Run()) |
| return false; |
| |
| sql::Statement s2(db->GetUniqueStatement( |
| "DELETE FROM autofill_profile_emails WHERE guid = ?")); |
| s2.BindString(0, guid); |
| |
| if (!s2.Run()) |
| return false; |
| |
| sql::Statement s3(db->GetUniqueStatement( |
| "DELETE FROM autofill_profile_phones WHERE guid = ?")); |
| s3.BindString(0, guid); |
| |
| return s3.Run(); |
| } |
| |
| WebDatabaseTable::TypeKey GetKey() { |
| // We just need a unique constant. Use the address of a static that |
| // COMDAT folding won't touch in an optimizing linker. |
| static int table_key = 0; |
| return reinterpret_cast<void*>(&table_key); |
| } |
| |
| time_t GetEndTime(const base::Time& end) { |
| if (end.is_null() || end == base::Time::Max()) |
| return std::numeric_limits<time_t>::max(); |
| |
| return end.ToTimeT(); |
| } |
| |
| std::string ServerStatusEnumToString(CreditCard::ServerStatus status) { |
| switch (status) { |
| case CreditCard::EXPIRED: |
| return "EXPIRED"; |
| |
| case CreditCard::OK: |
| return "OK"; |
| } |
| |
| NOTREACHED(); |
| return "OK"; |
| } |
| |
| CreditCard::ServerStatus ServerStatusStringToEnum(const std::string& status) { |
| if (status == "EXPIRED") |
| return CreditCard::EXPIRED; |
| |
| DCHECK_EQ("OK", status); |
| return CreditCard::OK; |
| } |
| |
| // Returns |s| with |escaper| in front of each of occurrence of a character from |
| // |special_chars|. Any occurrence of |escaper| in |s| is doubled. For example, |
| // Substitute("hello_world!", "_%", '!'') returns "hello!_world!!". |
| base::string16 Substitute(const base::string16& s, |
| const base::string16& special_chars, |
| const base::char16& escaper) { |
| // Prepend |escaper| to the list of |special_chars|. |
| base::string16 escape_wildcards(special_chars); |
| escape_wildcards.insert(escape_wildcards.begin(), escaper); |
| |
| // Prepend the |escaper| just before |special_chars| in |s|. |
| base::string16 result(s); |
| for (base::char16 c : escape_wildcards) { |
| for (size_t pos = 0; (pos = result.find(c, pos)) != base::string16::npos; |
| pos += 2) { |
| result.insert(result.begin() + pos, escaper); |
| } |
| } |
| |
| return result; |
| } |
| |
| } // namespace |
| |
| // static |
| const size_t AutofillTable::kMaxDataLength = 1024; |
| |
| AutofillTable::AutofillTable() |
| : autofill_table_encryptor_( |
| AutofillTableEncryptorFactory::GetInstance()->Create()) { |
| DCHECK(autofill_table_encryptor_); |
| } |
| |
| AutofillTable::~AutofillTable() {} |
| |
| AutofillTable* AutofillTable::FromWebDatabase(WebDatabase* db) { |
| return static_cast<AutofillTable*>(db->GetTable(GetKey())); |
| } |
| |
| WebDatabaseTable::TypeKey AutofillTable::GetTypeKey() const { |
| return GetKey(); |
| } |
| |
| bool AutofillTable::CreateTablesIfNecessary() { |
| return (InitMainTable() && InitCreditCardsTable() && InitProfilesTable() && |
| InitProfileNamesTable() && InitProfileEmailsTable() && |
| InitProfilePhonesTable() && InitProfileTrashTable() && |
| InitMaskedCreditCardsTable() && InitUnmaskedCreditCardsTable() && |
| InitServerCardMetadataTable() && InitServerAddressesTable() && |
| InitServerAddressMetadataTable() && InitAutofillSyncMetadataTable() && |
| InitModelTypeStateTable()); |
| } |
| |
| bool AutofillTable::IsSyncable() { |
| return true; |
| } |
| |
| bool AutofillTable::MigrateToVersion(int version, |
| bool* update_compatible_version) { |
| // Migrate if necessary. |
| switch (version) { |
| case 54: |
| *update_compatible_version = true; |
| return MigrateToVersion54AddI18nFieldsAndRemoveDeprecatedFields(); |
| case 55: |
| *update_compatible_version = true; |
| return MigrateToVersion55MergeAutofillDatesTable(); |
| case 56: |
| *update_compatible_version = true; |
| return MigrateToVersion56AddProfileLanguageCodeForFormatting(); |
| case 57: |
| *update_compatible_version = true; |
| return MigrateToVersion57AddFullNameField(); |
| case 60: |
| *update_compatible_version = false; |
| return MigrateToVersion60AddServerCards(); |
| case 61: |
| *update_compatible_version = false; |
| return MigrateToVersion61AddUsageStats(); |
| case 62: |
| *update_compatible_version = false; |
| return MigrateToVersion62AddUsageStatsForUnmaskedCards(); |
| case 63: |
| *update_compatible_version = false; |
| return MigrateToVersion63AddServerRecipientName(); |
| case 64: |
| *update_compatible_version = false; |
| return MigrateToVersion64AddUnmaskDate(); |
| case 65: |
| *update_compatible_version = false; |
| return MigrateToVersion65AddServerMetadataTables(); |
| case 66: |
| *update_compatible_version = false; |
| return MigrateToVersion66AddCardBillingAddress(); |
| case 67: |
| *update_compatible_version = false; |
| return MigrateToVersion67AddMaskedCardBillingAddress(); |
| case 70: |
| *update_compatible_version = false; |
| return MigrateToVersion70AddSyncMetadata(); |
| case 71: |
| *update_compatible_version = true; |
| return MigrateToVersion71AddHasConvertedAndBillingAddressIdMetadata(); |
| case 72: |
| *update_compatible_version = true; |
| return MigrateToVersion72RenameCardTypeToIssuerNetwork(); |
| case 73: |
| *update_compatible_version = false; |
| return MigrateToVersion73AddMaskedCardBankName(); |
| case 74: |
| *update_compatible_version = false; |
| return MigrateToVersion74AddServerCardTypeColumn(); |
| case 75: |
| *update_compatible_version = false; |
| return MigrateToVersion75AddProfileValidityBitfieldColumn(); |
| case 78: |
| *update_compatible_version = true; |
| return MigrateToVersion78AddModelTypeColumns(); |
| } |
| return true; |
| } |
| |
| bool AutofillTable::AddFormFieldValues( |
| const std::vector<FormFieldData>& elements, |
| std::vector<AutofillChange>* changes) { |
| return AddFormFieldValuesTime(elements, changes, AutofillClock::Now()); |
| } |
| |
| bool AutofillTable::AddFormFieldValue(const FormFieldData& element, |
| std::vector<AutofillChange>* changes) { |
| return AddFormFieldValueTime(element, changes, AutofillClock::Now()); |
| } |
| |
| bool AutofillTable::GetFormValuesForElementName( |
| const base::string16& name, |
| const base::string16& prefix, |
| std::vector<base::string16>* values, |
| int limit) { |
| DCHECK(values); |
| bool succeeded = false; |
| |
| if (prefix.empty()) { |
| sql::Statement s; |
| s.Assign( |
| db_->GetUniqueStatement("SELECT value FROM autofill WHERE name = ? " |
| "ORDER BY count DESC LIMIT ?")); |
| s.BindString16(0, name); |
| s.BindInt(1, limit); |
| |
| values->clear(); |
| while (s.Step()) |
| values->push_back(s.ColumnString16(0)); |
| |
| succeeded = s.Succeeded(); |
| } else { |
| base::string16 prefix_lower = base::i18n::ToLower(prefix); |
| base::string16 next_prefix = prefix_lower; |
| next_prefix.back()++; |
| |
| sql::Statement s1; |
| s1.Assign( |
| db_->GetUniqueStatement("SELECT value FROM autofill " |
| "WHERE name = ? AND " |
| "value_lower >= ? AND " |
| "value_lower < ? " |
| "ORDER BY count DESC " |
| "LIMIT ?")); |
| s1.BindString16(0, name); |
| s1.BindString16(1, prefix_lower); |
| s1.BindString16(2, next_prefix); |
| s1.BindInt(3, limit); |
| |
| values->clear(); |
| while (s1.Step()) |
| values->push_back(s1.ColumnString16(0)); |
| |
| succeeded = s1.Succeeded(); |
| |
| if (IsFeatureSubstringMatchEnabled()) { |
| sql::Statement s2; |
| s2.Assign(db_->GetUniqueStatement( |
| "SELECT value FROM autofill " |
| "WHERE name = ? AND (" |
| " value LIKE '% ' || :prefix || '%' ESCAPE '!' OR " |
| " value LIKE '%.' || :prefix || '%' ESCAPE '!' OR " |
| " value LIKE '%,' || :prefix || '%' ESCAPE '!' OR " |
| " value LIKE '%-' || :prefix || '%' ESCAPE '!' OR " |
| " value LIKE '%@' || :prefix || '%' ESCAPE '!' OR " |
| " value LIKE '%!_' || :prefix || '%' ESCAPE '!' ) " |
| "ORDER BY count DESC " |
| "LIMIT ?")); |
| |
| s2.BindString16(0, name); |
| // escaper as L'!' -> 0x21. |
| s2.BindString16(1, |
| Substitute(prefix_lower, base::ASCIIToUTF16("_%"), 0x21)); |
| s2.BindInt(2, limit); |
| while (s2.Step()) |
| values->push_back(s2.ColumnString16(0)); |
| |
| succeeded &= s2.Succeeded(); |
| } |
| } |
| |
| return succeeded; |
| } |
| |
| bool AutofillTable::RemoveFormElementsAddedBetween( |
| const base::Time& delete_begin, |
| const base::Time& delete_end, |
| std::vector<AutofillChange>* changes) { |
| const time_t delete_begin_time_t = delete_begin.ToTimeT(); |
| const time_t delete_end_time_t = GetEndTime(delete_end); |
| |
| // Query for the name, value, count, and access dates of all form elements |
| // that were used between the given times. |
| sql::Statement s(db_->GetUniqueStatement( |
| "SELECT name, value, count, date_created, date_last_used FROM autofill " |
| "WHERE (date_created >= ? AND date_created < ?) OR " |
| " (date_last_used >= ? AND date_last_used < ?)")); |
| s.BindInt64(0, delete_begin_time_t); |
| s.BindInt64(1, delete_end_time_t); |
| s.BindInt64(2, delete_begin_time_t); |
| s.BindInt64(3, delete_end_time_t); |
| |
| std::vector<AutofillUpdate> updates; |
| std::vector<AutofillChange> tentative_changes; |
| while (s.Step()) { |
| base::string16 name = s.ColumnString16(0); |
| base::string16 value = s.ColumnString16(1); |
| int count = s.ColumnInt(2); |
| time_t date_created_time_t = s.ColumnInt64(3); |
| time_t date_last_used_time_t = s.ColumnInt64(4); |
| |
| // If *all* uses of the element were between |delete_begin| and |
| // |delete_end|, then delete the element. Otherwise, update the use |
| // timestamps and use count. |
| AutofillChange::Type change_type; |
| if (date_created_time_t >= delete_begin_time_t && |
| date_last_used_time_t < delete_end_time_t) { |
| change_type = AutofillChange::REMOVE; |
| } else { |
| change_type = AutofillChange::UPDATE; |
| |
| // For all updated elements, set either date_created or date_last_used so |
| // that the range [date_created, date_last_used] no longer overlaps with |
| // [delete_begin, delete_end). Update the count by interpolating. |
| // Precisely, compute the average amount of time between increments to the |
| // count in the original range [date_created, date_last_used]: |
| // avg_delta = (date_last_used_orig - date_created_orig) / (count - 1) |
| // The count can be expressed as |
| // count = 1 + (date_last_used - date_created) / avg_delta |
| // Hence, update the count to |
| // count_new = 1 + (date_last_used_new - date_created_new) / avg_delta |
| // = 1 + ((count - 1) * |
| // (date_last_used_new - date_created_new) / |
| // (date_last_used_orig - date_created_orig)) |
| // Interpolating might not give a result that completely accurately |
| // reflects the user's history, but it's the best that can be done given |
| // the information in the database. |
| AutofillUpdate updated_entry; |
| updated_entry.name = name; |
| updated_entry.value = value; |
| updated_entry.date_created = date_created_time_t < delete_begin_time_t |
| ? date_created_time_t |
| : delete_end_time_t; |
| updated_entry.date_last_used = date_last_used_time_t >= delete_end_time_t |
| ? date_last_used_time_t |
| : delete_begin_time_t - 1; |
| updated_entry.count = |
| 1 + gfx::ToRoundedInt( |
| 1.0 * (count - 1) * |
| (updated_entry.date_last_used - updated_entry.date_created) / |
| (date_last_used_time_t - date_created_time_t)); |
| updates.push_back(updated_entry); |
| } |
| |
| tentative_changes.push_back( |
| AutofillChange(change_type, AutofillKey(name, value))); |
| } |
| if (!s.Succeeded()) |
| return false; |
| |
| // As a single transaction, remove or update the elements appropriately. |
| sql::Statement s_delete(db_->GetUniqueStatement( |
| "DELETE FROM autofill WHERE date_created >= ? AND date_last_used < ?")); |
| s_delete.BindInt64(0, delete_begin_time_t); |
| s_delete.BindInt64(1, delete_end_time_t); |
| sql::Transaction transaction(db_); |
| if (!transaction.Begin()) |
| return false; |
| if (!s_delete.Run()) |
| return false; |
| for (size_t i = 0; i < updates.size(); ++i) { |
| sql::Statement s_update(db_->GetUniqueStatement( |
| "UPDATE autofill SET date_created = ?, date_last_used = ?, count = ?" |
| "WHERE name = ? AND value = ?")); |
| s_update.BindInt64(0, updates[i].date_created); |
| s_update.BindInt64(1, updates[i].date_last_used); |
| s_update.BindInt(2, updates[i].count); |
| s_update.BindString16(3, updates[i].name); |
| s_update.BindString16(4, updates[i].value); |
| if (!s_update.Run()) |
| return false; |
| } |
| if (!transaction.Commit()) |
| return false; |
| |
| *changes = tentative_changes; |
| return true; |
| } |
| |
| bool AutofillTable::RemoveExpiredFormElements( |
| std::vector<AutofillChange>* changes) { |
| base::Time expiration_time = |
| AutofillClock::Now() - base::TimeDelta::FromDays(kExpirationPeriodInDays); |
| |
| // Query for the name and value of all form elements that were last used |
| // before the |expiration_time|. |
| sql::Statement select_for_delete(db_->GetUniqueStatement( |
| "SELECT name, value FROM autofill WHERE date_last_used < ?")); |
| select_for_delete.BindInt64(0, expiration_time.ToTimeT()); |
| std::vector<AutofillChange> tentative_changes; |
| while (select_for_delete.Step()) { |
| base::string16 name = select_for_delete.ColumnString16(0); |
| base::string16 value = select_for_delete.ColumnString16(1); |
| tentative_changes.push_back( |
| AutofillChange(AutofillChange::REMOVE, AutofillKey(name, value))); |
| } |
| |
| if (!select_for_delete.Succeeded()) |
| return false; |
| |
| sql::Statement delete_data_statement( |
| db_->GetUniqueStatement("DELETE FROM autofill WHERE date_last_used < ?")); |
| delete_data_statement.BindInt64(0, expiration_time.ToTimeT()); |
| if (!delete_data_statement.Run()) |
| return false; |
| |
| *changes = tentative_changes; |
| return true; |
| } |
| |
| bool AutofillTable::RemoveFormElement(const base::string16& name, |
| const base::string16& value) { |
| sql::Statement s(db_->GetUniqueStatement( |
| "DELETE FROM autofill WHERE name = ? AND value= ?")); |
| s.BindString16(0, name); |
| s.BindString16(1, value); |
| return s.Run(); |
| } |
| |
| int AutofillTable::GetCountOfValuesContainedBetween(const base::Time& begin, |
| const base::Time& end) { |
| const time_t begin_time_t = begin.ToTimeT(); |
| const time_t end_time_t = GetEndTime(end); |
| |
| sql::Statement s(db_->GetUniqueStatement( |
| "SELECT COUNT(DISTINCT(value1)) FROM ( " |
| " SELECT value AS value1 FROM autofill " |
| " WHERE NOT EXISTS ( " |
| " SELECT value AS value2, date_created, date_last_used FROM autofill " |
| " WHERE value1 = value2 AND " |
| " (date_created < ? OR date_last_used >= ?)))")); |
| s.BindInt64(0, begin_time_t); |
| s.BindInt64(1, end_time_t); |
| |
| if (!s.Step()) { |
| NOTREACHED(); |
| return false; |
| } |
| return s.ColumnInt(0); |
| } |
| |
| bool AutofillTable::GetAllAutofillEntries(std::vector<AutofillEntry>* entries) { |
| sql::Statement s(db_->GetUniqueStatement( |
| "SELECT name, value, date_created, date_last_used FROM autofill")); |
| |
| while (s.Step()) { |
| base::string16 name = s.ColumnString16(0); |
| base::string16 value = s.ColumnString16(1); |
| base::Time date_created = base::Time::FromTimeT(s.ColumnInt64(2)); |
| base::Time date_last_used = base::Time::FromTimeT(s.ColumnInt64(3)); |
| entries->push_back( |
| AutofillEntry(AutofillKey(name, value), date_created, date_last_used)); |
| } |
| |
| return s.Succeeded(); |
| } |
| |
| bool AutofillTable::GetAutofillTimestamps(const base::string16& name, |
| const base::string16& value, |
| base::Time* date_created, |
| base::Time* date_last_used) { |
| sql::Statement s(db_->GetUniqueStatement( |
| "SELECT date_created, date_last_used FROM autofill " |
| "WHERE name = ? AND value = ?")); |
| s.BindString16(0, name); |
| s.BindString16(1, value); |
| if (!s.Step()) |
| return false; |
| |
| *date_created = base::Time::FromTimeT(s.ColumnInt64(0)); |
| *date_last_used = base::Time::FromTimeT(s.ColumnInt64(1)); |
| |
| DCHECK(!s.Step()); |
| return true; |
| } |
| |
| bool AutofillTable::UpdateAutofillEntries( |
| const std::vector<AutofillEntry>& entries) { |
| if (entries.empty()) |
| return true; |
| |
| // Remove all existing entries. |
| for (size_t i = 0; i < entries.size(); ++i) { |
| sql::Statement s(db_->GetUniqueStatement( |
| "DELETE FROM autofill WHERE name = ? AND value = ?")); |
| s.BindString16(0, entries[i].key().name()); |
| s.BindString16(1, entries[i].key().value()); |
| if (!s.Run()) |
| return false; |
| } |
| |
| // Insert all the supplied autofill entries. |
| for (size_t i = 0; i < entries.size(); ++i) { |
| if (!InsertAutofillEntry(entries[i])) |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool AutofillTable::AddAutofillProfile(const AutofillProfile& profile) { |
| if (IsAutofillGUIDInTrash(profile.guid())) |
| return true; |
| |
| sql::Statement s(db_->GetUniqueStatement( |
| "INSERT INTO autofill_profiles" |
| "(guid, company_name, street_address, dependent_locality, city, state," |
| " zipcode, sorting_code, country_code, use_count, use_date, " |
| " date_modified, origin, language_code, validity_bitfield)" |
| "VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)")); |
| BindAutofillProfileToStatement(profile, AutofillClock::Now(), &s); |
| |
| if (!s.Run()) |
| return false; |
| |
| return AddAutofillProfilePieces(profile, db_); |
| } |
| |
| bool AutofillTable::UpdateAutofillProfile(const AutofillProfile& profile) { |
| DCHECK(base::IsValidGUID(profile.guid())); |
| |
| // Don't update anything until the trash has been emptied. There may be |
| // pending modifications to process. |
| if (!IsAutofillProfilesTrashEmpty()) |
| return true; |
| |
| std::unique_ptr<AutofillProfile> old_profile = |
| GetAutofillProfile(profile.guid()); |
| if (!old_profile) |
| return false; |
| |
| bool update_modification_date = *old_profile != profile; |
| |
| sql::Statement s(db_->GetUniqueStatement( |
| "UPDATE autofill_profiles " |
| "SET guid=?, company_name=?, street_address=?, dependent_locality=?, " |
| " city=?, state=?, zipcode=?, sorting_code=?, country_code=?, " |
| " use_count=?, use_date=?, date_modified=?, origin=?, " |
| " language_code=?, validity_bitfield=? " |
| "WHERE guid=?")); |
| BindAutofillProfileToStatement(profile, |
| update_modification_date |
| ? AutofillClock::Now() |
| : old_profile->modification_date(), |
| &s); |
| s.BindString(15, profile.guid()); |
| |
| bool result = s.Run(); |
| DCHECK_GT(db_->GetLastChangeCount(), 0); |
| if (!result) |
| return result; |
| |
| // Remove the old names, emails, and phone numbers. |
| if (!RemoveAutofillProfilePieces(profile.guid(), db_)) |
| return false; |
| |
| return AddAutofillProfilePieces(profile, db_); |
| } |
| |
| bool AutofillTable::RemoveAutofillProfile(const std::string& guid) { |
| DCHECK(base::IsValidGUID(guid)); |
| |
| if (IsAutofillGUIDInTrash(guid)) { |
| sql::Statement s_trash(db_->GetUniqueStatement( |
| "DELETE FROM autofill_profiles_trash WHERE guid = ?")); |
| s_trash.BindString(0, guid); |
| |
| bool success = s_trash.Run(); |
| DCHECK_GT(db_->GetLastChangeCount(), 0) << "Expected item in trash"; |
| return success; |
| } |
| |
| sql::Statement s( |
| db_->GetUniqueStatement("DELETE FROM autofill_profiles WHERE guid = ?")); |
| s.BindString(0, guid); |
| |
| if (!s.Run()) |
| return false; |
| |
| return RemoveAutofillProfilePieces(guid, db_); |
| } |
| |
| std::unique_ptr<AutofillProfile> AutofillTable::GetAutofillProfile( |
| const std::string& guid) { |
| DCHECK(base::IsValidGUID(guid)); |
| sql::Statement s(db_->GetUniqueStatement( |
| "SELECT guid, company_name, street_address, dependent_locality, city," |
| " state, zipcode, sorting_code, country_code, use_count, use_date," |
| " date_modified, origin, language_code, validity_bitfield " |
| "FROM autofill_profiles " |
| "WHERE guid=?")); |
| s.BindString(0, guid); |
| |
| std::unique_ptr<AutofillProfile> p; |
| if (!s.Step()) |
| return p; |
| |
| p = AutofillProfileFromStatement(s); |
| |
| // Get associated name info. |
| AddAutofillProfileNamesToProfile(db_, p.get()); |
| |
| // Get associated email info. |
| AddAutofillProfileEmailsToProfile(db_, p.get()); |
| |
| // Get associated phone info. |
| AddAutofillProfilePhonesToProfile(db_, p.get()); |
| |
| return p; |
| } |
| |
| bool AutofillTable::GetAutofillProfiles( |
| std::vector<std::unique_ptr<AutofillProfile>>* profiles) { |
| DCHECK(profiles); |
| profiles->clear(); |
| |
| sql::Statement s(db_->GetUniqueStatement( |
| "SELECT guid FROM autofill_profiles ORDER BY date_modified DESC, guid")); |
| |
| while (s.Step()) { |
| std::string guid = s.ColumnString(0); |
| std::unique_ptr<AutofillProfile> profile = GetAutofillProfile(guid); |
| if (!profile) |
| return false; |
| profiles->push_back(std::move(profile)); |
| } |
| |
| return s.Succeeded(); |
| } |
| |
| bool AutofillTable::GetServerProfiles( |
| std::vector<std::unique_ptr<AutofillProfile>>* profiles) const { |
| profiles->clear(); |
| |
| sql::Statement s(db_->GetUniqueStatement( |
| "SELECT " |
| "id," |
| "use_count," |
| "use_date," |
| "recipient_name," |
| "company_name," |
| "street_address," |
| "address_1," // ADDRESS_HOME_STATE |
| "address_2," // ADDRESS_HOME_CITY |
| "address_3," // ADDRESS_HOME_DEPENDENT_LOCALITY |
| "address_4," // Not supported in AutofillProfile yet. |
| "postal_code," // ADDRESS_HOME_ZIP |
| "sorting_code," // ADDRESS_HOME_SORTING_CODE |
| "country_code," // ADDRESS_HOME_COUNTRY |
| "phone_number," // PHONE_HOME_WHOLE_NUMBER |
| "language_code, " |
| "has_converted " |
| "FROM server_addresses addresses " |
| "LEFT OUTER JOIN server_address_metadata USING (id)")); |
| |
| while (s.Step()) { |
| int index = 0; |
| std::unique_ptr<AutofillProfile> profile = |
| std::make_unique<AutofillProfile>(AutofillProfile::SERVER_PROFILE, |
| s.ColumnString(index++)); |
| profile->set_use_count(s.ColumnInt64(index++)); |
| profile->set_use_date( |
| base::Time::FromInternalValue(s.ColumnInt64(index++))); |
| // Modification date is not tracked for server profiles. Explicitly set it |
| // here to override the default value of AutofillClock::Now(). |
| profile->set_modification_date(base::Time()); |
| |
| base::string16 recipient_name = s.ColumnString16(index++); |
| profile->SetRawInfo(COMPANY_NAME, s.ColumnString16(index++)); |
| profile->SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, s.ColumnString16(index++)); |
| profile->SetRawInfo(ADDRESS_HOME_STATE, s.ColumnString16(index++)); |
| profile->SetRawInfo(ADDRESS_HOME_CITY, s.ColumnString16(index++)); |
| profile->SetRawInfo(ADDRESS_HOME_DEPENDENT_LOCALITY, |
| s.ColumnString16(index++)); |
| index++; // Skip address_4 which we haven't added to AutofillProfile yet. |
| profile->SetRawInfo(ADDRESS_HOME_ZIP, s.ColumnString16(index++)); |
| profile->SetRawInfo(ADDRESS_HOME_SORTING_CODE, s.ColumnString16(index++)); |
| profile->SetRawInfo(ADDRESS_HOME_COUNTRY, s.ColumnString16(index++)); |
| base::string16 phone_number = s.ColumnString16(index++); |
| profile->set_language_code(s.ColumnString(index++)); |
| profile->set_has_converted(s.ColumnBool(index++)); |
| |
| // SetInfo instead of SetRawInfo so the constituent pieces will be parsed |
| // for these data types. |
| profile->SetInfo(NAME_FULL, recipient_name, profile->language_code()); |
| profile->SetInfo(PHONE_HOME_WHOLE_NUMBER, phone_number, |
| profile->language_code()); |
| |
| profiles->push_back(std::move(profile)); |
| } |
| |
| return s.Succeeded(); |
| } |
| |
| void AutofillTable::SetServerProfiles( |
| const std::vector<AutofillProfile>& profiles) { |
| sql::Transaction transaction(db_); |
| if (!transaction.Begin()) |
| return; |
| |
| // Delete all old ones first. |
| sql::Statement delete_old( |
| db_->GetUniqueStatement("DELETE FROM server_addresses")); |
| delete_old.Run(); |
| |
| sql::Statement insert(db_->GetUniqueStatement( |
| "INSERT INTO server_addresses(" |
| "id," |
| "recipient_name," |
| "company_name," |
| "street_address," |
| "address_1," // ADDRESS_HOME_STATE |
| "address_2," // ADDRESS_HOME_CITY |
| "address_3," // ADDRESS_HOME_DEPENDENT_LOCALITY |
| "address_4," // Not supported in AutofillProfile yet. |
| "postal_code," // ADDRESS_HOME_ZIP |
| "sorting_code," // ADDRESS_HOME_SORTING_CODE |
| "country_code," // ADDRESS_HOME_COUNTRY |
| "phone_number," // PHONE_HOME_WHOLE_NUMBER |
| "language_code) " |
| "VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)")); |
| for (const auto& profile : profiles) { |
| DCHECK(profile.record_type() == AutofillProfile::SERVER_PROFILE); |
| |
| int index = 0; |
| insert.BindString(index++, profile.server_id()); |
| insert.BindString16(index++, profile.GetRawInfo(NAME_FULL)); |
| insert.BindString16(index++, profile.GetRawInfo(COMPANY_NAME)); |
| insert.BindString16(index++, |
| profile.GetRawInfo(ADDRESS_HOME_STREET_ADDRESS)); |
| insert.BindString16(index++, profile.GetRawInfo(ADDRESS_HOME_STATE)); |
| insert.BindString16(index++, profile.GetRawInfo(ADDRESS_HOME_CITY)); |
| insert.BindString16(index++, |
| profile.GetRawInfo(ADDRESS_HOME_DEPENDENT_LOCALITY)); |
| index++; // SKip address_4 which we haven't added to AutofillProfile yet. |
| insert.BindString16(index++, profile.GetRawInfo(ADDRESS_HOME_ZIP)); |
| insert.BindString16(index++, profile.GetRawInfo(ADDRESS_HOME_SORTING_CODE)); |
| insert.BindString16(index++, profile.GetRawInfo(ADDRESS_HOME_COUNTRY)); |
| insert.BindString16(index++, profile.GetRawInfo(PHONE_HOME_WHOLE_NUMBER)); |
| insert.BindString(index++, profile.language_code()); |
| |
| insert.Run(); |
| insert.Reset(true); |
| |
| // Save the use count and use date of the profile. |
| UpdateServerAddressMetadata(profile); |
| } |
| |
| // Delete metadata that's no longer relevant. |
| sql::Statement metadata_delete(db_->GetUniqueStatement( |
| "DELETE FROM server_address_metadata WHERE id NOT IN " |
| "(SELECT id FROM server_addresses)")); |
| metadata_delete.Run(); |
| |
| transaction.Commit(); |
| } |
| |
| bool AutofillTable::AddCreditCard(const CreditCard& credit_card) { |
| sql::Statement s(db_->GetUniqueStatement( |
| "INSERT INTO credit_cards" |
| "(guid, name_on_card, expiration_month, expiration_year, " |
| " card_number_encrypted, use_count, use_date, date_modified, origin," |
| " billing_address_id)" |
| "VALUES (?,?,?,?,?,?,?,?,?,?)")); |
| BindCreditCardToStatement(credit_card, AutofillClock::Now(), &s, |
| *autofill_table_encryptor_); |
| |
| if (!s.Run()) |
| return false; |
| |
| DCHECK_GT(db_->GetLastChangeCount(), 0); |
| return true; |
| } |
| |
| bool AutofillTable::UpdateCreditCard(const CreditCard& credit_card) { |
| DCHECK(base::IsValidGUID(credit_card.guid())); |
| |
| std::unique_ptr<CreditCard> old_credit_card = |
| GetCreditCard(credit_card.guid()); |
| if (!old_credit_card) |
| return false; |
| |
| bool update_modification_date = *old_credit_card != credit_card; |
| |
| sql::Statement s(db_->GetUniqueStatement( |
| "UPDATE credit_cards " |
| "SET guid=?, name_on_card=?, expiration_month=?," |
| "expiration_year=?, card_number_encrypted=?, use_count=?, use_date=?," |
| "date_modified=?, origin=?, billing_address_id=?" |
| "WHERE guid=?1")); |
| BindCreditCardToStatement(credit_card, |
| update_modification_date |
| ? AutofillClock::Now() |
| : old_credit_card->modification_date(), |
| &s, *autofill_table_encryptor_); |
| |
| bool result = s.Run(); |
| DCHECK_GT(db_->GetLastChangeCount(), 0); |
| return result; |
| } |
| |
| bool AutofillTable::RemoveCreditCard(const std::string& guid) { |
| DCHECK(base::IsValidGUID(guid)); |
| sql::Statement s( |
| db_->GetUniqueStatement("DELETE FROM credit_cards WHERE guid = ?")); |
| s.BindString(0, guid); |
| |
| return s.Run(); |
| } |
| |
| bool AutofillTable::AddFullServerCreditCard(const CreditCard& credit_card) { |
| DCHECK_EQ(CreditCard::FULL_SERVER_CARD, credit_card.record_type()); |
| DCHECK(!credit_card.number().empty()); |
| DCHECK(!credit_card.server_id().empty()); |
| |
| sql::Transaction transaction(db_); |
| if (!transaction.Begin()) |
| return false; |
| |
| // Make sure there aren't duplicates for this card. |
| DeleteFromUnmaskedCreditCards(credit_card.server_id()); |
| DeleteFromMaskedCreditCards(credit_card.server_id()); |
| |
| CreditCard masked(credit_card); |
| masked.set_record_type(CreditCard::MASKED_SERVER_CARD); |
| masked.SetNumber(credit_card.LastFourDigits()); |
| masked.RecordAndLogUse(); |
| DCHECK(!masked.network().empty()); |
| AddMaskedCreditCards({masked}); |
| |
| AddUnmaskedCreditCard(credit_card.server_id(), credit_card.number()); |
| |
| transaction.Commit(); |
| |
| return db_->GetLastChangeCount() > 0; |
| } |
| |
| std::unique_ptr<CreditCard> AutofillTable::GetCreditCard( |
| const std::string& guid) { |
| DCHECK(base::IsValidGUID(guid)); |
| sql::Statement s(db_->GetUniqueStatement( |
| "SELECT guid, name_on_card, expiration_month, expiration_year, " |
| "card_number_encrypted, use_count, use_date, date_modified, " |
| "origin, billing_address_id " |
| "FROM credit_cards " |
| "WHERE guid = ?")); |
| s.BindString(0, guid); |
| |
| if (!s.Step()) |
| return std::unique_ptr<CreditCard>(); |
| |
| return CreditCardFromStatement(s, *autofill_table_encryptor_); |
| } |
| |
| bool AutofillTable::GetCreditCards( |
| std::vector<std::unique_ptr<CreditCard>>* credit_cards) { |
| DCHECK(credit_cards); |
| credit_cards->clear(); |
| |
| sql::Statement s(db_->GetUniqueStatement( |
| "SELECT guid FROM credit_cards ORDER BY date_modified DESC, guid")); |
| |
| while (s.Step()) { |
| std::string guid = s.ColumnString(0); |
| std::unique_ptr<CreditCard> credit_card = GetCreditCard(guid); |
| if (!credit_card) |
| return false; |
| credit_cards->push_back(std::move(credit_card)); |
| } |
| |
| return s.Succeeded(); |
| } |
| |
| bool AutofillTable::GetServerCreditCards( |
| std::vector<std::unique_ptr<CreditCard>>* credit_cards) const { |
| credit_cards->clear(); |
| |
| sql::Statement s(db_->GetUniqueStatement( |
| "SELECT " |
| "card_number_encrypted, " // 0 |
| "last_four," // 1 |
| "masked.id," // 2 |
| "metadata.use_count," // 3 |
| "metadata.use_date," // 4 |
| "network," // 5 |
| "type," // 6 |
| "status," // 7 |
| "name_on_card," // 8 |
| "exp_month," // 9 |
| "exp_year," // 10 |
| "metadata.billing_address_id," // 11 |
| "bank_name " // 12 |
| "FROM masked_credit_cards masked " |
| "LEFT OUTER JOIN unmasked_credit_cards USING (id) " |
| "LEFT OUTER JOIN server_card_metadata metadata USING (id)")); |
| while (s.Step()) { |
| int index = 0; |
| |
| // If the card_number_encrypted field is nonempty, we can assume this card |
| // is a full card, otherwise it's masked. |
| base::string16 full_card_number = |
| UnencryptedCardFromColumn(s, index++, *autofill_table_encryptor_); |
| base::string16 last_four = s.ColumnString16(index++); |
| CreditCard::RecordType record_type = full_card_number.empty() |
| ? CreditCard::MASKED_SERVER_CARD |
| : CreditCard::FULL_SERVER_CARD; |
| std::string server_id = s.ColumnString(index++); |
| std::unique_ptr<CreditCard> card = |
| std::make_unique<CreditCard>(record_type, server_id); |
| card->SetRawInfo(CREDIT_CARD_NUMBER, |
| record_type == CreditCard::MASKED_SERVER_CARD |
| ? last_four |
| : full_card_number); |
| card->set_use_count(s.ColumnInt64(index++)); |
| card->set_use_date(base::Time::FromInternalValue(s.ColumnInt64(index++))); |
| // Modification date is not tracked for server cards. Explicitly set it here |
| // to override the default value of AutofillClock::Now(). |
| card->set_modification_date(base::Time()); |
| |
| std::string card_network = s.ColumnString(index++); |
| if (record_type == CreditCard::MASKED_SERVER_CARD) { |
| // The issuer network must be set after setting the number to override the |
| // autodetected issuer network. |
| card->SetNetworkForMaskedCard(card_network.c_str()); |
| } else { |
| DCHECK_EQ(CreditCard::GetCardNetwork(full_card_number), card_network); |
| } |
| |
| int card_type = s.ColumnInt(index++); |
| if (card_type >= CreditCard::CARD_TYPE_UNKNOWN && |
| card_type <= CreditCard::CARD_TYPE_PREPAID) { |
| card->set_card_type(static_cast<CreditCard::CardType>(card_type)); |
| } |
| |
| card->SetServerStatus(ServerStatusStringToEnum(s.ColumnString(index++))); |
| card->SetRawInfo(CREDIT_CARD_NAME_FULL, s.ColumnString16(index++)); |
| card->SetRawInfo(CREDIT_CARD_EXP_MONTH, s.ColumnString16(index++)); |
| card->SetRawInfo(CREDIT_CARD_EXP_4_DIGIT_YEAR, s.ColumnString16(index++)); |
| card->set_billing_address_id(s.ColumnString(index++)); |
| card->set_bank_name(s.ColumnString(index++)); |
| credit_cards->push_back(std::move(card)); |
| } |
| return s.Succeeded(); |
| } |
| |
| void AutofillTable::SetServerCreditCards( |
| const std::vector<CreditCard>& credit_cards) { |
| sql::Transaction transaction(db_); |
| if (!transaction.Begin()) |
| return; |
| |
| // Delete all old values. |
| sql::Statement masked_delete( |
| db_->GetUniqueStatement("DELETE FROM masked_credit_cards")); |
| masked_delete.Run(); |
| |
| AddMaskedCreditCards(credit_cards); |
| |
| // Delete all items in the unmasked table that aren't in the new set. |
| sql::Statement unmasked_delete(db_->GetUniqueStatement( |
| "DELETE FROM unmasked_credit_cards WHERE id NOT IN " |
| "(SELECT id FROM masked_credit_cards)")); |
| unmasked_delete.Run(); |
| // Do the same for metadata. |
| sql::Statement metadata_delete(db_->GetUniqueStatement( |
| "DELETE FROM server_card_metadata WHERE id NOT IN " |
| "(SELECT id FROM masked_credit_cards)")); |
| metadata_delete.Run(); |
| |
| transaction.Commit(); |
| } |
| |
| bool AutofillTable::UnmaskServerCreditCard(const CreditCard& masked, |
| const base::string16& full_number) { |
| sql::Transaction transaction(db_); |
| if (!transaction.Begin()) |
| return false; |
| |
| // Make sure there aren't duplicates for this card. |
| DeleteFromUnmaskedCreditCards(masked.server_id()); |
| |
| AddUnmaskedCreditCard(masked.server_id(), full_number); |
| |
| CreditCard unmasked = masked; |
| unmasked.set_record_type(CreditCard::FULL_SERVER_CARD); |
| unmasked.SetNumber(full_number); |
| unmasked.RecordAndLogUse(); |
| UpdateServerCardMetadata(unmasked); |
| |
| transaction.Commit(); |
| |
| return db_->GetLastChangeCount() > 0; |
| } |
| |
| bool AutofillTable::MaskServerCreditCard(const std::string& id) { |
| return DeleteFromUnmaskedCreditCards(id); |
| } |
| |
| bool AutofillTable::UpdateServerCardMetadata(const CreditCard& credit_card) { |
| DCHECK_NE(CreditCard::LOCAL_CARD, credit_card.record_type()); |
| |
| sql::Statement remove( |
| db_->GetUniqueStatement("DELETE FROM server_card_metadata WHERE id = ?")); |
| remove.BindString(0, credit_card.server_id()); |
| remove.Run(); |
| |
| sql::Statement s( |
| db_->GetUniqueStatement("INSERT INTO server_card_metadata(use_count, " |
| "use_date, billing_address_id, id)" |
| "VALUES (?,?,?,?)")); |
| s.BindInt64(0, credit_card.use_count()); |
| s.BindInt64(1, credit_card.use_date().ToInternalValue()); |
| s.BindString(2, credit_card.billing_address_id()); |
| s.BindString(3, credit_card.server_id()); |
| s.Run(); |
| |
| return db_->GetLastChangeCount() > 0; |
| } |
| |
| // TODO(crbug.com/680182): Record the address conversion status when a server |
| // address gets converted. |
| bool AutofillTable::UpdateServerAddressMetadata( |
| const AutofillProfile& profile) { |
| DCHECK_EQ(AutofillProfile::SERVER_PROFILE, profile.record_type()); |
| |
| sql::Transaction transaction(db_); |
| if (!transaction.Begin()) |
| return false; |
| |
| sql::Statement remove(db_->GetUniqueStatement( |
| "DELETE FROM server_address_metadata WHERE id = ?")); |
| remove.BindString(0, profile.server_id()); |
| remove.Run(); |
| |
| sql::Statement s( |
| db_->GetUniqueStatement("INSERT INTO server_address_metadata(use_count, " |
| "use_date, has_converted, id)" |
| "VALUES (?,?,?,?)")); |
| s.BindInt64(0, profile.use_count()); |
| s.BindInt64(1, profile.use_date().ToInternalValue()); |
| s.BindBool(2, profile.has_converted()); |
| s.BindString(3, profile.server_id()); |
| s.Run(); |
| |
| transaction.Commit(); |
| |
| return db_->GetLastChangeCount() > 0; |
| } |
| |
| bool AutofillTable::ClearAllServerData() { |
| sql::Transaction transaction(db_); |
| if (!transaction.Begin()) |
| return false; // Some error, nothing was changed. |
| |
| sql::Statement masked( |
| db_->GetUniqueStatement("DELETE FROM masked_credit_cards")); |
| masked.Run(); |
| bool changed = db_->GetLastChangeCount() > 0; |
| |
| sql::Statement unmasked( |
| db_->GetUniqueStatement("DELETE FROM unmasked_credit_cards")); |
| unmasked.Run(); |
| changed |= db_->GetLastChangeCount() > 0; |
| |
| sql::Statement addresses( |
| db_->GetUniqueStatement("DELETE FROM server_addresses")); |
| addresses.Run(); |
| changed |= db_->GetLastChangeCount() > 0; |
| |
| sql::Statement card_metadata( |
| db_->GetUniqueStatement("DELETE FROM server_card_metadata")); |
| card_metadata.Run(); |
| changed |= db_->GetLastChangeCount() > 0; |
| |
| sql::Statement address_metadata( |
| db_->GetUniqueStatement("DELETE FROM server_address_metadata")); |
| address_metadata.Run(); |
| changed |= db_->GetLastChangeCount() > 0; |
| |
| transaction.Commit(); |
| return changed; |
| } |
| |
| bool AutofillTable::ClearAllLocalData() { |
| sql::Transaction transaction(db_); |
| if (!transaction.Begin()) |
| return false; // Some error, nothing was changed. |
| |
| ClearAutofillProfiles(); |
| bool changed = db_->GetLastChangeCount() > 0; |
| ClearCreditCards(); |
| changed |= db_->GetLastChangeCount() > 0; |
| |
| transaction.Commit(); |
| return changed; |
| } |
| |
| bool AutofillTable::RemoveAutofillDataModifiedBetween( |
| const base::Time& delete_begin, |
| const base::Time& delete_end, |
| std::vector<std::string>* profile_guids, |
| std::vector<std::string>* credit_card_guids) { |
| DCHECK(delete_end.is_null() || delete_begin < delete_end); |
| |
| time_t delete_begin_t = delete_begin.ToTimeT(); |
| time_t delete_end_t = GetEndTime(delete_end); |
| |
| // Remember Autofill profiles in the time range. |
| sql::Statement s_profiles_get(db_->GetUniqueStatement( |
| "SELECT guid FROM autofill_profiles " |
| "WHERE date_modified >= ? AND date_modified < ?")); |
| s_profiles_get.BindInt64(0, delete_begin_t); |
| s_profiles_get.BindInt64(1, delete_end_t); |
| |
| profile_guids->clear(); |
| while (s_profiles_get.Step()) { |
| std::string guid = s_profiles_get.ColumnString(0); |
| profile_guids->push_back(guid); |
| } |
| if (!s_profiles_get.Succeeded()) |
| return false; |
| |
| // Remove the profile pieces. |
| for (const std::string& guid : *profile_guids) { |
| if (!RemoveAutofillProfilePieces(guid, db_)) |
| return false; |
| } |
| |
| // Remove Autofill profiles in the time range. |
| sql::Statement s_profiles(db_->GetUniqueStatement( |
| "DELETE FROM autofill_profiles " |
| "WHERE date_modified >= ? AND date_modified < ?")); |
| s_profiles.BindInt64(0, delete_begin_t); |
| s_profiles.BindInt64(1, delete_end_t); |
| |
| if (!s_profiles.Run()) |
| return false; |
| |
| // Remember Autofill credit cards in the time range. |
| sql::Statement s_credit_cards_get(db_->GetUniqueStatement( |
| "SELECT guid FROM credit_cards " |
| "WHERE date_modified >= ? AND date_modified < ?")); |
| s_credit_cards_get.BindInt64(0, delete_begin_t); |
| s_credit_cards_get.BindInt64(1, delete_end_t); |
| |
| credit_card_guids->clear(); |
| while (s_credit_cards_get.Step()) { |
| std::string guid = s_credit_cards_get.ColumnString(0); |
| credit_card_guids->push_back(guid); |
| } |
| if (!s_credit_cards_get.Succeeded()) |
| return false; |
| |
| // Remove Autofill credit cards in the time range. |
| sql::Statement s_credit_cards(db_->GetUniqueStatement( |
| "DELETE FROM credit_cards " |
| "WHERE date_modified >= ? AND date_modified < ?")); |
| s_credit_cards.BindInt64(0, delete_begin_t); |
| s_credit_cards.BindInt64(1, delete_end_t); |
| if (!s_credit_cards.Run()) |
| return false; |
| |
| // Remove unmasked credit cards in the time range. |
| sql::Statement s_unmasked_cards( |
| db_->GetUniqueStatement("DELETE FROM unmasked_credit_cards " |
| "WHERE unmask_date >= ? AND unmask_date < ?")); |
| s_unmasked_cards.BindInt64(0, delete_begin.ToInternalValue()); |
| s_unmasked_cards.BindInt64(1, delete_end.ToInternalValue()); |
| return s_unmasked_cards.Run(); |
| } |
| |
| bool AutofillTable::RemoveOriginURLsModifiedBetween( |
| const base::Time& delete_begin, |
| const base::Time& delete_end, |
| std::vector<std::unique_ptr<AutofillProfile>>* profiles) { |
| DCHECK(delete_end.is_null() || delete_begin < delete_end); |
| |
| time_t delete_begin_t = delete_begin.ToTimeT(); |
| time_t delete_end_t = GetEndTime(delete_end); |
| |
| // Remember Autofill profiles with URL origins in the time range. |
| sql::Statement s_profiles_get(db_->GetUniqueStatement( |
| "SELECT guid, origin FROM autofill_profiles " |
| "WHERE date_modified >= ? AND date_modified < ?")); |
| s_profiles_get.BindInt64(0, delete_begin_t); |
| s_profiles_get.BindInt64(1, delete_end_t); |
| |
| std::vector<std::string> profile_guids; |
| while (s_profiles_get.Step()) { |
| std::string guid = s_profiles_get.ColumnString(0); |
| std::string origin = s_profiles_get.ColumnString(1); |
| if (GURL(origin).is_valid()) |
| profile_guids.push_back(guid); |
| } |
| if (!s_profiles_get.Succeeded()) |
| return false; |
| |
| // Clear out the origins for the found Autofill profiles. |
| for (const std::string& guid : profile_guids) { |
| sql::Statement s_profile(db_->GetUniqueStatement( |
| "UPDATE autofill_profiles SET origin='' WHERE guid=?")); |
| s_profile.BindString(0, guid); |
| if (!s_profile.Run()) |
| return false; |
| |
| std::unique_ptr<AutofillProfile> profile = GetAutofillProfile(guid); |
| if (!profile) |
| return false; |
| |
| profiles->push_back(std::move(profile)); |
| } |
| |
| // Remember Autofill credit cards with URL origins in the time range. |
| sql::Statement s_credit_cards_get(db_->GetUniqueStatement( |
| "SELECT guid, origin FROM credit_cards " |
| "WHERE date_modified >= ? AND date_modified < ?")); |
| s_credit_cards_get.BindInt64(0, delete_begin_t); |
| s_credit_cards_get.BindInt64(1, delete_end_t); |
| |
| std::vector<std::string> credit_card_guids; |
| while (s_credit_cards_get.Step()) { |
| std::string guid = s_credit_cards_get.ColumnString(0); |
| std::string origin = s_credit_cards_get.ColumnString(1); |
| if (GURL(origin).is_valid()) |
| credit_card_guids.push_back(guid); |
| } |
| if (!s_credit_cards_get.Succeeded()) |
| return false; |
| |
| // Clear out the origins for the found credit cards. |
| for (const std::string& guid : credit_card_guids) { |
| sql::Statement s_credit_card(db_->GetUniqueStatement( |
| "UPDATE credit_cards SET origin='' WHERE guid=?")); |
| s_credit_card.BindString(0, guid); |
| if (!s_credit_card.Run()) |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool AutofillTable::GetAutofillProfilesInTrash( |
| std::vector<std::string>* guids) { |
| guids->clear(); |
| |
| sql::Statement s( |
| db_->GetUniqueStatement("SELECT guid FROM autofill_profiles_trash")); |
| |
| while (s.Step()) { |
| std::string guid = s.ColumnString(0); |
| guids->push_back(guid); |
| } |
| |
| return s.Succeeded(); |
| } |
| |
| bool AutofillTable::EmptyAutofillProfilesTrash() { |
| sql::Statement s( |
| db_->GetUniqueStatement("DELETE FROM autofill_profiles_trash")); |
| |
| return s.Run(); |
| } |
| |
| bool AutofillTable::AddAutofillGUIDToTrash(const std::string& guid) { |
| sql::Statement s(db_->GetUniqueStatement( |
| "INSERT INTO autofill_profiles_trash (guid) VALUES (?)")); |
| s.BindString(0, guid); |
| |
| return s.Run(); |
| } |
| |
| bool AutofillTable::ClearAutofillProfiles() { |
| sql::Statement s1(db_->GetUniqueStatement("DELETE FROM autofill_profiles")); |
| |
| if (!s1.Run()) |
| return false; |
| |
| sql::Statement s2( |
| db_->GetUniqueStatement("DELETE FROM autofill_profile_names")); |
| |
| if (!s2.Run()) |
| return false; |
| |
| sql::Statement s3( |
| db_->GetUniqueStatement("DELETE FROM autofill_profile_emails")); |
| |
| if (!s3.Run()) |
| return false; |
| |
| sql::Statement s4( |
| db_->GetUniqueStatement("DELETE FROM autofill_profile_phones")); |
| |
| return s4.Run(); |
| } |
| |
| bool AutofillTable::ClearCreditCards() { |
| sql::Statement s1(db_->GetUniqueStatement("DELETE FROM credit_cards")); |
| return s1.Run(); |
| } |
| |
| bool AutofillTable::GetAllSyncMetadata(syncer::ModelType model_type, |
| syncer::MetadataBatch* metadata_batch) { |
| DCHECK(SupportsMetadataForModelType(model_type)) |
| << "Model type " << model_type << " not supported for metadata"; |
| DCHECK(metadata_batch); |
| if (!GetAllSyncEntityMetadata(model_type, metadata_batch)) { |
| return false; |
| } |
| |
| sync_pb::ModelTypeState model_type_state; |
| if (!GetModelTypeState(model_type, &model_type_state)) |
| return false; |
| |
| metadata_batch->SetModelTypeState(model_type_state); |
| return true; |
| } |
| |
| bool AutofillTable::UpdateSyncMetadata( |
| syncer::ModelType model_type, |
| const std::string& storage_key, |
| const sync_pb::EntityMetadata& metadata) { |
| DCHECK(SupportsMetadataForModelType(model_type)) |
| << "Model type " << model_type << " not supported for metadata"; |
| |
| sql::Statement s(db_->GetUniqueStatement( |
| "INSERT OR REPLACE INTO autofill_sync_metadata " |
| "(model_type, storage_key, value) VALUES(?, ?, ?)")); |
| s.BindInt(0, GetKeyValueForModelType(model_type)); |
| s.BindString(1, storage_key); |
| s.BindString(2, metadata.SerializeAsString()); |
| |
| return s.Run(); |
| } |
| |
| bool AutofillTable::ClearSyncMetadata(syncer::ModelType model_type, |
| const std::string& storage_key) { |
| DCHECK(SupportsMetadataForModelType(model_type)) |
| << "Model type " << model_type << " not supported for metadata"; |
| |
| sql::Statement s( |
| db_->GetUniqueStatement("DELETE FROM autofill_sync_metadata WHERE " |
| "model_type=? AND storage_key=?")); |
| s.BindInt(0, GetKeyValueForModelType(model_type)); |
| s.BindString(1, storage_key); |
| |
| return s.Run(); |
| } |
| |
| bool AutofillTable::UpdateModelTypeState( |
| syncer::ModelType model_type, |
| const sync_pb::ModelTypeState& model_type_state) { |
| DCHECK(SupportsMetadataForModelType(model_type)) |
| << "Model type " << model_type << " not supported for metadata"; |
| |
| // Hardcode the id to force a collision, ensuring that there remains only a |
| // single entry. |
| sql::Statement s(db_->GetUniqueStatement( |
| "INSERT OR REPLACE INTO autofill_model_type_state (model_type, value) " |
| "VALUES(?,?)")); |
| s.BindInt(0, GetKeyValueForModelType(model_type)); |
| s.BindString(1, model_type_state.SerializeAsString()); |
| |
| return s.Run(); |
| } |
| |
| bool AutofillTable::ClearModelTypeState(syncer::ModelType model_type) { |
| DCHECK(SupportsMetadataForModelType(model_type)) |
| << "Model type " << model_type << " not supported for metadata"; |
| |
| sql::Statement s(db_->GetUniqueStatement( |
| "DELETE FROM autofill_model_type_state WHERE model_type=?")); |
| s.BindInt(0, GetKeyValueForModelType(model_type)); |
| |
| return s.Run(); |
| } |
| |
| bool AutofillTable::RemoveOrphanAutofillTableRows() { |
| // Get all the orphan guids. |
| std::set<std::string> orphan_guids; |
| sql::Statement s_orphan_profile_pieces_get(db_->GetUniqueStatement( |
| "SELECT guid FROM (SELECT guid FROM autofill_profile_names UNION SELECT " |
| "guid FROM autofill_profile_emails UNION SELECT guid FROM " |
| "autofill_profile_phones) WHERE guid NOT IN (SELECT guid FROM " |
| "autofill_profiles)")); |
| |
| // Put the orphan guids in a set. |
| while (s_orphan_profile_pieces_get.Step()) |
| orphan_guids.insert(s_orphan_profile_pieces_get.ColumnString(0)); |
| |
| if (!s_orphan_profile_pieces_get.Succeeded()) |
| return false; |
| |
| // Remove the profile pieces for the orphan guids. |
| for (const std::string& guid : orphan_guids) { |
| if (!RemoveAutofillProfilePieces(guid, db_)) |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool AutofillTable::MigrateToVersion54AddI18nFieldsAndRemoveDeprecatedFields() { |
| sql::Transaction transaction(db_); |
| if (!transaction.Begin()) |
| return false; |
| |
| // Test the existence of the |address_line_1| column as an indication that a |
| // migration is needed. It is possible that the new |autofill_profile_phones| |
| // schema is in place because the table was newly created when migrating from |
| // a pre-version-23 database. |
| if (db_->DoesColumnExist("autofill_profiles", "address_line_1")) { |
| // Create a temporary copy of the autofill_profiles table in the (newer) |
| // version 54 format. This table |
| // (a) adds columns for street_address, dependent_locality, and |
| // sorting_code, |
| // (b) removes the address_line_1 and address_line_2 columns, which are |
| // replaced by the street_address column, and |
| // (c) removes the country column, which was long deprecated. |
| if (db_->DoesTableExist("autofill_profiles_temp") || |
| !db_->Execute("CREATE TABLE autofill_profiles_temp ( " |
| "guid VARCHAR PRIMARY KEY, " |
| "company_name VARCHAR, " |
| "street_address VARCHAR, " |
| "dependent_locality VARCHAR, " |
| "city VARCHAR, " |
| "state VARCHAR, " |
| "zipcode VARCHAR, " |
| "sorting_code VARCHAR, " |
| "country_code VARCHAR, " |
| "date_modified INTEGER NOT NULL DEFAULT 0, " |
| "origin VARCHAR DEFAULT '')")) { |
| return false; |
| } |
| |
| // Copy over the data from the autofill_profiles table, taking care to merge |
| // the address lines 1 and 2 into the new street_address column. |
| if (!db_->Execute("INSERT INTO autofill_profiles_temp " |
| "SELECT guid, company_name, '', '', city, state, zipcode," |
| " '', country_code, date_modified, origin " |
| "FROM autofill_profiles")) { |
| return false; |
| } |
| sql::Statement s(db_->GetUniqueStatement( |
| "SELECT guid, address_line_1, address_line_2 FROM autofill_profiles")); |
| while (s.Step()) { |
| std::string guid = s.ColumnString(0); |
| base::string16 line1 = s.ColumnString16(1); |
| base::string16 line2 = s.ColumnString16(2); |
| base::string16 street_address = line1; |
| if (!line2.empty()) |
| street_address += base::ASCIIToUTF16("\n") + line2; |
| |
| sql::Statement s_update(db_->GetUniqueStatement( |
| "UPDATE autofill_profiles_temp SET street_address=? WHERE guid=?")); |
| s_update.BindString16(0, street_address); |
| s_update.BindString(1, guid); |
| if (!s_update.Run()) |
| return false; |
| } |
| if (!s.Succeeded()) |
| return false; |
| |
| // Delete the existing (version 53) table and replace it with the contents |
| // of the temporary table. |
| if (!db_->Execute("DROP TABLE autofill_profiles") || |
| !db_->Execute("ALTER TABLE autofill_profiles_temp " |
| "RENAME TO autofill_profiles")) { |
| return false; |
| } |
| } |
| |
| // Test the existence of the |type| column as an indication that a migration |
| // is needed. It is possible that the new |autofill_profile_phones| schema is |
| // in place because the table was newly created when migrating from a |
| // pre-version-23 database. |
| if (db_->DoesColumnExist("autofill_profile_phones", "type")) { |
| // Create a temporary copy of the autofill_profile_phones table in the |
| // (newer) version 54 format. This table removes the deprecated |type| |
| // column. |
| if (db_->DoesTableExist("autofill_profile_phones_temp") || |
| !db_->Execute("CREATE TABLE autofill_profile_phones_temp ( " |
| "guid VARCHAR, " |
| "number VARCHAR)")) { |
| return false; |
| } |
| |
| // Copy over the data from the autofill_profile_phones table. |
| if (!db_->Execute("INSERT INTO autofill_profile_phones_temp " |
| "SELECT guid, number FROM autofill_profile_phones")) { |
| return false; |
| } |
| |
| // Delete the existing (version 53) table and replace it with the contents |
| // of the temporary table. |
| if (!db_->Execute("DROP TABLE autofill_profile_phones")) |
| return false; |
| if (!db_->Execute("ALTER TABLE autofill_profile_phones_temp " |
| "RENAME TO autofill_profile_phones")) { |
| return false; |
| } |
| } |
| |
| return transaction.Commit(); |
| } |
| |
| bool AutofillTable::MigrateToVersion55MergeAutofillDatesTable() { |
| sql::Transaction transaction(db_); |
| if (!transaction.Begin()) |
| return false; |
| |
| if (db_->DoesTableExist("autofill_temp") || |
| !db_->Execute("CREATE TABLE autofill_temp (" |
| "name VARCHAR, " |
| "value VARCHAR, " |
| "value_lower VARCHAR, " |
| "date_created INTEGER DEFAULT 0, " |
| "date_last_used INTEGER DEFAULT 0, " |
| "count INTEGER DEFAULT 1, " |
| "PRIMARY KEY (name, value))")) { |
| return false; |
| } |
| |
| // Slurp up the data from the existing table and write it to the new table. |
| sql::Statement s(db_->GetUniqueStatement( |
| "SELECT name, value, value_lower, count, MIN(date_created)," |
| " MAX(date_created) " |
| "FROM autofill a JOIN autofill_dates ad ON a.pair_id=ad.pair_id " |
| "GROUP BY name, value, value_lower, count")); |
| while (s.Step()) { |
| sql::Statement s_insert(db_->GetUniqueStatement( |
| "INSERT INTO autofill_temp " |
| "(name, value, value_lower, count, date_created, date_last_used) " |
| "VALUES (?, ?, ?, ?, ?, ?)")); |
| s_insert.BindString16(0, s.ColumnString16(0)); |
| s_insert.BindString16(1, s.ColumnString16(1)); |
| s_insert.BindString16(2, s.ColumnString16(2)); |
| s_insert.BindInt(3, s.ColumnInt(3)); |
| s_insert.BindInt64(4, s.ColumnInt64(4)); |
| s_insert.BindInt64(5, s.ColumnInt64(5)); |
| if (!s_insert.Run()) |
| return false; |
| } |
| |
| if (!s.Succeeded()) |
| return false; |
| |
| // Delete the existing (version 54) tables and replace them with the contents |
| // of the temporary table. |
| if (!db_->Execute("DROP TABLE autofill") || |
| !db_->Execute("DROP TABLE autofill_dates") || |
| !db_->Execute("ALTER TABLE autofill_temp " |
| "RENAME TO autofill")) { |
| return false; |
| } |
| |
| // Create indices on the new table, for fast lookups. |
| if (!db_->Execute("CREATE INDEX autofill_name ON autofill (name)") || |
| !db_->Execute("CREATE INDEX autofill_name_value_lower ON " |
| "autofill (name, value_lower)")) { |
| return false; |
| } |
| |
| return transaction.Commit(); |
| } |
| |
| bool AutofillTable::MigrateToVersion56AddProfileLanguageCodeForFormatting() { |
| return db_->Execute( |
| "ALTER TABLE autofill_profiles ADD COLUMN language_code VARCHAR"); |
| } |
| |
| bool AutofillTable::MigrateToVersion57AddFullNameField() { |
| return db_->Execute( |
| "ALTER TABLE autofill_profile_names ADD COLUMN full_name VARCHAR"); |
| } |
| |
| bool AutofillTable::MigrateToVersion60AddServerCards() { |
| sql::Transaction transaction(db_); |
| if (!transaction.Begin()) |
| return false; |
| |
| if (!db_->DoesTableExist("masked_credit_cards") && |
| !db_->Execute("CREATE TABLE masked_credit_cards (" |
| "id VARCHAR," |
| "status VARCHAR," |
| "name_on_card VARCHAR," |
| "type VARCHAR," |
| "last_four VARCHAR," |
| "exp_month INTEGER DEFAULT 0," |
| "exp_year INTEGER DEFAULT 0)")) { |
| return false; |
| } |
| |
| if (!db_->DoesTableExist("unmasked_credit_cards") && |
| !db_->Execute("CREATE TABLE unmasked_credit_cards (" |
| "id VARCHAR," |
| "card_number_encrypted VARCHAR)")) { |
| return false; |
| } |
| |
| if (!db_->DoesTableExist("server_addresses") && |
| !db_->Execute("CREATE TABLE server_addresses (" |
| "id VARCHAR," |
| "company_name VARCHAR," |
| "street_address VARCHAR," |
| "address_1 VARCHAR," |
| "address_2 VARCHAR," |
| "address_3 VARCHAR," |
| "address_4 VARCHAR," |
| "postal_code VARCHAR," |
| "sorting_code VARCHAR," |
| "country_code VARCHAR," |
| "language_code VARCHAR)")) { |
| return false; |
| } |
| |
| return transaction.Commit(); |
| } |
| |
| bool AutofillTable::MigrateToVersion61AddUsageStats() { |
| sql::Transaction transaction(db_); |
| if (!transaction.Begin()) |
| return false; |
| |
| // Add use_count to autofill_profiles. |
| if (!db_->DoesColumnExist("autofill_profiles", "use_count") && |
| !db_->Execute("ALTER TABLE autofill_profiles ADD COLUMN " |
| "use_count INTEGER NOT NULL DEFAULT 0")) { |
| return false; |
| } |
| |
| // Add use_date to autofill_profiles. |
| if (!db_->DoesColumnExist("autofill_profiles", "use_date") && |
| !db_->Execute("ALTER TABLE autofill_profiles ADD COLUMN " |
| "use_date INTEGER NOT NULL DEFAULT 0")) { |
| return false; |
| } |
| |
| // Add use_count to credit_cards. |
| if (!db_->DoesColumnExist("credit_cards", "use_count") && |
| !db_->Execute("ALTER TABLE credit_cards ADD COLUMN " |
| "use_count INTEGER NOT NULL DEFAULT 0")) { |
| return false; |
| } |
| |
| // Add use_date to credit_cards. |
| if (!db_->DoesColumnExist("credit_cards", "use_date") && |
| !db_->Execute("ALTER TABLE credit_cards ADD COLUMN " |
| "use_date INTEGER NOT NULL DEFAULT 0")) { |
| return false; |
| } |
| |
| return transaction.Commit(); |
| } |
| |
| bool AutofillTable::MigrateToVersion62AddUsageStatsForUnmaskedCards() { |
| sql::Transaction transaction(db_); |
| if (!transaction.Begin()) |
| return false; |
| |
| // Add use_count to unmasked_credit_cards. |
| if (!db_->DoesColumnExist("unmasked_credit_cards", "use_count") && |
| !db_->Execute("ALTER TABLE unmasked_credit_cards ADD COLUMN " |
| "use_count INTEGER NOT NULL DEFAULT 0")) { |
| return false; |
| } |
| |
| // Add use_date to unmasked_credit_cards. |
| if (!db_->DoesColumnExist("unmasked_credit_cards", "use_date") && |
| !db_->Execute("ALTER TABLE unmasked_credit_cards ADD COLUMN " |
| "use_date INTEGER NOT NULL DEFAULT 0")) { |
| return false; |
| } |
| |
| return transaction.Commit(); |
| } |
| |
| bool AutofillTable::MigrateToVersion63AddServerRecipientName() { |
| if (!db_->DoesColumnExist("server_addresses", "recipient_name") && |
| !db_->Execute("ALTER TABLE server_addresses ADD COLUMN " |
| "recipient_name VARCHAR")) { |
| return false; |
| } |
| return true; |
| } |
| |
| bool AutofillTable::MigrateToVersion64AddUnmaskDate() { |
| sql::Transaction transaction(db_); |
| if (!transaction.Begin()) |
| return false; |
| |
| if (!db_->DoesColumnExist("unmasked_credit_cards", "unmask_date") && |
| !db_->Execute("ALTER TABLE unmasked_credit_cards ADD COLUMN " |
| "unmask_date INTEGER NOT NULL DEFAULT 0")) { |
| return false; |
| } |
| if (!db_->DoesColumnExist("server_addresses", "phone_number") && |
| !db_->Execute("ALTER TABLE server_addresses ADD COLUMN " |
| "phone_number VARCHAR")) { |
| return false; |
| } |
| |
| return transaction.Commit(); |
| } |
| |
| bool AutofillTable::MigrateToVersion65AddServerMetadataTables() { |
| sql::Transaction transaction(db_); |
| if (!transaction.Begin()) |
| return false; |
| |
| if (!db_->DoesTableExist("server_card_metadata") && |
| !db_->Execute("CREATE TABLE server_card_metadata (" |
| "id VARCHAR NOT NULL," |
| "use_count INTEGER NOT NULL DEFAULT 0, " |
| "use_date INTEGER NOT NULL DEFAULT 0)")) { |
| return false; |
| } |
| |
| // This clobbers existing usage metadata, which is not synced and only |
| // applies to unmasked cards. Trying to migrate the usage metadata would be |
| // tricky as multiple devices for the same user get DB upgrades. |
| if (!db_->Execute("UPDATE unmasked_credit_cards " |
| "SET use_count=0, use_date=0")) { |
| return false; |
| } |
| |
| if (!db_->DoesTableExist("server_address_metadata") && |
| !db_->Execute("CREATE TABLE server_address_metadata (" |
| "id VARCHAR NOT NULL," |
| "use_count INTEGER NOT NULL DEFAULT 0, " |
| "use_date INTEGER NOT NULL DEFAULT 0)")) { |
| return false; |
| } |
| |
| // Get existing server addresses and generate IDs for them. |
| sql::Statement s(db_->GetUniqueStatement( |
| "SELECT " |
| "id," |
| "recipient_name," |
| "company_name," |
| "street_address," |
| "address_1," // ADDRESS_HOME_STATE |
| "address_2," // ADDRESS_HOME_CITY |
| "address_3," // ADDRESS_HOME_DEPENDENT_LOCALITY |
| "address_4," // Not supported in AutofillProfile yet. |
| "postal_code," // ADDRESS_HOME_ZIP |
| "sorting_code," // ADDRESS_HOME_SORTING_CODE |
| "country_code," // ADDRESS_HOME_COUNTRY |
| "phone_number," // PHONE_HOME_WHOLE_NUMBER |
| "language_code " |
| "FROM server_addresses addresses")); |
| std::vector<AutofillProfile> profiles; |
| while (s.Step()) { |
| int index = 0; |
| AutofillProfile profile(AutofillProfile::SERVER_PROFILE, |
| s.ColumnString(index++)); |
| |
| base::string16 recipient_name = s.ColumnString16(index++); |
| profile.SetRawInfo(COMPANY_NAME, s.ColumnString16(index++)); |
| profile.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, s.ColumnString16(index++)); |
| profile.SetRawInfo(ADDRESS_HOME_STATE, s.ColumnString16(index++)); |
| profile.SetRawInfo(ADDRESS_HOME_CITY, s.ColumnString16(index++)); |
| profile.SetRawInfo(ADDRESS_HOME_DEPENDENT_LOCALITY, |
| s.ColumnString16(index++)); |
| index++; // Skip address_4 which we haven't added to AutofillProfile yet. |
| profile.SetRawInfo(ADDRESS_HOME_ZIP, s.ColumnString16(index++)); |
| profile.SetRawInfo(ADDRESS_HOME_SORTING_CODE, s.ColumnString16(index++)); |
| profile.SetRawInfo(ADDRESS_HOME_COUNTRY, s.ColumnString16(index++)); |
| base::string16 phone_number = s.ColumnString16(index++); |
| profile.set_language_code(s.ColumnString(index++)); |
| profile.SetInfo(NAME_FULL, recipient_name, profile.language_code()); |
| profile.SetInfo(PHONE_HOME_WHOLE_NUMBER, phone_number, |
| profile.language_code()); |
| profile.GenerateServerProfileIdentifier(); |
| profiles.push_back(profile); |
| } |
| |
| // Reinsert with the generated IDs. |
| sql::Statement delete_old( |
| db_->GetUniqueStatement("DELETE FROM server_addresses")); |
| delete_old.Run(); |
| |
| sql::Statement insert(db_->GetUniqueStatement( |
| "INSERT INTO server_addresses(" |
| "id," |
| "recipient_name," |
| "company_name," |
| "street_address," |
| "address_1," // ADDRESS_HOME_STATE |
| "address_2," // ADDRESS_HOME_CITY |
| "address_3," // ADDRESS_HOME_DEPENDENT_LOCALITY |
| "address_4," // Not supported in AutofillProfile yet. |
| "postal_code," // ADDRESS_HOME_ZIP |
| "sorting_code," // ADDRESS_HOME_SORTING_CODE |
| "country_code," // ADDRESS_HOME_COUNTRY |
| "phone_number," // PHONE_HOME_WHOLE_NUMBER |
| "language_code) " |
| "VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)")); |
| for (const AutofillProfile& profile : profiles) { |
| int index = 0; |
| insert.BindString(index++, profile.server_id()); |
| insert.BindString16(index++, profile.GetRawInfo(NAME_FULL)); |
| insert.BindString16(index++, profile.GetRawInfo(COMPANY_NAME)); |
| insert.BindString16(index++, |
| profile.GetRawInfo(ADDRESS_HOME_STREET_ADDRESS)); |
| insert.BindString16(index++, profile.GetRawInfo(ADDRESS_HOME_STATE)); |
| insert.BindString16(index++, profile.GetRawInfo(ADDRESS_HOME_CITY)); |
| insert.BindString16(index++, |
| profile.GetRawInfo(ADDRESS_HOME_DEPENDENT_LOCALITY)); |
| index++; // SKip address_4 which we haven't added to AutofillProfile yet. |
| insert.BindString16(index++, profile.GetRawInfo(ADDRESS_HOME_ZIP)); |
| insert.BindString16(index++, profile.GetRawInfo(ADDRESS_HOME_SORTING_CODE)); |
| insert.BindString16(index++, profile.GetRawInfo(ADDRESS_HOME_COUNTRY)); |
| insert.BindString16(index++, profile.GetRawInfo(PHONE_HOME_WHOLE_NUMBER)); |
| insert.BindString(index++, profile.language_code()); |
| insert.Run(); |
| insert.Reset(true); |
| } |
| |
| return transaction.Commit(); |
| } |
| |
| bool AutofillTable::MigrateToVersion66AddCardBillingAddress() { |
| // The default value for this column is null, but Connection::ColumnString() |
| // returns an empty string for that. |
| return db_->Execute( |
| "ALTER TABLE credit_cards ADD COLUMN billing_address_id VARCHAR"); |
| } |
| |
| bool AutofillTable::MigrateToVersion67AddMaskedCardBillingAddress() { |
| // The default value for this column is null, but Connection::ColumnString() |
| // returns an empty string for that. |
| return db_->Execute( |
| "ALTER TABLE masked_credit_cards ADD COLUMN billing_address_id VARCHAR"); |
| } |
| |
| bool AutofillTable::MigrateToVersion70AddSyncMetadata() { |
| if (!db_->Execute("CREATE TABLE autofill_sync_metadata (" |
| "storage_key VARCHAR PRIMARY KEY NOT NULL," |
| "value BLOB)")) { |
| return false; |
| } |
| return db_->Execute( |
| "CREATE TABLE autofill_model_type_state (id INTEGER PRIMARY KEY, value " |
| "BLOB)"); |
| } |
| |
| bool AutofillTable:: |
| MigrateToVersion71AddHasConvertedAndBillingAddressIdMetadata() { |
| sql::Transaction transaction(db_); |
| if (!transaction.Begin()) |
| return false; |
| |
| // Add the new has_converted column to the server_address_metadata table. |
| if (!db_->DoesColumnExist("server_address_metadata", "has_converted") && |
| !db_->Execute("ALTER TABLE server_address_metadata ADD COLUMN " |
| "has_converted BOOL NOT NULL DEFAULT FALSE")) { |
| return false; |
| } |
| |
| // Add the new billing_address_id column to the server_card_metadata table. |
| if (!db_->DoesColumnExist("server_card_metadata", "billing_address_id") && |
| !db_->Execute("ALTER TABLE server_card_metadata ADD COLUMN " |
| "billing_address_id VARCHAR")) { |
| return false; |
| } |
| |
| // Copy over the billing_address_id from the masked_server_cards to |
| // server_card_metadata. |
| if (!db_->Execute("UPDATE server_card_metadata " |
| "SET billing_address_id = " |
| "(SELECT billing_address_id " |
| "FROM masked_credit_cards " |
| "WHERE id = server_card_metadata.id)")) { |
| return false; |
| } |
| |
| if (db_->DoesTableExist("masked_credit_cards_temp") && |
| !db_->Execute("DROP TABLE masked_credit_cards_temp")) { |
| return false; |
| } |
| |
| // Remove the billing_address_id column from the masked_credit_cards table. |
| // Create a temporary table that is a copy of masked_credit_cards but without |
| // the billing_address_id column. |
| if (!db_->Execute("CREATE TABLE masked_credit_cards_temp (" |
| "id VARCHAR," |
| "status VARCHAR," |
| "name_on_card VARCHAR," |
| "type VARCHAR," |
| "last_four VARCHAR," |
| "exp_month INTEGER DEFAULT 0," |
| "exp_year INTEGER DEFAULT 0)")) { |
| return false; |
| } |
| // Copy over the data from the original masked_credit_cards table. |
| if (!db_->Execute("INSERT INTO masked_credit_cards_temp " |
| "SELECT id, status, name_on_card, type, last_four, " |
| "exp_month, exp_year " |
| "FROM masked_credit_cards")) { |
| return false; |
| } |
| // Delete the existing table and replace it with the contents of the |
| // temporary table. |
| if (!db_->Execute("DROP TABLE masked_credit_cards") || |
| !db_->Execute("ALTER TABLE masked_credit_cards_temp " |
| "RENAME TO masked_credit_cards")) { |
| return false; |
| } |
| |
| return transaction.Commit(); |
| } |
| |
| bool AutofillTable::MigrateToVersion72RenameCardTypeToIssuerNetwork() { |
| sql::Transaction transaction(db_); |
| if (!transaction.Begin()) |
| return false; |
| |
| if (db_->DoesTableExist("masked_credit_cards_temp") && |
| !db_->Execute("DROP TABLE masked_credit_cards_temp")) { |
| return false; |
| } |
| |
| return db_->Execute( |
| "CREATE TABLE masked_credit_cards_temp (" |
| "id VARCHAR," |
| "status VARCHAR," |
| "name_on_card VARCHAR," |
| "network VARCHAR," |
| "last_four VARCHAR," |
| "exp_month INTEGER DEFAULT 0," |
| "exp_year INTEGER DEFAULT 0)") && |
| db_->Execute( |
| "INSERT INTO masked_credit_cards_temp (" |
| "id, status, name_on_card, network, last_four, exp_month, exp_year" |
| ") SELECT " |
| "id, status, name_on_card, type, last_four, exp_month, exp_year" |
| " FROM masked_credit_cards") && |
| db_->Execute("DROP TABLE masked_credit_cards") && |
| db_->Execute( |
| "ALTER TABLE masked_credit_cards_temp " |
| "RENAME TO masked_credit_cards") && |
| transaction.Commit(); |
| } |
| |
| bool AutofillTable::MigrateToVersion73AddMaskedCardBankName() { |
| // Add the new bank_name column to the masked_credit_cards table. |
| return db_->DoesColumnExist("masked_credit_cards", "bank_name") || |
| db_->Execute( |
| "ALTER TABLE masked_credit_cards ADD COLUMN bank_name VARCHAR"); |
| } |
| |
| bool AutofillTable::MigrateToVersion74AddServerCardTypeColumn() { |
| // Version 73 was actually used by two different schemas; an attempt to add |
| // the "type" column (as in this version 74) was landed and reverted, and then |
| // the "bank_name" column was added (and stuck). Some clients may have been |
| // upgraded to one and some the other. Figure out which is the case. |
| const bool added_type_column_in_v73 = |
| db_->DoesColumnExist("masked_credit_cards", "type"); |
| |
| // If we previously added the "type" column, then it's already present with |
| // the correct semantics, but we now need to run the "bank_name" migration. |
| // Otherwise, we need to add "type" now. |
| return added_type_column_in_v73 ? MigrateToVersion73AddMaskedCardBankName() |
| : db_->Execute( |
| "ALTER TABLE masked_credit_cards ADD " |
| "COLUMN type INTEGER DEFAULT 0"); |
| } |
| |
| bool AutofillTable::MigrateToVersion75AddProfileValidityBitfieldColumn() { |
| // Add the new valdity_bitfield column to the autofill_profiles table. |
| return db_->Execute( |
| "ALTER TABLE autofill_profiles ADD COLUMN validity_bitfield UNSIGNED NOT " |
| "NULL DEFAULT 0"); |
| } |
| |
| bool AutofillTable::MigrateToVersion78AddModelTypeColumns() { |
| // Add the new model type columns to the autofill metadata tables. |
| sql::Transaction transaction(db_); |
| if (!transaction.Begin()) |
| return false; |
| |
| if (db_->DoesTableExist("autofill_sync_metadata_temp") && |
| !db_->Execute("DROP TABLE autofill_sync_metadata_temp")) { |
| return false; |
| } |
| |
| if (db_->DoesTableExist("autofill_model_type_state_temp") && |
| !db_->Execute("DROP TABLE autofill_model_type_state_temp")) { |
| return false; |
| } |
| |
| if (!db_->Execute("CREATE TABLE autofill_sync_metadata_temp (" |
| "model_type INTEGER NOT NULL, " |
| "storage_key VARCHAR NOT NULL, " |
| "value BLOB, " |
| "PRIMARY KEY (model_type, storage_key))") || |
| !db_->Execute("CREATE TABLE autofill_model_type_state_temp (" |
| "model_type INTEGER NOT NULL PRIMARY KEY, value BLOB)")) { |
| return false; |
| } |
| |
| sql::Statement insert_metadata( |
| db_->GetUniqueStatement("INSERT INTO autofill_sync_metadata_temp " |
| "(model_type, storage_key, value) " |
| "SELECT ?, storage_key, value " |
| "FROM autofill_sync_metadata")); |
| insert_metadata.BindInt(0, syncer::ModelTypeToHistogramInt(syncer::AUTOFILL)); |
| |
| // Prior to this migration, the table was a singleton, containing only one |
| // entry with id being hard-coded to 1. |
| sql::Statement insert_state( |
| db_->GetUniqueStatement("INSERT INTO autofill_model_type_state_temp " |
| "(model_type, value) SELECT ?, value " |
| "FROM autofill_model_type_state WHERE id=1")); |
| insert_state.BindInt(0, syncer::ModelTypeToHistogramInt(syncer::AUTOFILL)); |
| |
| if (!insert_metadata.Run() || !insert_state.Run()) { |
| return false; |
| } |
| |
| return db_->Execute("DROP TABLE autofill_sync_metadata") && |
| db_->Execute( |
| "ALTER TABLE autofill_sync_metadata_temp " |
| "RENAME TO autofill_sync_metadata") && |
| db_->Execute("DROP TABLE autofill_model_type_state") && |
| db_->Execute( |
| "ALTER TABLE autofill_model_type_state_temp " |
| "RENAME TO autofill_model_type_state") && |
| transaction.Commit(); |
| } |
| |
| bool AutofillTable::AddFormFieldValuesTime( |
| const std::vector<FormFieldData>& elements, |
| std::vector<AutofillChange>* changes, |
| base::Time time) { |
| // Only add one new entry for each unique element name. Use |seen_names| to |
| // track this. Add up to |kMaximumUniqueNames| unique entries per form. |
| const size_t kMaximumUniqueNames = 256; |
| std::set<base::string16> seen_names; |
| bool result = true; |
| for (const FormFieldData& element : elements) { |
| if (seen_names.size() >= kMaximumUniqueNames) |
| break; |
| if (base::ContainsKey(seen_names, element.name)) |
| continue; |
| result = result && AddFormFieldValueTime(element, changes, time); |
| seen_names.insert(element.name); |
| } |
| return result; |
| } |
| |
| bool AutofillTable::AddFormFieldValueTime(const FormFieldData& element, |
| std::vector<AutofillChange>* changes, |
| base::Time time) { |
| sql::Statement s_exists(db_->GetUniqueStatement( |
| "SELECT COUNT(*) FROM autofill WHERE name = ? AND value = ?")); |
| s_exists.BindString16(0, element.name); |
| s_exists.BindString16(1, element.value); |
| if (!s_exists.Step()) |
| return false; |
| |
| bool already_exists = s_exists.ColumnInt(0) > 0; |
| if (already_exists) { |
| sql::Statement s(db_->GetUniqueStatement( |
| "UPDATE autofill SET date_last_used = ?, count = count + 1 " |
| "WHERE name = ? AND value = ?")); |
| s.BindInt64(0, time.ToTimeT()); |
| s.BindString16(1, element.name); |
| s.BindString16(2, element.value); |
| if (!s.Run()) |
| return false; |
| } else { |
| time_t time_as_time_t = time.ToTimeT(); |
| sql::Statement s(db_->GetUniqueStatement( |
| "INSERT INTO autofill " |
| "(name, value, value_lower, date_created, date_last_used, count) " |
| "VALUES (?, ?, ?, ?, ?, ?)")); |
| s.BindString16(0, element.name); |
| s.BindString16(1, element.value); |
| s.BindString16(2, base::i18n::ToLower(element.value)); |
| s.BindInt64(3, time_as_time_t); |
| s.BindInt64(4, time_as_time_t); |
| s.BindInt(5, 1); |
| if (!s.Run()) |
| return false; |
| } |
| |
| AutofillChange::Type change_type = |
| already_exists ? AutofillChange::UPDATE : AutofillChange::ADD; |
| changes->push_back( |
| AutofillChange(change_type, AutofillKey(element.name, element.value))); |
| return true; |
| } |
| |
| bool AutofillTable::SupportsMetadataForModelType( |
| syncer::ModelType model_type) const { |
| return (model_type == syncer::AUTOFILL || |
| model_type == syncer::AUTOFILL_PROFILE || |
| model_type == syncer::AUTOFILL_WALLET_DATA || |
| model_type == syncer::AUTOFILL_WALLET_METADATA); |
| } |
| |
| int AutofillTable::GetKeyValueForModelType(syncer::ModelType model_type) const { |
| return syncer::ModelTypeToStableIdentifier(model_type); |
| } |
| |
| bool AutofillTable::GetAllSyncEntityMetadata( |
| syncer::ModelType model_type, |
| syncer::MetadataBatch* metadata_batch) { |
| DCHECK(SupportsMetadataForModelType(model_type)) |
| << "Model type " << model_type << " not supported for metadata"; |
| DCHECK(metadata_batch); |
| |
| sql::Statement s( |
| db_->GetUniqueStatement("SELECT storage_key, value FROM " |
| "autofill_sync_metadata WHERE model_type=?")); |
| s.BindInt(0, GetKeyValueForModelType(model_type)); |
| |
| while (s.Step()) { |
| std::string storage_key = s.ColumnString(0); |
| std::string serialized_metadata = s.ColumnString(1); |
| sync_pb::EntityMetadata entity_metadata; |
| if (entity_metadata.ParseFromString(serialized_metadata)) { |
| metadata_batch->AddMetadata(storage_key, entity_metadata); |
| } else { |
| DLOG(WARNING) << "Failed to deserialize AUTOFILL model type " |
| "sync_pb::EntityMetadata."; |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| bool AutofillTable::GetModelTypeState(syncer::ModelType model_type, |
| sync_pb::ModelTypeState* state) { |
| DCHECK(SupportsMetadataForModelType(model_type)) |
| << "Model type " << model_type << " not supported for metadata"; |
| |
| sql::Statement s(db_->GetUniqueStatement( |
| "SELECT value FROM autofill_model_type_state WHERE model_type=?")); |
| s.BindInt(0, GetKeyValueForModelType(model_type)); |
| |
| if (!s.Step()) { |
| return true; |
| } |
| |
| std::string serialized_state = s.ColumnString(0); |
| return state->ParseFromString(serialized_state); |
| } |
| |
| bool AutofillTable::InsertAutofillEntry(const AutofillEntry& entry) { |
| std::string sql = |
| "INSERT INTO autofill " |
| "(name, value, value_lower, date_created, date_last_used, count) " |
| "VALUES (?, ?, ?, ?, ?, ?)"; |
| sql::Statement s(db_->GetUniqueStatement(sql.c_str())); |
| s.BindString16(0, entry.key().name()); |
| s.BindString16(1, entry.key().value()); |
| s.BindString16(2, base::i18n::ToLower(entry.key().value())); |
| s.BindInt64(3, entry.date_created().ToTimeT()); |
| s.BindInt64(4, entry.date_last_used().ToTimeT()); |
| // TODO(isherman): The counts column is currently synced implicitly as the |
| // number of timestamps. Sync the value explicitly instead, since the DB now |
| // only saves the first and last timestamp, which makes counting timestamps |
| // completely meaningless as a way to track frequency of usage. |
| s.BindInt(5, entry.date_last_used() == entry.date_created() ? 1 : 2); |
| return s.Run(); |
| } |
| |
| bool AutofillTable::IsAutofillProfilesTrashEmpty() { |
| sql::Statement s( |
| db_->GetUniqueStatement("SELECT guid FROM autofill_profiles_trash")); |
| |
| return !s.Step(); |
| } |
| |
| bool AutofillTable::IsAutofillGUIDInTrash(const std::string& guid) { |
| sql::Statement s(db_->GetUniqueStatement( |
| "SELECT guid FROM autofill_profiles_trash WHERE guid = ?")); |
| s.BindString(0, guid); |
| |
| return s.Step(); |
| } |
| |
| void AutofillTable::AddMaskedCreditCards( |
| const std::vector<CreditCard>& credit_cards) { |
| DCHECK_GT(db_->transaction_nesting(), 0); |
| sql::Statement masked_insert( |
| db_->GetUniqueStatement("INSERT INTO masked_credit_cards(" |
| "id," // 0 |
| "network," // 1 |
| "type," // 2 |
| "status," // 3 |
| "name_on_card," // 4 |
| "last_four," // 5 |
| "exp_month," // 6 |
| "exp_year," // 7 |
| "bank_name)" // 8 |
| "VALUES (?,?,?,?,?,?,?,?,?)")); |
| for (const CreditCard& card : credit_cards) { |
| DCHECK_EQ(CreditCard::MASKED_SERVER_CARD, card.record_type()); |
| masked_insert.BindString(0, card.server_id()); |
| masked_insert.BindString(1, card.network()); |
| masked_insert.BindInt(2, card.card_type()); |
| masked_insert.BindString(3, |
| ServerStatusEnumToString(card.GetServerStatus())); |
| masked_insert.BindString16(4, card.GetRawInfo(CREDIT_CARD_NAME_FULL)); |
| masked_insert.BindString16(5, card.LastFourDigits()); |
| masked_insert.BindString16(6, card.GetRawInfo(CREDIT_CARD_EXP_MONTH)); |
| masked_insert.BindString16(7, |
| card.GetRawInfo(CREDIT_CARD_EXP_4_DIGIT_YEAR)); |
| masked_insert.BindString(8, card.bank_name()); |
| masked_insert.Run(); |
| masked_insert.Reset(true); |
| |
| // Save the use count and use date of the card. |
| UpdateServerCardMetadata(card); |
| } |
| } |
| |
| void AutofillTable::AddUnmaskedCreditCard(const std::string& id, |
| const base::string16& full_number) { |
| sql::Statement s( |
| db_->GetUniqueStatement("INSERT INTO unmasked_credit_cards(" |
| "id," |
| "card_number_encrypted," |
| "unmask_date)" |
| "VALUES (?,?,?)")); |
| s.BindString(0, id); |
| |
| std::string encrypted_data; |
| autofill_table_encryptor_->EncryptString16(full_number, &encrypted_data); |
| s.BindBlob(1, encrypted_data.data(), |
| static_cast<int>(encrypted_data.length())); |
| s.BindInt64(2, AutofillClock::Now().ToInternalValue()); // unmask_date |
| |
| s.Run(); |
| } |
| |
| bool AutofillTable::DeleteFromMaskedCreditCards(const std::string& id) { |
| sql::Statement s( |
| db_->GetUniqueStatement("DELETE FROM masked_credit_cards WHERE id = ?")); |
| s.BindString(0, id); |
| s.Run(); |
| return db_->GetLastChangeCount() > 0; |
| } |
| |
| bool AutofillTable::DeleteFromUnmaskedCreditCards(const std::string& id) { |
| sql::Statement s(db_->GetUniqueStatement( |
| "DELETE FROM unmasked_credit_cards WHERE id = ?")); |
| s.BindString(0, id); |
| s.Run(); |
| return db_->GetLastChangeCount() > 0; |
| } |
| |
| bool AutofillTable::InitMainTable() { |
| if (!db_->DoesTableExist("autofill")) { |
| if (!db_->Execute("CREATE TABLE autofill (" |
| "name VARCHAR, " |
| "value VARCHAR, " |
| "value_lower VARCHAR, " |
| "date_created INTEGER DEFAULT 0, " |
| "date_last_used INTEGER DEFAULT 0, " |
| "count INTEGER DEFAULT 1, " |
| "PRIMARY KEY (name, value))") || |
| !db_->Execute("CREATE INDEX autofill_name ON autofill (name)") || |
| !db_->Execute("CREATE INDEX autofill_name_value_lower ON " |
| "autofill (name, value_lower)")) { |
| NOTREACHED(); |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| bool AutofillTable::InitCreditCardsTable() { |
| if (!db_->DoesTableExist("credit_cards")) { |
| if (!db_->Execute("CREATE TABLE credit_cards ( " |
| "guid VARCHAR PRIMARY KEY, " |
| "name_on_card VARCHAR, " |
| "expiration_month INTEGER, " |
| "expiration_year INTEGER, " |
| "card_number_encrypted BLOB, " |
| "date_modified INTEGER NOT NULL DEFAULT 0, " |
| "origin VARCHAR DEFAULT '', " |
| "use_count INTEGER NOT NULL DEFAULT 0, " |
| "use_date INTEGER NOT NULL DEFAULT 0, " |
| "billing_address_id VARCHAR) ")) { |
| NOTREACHED(); |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| bool AutofillTable::InitProfilesTable() { |
| if (!db_->DoesTableExist("autofill_profiles")) { |
| if (!db_->Execute("CREATE TABLE autofill_profiles ( " |
| "guid VARCHAR PRIMARY KEY, " |
| "company_name VARCHAR, " |
| "street_address VARCHAR, " |
| "dependent_locality VARCHAR, " |
| "city VARCHAR, " |
| "state VARCHAR, " |
| "zipcode VARCHAR, " |
| "sorting_code VARCHAR, " |
| "country_code VARCHAR, " |
| "date_modified INTEGER NOT NULL DEFAULT 0, " |
| "origin VARCHAR DEFAULT '', " |
| "language_code VARCHAR, " |
| "use_count INTEGER NOT NULL DEFAULT 0, " |
| "use_date INTEGER NOT NULL DEFAULT 0, " |
| "validity_bitfield UNSIGNED NOT NULL DEFAULT 0) ")) { |
| NOTREACHED(); |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| bool AutofillTable::InitProfileNamesTable() { |
| if (!db_->DoesTableExist("autofill_profile_names")) { |
| if (!db_->Execute("CREATE TABLE autofill_profile_names ( " |
| "guid VARCHAR, " |
| "first_name VARCHAR, " |
| "middle_name VARCHAR, " |
| "last_name VARCHAR, " |
| "full_name VARCHAR)")) { |
| NOTREACHED(); |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| bool AutofillTable::InitProfileEmailsTable() { |
| if (!db_->DoesTableExist("autofill_profile_emails")) { |
| if (!db_->Execute("CREATE TABLE autofill_profile_emails ( " |
| "guid VARCHAR, " |
| "email VARCHAR)")) { |
| NOTREACHED(); |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| bool AutofillTable::InitProfilePhonesTable() { |
| if (!db_->DoesTableExist("autofill_profile_phones")) { |
| if (!db_->Execute("CREATE TABLE autofill_profile_phones ( " |
| "guid VARCHAR, " |
| "number VARCHAR)")) { |
| NOTREACHED(); |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| bool AutofillTable::InitProfileTrashTable() { |
| if (!db_->DoesTableExist("autofill_profiles_trash")) { |
| if (!db_->Execute("CREATE TABLE autofill_profiles_trash ( " |
| "guid VARCHAR)")) { |
| NOTREACHED(); |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| bool AutofillTable::InitMaskedCreditCardsTable() { |
| if (!db_->DoesTableExist("masked_credit_cards")) { |
| if (!db_->Execute("CREATE TABLE masked_credit_cards (" |
| "id VARCHAR," |
| "status VARCHAR," |
| "name_on_card VARCHAR," |
| "network VARCHAR," |
| "last_four VARCHAR," |
| "exp_month INTEGER DEFAULT 0," |
| "exp_year INTEGER DEFAULT 0, " |
| "bank_name VARCHAR, " |
| "type INTEGER DEFAULT 0)")) { |
| NOTREACHED(); |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| bool AutofillTable::InitUnmaskedCreditCardsTable() { |
| if (!db_->DoesTableExist("unmasked_credit_cards")) { |
| if (!db_->Execute("CREATE TABLE unmasked_credit_cards (" |
| "id VARCHAR," |
| "card_number_encrypted VARCHAR, " |
| "use_count INTEGER NOT NULL DEFAULT 0, " |
| "use_date INTEGER NOT NULL DEFAULT 0, " |
| "unmask_date INTEGER NOT NULL DEFAULT 0)")) { |
| NOTREACHED(); |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| bool AutofillTable::InitServerCardMetadataTable() { |
| if (!db_->DoesTableExist("server_card_metadata")) { |
| if (!db_->Execute("CREATE TABLE server_card_metadata (" |
| "id VARCHAR NOT NULL," |
| "use_count INTEGER NOT NULL DEFAULT 0, " |
| "use_date INTEGER NOT NULL DEFAULT 0, " |
| "billing_address_id VARCHAR)")) { |
| NOTREACHED(); |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| bool AutofillTable::InitServerAddressesTable() { |
| if (!db_->DoesTableExist("server_addresses")) { |
| // The space after language_code is necessary to match what sqlite does |
| // when it appends the column in migration. |
| if (!db_->Execute("CREATE TABLE server_addresses (" |
| "id VARCHAR," |
| "company_name VARCHAR," |
| "street_address VARCHAR," |
| "address_1 VARCHAR," |
| "address_2 VARCHAR," |
| "address_3 VARCHAR," |
| "address_4 VARCHAR," |
| "postal_code VARCHAR," |
| "sorting_code VARCHAR," |
| "country_code VARCHAR," |
| "language_code VARCHAR, " // Space required. |
| "recipient_name VARCHAR, " // Ditto. |
| "phone_number VARCHAR)")) { |
| NOTREACHED(); |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| bool AutofillTable::InitServerAddressMetadataTable() { |
| if (!db_->DoesTableExist("server_address_metadata")) { |
| if (!db_->Execute("CREATE TABLE server_address_metadata (" |
| "id VARCHAR NOT NULL," |
| "use_count INTEGER NOT NULL DEFAULT 0, " |
| "use_date INTEGER NOT NULL DEFAULT 0, " |
| "has_converted BOOL NOT NULL DEFAULT FALSE)")) { |
| NOTREACHED(); |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| bool AutofillTable::InitAutofillSyncMetadataTable() { |
| if (!db_->DoesTableExist("autofill_sync_metadata")) { |
| if (!db_->Execute("CREATE TABLE autofill_sync_metadata (" |
| "model_type INTEGER NOT NULL, " |
| "storage_key VARCHAR NOT NULL, " |
| "value BLOB, " |
| "PRIMARY KEY (model_type, storage_key))")) { |
| NOTREACHED(); |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| bool AutofillTable::InitModelTypeStateTable() { |
| if (!db_->DoesTableExist("autofill_model_type_state")) { |
| if (!db_->Execute("CREATE TABLE autofill_model_type_state (" |
| "model_type INTEGER NOT NULL PRIMARY KEY, value BLOB)")) { |
| NOTREACHED(); |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| } // namespace autofill |