| 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..aca25f368caa850c5408200822cec40197ee9d56 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,8 +31,361 @@ 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;
|
| -} // anonymous namespace
|
| +
|
| +// 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() &&
|
| @@ -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,618 @@ bool AutofillTable::InitProfileTrashTable() {
|
| }
|
| 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::MigrateToVersion36MergeAndCullOlderProfiles() {
|
| + 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;
|
| +}
|
|
|