Chromium Code Reviews| Index: chrome/browser/webdata/autofill_table.cc |
| diff --git a/chrome/browser/webdata/autofill_table.cc b/chrome/browser/webdata/autofill_table.cc |
| index 33e048372e5aacee55c1df8a5ec9381adcbb3366..e8bd0e4f99941433bb850be4b596179fc77f922f 100644 |
| --- a/chrome/browser/webdata/autofill_table.cc |
| +++ b/chrome/browser/webdata/autofill_table.cc |
| @@ -4,7 +4,16 @@ |
| #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" |
| @@ -12,8 +21,8 @@ |
| #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/browser/webdata/autofill_util.h" |
| #include "chrome/common/guid.h" |
| #include "ui/base/l10n/l10n_util.h" |
| #include "webkit/glue/form_field.h" |
| @@ -22,7 +31,360 @@ 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(); |
| +} |
| + |
| } // anonymous namespace |
|
dhollowa
2011/04/04 16:13:20
nit: s/anonymous namespace/namespace/
Bons
2011/04/05 11:05:13
Done.
|
| bool AutofillTable::Init() { |
| @@ -537,7 +899,7 @@ bool AutofillTable::AddAutofillProfile(const AutofillProfile& profile) { |
| return false; |
| } |
| - autofill_util::BindAutofillProfileToStatement(profile, &s); |
| + BindAutofillProfileToStatement(profile, &s); |
| if (!s.Run()) { |
| NOTREACHED(); |
| @@ -547,7 +909,7 @@ bool AutofillTable::AddAutofillProfile(const AutofillProfile& profile) { |
| if (!s.Succeeded()) |
| return false; |
| - return autofill_util::AddAutofillProfilePieces(profile, db_); |
| + return AddAutofillProfilePieces(profile, db_); |
| } |
| bool AutofillTable::GetAutofillProfile(const std::string& guid, |
| @@ -571,19 +933,19 @@ bool AutofillTable::GetAutofillProfile(const std::string& guid, |
| if (!s.Succeeded()) |
| return false; |
| - scoped_ptr<AutofillProfile> p(autofill_util::AutofillProfileFromStatement(s)); |
| + scoped_ptr<AutofillProfile> p(AutofillProfileFromStatement(s)); |
| // Get associated name info. |
| - autofill_util::AddAutofillProfileNamesToProfile(db_, p.get()); |
| + AddAutofillProfileNamesToProfile(db_, p.get()); |
| // Get associated email info. |
| - autofill_util::AddAutofillProfileEmailsToProfile(db_, p.get()); |
| + AddAutofillProfileEmailsToProfile(db_, p.get()); |
| // Get associated phone info. |
| - autofill_util::AddAutofillProfilePhonesToProfile(db_, p.get()); |
| + AddAutofillProfilePhonesToProfile(db_, p.get()); |
| // Get associated fax info. |
| - autofill_util::AddAutofillProfileFaxesToProfile(db_, p.get()); |
| + AddAutofillProfileFaxesToProfile(db_, p.get()); |
| *profile = p.release(); |
| return true; |
| @@ -680,7 +1042,7 @@ bool AutofillTable::UpdateAutofillProfileMulti(const AutofillProfile& profile) { |
| return false; |
| } |
| - autofill_util::BindAutofillProfileToStatement(profile, &s); |
| + BindAutofillProfileToStatement(profile, &s); |
| s.BindString(10, profile.guid()); |
| bool result = s.Run(); |
| DCHECK_GT(db_->GetLastChangeCount(), 0); |
| @@ -688,10 +1050,10 @@ bool AutofillTable::UpdateAutofillProfileMulti(const AutofillProfile& profile) { |
| return result; |
| // Remove the old names, emails, and phone/fax numbers. |
| - if (!autofill_util::RemoveAutofillProfilePieces(profile.guid(), db_)) |
| + if (!RemoveAutofillProfilePieces(profile.guid(), db_)) |
| return false; |
| - return autofill_util::AddAutofillProfilePieces(profile, db_); |
| + return AddAutofillProfilePieces(profile, db_); |
| } |
| bool AutofillTable::RemoveAutofillProfile(const std::string& guid) { |
| @@ -724,7 +1086,7 @@ bool AutofillTable::RemoveAutofillProfile(const std::string& guid) { |
| if (!s.Run()) |
| return false; |
| - return autofill_util::RemoveAutofillProfilePieces(guid, db_); |
| + return RemoveAutofillProfilePieces(guid, db_); |
| } |
| bool AutofillTable::ClearAutofillProfiles() { |
| @@ -782,7 +1144,7 @@ bool AutofillTable::AddCreditCard(const CreditCard& credit_card) { |
| return false; |
| } |
| - autofill_util::BindCreditCardToStatement(credit_card, &s); |
| + BindCreditCardToStatement(credit_card, &s); |
| if (!s.Run()) { |
| NOTREACHED(); |
| @@ -810,7 +1172,7 @@ bool AutofillTable::GetCreditCard(const std::string& guid, |
| if (!s.Step()) |
| return false; |
| - *credit_card = autofill_util::CreditCardFromStatement(s); |
| + *credit_card = CreditCardFromStatement(s); |
| return s.Succeeded(); |
| } |
| @@ -861,7 +1223,7 @@ bool AutofillTable::UpdateCreditCard(const CreditCard& credit_card) { |
| return false; |
| } |
| - autofill_util::BindCreditCardToStatement(credit_card, &s); |
| + BindCreditCardToStatement(credit_card, &s); |
| s.BindString(6, credit_card.guid()); |
| bool result = s.Run(); |
| DCHECK_GT(db_->GetLastChangeCount(), 0); |
| @@ -1148,3 +1510,620 @@ bool AutofillTable::InitProfileTrashTable() { |
| } |
| return true; |
| } |
| + |
| +bool AutofillTable::AddCardNumberEncryptedColumnForVersion23() { |
| + // Add the card_number_encrypted column if credit card table was not |
|
dhollowa
2011/04/04 16:13:20
This comment block now describes the migration met
Bons
2011/04/05 11:05:13
Done.
|
| + // 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. |
| + 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; |
| +} |
| + |
| +bool AutofillTable::CleanupOversizedStringFieldsForVersion24() { |
| + // 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. |
| + 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; |
| +} |
| + |
| +bool AutofillTable::UpdateLegacyCreditCardsForVersion27() { |
| + // 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; |
| + } |
| + |
| + // 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. |
| + 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::AddDateModifedForVersion30() { |
| + // 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::AddGUIDToCreditCardsAndProfilesForVersion31() { |
| + // Add |guid| column to |autofill_profiles| table. |
| + // 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; |
| + } |
| + } |
| + |
| + // Add |guid| column to |credit_cards| table. |
| + // 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::UpdateProfilesAndCreditCardsForVersion32() { |
| + 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; |
| +} |
| + |
| +bool AutofillTable::UpdateProfilesBasedOnFirstNameForVersion33() { |
| + // 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. |
| + 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; |
| +} |
| + |
| +bool AutofillTable::UpdatesProfilesBasedOnCountryCodeForVersion34() { |
| + // 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. |
| + 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; |
| +} |
| + |
| +bool AutofillTable::CorrectGreatBritainCountryCodesForVersion35() { |
| + // 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. |
| + sql::Statement s(db_->GetUniqueStatement( |
| + "UPDATE autofill_profiles SET country_code=\"GB\" " |
| + "WHERE country_code=\"UK\"")); |
| + |
| + return s.Run(); |
| +} |
| + |
| +bool AutofillTable::MergeAndCullOlderProfilesForVersion36() { |
| + // Merge and cull older profiles where possible. |
| + 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; |
| +} |