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; |
+} |