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