| // Copyright (c) 2011 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 "chrome/browser/webdata/autofill_table.h" |
| |
| #include <algorithm> |
| #include <limits> |
| #include <map> |
| #include <set> |
| #include <string> |
| #include <vector> |
| |
| #include "app/sql/statement.h" |
| #include "base/logging.h" |
| #include "base/string_number_conversions.h" |
| #include "base/time.h" |
| #include "base/tuple.h" |
| #include "chrome/browser/autofill/autofill_country.h" |
| #include "chrome/browser/autofill/autofill_profile.h" |
| #include "chrome/browser/autofill/autofill_type.h" |
| #include "chrome/browser/autofill/credit_card.h" |
| #include "chrome/browser/autofill/personal_data_manager.h" |
| #include "chrome/browser/password_manager/encryptor.h" |
| #include "chrome/browser/webdata/autofill_change.h" |
| #include "chrome/common/guid.h" |
| #include "ui/base/l10n/l10n_util.h" |
| #include "webkit/glue/form_field.h" |
| |
| using base::Time; |
| using webkit_glue::FormField; |
| |
| namespace { |
| |
| // Constants for the |autofill_profile_phones| |type| column. |
| enum AutofillPhoneType { |
| kAutofillPhoneNumber = 0, |
| kAutofillFaxNumber = 1 |
| }; |
| |
| typedef std::vector<Tuple3<int64, string16, string16> > AutofillElementList; |
| |
| // TODO(dhollowa): Find a common place for this. It is duplicated in |
| // personal_data_manager.cc. |
| template<typename T> |
| T* address_of(T& v) { |
| return &v; |
| } |
| |
| // The maximum length allowed for form data. |
| const size_t kMaxDataLength = 1024; |
| |
| string16 LimitDataSize(const string16& data) { |
| if (data.size() > kMaxDataLength) |
| return data.substr(0, kMaxDataLength); |
| |
| return data; |
| } |
| |
| void BindAutofillProfileToStatement(const AutofillProfile& profile, |
| sql::Statement* s) { |
| DCHECK(guid::IsValidGUID(profile.guid())); |
| s->BindString(0, profile.guid()); |
| |
| string16 text = profile.GetInfo(COMPANY_NAME); |
| s->BindString16(1, LimitDataSize(text)); |
| text = profile.GetInfo(ADDRESS_HOME_LINE1); |
| s->BindString16(2, LimitDataSize(text)); |
| text = profile.GetInfo(ADDRESS_HOME_LINE2); |
| s->BindString16(3, LimitDataSize(text)); |
| text = profile.GetInfo(ADDRESS_HOME_CITY); |
| s->BindString16(4, LimitDataSize(text)); |
| text = profile.GetInfo(ADDRESS_HOME_STATE); |
| s->BindString16(5, LimitDataSize(text)); |
| text = profile.GetInfo(ADDRESS_HOME_ZIP); |
| s->BindString16(6, LimitDataSize(text)); |
| text = profile.GetInfo(ADDRESS_HOME_COUNTRY); |
| s->BindString16(7, LimitDataSize(text)); |
| std::string country_code = profile.CountryCode(); |
| s->BindString(8, country_code); |
| s->BindInt64(9, Time::Now().ToTimeT()); |
| } |
| |
| AutofillProfile* AutofillProfileFromStatement(const sql::Statement& s) { |
| AutofillProfile* profile = new AutofillProfile; |
| profile->set_guid(s.ColumnString(0)); |
| DCHECK(guid::IsValidGUID(profile->guid())); |
| |
| profile->SetInfo(COMPANY_NAME, s.ColumnString16(1)); |
| profile->SetInfo(ADDRESS_HOME_LINE1, s.ColumnString16(2)); |
| profile->SetInfo(ADDRESS_HOME_LINE2, s.ColumnString16(3)); |
| profile->SetInfo(ADDRESS_HOME_CITY, s.ColumnString16(4)); |
| profile->SetInfo(ADDRESS_HOME_STATE, s.ColumnString16(5)); |
| profile->SetInfo(ADDRESS_HOME_ZIP, s.ColumnString16(6)); |
| // Intentionally skip column 7, which stores the localized country name. |
| profile->SetCountryCode(s.ColumnString(8)); |
| // Intentionally skip column 9, which stores the profile's modification date. |
| |
| return profile; |
| } |
| |
| void BindCreditCardToStatement(const CreditCard& credit_card, |
| sql::Statement* s) { |
| DCHECK(guid::IsValidGUID(credit_card.guid())); |
| s->BindString(0, credit_card.guid()); |
| |
| string16 text = credit_card.GetInfo(CREDIT_CARD_NAME); |
| s->BindString16(1, LimitDataSize(text)); |
| text = credit_card.GetInfo(CREDIT_CARD_EXP_MONTH); |
| s->BindString16(2, LimitDataSize(text)); |
| text = credit_card.GetInfo(CREDIT_CARD_EXP_4_DIGIT_YEAR); |
| s->BindString16(3, LimitDataSize(text)); |
| text = credit_card.GetInfo(CREDIT_CARD_NUMBER); |
| std::string encrypted_data; |
| Encryptor::EncryptString16(text, &encrypted_data); |
| s->BindBlob(4, encrypted_data.data(), |
| static_cast<int>(encrypted_data.length())); |
| s->BindInt64(5, Time::Now().ToTimeT()); |
| } |
| |
| CreditCard* CreditCardFromStatement(const sql::Statement& s) { |
| CreditCard* credit_card = new CreditCard; |
| |
| credit_card->set_guid(s.ColumnString(0)); |
| DCHECK(guid::IsValidGUID(credit_card->guid())); |
| |
| credit_card->SetInfo(CREDIT_CARD_NAME, s.ColumnString16(1)); |
| credit_card->SetInfo(CREDIT_CARD_EXP_MONTH, |
| s.ColumnString16(2)); |
| credit_card->SetInfo(CREDIT_CARD_EXP_4_DIGIT_YEAR, |
| s.ColumnString16(3)); |
| int encrypted_number_len = s.ColumnByteLength(4); |
| string16 credit_card_number; |
| if (encrypted_number_len) { |
| std::string encrypted_number; |
| encrypted_number.resize(encrypted_number_len); |
| memcpy(&encrypted_number[0], s.ColumnBlob(4), encrypted_number_len); |
| Encryptor::DecryptString16(encrypted_number, &credit_card_number); |
| } |
| credit_card->SetInfo(CREDIT_CARD_NUMBER, credit_card_number); |
| // Intentionally skip column 5, which stores the modification date. |
| |
| return credit_card; |
| } |
| |
| bool AddAutofillProfileNamesToProfile(sql::Connection* db, |
| AutofillProfile* profile) { |
| sql::Statement s(db->GetUniqueStatement( |
| "SELECT guid, first_name, middle_name, last_name " |
| "FROM autofill_profile_names " |
| "WHERE guid=?")); |
| if (!s) { |
| NOTREACHED() << "Statement prepare failed"; |
| return false; |
| } |
| s.BindString(0, profile->guid()); |
| |
| std::vector<string16> first_names; |
| std::vector<string16> middle_names; |
| std::vector<string16> last_names; |
| while (s.Step()) { |
| DCHECK_EQ(profile->guid(), s.ColumnString(0)); |
| first_names.push_back(s.ColumnString16(1)); |
| middle_names.push_back(s.ColumnString16(2)); |
| last_names.push_back(s.ColumnString16(3)); |
| } |
| profile->SetMultiInfo(NAME_FIRST, first_names); |
| profile->SetMultiInfo(NAME_MIDDLE, middle_names); |
| profile->SetMultiInfo(NAME_LAST, last_names); |
| return true; |
| } |
| |
| bool AddAutofillProfileEmailsToProfile(sql::Connection* db, |
| AutofillProfile* profile) { |
| sql::Statement s(db->GetUniqueStatement( |
| "SELECT guid, email " |
| "FROM autofill_profile_emails " |
| "WHERE guid=?")); |
| if (!s) { |
| NOTREACHED() << "Statement prepare failed"; |
| return false; |
| } |
| s.BindString(0, profile->guid()); |
| |
| std::vector<string16> emails; |
| while (s.Step()) { |
| DCHECK_EQ(profile->guid(), s.ColumnString(0)); |
| emails.push_back(s.ColumnString16(1)); |
| } |
| profile->SetMultiInfo(EMAIL_ADDRESS, emails); |
| return true; |
| } |
| |
| bool AddAutofillProfilePhonesToProfile(sql::Connection* db, |
| AutofillProfile* profile) { |
| sql::Statement s(db->GetUniqueStatement( |
| "SELECT guid, type, number " |
| "FROM autofill_profile_phones " |
| "WHERE guid=? AND type=?")); |
| if (!s) { |
| NOTREACHED() << "Statement prepare failed"; |
| return false; |
| } |
| s.BindString(0, profile->guid()); |
| s.BindInt(1, kAutofillPhoneNumber); |
| |
| std::vector<string16> numbers; |
| while (s.Step()) { |
| DCHECK_EQ(profile->guid(), s.ColumnString(0)); |
| numbers.push_back(s.ColumnString16(2)); |
| } |
| profile->SetMultiInfo(PHONE_HOME_WHOLE_NUMBER, numbers); |
| return true; |
| } |
| |
| bool AddAutofillProfileFaxesToProfile(sql::Connection* db, |
| AutofillProfile* profile) { |
| sql::Statement s(db->GetUniqueStatement( |
| "SELECT guid, type, number " |
| "FROM autofill_profile_phones " |
| "WHERE guid=? AND type=?")); |
| if (!s) { |
| NOTREACHED() << "Statement prepare failed"; |
| return false; |
| } |
| s.BindString(0, profile->guid()); |
| s.BindInt(1, kAutofillFaxNumber); |
| |
| std::vector<string16> numbers; |
| while (s.Step()) { |
| DCHECK_EQ(profile->guid(), s.ColumnString(0)); |
| numbers.push_back(s.ColumnString16(2)); |
| } |
| profile->SetMultiInfo(PHONE_FAX_WHOLE_NUMBER, numbers); |
| return true; |
| } |
| |
| |
| bool AddAutofillProfileNames(const AutofillProfile& profile, |
| sql::Connection* db) { |
| std::vector<string16> first_names; |
| profile.GetMultiInfo(NAME_FIRST, &first_names); |
| std::vector<string16> middle_names; |
| profile.GetMultiInfo(NAME_MIDDLE, &middle_names); |
| std::vector<string16> last_names; |
| profile.GetMultiInfo(NAME_LAST, &last_names); |
| DCHECK_EQ(first_names.size(), middle_names.size()); |
| DCHECK_EQ(middle_names.size(), last_names.size()); |
| |
| for (size_t i = 0; i < first_names.size(); ++i) { |
| // Add the new name. |
| sql::Statement s(db->GetUniqueStatement( |
| "INSERT INTO autofill_profile_names" |
| " (guid, first_name, middle_name, last_name) " |
| "VALUES (?,?,?,?)")); |
| if (!s) { |
| NOTREACHED(); |
| return false; |
| } |
| s.BindString(0, profile.guid()); |
| s.BindString16(1, first_names[i]); |
| s.BindString16(2, middle_names[i]); |
| s.BindString16(3, last_names[i]); |
| |
| if (!s.Run()) { |
| NOTREACHED(); |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| bool AddAutofillProfileEmails(const AutofillProfile& profile, |
| sql::Connection* db) { |
| std::vector<string16> emails; |
| profile.GetMultiInfo(EMAIL_ADDRESS, &emails); |
| |
| for (size_t i = 0; i < emails.size(); ++i) { |
| // Add the new email. |
| sql::Statement s(db->GetUniqueStatement( |
| "INSERT INTO autofill_profile_emails" |
| " (guid, email) " |
| "VALUES (?,?)")); |
| if (!s) { |
| NOTREACHED(); |
| return false; |
| } |
| s.BindString(0, profile.guid()); |
| s.BindString16(1, emails[i]); |
| |
| if (!s.Run()) { |
| NOTREACHED(); |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| bool AddAutofillProfilePhones(const AutofillProfile& profile, |
| AutofillPhoneType phone_type, |
| sql::Connection* db) { |
| AutofillFieldType field_type; |
| if (phone_type == kAutofillPhoneNumber) { |
| field_type = PHONE_HOME_WHOLE_NUMBER; |
| } else if (phone_type == kAutofillFaxNumber) { |
| field_type = PHONE_FAX_WHOLE_NUMBER; |
| } else { |
| NOTREACHED(); |
| return false; |
| } |
| |
| std::vector<string16> numbers; |
| profile.GetMultiInfo(field_type, &numbers); |
| |
| for (size_t i = 0; i < numbers.size(); ++i) { |
| // Add the new number. |
| sql::Statement s(db->GetUniqueStatement( |
| "INSERT INTO autofill_profile_phones" |
| " (guid, type, number) " |
| "VALUES (?,?,?)")); |
| if (!s) { |
| NOTREACHED(); |
| return false; |
| } |
| s.BindString(0, profile.guid()); |
| s.BindInt(1, phone_type); |
| s.BindString16(2, numbers[i]); |
| |
| if (!s.Run()) { |
| NOTREACHED(); |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| bool AddAutofillProfilePieces(const AutofillProfile& profile, |
| sql::Connection* db) { |
| if (!AddAutofillProfileNames(profile, db)) |
| return false; |
| |
| if (!AddAutofillProfileEmails(profile, db)) |
| return false; |
| |
| if (!AddAutofillProfilePhones(profile, kAutofillPhoneNumber, db)) |
| return false; |
| |
| if (!AddAutofillProfilePhones(profile, kAutofillFaxNumber, db)) |
| return false; |
| |
| return true; |
| } |
| |
| bool RemoveAutofillProfilePieces(const std::string& guid, sql::Connection* db) { |
| sql::Statement s1(db->GetUniqueStatement( |
| "DELETE FROM autofill_profile_names WHERE guid = ?")); |
| if (!s1) { |
| NOTREACHED() << "Statement prepare failed"; |
| return false; |
| } |
| |
| s1.BindString(0, guid); |
| if (!s1.Run()) |
| return false; |
| |
| sql::Statement s2(db->GetUniqueStatement( |
| "DELETE FROM autofill_profile_emails WHERE guid = ?")); |
| if (!s2) { |
| NOTREACHED() << "Statement prepare failed"; |
| return false; |
| } |
| |
| s2.BindString(0, guid); |
| if (!s2.Run()) |
| return false; |
| |
| sql::Statement s3(db->GetUniqueStatement( |
| "DELETE FROM autofill_profile_phones WHERE guid = ?")); |
| if (!s3) { |
| NOTREACHED() << "Statement prepare failed"; |
| return false; |
| } |
| |
| s3.BindString(0, guid); |
| return s3.Run(); |
| } |
| |
| } // namespace |
| |
| bool AutofillTable::Init() { |
| return (InitMainTable() && InitCreditCardsTable() && InitDatesTable() && |
| InitProfilesTable() && InitProfileNamesTable() && |
| InitProfileEmailsTable() && InitProfilePhonesTable() && |
| InitProfileTrashTable()); |
| } |
| |
| bool AutofillTable::IsSyncable() { |
| return true; |
| } |
| |
| bool AutofillTable::AddFormFieldValues(const std::vector<FormField>& elements, |
| std::vector<AutofillChange>* changes) { |
| return AddFormFieldValuesTime(elements, changes, Time::Now()); |
| } |
| |
| bool AutofillTable::AddFormFieldValue(const FormField& element, |
| std::vector<AutofillChange>* changes) { |
| return AddFormFieldValueTime(element, changes, base::Time::Now()); |
| } |
| |
| bool AutofillTable::GetFormValuesForElementName(const string16& name, |
| const string16& prefix, |
| std::vector<string16>* values, |
| int limit) { |
| DCHECK(values); |
| sql::Statement s; |
| |
| if (prefix.empty()) { |
| s.Assign(db_->GetUniqueStatement( |
| "SELECT value FROM autofill " |
| "WHERE name = ? " |
| "ORDER BY count DESC " |
| "LIMIT ?")); |
| if (!s) { |
| NOTREACHED() << "Statement prepare failed"; |
| return false; |
| } |
| |
| s.BindString16(0, name); |
| s.BindInt(1, limit); |
| } else { |
| string16 prefix_lower = l10n_util::ToLower(prefix); |
| string16 next_prefix = prefix_lower; |
| next_prefix[next_prefix.length() - 1]++; |
| |
| s.Assign(db_->GetUniqueStatement( |
| "SELECT value FROM autofill " |
| "WHERE name = ? AND " |
| "value_lower >= ? AND " |
| "value_lower < ? " |
| "ORDER BY count DESC " |
| "LIMIT ?")); |
| if (!s) { |
| NOTREACHED() << "Statement prepare failed"; |
| return false; |
| } |
| |
| s.BindString16(0, name); |
| s.BindString16(1, prefix_lower); |
| s.BindString16(2, next_prefix); |
| s.BindInt(3, limit); |
| } |
| |
| values->clear(); |
| while (s.Step()) |
| values->push_back(s.ColumnString16(0)); |
| return s.Succeeded(); |
| } |
| |
| bool AutofillTable::RemoveFormElementsAddedBetween( |
| base::Time delete_begin, |
| base::Time delete_end, |
| std::vector<AutofillChange>* changes) { |
| DCHECK(changes); |
| // Query for the pair_id, name, and value of all form elements that |
| // were used between the given times. |
| sql::Statement s(db_->GetUniqueStatement( |
| "SELECT DISTINCT a.pair_id, a.name, a.value " |
| "FROM autofill_dates ad JOIN autofill a ON ad.pair_id = a.pair_id " |
| "WHERE ad.date_created >= ? AND ad.date_created < ?")); |
| if (!s) { |
| NOTREACHED() << "Statement 1 prepare failed"; |
| return false; |
| } |
| s.BindInt64(0, delete_begin.ToTimeT()); |
| s.BindInt64(1, |
| delete_end.is_null() ? |
| std::numeric_limits<int64>::max() : |
| delete_end.ToTimeT()); |
| |
| AutofillElementList elements; |
| while (s.Step()) { |
| elements.push_back(MakeTuple(s.ColumnInt64(0), |
| s.ColumnString16(1), |
| s.ColumnString16(2))); |
| } |
| |
| if (!s.Succeeded()) { |
| NOTREACHED(); |
| return false; |
| } |
| |
| for (AutofillElementList::iterator itr = elements.begin(); |
| itr != elements.end(); itr++) { |
| int how_many = 0; |
| if (!RemoveFormElementForTimeRange(itr->a, delete_begin, delete_end, |
| &how_many)) { |
| return false; |
| } |
| bool was_removed = false; |
| if (!AddToCountOfFormElement(itr->a, -how_many, &was_removed)) |
| return false; |
| AutofillChange::Type change_type = |
| was_removed ? AutofillChange::REMOVE : AutofillChange::UPDATE; |
| changes->push_back(AutofillChange(change_type, |
| AutofillKey(itr->b, itr->c))); |
| } |
| |
| return true; |
| } |
| |
| bool AutofillTable::RemoveFormElementForTimeRange(int64 pair_id, |
| const Time delete_begin, |
| const Time delete_end, |
| int* how_many) { |
| sql::Statement s(db_->GetUniqueStatement( |
| "DELETE FROM autofill_dates WHERE pair_id = ? AND " |
| "date_created >= ? AND date_created < ?")); |
| if (!s) { |
| NOTREACHED() << "Statement 1 prepare failed"; |
| return false; |
| } |
| s.BindInt64(0, pair_id); |
| s.BindInt64(1, delete_begin.is_null() ? 0 : delete_begin.ToTimeT()); |
| s.BindInt64(2, delete_end.is_null() ? std::numeric_limits<int64>::max() : |
| delete_end.ToTimeT()); |
| |
| bool result = s.Run(); |
| if (how_many) |
| *how_many = db_->GetLastChangeCount(); |
| |
| return result; |
| } |
| |
| bool AutofillTable::AddToCountOfFormElement(int64 pair_id, |
| int delta, |
| bool* was_removed) { |
| DCHECK(was_removed); |
| int count = 0; |
| *was_removed = false; |
| |
| if (!GetCountOfFormElement(pair_id, &count)) |
| return false; |
| |
| if (count + delta == 0) { |
| if (!RemoveFormElementForID(pair_id)) |
| return false; |
| *was_removed = true; |
| } else { |
| if (!SetCountOfFormElement(pair_id, count + delta)) |
| return false; |
| } |
| return true; |
| } |
| |
| bool AutofillTable::GetIDAndCountOfFormElement( |
| const FormField& element, |
| int64* pair_id, |
| int* count) { |
| sql::Statement s(db_->GetUniqueStatement( |
| "SELECT pair_id, count FROM autofill " |
| "WHERE name = ? AND value = ?")); |
| if (!s) { |
| NOTREACHED() << "Statement prepare failed"; |
| return false; |
| } |
| |
| s.BindString16(0, element.name); |
| s.BindString16(1, element.value); |
| |
| *pair_id = 0; |
| *count = 0; |
| |
| if (s.Step()) { |
| *pair_id = s.ColumnInt64(0); |
| *count = s.ColumnInt(1); |
| } |
| |
| return true; |
| } |
| |
| bool AutofillTable::GetCountOfFormElement(int64 pair_id, int* count) { |
| sql::Statement s(db_->GetUniqueStatement( |
| "SELECT count FROM autofill WHERE pair_id = ?")); |
| if (!s) { |
| NOTREACHED() << "Statement prepare failed"; |
| return false; |
| } |
| |
| s.BindInt64(0, pair_id); |
| |
| if (s.Step()) { |
| *count = s.ColumnInt(0); |
| return true; |
| } |
| return false; |
| } |
| |
| bool AutofillTable::SetCountOfFormElement(int64 pair_id, int count) { |
| sql::Statement s(db_->GetUniqueStatement( |
| "UPDATE autofill SET count = ? WHERE pair_id = ?")); |
| if (!s) { |
| NOTREACHED() << "Statement prepare failed"; |
| return false; |
| } |
| |
| s.BindInt(0, count); |
| s.BindInt64(1, pair_id); |
| if (!s.Run()) { |
| NOTREACHED(); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool AutofillTable::InsertFormElement(const FormField& element, |
| int64* pair_id) { |
| sql::Statement s(db_->GetUniqueStatement( |
| "INSERT INTO autofill (name, value, value_lower) VALUES (?,?,?)")); |
| if (!s) { |
| NOTREACHED() << "Statement prepare failed"; |
| return false; |
| } |
| |
| s.BindString16(0, element.name); |
| s.BindString16(1, element.value); |
| s.BindString16(2, l10n_util::ToLower(element.value)); |
| |
| if (!s.Run()) { |
| NOTREACHED(); |
| return false; |
| } |
| |
| *pair_id = db_->GetLastInsertRowId(); |
| return true; |
| } |
| |
| bool AutofillTable::InsertPairIDAndDate(int64 pair_id, |
| base::Time date_created) { |
| sql::Statement s(db_->GetUniqueStatement( |
| "INSERT INTO autofill_dates " |
| "(pair_id, date_created) VALUES (?, ?)")); |
| if (!s) { |
| NOTREACHED() << "Statement prepare failed"; |
| return false; |
| } |
| |
| s.BindInt64(0, pair_id); |
| s.BindInt64(1, date_created.ToTimeT()); |
| |
| if (!s.Run()) { |
| NOTREACHED(); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool AutofillTable::AddFormFieldValuesTime( |
| const std::vector<FormField>& 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<string16> seen_names; |
| bool result = true; |
| for (std::vector<FormField>::const_iterator |
| itr = elements.begin(); |
| itr != elements.end(); |
| itr++) { |
| if (seen_names.size() >= kMaximumUniqueNames) |
| break; |
| if (seen_names.find(itr->name) != seen_names.end()) |
| continue; |
| result = result && AddFormFieldValueTime(*itr, changes, time); |
| seen_names.insert(itr->name); |
| } |
| return result; |
| } |
| |
| bool AutofillTable::ClearAutofillEmptyValueElements() { |
| sql::Statement s(db_->GetUniqueStatement( |
| "SELECT pair_id FROM autofill WHERE TRIM(value)= \"\"")); |
| if (!s) { |
| NOTREACHED() << "Statement prepare failed"; |
| return false; |
| } |
| |
| std::set<int64> ids; |
| while (s.Step()) |
| ids.insert(s.ColumnInt64(0)); |
| |
| bool success = true; |
| for (std::set<int64>::const_iterator iter = ids.begin(); iter != ids.end(); |
| ++iter) { |
| if (!RemoveFormElementForID(*iter)) |
| success = false; |
| } |
| |
| return success; |
| } |
| |
| bool AutofillTable::GetAllAutofillEntries(std::vector<AutofillEntry>* entries) { |
| DCHECK(entries); |
| sql::Statement s(db_->GetUniqueStatement( |
| "SELECT name, value, date_created FROM autofill a JOIN " |
| "autofill_dates ad ON a.pair_id=ad.pair_id")); |
| |
| if (!s) { |
| NOTREACHED() << "Statement prepare failed"; |
| return false; |
| } |
| |
| bool first_entry = true; |
| AutofillKey* current_key_ptr = NULL; |
| std::vector<base::Time>* timestamps_ptr = NULL; |
| string16 name, value; |
| base::Time time; |
| while (s.Step()) { |
| name = s.ColumnString16(0); |
| value = s.ColumnString16(1); |
| time = Time::FromTimeT(s.ColumnInt64(2)); |
| |
| if (first_entry) { |
| current_key_ptr = new AutofillKey(name, value); |
| |
| timestamps_ptr = new std::vector<base::Time>; |
| timestamps_ptr->push_back(time); |
| |
| first_entry = false; |
| } else { |
| // we've encountered the next entry |
| if (current_key_ptr->name().compare(name) != 0 || |
| current_key_ptr->value().compare(value) != 0) { |
| AutofillEntry entry(*current_key_ptr, *timestamps_ptr); |
| entries->push_back(entry); |
| |
| delete current_key_ptr; |
| delete timestamps_ptr; |
| |
| current_key_ptr = new AutofillKey(name, value); |
| timestamps_ptr = new std::vector<base::Time>; |
| } |
| timestamps_ptr->push_back(time); |
| } |
| } |
| // If there is at least one result returned, first_entry will be false. |
| // For this case we need to do a final cleanup step. |
| if (!first_entry) { |
| AutofillEntry entry(*current_key_ptr, *timestamps_ptr); |
| entries->push_back(entry); |
| delete current_key_ptr; |
| delete timestamps_ptr; |
| } |
| |
| return s.Succeeded(); |
| } |
| |
| bool AutofillTable::GetAutofillTimestamps(const string16& name, |
| const string16& value, |
| std::vector<base::Time>* timestamps) { |
| DCHECK(timestamps); |
| sql::Statement s(db_->GetUniqueStatement( |
| "SELECT date_created FROM autofill a JOIN " |
| "autofill_dates ad ON a.pair_id=ad.pair_id " |
| "WHERE a.name = ? AND a.value = ?")); |
| |
| if (!s) { |
| NOTREACHED() << "Statement prepare failed"; |
| return false; |
| } |
| |
| s.BindString16(0, name); |
| s.BindString16(1, value); |
| while (s.Step()) { |
| timestamps->push_back(Time::FromTimeT(s.ColumnInt64(0))); |
| } |
| |
| return s.Succeeded(); |
| } |
| |
| bool AutofillTable::UpdateAutofillEntries( |
| const std::vector<AutofillEntry>& entries) { |
| if (!entries.size()) |
| return true; |
| |
| // Remove all existing entries. |
| for (size_t i = 0; i < entries.size(); i++) { |
| std::string sql = "SELECT pair_id FROM autofill " |
| "WHERE name = ? AND value = ?"; |
| sql::Statement s(db_->GetUniqueStatement(sql.c_str())); |
| if (!s.is_valid()) { |
| NOTREACHED() << "Statement prepare failed"; |
| return false; |
| } |
| |
| s.BindString16(0, entries[i].key().name()); |
| s.BindString16(1, entries[i].key().value()); |
| if (s.Step()) { |
| if (!RemoveFormElementForID(s.ColumnInt64(0))) |
| 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::InsertAutofillEntry(const AutofillEntry& entry) { |
| std::string sql = "INSERT INTO autofill (name, value, value_lower, count) " |
| "VALUES (?, ?, ?, ?)"; |
| sql::Statement s(db_->GetUniqueStatement(sql.c_str())); |
| if (!s.is_valid()) { |
| NOTREACHED() << "Statement prepare failed"; |
| return false; |
| } |
| |
| s.BindString16(0, entry.key().name()); |
| s.BindString16(1, entry.key().value()); |
| s.BindString16(2, l10n_util::ToLower(entry.key().value())); |
| s.BindInt(3, entry.timestamps().size()); |
| |
| if (!s.Run()) { |
| NOTREACHED(); |
| return false; |
| } |
| |
| int64 pair_id = db_->GetLastInsertRowId(); |
| for (size_t i = 0; i < entry.timestamps().size(); i++) { |
| if (!InsertPairIDAndDate(pair_id, entry.timestamps()[i])) |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool AutofillTable::AddFormFieldValueTime(const FormField& element, |
| std::vector<AutofillChange>* changes, |
| base::Time time) { |
| int count = 0; |
| int64 pair_id; |
| |
| if (!GetIDAndCountOfFormElement(element, &pair_id, &count)) |
| return false; |
| |
| if (count == 0 && !InsertFormElement(element, &pair_id)) |
| return false; |
| |
| if (!SetCountOfFormElement(pair_id, count + 1)) |
| return false; |
| |
| if (!InsertPairIDAndDate(pair_id, time)) |
| return false; |
| |
| AutofillChange::Type change_type = |
| count == 0 ? AutofillChange::ADD : AutofillChange::UPDATE; |
| changes->push_back( |
| AutofillChange(change_type, |
| AutofillKey(element.name, element.value))); |
| return true; |
| } |
| |
| |
| bool AutofillTable::RemoveFormElement(const string16& name, |
| const string16& value) { |
| // Find the id for that pair. |
| sql::Statement s(db_->GetUniqueStatement( |
| "SELECT pair_id FROM autofill WHERE name = ? AND value= ?")); |
| if (!s) { |
| NOTREACHED() << "Statement 1 prepare failed"; |
| return false; |
| } |
| s.BindString16(0, name); |
| s.BindString16(1, value); |
| |
| if (s.Step()) |
| return RemoveFormElementForID(s.ColumnInt64(0)); |
| return false; |
| } |
| |
| bool AutofillTable::AddAutofillProfile(const AutofillProfile& profile) { |
| if (IsAutofillGUIDInTrash(profile.guid())) |
| return true; |
| |
| sql::Statement s(db_->GetUniqueStatement( |
| "INSERT INTO autofill_profiles" |
| "(guid, company_name, address_line_1, address_line_2, city, state," |
| " zipcode, country, country_code, date_modified)" |
| "VALUES (?,?,?,?,?,?,?,?,?,?)")); |
| if (!s) { |
| NOTREACHED() << "Statement prepare failed"; |
| return false; |
| } |
| |
| BindAutofillProfileToStatement(profile, &s); |
| |
| if (!s.Run()) { |
| NOTREACHED(); |
| return false; |
| } |
| |
| if (!s.Succeeded()) |
| return false; |
| |
| return AddAutofillProfilePieces(profile, db_); |
| } |
| |
| bool AutofillTable::GetAutofillProfile(const std::string& guid, |
| AutofillProfile** profile) { |
| DCHECK(guid::IsValidGUID(guid)); |
| DCHECK(profile); |
| sql::Statement s(db_->GetUniqueStatement( |
| "SELECT guid, company_name, address_line_1, address_line_2, city, state," |
| " zipcode, country, country_code, date_modified " |
| "FROM autofill_profiles " |
| "WHERE guid=?")); |
| if (!s) { |
| NOTREACHED() << "Statement prepare failed"; |
| return false; |
| } |
| |
| s.BindString(0, guid); |
| if (!s.Step()) |
| return false; |
| |
| if (!s.Succeeded()) |
| return false; |
| |
| scoped_ptr<AutofillProfile> 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()); |
| |
| // Get associated fax info. |
| AddAutofillProfileFaxesToProfile(db_, p.get()); |
| |
| *profile = p.release(); |
| return true; |
| } |
| |
| bool AutofillTable::GetAutofillProfiles( |
| std::vector<AutofillProfile*>* profiles) { |
| DCHECK(profiles); |
| profiles->clear(); |
| |
| sql::Statement s(db_->GetUniqueStatement( |
| "SELECT guid " |
| "FROM autofill_profiles")); |
| if (!s) { |
| NOTREACHED() << "Statement prepare failed"; |
| return false; |
| } |
| |
| while (s.Step()) { |
| std::string guid = s.ColumnString(0); |
| AutofillProfile* profile = NULL; |
| if (!GetAutofillProfile(guid, &profile)) |
| return false; |
| profiles->push_back(profile); |
| } |
| |
| return s.Succeeded(); |
| } |
| |
| bool AutofillTable::UpdateAutofillProfile(const AutofillProfile& profile) { |
| DCHECK(guid::IsValidGUID(profile.guid())); |
| |
| // Don't update anything until the trash has been emptied. There may be |
| // pending modifications to process. |
| if (!IsAutofillProfilesTrashEmpty()) |
| return true; |
| |
| AutofillProfile* tmp_profile = NULL; |
| if (!GetAutofillProfile(profile.guid(), &tmp_profile)) |
| return false; |
| |
| // Preserve appropriate modification dates by not updating unchanged profiles. |
| scoped_ptr<AutofillProfile> old_profile(tmp_profile); |
| if (old_profile->Compare(profile) == 0) |
| return true; |
| |
| AutofillProfile new_profile(profile); |
| std::vector<string16> values; |
| |
| old_profile->GetMultiInfo(NAME_FULL, &values); |
| values[0] = new_profile.GetInfo(NAME_FULL); |
| new_profile.SetMultiInfo(NAME_FULL, values); |
| |
| old_profile->GetMultiInfo(EMAIL_ADDRESS, &values); |
| values[0] = new_profile.GetInfo(EMAIL_ADDRESS); |
| new_profile.SetMultiInfo(EMAIL_ADDRESS, values); |
| |
| old_profile->GetMultiInfo(PHONE_HOME_WHOLE_NUMBER, &values); |
| values[0] = new_profile.GetInfo(PHONE_HOME_WHOLE_NUMBER); |
| new_profile.SetMultiInfo(PHONE_HOME_WHOLE_NUMBER, values); |
| |
| old_profile->GetMultiInfo(PHONE_FAX_WHOLE_NUMBER, &values); |
| values[0] = new_profile.GetInfo(PHONE_FAX_WHOLE_NUMBER); |
| new_profile.SetMultiInfo(PHONE_FAX_WHOLE_NUMBER, values); |
| |
| return UpdateAutofillProfileMulti(new_profile); |
| } |
| |
| bool AutofillTable::UpdateAutofillProfileMulti(const AutofillProfile& profile) { |
| DCHECK(guid::IsValidGUID(profile.guid())); |
| |
| // Don't update anything until the trash has been emptied. There may be |
| // pending modifications to process. |
| if (!IsAutofillProfilesTrashEmpty()) |
| return true; |
| |
| AutofillProfile* tmp_profile = NULL; |
| if (!GetAutofillProfile(profile.guid(), &tmp_profile)) |
| return false; |
| |
| // Preserve appropriate modification dates by not updating unchanged profiles. |
| scoped_ptr<AutofillProfile> old_profile(tmp_profile); |
| if (old_profile->CompareMulti(profile) == 0) |
| return true; |
| |
| sql::Statement s(db_->GetUniqueStatement( |
| "UPDATE autofill_profiles " |
| "SET guid=?, company_name=?, address_line_1=?, address_line_2=?, " |
| " city=?, state=?, zipcode=?, country=?, country_code=?, " |
| " date_modified=? " |
| "WHERE guid=?")); |
| if (!s) { |
| NOTREACHED() << "Statement prepare failed"; |
| return false; |
| } |
| |
| BindAutofillProfileToStatement(profile, &s); |
| s.BindString(10, profile.guid()); |
| bool result = s.Run(); |
| DCHECK_GT(db_->GetLastChangeCount(), 0); |
| if (!result) |
| return result; |
| |
| // Remove the old names, emails, and phone/fax numbers. |
| if (!RemoveAutofillProfilePieces(profile.guid(), db_)) |
| return false; |
| |
| return AddAutofillProfilePieces(profile, db_); |
| } |
| |
| bool AutofillTable::RemoveAutofillProfile(const std::string& guid) { |
| DCHECK(guid::IsValidGUID(guid)); |
| |
| if (IsAutofillGUIDInTrash(guid)) { |
| sql::Statement s_trash(db_->GetUniqueStatement( |
| "DELETE FROM autofill_profiles_trash WHERE guid = ?")); |
| if (!s_trash) { |
| NOTREACHED() << "Statement prepare failed"; |
| return false; |
| } |
| s_trash.BindString(0, guid); |
| if (!s_trash.Run()) { |
| NOTREACHED() << "Expected item in trash."; |
| return false; |
| } |
| |
| return true; |
| } |
| |
| sql::Statement s(db_->GetUniqueStatement( |
| "DELETE FROM autofill_profiles WHERE guid = ?")); |
| if (!s) { |
| NOTREACHED() << "Statement prepare failed"; |
| return false; |
| } |
| |
| s.BindString(0, guid); |
| if (!s.Run()) |
| return false; |
| |
| return RemoveAutofillProfilePieces(guid, db_); |
| } |
| |
| bool AutofillTable::ClearAutofillProfiles() { |
| sql::Statement s1(db_->GetUniqueStatement( |
| "DELETE FROM autofill_profiles")); |
| if (!s1) { |
| NOTREACHED() << "Statement prepare failed"; |
| return false; |
| } |
| |
| if (!s1.Run()) |
| return false; |
| |
| sql::Statement s2(db_->GetUniqueStatement( |
| "DELETE FROM autofill_profile_names")); |
| if (!s2) { |
| NOTREACHED() << "Statement prepare failed"; |
| return false; |
| } |
| |
| if (!s2.Run()) |
| return false; |
| |
| sql::Statement s3(db_->GetUniqueStatement( |
| "DELETE FROM autofill_profile_emails")); |
| if (!s3) { |
| NOTREACHED() << "Statement prepare failed"; |
| return false; |
| } |
| |
| if (!s3.Run()) |
| return false; |
| |
| sql::Statement s4(db_->GetUniqueStatement( |
| "DELETE FROM autofill_profile_phones")); |
| if (!s4) { |
| NOTREACHED() << "Statement prepare failed"; |
| return false; |
| } |
| |
| if (!s4.Run()) |
| return false; |
| |
| return true; |
| } |
| |
| 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, date_modified)" |
| "VALUES (?,?,?,?,?,?)")); |
| if (!s) { |
| NOTREACHED() << "Statement prepare failed"; |
| return false; |
| } |
| |
| BindCreditCardToStatement(credit_card, &s); |
| |
| if (!s.Run()) { |
| NOTREACHED(); |
| return false; |
| } |
| |
| DCHECK_GT(db_->GetLastChangeCount(), 0); |
| return s.Succeeded(); |
| } |
| |
| bool AutofillTable::GetCreditCard(const std::string& guid, |
| CreditCard** credit_card) { |
| DCHECK(guid::IsValidGUID(guid)); |
| sql::Statement s(db_->GetUniqueStatement( |
| "SELECT guid, name_on_card, expiration_month, expiration_year, " |
| "card_number_encrypted, date_modified " |
| "FROM credit_cards " |
| "WHERE guid = ?")); |
| if (!s) { |
| NOTREACHED() << "Statement prepare failed"; |
| return false; |
| } |
| |
| s.BindString(0, guid); |
| if (!s.Step()) |
| return false; |
| |
| *credit_card = CreditCardFromStatement(s); |
| |
| return s.Succeeded(); |
| } |
| |
| bool AutofillTable::GetCreditCards( |
| std::vector<CreditCard*>* credit_cards) { |
| DCHECK(credit_cards); |
| credit_cards->clear(); |
| |
| sql::Statement s(db_->GetUniqueStatement( |
| "SELECT guid " |
| "FROM credit_cards")); |
| if (!s) { |
| NOTREACHED() << "Statement prepare failed"; |
| return false; |
| } |
| |
| while (s.Step()) { |
| std::string guid = s.ColumnString(0); |
| CreditCard* credit_card = NULL; |
| if (!GetCreditCard(guid, &credit_card)) |
| return false; |
| credit_cards->push_back(credit_card); |
| } |
| |
| return s.Succeeded(); |
| } |
| |
| bool AutofillTable::UpdateCreditCard(const CreditCard& credit_card) { |
| DCHECK(guid::IsValidGUID(credit_card.guid())); |
| |
| CreditCard* tmp_credit_card = NULL; |
| if (!GetCreditCard(credit_card.guid(), &tmp_credit_card)) |
| return false; |
| |
| // Preserve appropriate modification dates by not updating unchanged cards. |
| scoped_ptr<CreditCard> old_credit_card(tmp_credit_card); |
| if (*old_credit_card == credit_card) |
| return true; |
| |
| sql::Statement s(db_->GetUniqueStatement( |
| "UPDATE credit_cards " |
| "SET guid=?, name_on_card=?, expiration_month=?, " |
| " expiration_year=?, card_number_encrypted=?, date_modified=? " |
| "WHERE guid=?")); |
| if (!s) { |
| NOTREACHED() << "Statement prepare failed"; |
| return false; |
| } |
| |
| BindCreditCardToStatement(credit_card, &s); |
| s.BindString(6, credit_card.guid()); |
| bool result = s.Run(); |
| DCHECK_GT(db_->GetLastChangeCount(), 0); |
| return result; |
| } |
| |
| bool AutofillTable::RemoveCreditCard(const std::string& guid) { |
| DCHECK(guid::IsValidGUID(guid)); |
| sql::Statement s(db_->GetUniqueStatement( |
| "DELETE FROM credit_cards WHERE guid = ?")); |
| if (!s) { |
| NOTREACHED() << "Statement prepare failed"; |
| return false; |
| } |
| |
| s.BindString(0, guid); |
| return s.Run(); |
| } |
| |
| bool AutofillTable::RemoveAutofillProfilesAndCreditCardsModifiedBetween( |
| base::Time delete_begin, |
| 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 = delete_end.is_null() ? |
| std::numeric_limits<time_t>::max() : |
| delete_end.ToTimeT(); |
| |
| // 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 < ?")); |
| if (!s_profiles_get) { |
| NOTREACHED() << "Autofill profiles statement prepare failed"; |
| return false; |
| } |
| |
| 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); |
| } |
| |
| // Remove Autofill profiles in the time range. |
| sql::Statement s_profiles(db_->GetUniqueStatement( |
| "DELETE FROM autofill_profiles " |
| "WHERE date_modified >= ? AND date_modified < ?")); |
| if (!s_profiles) { |
| NOTREACHED() << "Autofill profiles statement prepare failed"; |
| return false; |
| } |
| |
| s_profiles.BindInt64(0, delete_begin_t); |
| s_profiles.BindInt64(1, delete_end_t); |
| s_profiles.Run(); |
| |
| if (!s_profiles.Succeeded()) { |
| NOTREACHED(); |
| 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 < ?")); |
| if (!s_credit_cards_get) { |
| NOTREACHED() << "Autofill profiles statement prepare failed"; |
| return false; |
| } |
| |
| 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); |
| } |
| |
| // 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 < ?")); |
| if (!s_credit_cards) { |
| NOTREACHED() << "Autofill credit cards statement prepare failed"; |
| return false; |
| } |
| |
| s_credit_cards.BindInt64(0, delete_begin_t); |
| s_credit_cards.BindInt64(1, delete_end_t); |
| s_credit_cards.Run(); |
| |
| if (!s_credit_cards.Succeeded()) { |
| NOTREACHED(); |
| 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")); |
| if (!s) { |
| NOTREACHED() << "Statement prepare failed"; |
| return false; |
| } |
| |
| 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")); |
| if (!s) { |
| NOTREACHED() << "Statement prepare failed"; |
| return false; |
| } |
| |
| return s.Run(); |
| } |
| |
| |
| bool AutofillTable::RemoveFormElementForID(int64 pair_id) { |
| sql::Statement s(db_->GetUniqueStatement( |
| "DELETE FROM autofill WHERE pair_id = ?")); |
| if (!s) { |
| NOTREACHED() << "Statement prepare failed"; |
| return false; |
| } |
| s.BindInt64(0, pair_id); |
| if (s.Run()) { |
| return RemoveFormElementForTimeRange(pair_id, base::Time(), base::Time(), |
| NULL); |
| } |
| return false; |
| } |
| |
| bool AutofillTable::AddAutofillGUIDToTrash(const std::string& guid) { |
| sql::Statement s(db_->GetUniqueStatement( |
| "INSERT INTO autofill_profiles_trash" |
| " (guid) " |
| "VALUES (?)")); |
| if (!s) { |
| NOTREACHED(); |
| return sql::INIT_FAILURE; |
| } |
| |
| s.BindString(0, guid); |
| if (!s.Run()) { |
| NOTREACHED(); |
| return false; |
| } |
| return true; |
| } |
| |
| bool AutofillTable::IsAutofillProfilesTrashEmpty() { |
| sql::Statement s(db_->GetUniqueStatement( |
| "SELECT guid " |
| "FROM autofill_profiles_trash")); |
| if (!s) { |
| NOTREACHED() << "Statement prepare failed"; |
| return false; |
| } |
| |
| return !s.Step(); |
| } |
| |
| bool AutofillTable::IsAutofillGUIDInTrash(const std::string& guid) { |
| sql::Statement s(db_->GetUniqueStatement( |
| "SELECT guid " |
| "FROM autofill_profiles_trash " |
| "WHERE guid = ?")); |
| if (!s) { |
| NOTREACHED() << "Statement prepare failed"; |
| return false; |
| } |
| |
| s.BindString(0, guid); |
| return s.Step(); |
| } |
| |
| bool AutofillTable::InitMainTable() { |
| if (!db_->DoesTableExist("autofill")) { |
| if (!db_->Execute("CREATE TABLE autofill (" |
| "name VARCHAR, " |
| "value VARCHAR, " |
| "value_lower VARCHAR, " |
| "pair_id INTEGER PRIMARY KEY, " |
| "count INTEGER DEFAULT 1)")) { |
| NOTREACHED(); |
| return false; |
| } |
| if (!db_->Execute("CREATE INDEX autofill_name ON autofill (name)")) { |
| NOTREACHED(); |
| return false; |
| } |
| if (!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)")) { |
| NOTREACHED(); |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| bool AutofillTable::InitDatesTable() { |
| if (!db_->DoesTableExist("autofill_dates")) { |
| if (!db_->Execute("CREATE TABLE autofill_dates ( " |
| "pair_id INTEGER DEFAULT 0, " |
| "date_created INTEGER DEFAULT 0)")) { |
| NOTREACHED(); |
| return false; |
| } |
| if (!db_->Execute("CREATE INDEX autofill_dates_pair_id ON " |
| "autofill_dates (pair_id)")) { |
| 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, " |
| "address_line_1 VARCHAR, " |
| "address_line_2 VARCHAR, " |
| "city VARCHAR, " |
| "state VARCHAR, " |
| "zipcode VARCHAR, " |
| "country VARCHAR, " |
| "country_code VARCHAR, " |
| "date_modified INTEGER 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)")) { |
| 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, " |
| "type INTEGER DEFAULT 0, " |
| "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; |
| } |
| |
| // Add the card_number_encrypted column if credit card table was not |
| // created in this build (otherwise the column already exists). |
| // WARNING: Do not change the order of the execution of the SQL |
| // statements in this case! Profile corruption and data migration |
| // issues WILL OCCUR. See http://crbug.com/10913 |
| // |
| // The problem is that if a user has a profile which was created before |
| // r37036, when the credit_cards table was added, and then failed to |
| // update this profile between the credit card addition and the addition |
| // of the "encrypted" columns (44963), the next data migration will put |
| // the user's profile in an incoherent state: The user will update from |
| // a data profile set to be earlier than 22, and therefore pass through |
| // this update case. But because the user did not have a credit_cards |
| // table before starting Chrome, it will have just been initialized |
| // above, and so already have these columns -- and thus this data |
| // update step will have failed. |
| // |
| // The false assumption in this case is that at this step in the |
| // migration, the user has a credit card table, and that this |
| // table does not include encrypted columns! |
| // Because this case does not roll back the complete set of SQL |
| // transactions properly in case of failure (that is, it does not |
| // roll back the table initialization done above), the incoherent |
| // profile will now see itself as being at version 22 -- but include a |
| // fully initialized credit_cards table. Every time Chrome runs, it |
| // will try to update the web database and fail at this step, unless |
| // we allow for the faulty assumption described above by checking for |
| // the existence of the columns only AFTER we've executed the commands |
| // to add them. |
| bool AutofillTable::MigrateToVersion23AddCardNumberEncryptedColumn() { |
| if (!db_->DoesColumnExist("credit_cards", "card_number_encrypted")) { |
| if (!db_->Execute("ALTER TABLE credit_cards ADD COLUMN " |
| "card_number_encrypted BLOB DEFAULT NULL")) { |
| LOG(WARNING) << "Could not add card_number_encrypted to " |
| "credit_cards table."; |
| return false; |
| } |
| } |
| |
| if (!db_->DoesColumnExist("credit_cards", "verification_code_encrypted")) { |
| if (!db_->Execute("ALTER TABLE credit_cards ADD COLUMN " |
| "verification_code_encrypted BLOB DEFAULT NULL")) { |
| LOG(WARNING) << "Could not add verification_code_encrypted to " |
| "credit_cards table."; |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| // One-time cleanup for http://crbug.com/38364 - In the presence of |
| // multi-byte UTF-8 characters, that bug could cause Autofill strings |
| // to grow larger and more corrupt with each save. The cleanup removes |
| // any row with a string field larger than a reasonable size. The string |
| // fields examined here are precisely the ones that were subject to |
| // corruption by the original bug. |
| bool AutofillTable::MigrateToVersion24CleanupOversizedStringFields() { |
| const std::string autofill_is_too_big = |
| "max(length(name), length(value)) > 500"; |
| |
| const std::string credit_cards_is_too_big = |
| "max(length(label), length(name_on_card), length(type), " |
| " length(expiration_month), length(expiration_year), " |
| " length(billing_address), length(shipping_address) " |
| ") > 500"; |
| |
| const std::string autofill_profiles_is_too_big = |
| "max(length(label), length(first_name), " |
| " length(middle_name), length(last_name), length(email), " |
| " length(company_name), length(address_line_1), " |
| " length(address_line_2), length(city), length(state), " |
| " length(zipcode), length(country), length(phone), " |
| " length(fax)) > 500"; |
| |
| std::string query = "DELETE FROM autofill_dates WHERE pair_id IN (" |
| "SELECT pair_id FROM autofill WHERE " + autofill_is_too_big + ")"; |
| |
| if (!db_->Execute(query.c_str())) |
| return false; |
| |
| query = "DELETE FROM autofill WHERE " + autofill_is_too_big; |
| |
| if (!db_->Execute(query.c_str())) |
| return false; |
| |
| // Only delete from legacy credit card tables where specific columns exist. |
| if (db_->DoesColumnExist("credit_cards", "label") && |
| db_->DoesColumnExist("credit_cards", "name_on_card") && |
| db_->DoesColumnExist("credit_cards", "type") && |
| db_->DoesColumnExist("credit_cards", "expiration_month") && |
| db_->DoesColumnExist("credit_cards", "expiration_year") && |
| db_->DoesColumnExist("credit_cards", "billing_address") && |
| db_->DoesColumnExist("credit_cards", "shipping_address") && |
| db_->DoesColumnExist("autofill_profiles", "label")) { |
| query = "DELETE FROM credit_cards WHERE (" + credit_cards_is_too_big + |
| ") OR label IN (SELECT label FROM autofill_profiles WHERE " + |
| autofill_profiles_is_too_big + ")"; |
| |
| if (!db_->Execute(query.c_str())) |
| return false; |
| } |
| |
| if (db_->DoesColumnExist("autofill_profiles", "label")) { |
| query = "DELETE FROM autofill_profiles WHERE " + |
| autofill_profiles_is_too_big; |
| |
| if (!db_->Execute(query.c_str())) |
| return false; |
| } |
| |
| return true; |
| } |
| |
| // Change the credit_cards.billing_address column from a string to an |
| // int. The stored string is the label of an address, so we have to |
| // select the unique ID of this address using the label as a foreign |
| // key into the |autofill_profiles| table. |
| bool AutofillTable::MigrateToVersion27UpdateLegacyCreditCards() { |
| // Only migrate from legacy credit card tables where specific columns |
| // exist. |
| if (!(db_->DoesColumnExist("credit_cards", "unique_id") && |
| db_->DoesColumnExist("credit_cards", "billing_address") && |
| db_->DoesColumnExist("autofill_profiles", "unique_id"))) { |
| return true; |
| } |
| |
| std::string stmt = |
| "SELECT credit_cards.unique_id, autofill_profiles.unique_id " |
| "FROM autofill_profiles, credit_cards " |
| "WHERE credit_cards.billing_address = autofill_profiles.label"; |
| sql::Statement s(db_->GetUniqueStatement(stmt.c_str())); |
| if (!s) |
| return false; |
| |
| std::map<int, int> cc_billing_map; |
| while (s.Step()) |
| cc_billing_map[s.ColumnInt(0)] = s.ColumnInt(1); |
| |
| // Windows already stores the IDs as strings in |billing_address|. Try |
| // to convert those. |
| if (cc_billing_map.empty()) { |
| std::string stmt = "SELECT unique_id,billing_address FROM credit_cards"; |
| sql::Statement s(db_->GetUniqueStatement(stmt.c_str())); |
| if (!s) |
| return false; |
| |
| while (s.Step()) { |
| int id = 0; |
| if (base::StringToInt(s.ColumnString(1), &id)) |
| cc_billing_map[s.ColumnInt(0)] = id; |
| } |
| } |
| |
| if (!db_->Execute("CREATE TABLE credit_cards_temp ( " |
| "label VARCHAR, " |
| "unique_id INTEGER PRIMARY KEY, " |
| "name_on_card VARCHAR, " |
| "type VARCHAR, " |
| "card_number VARCHAR, " |
| "expiration_month INTEGER, " |
| "expiration_year INTEGER, " |
| "verification_code VARCHAR, " |
| "billing_address INTEGER, " |
| "shipping_address VARCHAR, " |
| "card_number_encrypted BLOB, " |
| "verification_code_encrypted BLOB)")) { |
| return false; |
| } |
| |
| if (!db_->Execute( |
| "INSERT INTO credit_cards_temp " |
| "SELECT label,unique_id,name_on_card,type,card_number," |
| "expiration_month,expiration_year,verification_code,0," |
| "shipping_address,card_number_encrypted," |
| "verification_code_encrypted FROM credit_cards")) { |
| return false; |
| } |
| |
| if (!db_->Execute("DROP TABLE credit_cards")) |
| return false; |
| |
| if (!db_->Execute("ALTER TABLE credit_cards_temp RENAME TO credit_cards")) |
| return false; |
| |
| for (std::map<int, int>::const_iterator iter = cc_billing_map.begin(); |
| iter != cc_billing_map.end(); ++iter) { |
| sql::Statement s(db_->GetCachedStatement( |
| SQL_FROM_HERE, |
| "UPDATE credit_cards SET billing_address=? WHERE unique_id=?")); |
| if (!s) |
| return false; |
| |
| s.BindInt(0, (*iter).second); |
| s.BindInt(1, (*iter).first); |
| |
| if (!s.Run()) |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool AutofillTable::MigrateToVersion30AddDateModifed() { |
| // Add date_modified to autofill_profiles. |
| if (!db_->DoesColumnExist("autofill_profiles", "date_modified")) { |
| if (!db_->Execute("ALTER TABLE autofill_profiles ADD COLUMN " |
| "date_modified INTEGER NON NULL DEFAULT 0")) { |
| return false; |
| } |
| |
| sql::Statement s(db_->GetUniqueStatement( |
| "UPDATE autofill_profiles SET date_modified=?")); |
| if (!s) |
| return false; |
| |
| s.BindInt64(0, Time::Now().ToTimeT()); |
| |
| if (!s.Run()) |
| return false; |
| } |
| |
| // Add date_modified to credit_cards. |
| if (!db_->DoesColumnExist("credit_cards", "date_modified")) { |
| if (!db_->Execute("ALTER TABLE credit_cards ADD COLUMN " |
| "date_modified INTEGER NON NULL DEFAULT 0")) { |
| return false; |
| } |
| |
| sql::Statement s(db_->GetUniqueStatement( |
| "UPDATE credit_cards SET date_modified=?")); |
| if (!s) |
| return false; |
| |
| s.BindInt64(0, Time::Now().ToTimeT()); |
| |
| if (!s.Run()) |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool AutofillTable::MigrateToVersion31AddGUIDToCreditCardsAndProfiles() { |
| // Note that we need to check for the guid column's existence due to the |
| // fact that for a version 22 database the |autofill_profiles| table |
| // gets created fresh with |InitAutofillProfilesTable|. |
| if (!db_->DoesColumnExist("autofill_profiles", "guid")) { |
| if (!db_->Execute("ALTER TABLE autofill_profiles ADD COLUMN " |
| "guid VARCHAR NOT NULL DEFAULT \"\"")) { |
| return false; |
| } |
| |
| // Set all the |guid| fields to valid values. |
| |
| sql::Statement s(db_->GetUniqueStatement("SELECT unique_id " |
| "FROM autofill_profiles")); |
| if (!s) |
| return false; |
| |
| while (s.Step()) { |
| sql::Statement update_s( |
| db_->GetUniqueStatement("UPDATE autofill_profiles " |
| "SET guid=? WHERE unique_id=?")); |
| if (!update_s) |
| return false; |
| update_s.BindString(0, guid::GenerateGUID()); |
| update_s.BindInt(1, s.ColumnInt(0)); |
| |
| if (!update_s.Run()) |
| return false; |
| } |
| } |
| |
| // Note that we need to check for the guid column's existence due to the |
| // fact that for a version 22 database the |autofill_profiles| table |
| // gets created fresh with |InitAutofillProfilesTable|. |
| if (!db_->DoesColumnExist("credit_cards", "guid")) { |
| if (!db_->Execute("ALTER TABLE credit_cards ADD COLUMN " |
| "guid VARCHAR NOT NULL DEFAULT \"\"")) { |
| return false; |
| } |
| |
| // Set all the |guid| fields to valid values. |
| |
| sql::Statement s(db_->GetUniqueStatement("SELECT unique_id " |
| "FROM credit_cards")); |
| if (!s) |
| return false; |
| |
| while (s.Step()) { |
| sql::Statement update_s( |
| db_->GetUniqueStatement("UPDATE credit_cards " |
| "set guid=? WHERE unique_id=?")); |
| if (!update_s) |
| return false; |
| update_s.BindString(0, guid::GenerateGUID()); |
| update_s.BindInt(1, s.ColumnInt(0)); |
| |
| if (!update_s.Run()) |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| bool AutofillTable::MigrateToVersion32UpdateProfilesAndCreditCards() { |
| if (db_->DoesColumnExist("autofill_profiles", "unique_id")) { |
| if (!db_->Execute("CREATE TABLE autofill_profiles_temp ( " |
| "guid VARCHAR PRIMARY KEY, " |
| "label VARCHAR, " |
| "first_name VARCHAR, " |
| "middle_name VARCHAR, " |
| "last_name VARCHAR, " |
| "email VARCHAR, " |
| "company_name VARCHAR, " |
| "address_line_1 VARCHAR, " |
| "address_line_2 VARCHAR, " |
| "city VARCHAR, " |
| "state VARCHAR, " |
| "zipcode VARCHAR, " |
| "country VARCHAR, " |
| "phone VARCHAR, " |
| "fax VARCHAR, " |
| "date_modified INTEGER NOT NULL DEFAULT 0)")) { |
| return false; |
| } |
| |
| if (!db_->Execute( |
| "INSERT INTO autofill_profiles_temp " |
| "SELECT guid, label, first_name, middle_name, last_name, email, " |
| "company_name, address_line_1, address_line_2, city, state, " |
| "zipcode, country, phone, fax, date_modified " |
| "FROM autofill_profiles")) { |
| return false; |
| } |
| |
| if (!db_->Execute("DROP TABLE autofill_profiles")) |
| return false; |
| |
| if (!db_->Execute( |
| "ALTER TABLE autofill_profiles_temp RENAME TO autofill_profiles")) { |
| return false; |
| } |
| } |
| |
| if (db_->DoesColumnExist("credit_cards", "unique_id")) { |
| if (!db_->Execute("CREATE TABLE credit_cards_temp ( " |
| "guid VARCHAR PRIMARY KEY, " |
| "label VARCHAR, " |
| "name_on_card VARCHAR, " |
| "expiration_month INTEGER, " |
| "expiration_year INTEGER, " |
| "card_number_encrypted BLOB, " |
| "date_modified INTEGER NOT NULL DEFAULT 0)")) { |
| return false; |
| } |
| |
| if (!db_->Execute( |
| "INSERT INTO credit_cards_temp " |
| "SELECT guid, label, name_on_card, expiration_month, " |
| "expiration_year, card_number_encrypted, date_modified " |
| "FROM credit_cards")) { |
| return false; |
| } |
| |
| if (!db_->Execute("DROP TABLE credit_cards")) |
| return false; |
| |
| if (!db_->Execute("ALTER TABLE credit_cards_temp RENAME TO credit_cards")) |
| return false; |
| } |
| |
| return true; |
| } |
| |
| // Test the existence of the |first_name| column as an indication that |
| // we need a migration. It is possible that the new |autofill_profiles| |
| // schema is in place because the table was newly created when migrating |
| // from a pre-version-22 database. |
| bool AutofillTable::MigrateToVersion33ProfilesBasedOnFirstName() { |
| if (db_->DoesColumnExist("autofill_profiles", "first_name")) { |
| // Create autofill_profiles_temp table that will receive the data. |
| if (!db_->DoesTableExist("autofill_profiles_temp")) { |
| if (!db_->Execute("CREATE TABLE autofill_profiles_temp ( " |
| "guid VARCHAR PRIMARY KEY, " |
| "company_name VARCHAR, " |
| "address_line_1 VARCHAR, " |
| "address_line_2 VARCHAR, " |
| "city VARCHAR, " |
| "state VARCHAR, " |
| "zipcode VARCHAR, " |
| "country VARCHAR, " |
| "date_modified INTEGER NOT NULL DEFAULT 0)")) { |
| return false; |
| } |
| } |
| |
| sql::Statement s(db_->GetUniqueStatement( |
| "SELECT guid, first_name, middle_name, last_name, email, " |
| "company_name, address_line_1, address_line_2, city, state, " |
| "zipcode, country, phone, fax, date_modified " |
| "FROM autofill_profiles")); |
| while (s.Step()) { |
| AutofillProfile profile; |
| profile.set_guid(s.ColumnString(0)); |
| DCHECK(guid::IsValidGUID(profile.guid())); |
| |
| profile.SetInfo(NAME_FIRST, s.ColumnString16(1)); |
| profile.SetInfo(NAME_MIDDLE, s.ColumnString16(2)); |
| profile.SetInfo(NAME_LAST, s.ColumnString16(3)); |
| profile.SetInfo(EMAIL_ADDRESS, s.ColumnString16(4)); |
| profile.SetInfo(COMPANY_NAME, s.ColumnString16(5)); |
| profile.SetInfo(ADDRESS_HOME_LINE1, s.ColumnString16(6)); |
| profile.SetInfo(ADDRESS_HOME_LINE2, s.ColumnString16(7)); |
| profile.SetInfo(ADDRESS_HOME_CITY, s.ColumnString16(8)); |
| profile.SetInfo(ADDRESS_HOME_STATE, s.ColumnString16(9)); |
| profile.SetInfo(ADDRESS_HOME_ZIP, s.ColumnString16(10)); |
| profile.SetInfo(ADDRESS_HOME_COUNTRY, s.ColumnString16(11)); |
| profile.SetInfo(PHONE_HOME_WHOLE_NUMBER, s.ColumnString16(12)); |
| profile.SetInfo(PHONE_FAX_WHOLE_NUMBER, s.ColumnString16(13)); |
| int64 date_modified = s.ColumnInt64(14); |
| |
| sql::Statement s_insert(db_->GetUniqueStatement( |
| "INSERT INTO autofill_profiles_temp" |
| "(guid, company_name, address_line_1, address_line_2, city," |
| " state, zipcode, country, date_modified)" |
| "VALUES (?,?,?,?,?,?,?,?,?)")); |
| if (!s) |
| return false; |
| |
| s_insert.BindString(0, profile.guid()); |
| s_insert.BindString16(1, profile.GetInfo(COMPANY_NAME)); |
| s_insert.BindString16(2, profile.GetInfo(ADDRESS_HOME_LINE1)); |
| s_insert.BindString16(3, profile.GetInfo(ADDRESS_HOME_LINE2)); |
| s_insert.BindString16(4, profile.GetInfo(ADDRESS_HOME_CITY)); |
| s_insert.BindString16(5, profile.GetInfo(ADDRESS_HOME_STATE)); |
| s_insert.BindString16(6, profile.GetInfo(ADDRESS_HOME_ZIP)); |
| s_insert.BindString16(7, profile.GetInfo(ADDRESS_HOME_COUNTRY)); |
| s_insert.BindInt64(8, date_modified); |
| |
| if (!s_insert.Run()) |
| return false; |
| |
| // Add the other bits: names, emails, and phone/fax. |
| if (!AddAutofillProfilePieces(profile, db_)) |
| return false; |
| } |
| |
| if (!db_->Execute("DROP TABLE autofill_profiles")) |
| return false; |
| |
| if (!db_->Execute( |
| "ALTER TABLE autofill_profiles_temp RENAME TO autofill_profiles")) { |
| return false; |
| } |
| } |
| |
| // Remove the labels column from the credit_cards table. |
| if (db_->DoesColumnExist("credit_cards", "label")) { |
| if (!db_->Execute("CREATE TABLE credit_cards_temp ( " |
| "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)")) { |
| return false; |
| } |
| |
| if (!db_->Execute( |
| "INSERT INTO credit_cards_temp " |
| "SELECT guid, name_on_card, expiration_month, " |
| "expiration_year, card_number_encrypted, date_modified " |
| "FROM credit_cards")) { |
| return false; |
| } |
| |
| if (!db_->Execute("DROP TABLE credit_cards")) |
| return false; |
| |
| if (!db_->Execute("ALTER TABLE credit_cards_temp RENAME TO credit_cards")) |
| return false; |
| } |
| |
| return true; |
| } |
| |
| // Test the existence of the |country_code| column as an indication that |
| // we need a migration. It is possible that the new |autofill_profiles| |
| // schema is in place because the table was newly created when migrating |
| // from a pre-version-22 database. |
| bool AutofillTable::MigrateToVersion34ProfilesBasedOnCountryCode() { |
| if (!db_->DoesColumnExist("autofill_profiles", "country_code")) { |
| if (!db_->Execute("ALTER TABLE autofill_profiles ADD COLUMN " |
| "country_code VARCHAR")) { |
| return false; |
| } |
| |
| // Set all the |country_code| fields to match existing |country| values. |
| sql::Statement s(db_->GetUniqueStatement("SELECT guid, country " |
| "FROM autofill_profiles")); |
| |
| if (!s) |
| return false; |
| |
| while (s.Step()) { |
| sql::Statement update_s( |
| db_->GetUniqueStatement("UPDATE autofill_profiles " |
| "SET country_code=? WHERE guid=?")); |
| if (!update_s) |
| return false; |
| |
| string16 country = s.ColumnString16(1); |
| std::string app_locale = AutofillCountry::ApplicationLocale(); |
| update_s.BindString(0, AutofillCountry::GetCountryCode(country, |
| app_locale)); |
| update_s.BindString(1, s.ColumnString(0)); |
| |
| if (!update_s.Run()) |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| // Correct all country codes with value "UK" to be "GB". This data |
| // was mistakenly introduced in build 686.0. This migration is to clean |
| // it up. See http://crbug.com/74511 for details. |
| bool AutofillTable::MigrateToVersion35GreatBritainCountryCodes() { |
| sql::Statement s(db_->GetUniqueStatement( |
| "UPDATE autofill_profiles SET country_code=\"GB\" " |
| "WHERE country_code=\"UK\"")); |
| |
| return s.Run(); |
| } |
| |
| // Merge and cull older profiles where possible. |
| bool AutofillTable::MigrateToVersion37MergeAndCullOlderProfiles() { |
| sql::Statement s(db_->GetUniqueStatement( |
| "SELECT guid, date_modified FROM autofill_profiles")); |
| if (!s) |
| return false; |
| |
| // Accumulate the good profiles. |
| std::vector<AutofillProfile> accumulated_profiles; |
| std::vector<AutofillProfile*> accumulated_profiles_p; |
| std::map<std::string, int64> modification_map; |
| while (s.Step()) { |
| std::string guid = s.ColumnString(0); |
| int64 date_modified = s.ColumnInt64(1); |
| modification_map.insert( |
| std::pair<std::string, int64>(guid, date_modified)); |
| AutofillProfile* profile = NULL; |
| if (!GetAutofillProfile(guid, &profile)) |
| return false; |
| |
| scoped_ptr<AutofillProfile> p(profile); |
| |
| if (PersonalDataManager::IsValidLearnableProfile(*p)) { |
| std::vector<AutofillProfile> merged_profiles; |
| bool merged = PersonalDataManager::MergeProfile( |
| *p, accumulated_profiles_p, &merged_profiles); |
| |
| std::swap(accumulated_profiles, merged_profiles); |
| |
| accumulated_profiles_p.clear(); |
| accumulated_profiles_p.resize(accumulated_profiles.size()); |
| std::transform(accumulated_profiles.begin(), |
| accumulated_profiles.end(), |
| accumulated_profiles_p.begin(), |
| address_of<AutofillProfile>); |
| |
| // If the profile got merged trash the original. |
| if (merged) |
| AddAutofillGUIDToTrash(p->guid()); |
| |
| } else { |
| // An invalid profile, so trash it. |
| AddAutofillGUIDToTrash(p->guid()); |
| } |
| } |
| |
| // Drop the current profiles. |
| if (!ClearAutofillProfiles()) |
| return false; |
| |
| // Add the newly merged profiles back in. |
| for (std::vector<AutofillProfile>::const_iterator |
| iter = accumulated_profiles.begin(); |
| iter != accumulated_profiles.end(); |
| ++iter) { |
| if (!AddAutofillProfile(*iter)) |
| return false; |
| |
| // Fix up the original modification date. |
| std::map<std::string, int64>::const_iterator date_item = |
| modification_map.find(iter->guid()); |
| if (date_item == modification_map.end()) |
| return false; |
| |
| sql::Statement s_date(db_->GetUniqueStatement( |
| "UPDATE autofill_profiles SET date_modified=? " |
| "WHERE guid=?")); |
| s_date.BindInt64(0, date_item->second); |
| s_date.BindString(1, iter->guid()); |
| if (!s_date.Run()) |
| return false; |
| } |
| |
| return true; |
| } |