// Copyright (c) 2010 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/web_database.h" #include #include #include #include #include "app/l10n_util.h" #include "app/sql/statement.h" #include "app/sql/transaction.h" #include "base/string_number_conversions.h" #include "base/string_split.h" #include "base/string_util.h" #include "base/tuple.h" #include "base/utf_string_conversions.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/diagnostics/sqlite_diagnostics.h" #include "chrome/browser/history/history_database.h" #include "chrome/browser/password_manager/encryptor.h" #include "chrome/browser/search_engines/template_url.h" #include "chrome/browser/webdata/autofill_change.h" #include "chrome/common/guid.h" #include "chrome/common/notification_service.h" #include "gfx/codec/png_codec.h" #include "third_party/skia/include/core/SkBitmap.h" #include "webkit/glue/form_field.h" #include "webkit/glue/password_form.h" // Encryptor is now in place for Windows and Mac. The Linux implementation // currently obfuscates only. Mac Encryptor implementation can block the // active thread while presenting UI to the user. See |encryptor_mac.mm| for // details. // For details on the Linux work see: // http://crbug.com/25404 using webkit_glue::FormField; using webkit_glue::PasswordForm; //////////////////////////////////////////////////////////////////////////////// // // Schema // Note: The database stores time in seconds, UTC. // // keywords Most of the columns mirror that of a field in // TemplateURL. See TemplateURL for more details. // id // short_name // keyword // favicon_url // url // show_in_default_list // safe_for_autoreplace // originating_url // date_created This column was added after we allowed keywords. // Keywords created before we started tracking // creation date have a value of 0 for this. // usage_count // input_encodings Semicolon separated list of supported input // encodings, may be empty. // suggest_url // prepopulate_id See TemplateURL::prepopulate_id. // autogenerate_keyword // logo_id See TemplateURL::logo_id // created_by_policy See TemplateURL::created_by_policy. This was added // in version 26. // instant_url See TemplateURL::instant_url. This was added // in version 29. // // logins // origin_url // action_url // username_element // username_value // password_element // password_value // submit_element // signon_realm The authority (scheme, host, port). // ssl_valid SSL status of page containing the form at first // impression. // preferred MRU bit. // date_created This column was added after logins support. "Legacy" // entries have a value of 0. // blacklisted_by_user Tracks whether or not the user opted to 'never // remember' // passwords for this site. // // autofill // name The name of the input as specified in the html. // value The literal contents of the text field. // value_lower The contents of the text field made lower_case. // pair_id An ID number unique to the row in the table. // count How many times the user has entered the string |value| // in a field of name |name|. // // autofill_dates This table associates a row to each separate time the // user submits a form containing a certain name/value // pair. The |pair_id| should match the |pair_id| field // in the appropriate row of the autofill table. // pair_id // date_created // // autofill_profiles This table contains AutoFill profile data added by the // user with the AutoFill dialog. Most of the columns are // standard entries in a contact information form. // // guid A guid string to uniquely identify the profile. // Added in version 31. // label The label of the profile. Presented to the user when // selecting profiles. // first_name // middle_name // last_name // email // company_name // address_line_1 // address_line_2 // city // state // zipcode // country // phone // fax // date_modified The date on which this profile was last modified. // Added in version 30. // // credit_cards This table contains credit card data added by the user // with the AutoFill dialog. Most of the columns are // standard entries in a credit card form. // // guid A guid string to uniquely identify the profile. // Added in version 31. // label The label of the credit card. Presented to the user // when selecting credit cards. // name_on_card // expiration_month // expiration_year // card_number_encrypted Stores encrypted credit card number. // date_modified The date on which this entry was last modified. // Added in version 30. // // web_app_icons // url URL of the web app. // width Width of the image. // height Height of the image. // image PNG encoded image data. // // web_apps // url URL of the web app. // has_all_images Do we have all the images? // //////////////////////////////////////////////////////////////////////////////// using base::Time; namespace { typedef std::vector > AutofillElementList; // Current version number. Note: when changing the current version number, // corresponding changes must happen in the unit tests, and new migration test // added. See |WebDatabaseMigrationTest::kCurrentTestedVersionNumber|. const int kCurrentVersionNumber = 32; const int kCompatibleVersionNumber = 32; // ID of the url column in keywords. const int kUrlIdPosition = 16; // Keys used in the meta table. const char* kDefaultSearchProviderKey = "Default Search Provider ID"; const char* kBuiltinKeywordVersion = "Builtin Keyword Version"; // The maximum length allowed for form data. const size_t kMaxDataLength = 1024; void BindURLToStatement(const TemplateURL& url, sql::Statement* s) { s->BindString(0, UTF16ToUTF8(url.short_name())); s->BindString(1, UTF16ToUTF8(url.keyword())); GURL favicon_url = url.GetFavIconURL(); if (!favicon_url.is_valid()) { s->BindString(2, std::string()); } else { s->BindString(2, history::HistoryDatabase::GURLToDatabaseURL( url.GetFavIconURL())); } s->BindString(3, url.url() ? url.url()->url() : std::string()); s->BindInt(4, url.safe_for_autoreplace() ? 1 : 0); if (!url.originating_url().is_valid()) { s->BindString(5, std::string()); } else { s->BindString(5, history::HistoryDatabase::GURLToDatabaseURL( url.originating_url())); } s->BindInt64(6, url.date_created().ToTimeT()); s->BindInt(7, url.usage_count()); s->BindString(8, JoinString(url.input_encodings(), ';')); s->BindInt(9, url.show_in_default_list() ? 1 : 0); s->BindString(10, url.suggestions_url() ? url.suggestions_url()->url() : std::string()); s->BindInt(11, url.prepopulate_id()); s->BindInt(12, url.autogenerate_keyword() ? 1 : 0); s->BindInt(13, url.logo_id()); s->BindBool(14, url.created_by_policy()); s->BindString(15, url.instant_url() ? url.instant_url()->url() : std::string()); } void InitPasswordFormFromStatement(PasswordForm* form, sql::Statement* s) { std::string tmp; string16 decrypted_password; tmp = s->ColumnString(0); form->origin = GURL(tmp); tmp = s->ColumnString(1); form->action = GURL(tmp); form->username_element = s->ColumnString16(2); form->username_value = s->ColumnString16(3); form->password_element = s->ColumnString16(4); int encrypted_password_len = s->ColumnByteLength(5); std::string encrypted_password; if (encrypted_password_len) { encrypted_password.resize(encrypted_password_len); memcpy(&encrypted_password[0], s->ColumnBlob(5), encrypted_password_len); Encryptor::DecryptString16(encrypted_password, &decrypted_password); } form->password_value = decrypted_password; form->submit_element = s->ColumnString16(6); tmp = s->ColumnString(7); form->signon_realm = tmp; form->ssl_valid = (s->ColumnInt(8) > 0); form->preferred = (s->ColumnInt(9) > 0); form->date_created = Time::FromTimeT(s->ColumnInt64(10)); form->blacklisted_by_user = (s->ColumnInt(11) > 0); int scheme_int = s->ColumnInt(12); DCHECK((scheme_int >= 0) && (scheme_int <= PasswordForm::SCHEME_OTHER)); form->scheme = static_cast(scheme_int); } // TODO(jhawkins): This is a temporary stop-gap measure designed to prevent // a malicious site from DOS'ing the browser with extremely large profile // data. The correct solution is to parse this data asynchronously. // See http://crbug.com/49332. 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()); s->BindString16(1, profile.Label()); string16 text = profile.GetFieldText(AutoFillType(NAME_FIRST)); s->BindString16(2, LimitDataSize(text)); text = profile.GetFieldText(AutoFillType(NAME_MIDDLE)); s->BindString16(3, LimitDataSize(text)); text = profile.GetFieldText(AutoFillType(NAME_LAST)); s->BindString16(4, LimitDataSize(text)); text = profile.GetFieldText(AutoFillType(EMAIL_ADDRESS)); s->BindString16(5, LimitDataSize(text)); text = profile.GetFieldText(AutoFillType(COMPANY_NAME)); s->BindString16(6, LimitDataSize(text)); text = profile.GetFieldText(AutoFillType(ADDRESS_HOME_LINE1)); s->BindString16(7, LimitDataSize(text)); text = profile.GetFieldText(AutoFillType(ADDRESS_HOME_LINE2)); s->BindString16(8, LimitDataSize(text)); text = profile.GetFieldText(AutoFillType(ADDRESS_HOME_CITY)); s->BindString16(9, LimitDataSize(text)); text = profile.GetFieldText(AutoFillType(ADDRESS_HOME_STATE)); s->BindString16(10, LimitDataSize(text)); text = profile.GetFieldText(AutoFillType(ADDRESS_HOME_ZIP)); s->BindString16(11, LimitDataSize(text)); text = profile.GetFieldText(AutoFillType(ADDRESS_HOME_COUNTRY)); s->BindString16(12, LimitDataSize(text)); text = profile.GetFieldText(AutoFillType(PHONE_HOME_WHOLE_NUMBER)); s->BindString16(13, LimitDataSize(text)); text = profile.GetFieldText(AutoFillType(PHONE_FAX_WHOLE_NUMBER)); s->BindString16(14, LimitDataSize(text)); s->BindInt64(15, 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->set_label(s.ColumnString16(1)); profile->SetInfo(AutoFillType(NAME_FIRST), s.ColumnString16(2)); profile->SetInfo(AutoFillType(NAME_MIDDLE), s.ColumnString16(3)); profile->SetInfo(AutoFillType(NAME_LAST), s.ColumnString16(4)); profile->SetInfo(AutoFillType(EMAIL_ADDRESS), s.ColumnString16(5)); profile->SetInfo(AutoFillType(COMPANY_NAME), s.ColumnString16(6)); profile->SetInfo(AutoFillType(ADDRESS_HOME_LINE1), s.ColumnString16(7)); profile->SetInfo(AutoFillType(ADDRESS_HOME_LINE2), s.ColumnString16(8)); profile->SetInfo(AutoFillType(ADDRESS_HOME_CITY), s.ColumnString16(9)); profile->SetInfo(AutoFillType(ADDRESS_HOME_STATE), s.ColumnString16(10)); profile->SetInfo(AutoFillType(ADDRESS_HOME_ZIP), s.ColumnString16(11)); profile->SetInfo(AutoFillType(ADDRESS_HOME_COUNTRY), s.ColumnString16(12)); profile->SetInfo(AutoFillType(PHONE_HOME_WHOLE_NUMBER), s.ColumnString16(13)); profile->SetInfo(AutoFillType(PHONE_FAX_WHOLE_NUMBER), s.ColumnString16(14)); // Intentionally skip column 15, 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()); s->BindString16(1, credit_card.Label()); string16 text = credit_card.GetFieldText(AutoFillType(CREDIT_CARD_NAME)); s->BindString16(2, LimitDataSize(text)); text = credit_card.GetFieldText(AutoFillType(CREDIT_CARD_EXP_MONTH)); s->BindString16(3, LimitDataSize(text)); text = credit_card.GetFieldText(AutoFillType(CREDIT_CARD_EXP_4_DIGIT_YEAR)); s->BindString16(4, LimitDataSize(text)); text = credit_card.GetFieldText(AutoFillType(CREDIT_CARD_NUMBER)); std::string encrypted_data; Encryptor::EncryptString16(text, &encrypted_data); s->BindBlob(5, encrypted_data.data(), static_cast(encrypted_data.length())); s->BindInt64(6, 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->set_label(s.ColumnString16(1)); credit_card->SetInfo(AutoFillType(CREDIT_CARD_NAME), s.ColumnString16(2)); credit_card->SetInfo(AutoFillType(CREDIT_CARD_EXP_MONTH), s.ColumnString16(3)); credit_card->SetInfo(AutoFillType(CREDIT_CARD_EXP_4_DIGIT_YEAR), s.ColumnString16(4)); int encrypted_number_len = s.ColumnByteLength(5); string16 credit_card_number; if (encrypted_number_len) { std::string encrypted_number; encrypted_number.resize(encrypted_number_len); memcpy(&encrypted_number[0], s.ColumnBlob(5), encrypted_number_len); Encryptor::DecryptString16(encrypted_number, &credit_card_number); } credit_card->SetInfo(AutoFillType(CREDIT_CARD_NUMBER), credit_card_number); // Intentionally skip column 6, which stores the modification date. return credit_card; } } // namespace WebDatabase::WebDatabase() { } WebDatabase::~WebDatabase() { } void WebDatabase::BeginTransaction() { db_.BeginTransaction(); } void WebDatabase::CommitTransaction() { db_.CommitTransaction(); } sql::InitStatus WebDatabase::Init(const FilePath& db_name) { // When running in unit tests, there is already a NotificationService object. // Since only one can exist at a time per thread, check first. if (!NotificationService::current()) notification_service_.reset(new NotificationService); // Set the exceptional sqlite error handler. db_.set_error_delegate(GetErrorHandlerForWebDb()); // We don't store that much data in the tables so use a small page size. // This provides a large benefit for empty tables (which is very likely with // the tables we create). db_.set_page_size(2048); // We shouldn't have much data and what access we currently have is quite // infrequent. So we go with a small cache size. db_.set_cache_size(32); // Run the database in exclusive mode. Nobody else should be accessing the // database while we're running, and this will give somewhat improved perf. db_.set_exclusive_locking(); if (!db_.Open(db_name)) return sql::INIT_FAILURE; // Initialize various tables sql::Transaction transaction(&db_); if (!transaction.Begin()) return sql::INIT_FAILURE; // Version check. if (!meta_table_.Init(&db_, kCurrentVersionNumber, kCompatibleVersionNumber)) return sql::INIT_FAILURE; if (meta_table_.GetCompatibleVersionNumber() > kCurrentVersionNumber) { LOG(WARNING) << "Web database is too new."; return sql::INIT_TOO_NEW; } // Initialize the tables. if (!InitKeywordsTable() || !InitLoginsTable() || !InitWebAppIconsTable() || !InitWebAppsTable() || !InitAutofillTable() || !InitAutofillDatesTable() || !InitAutoFillProfilesTable() || !InitCreditCardsTable() || !InitTokenServiceTable()) { LOG(WARNING) << "Unable to initialize the web database."; return sql::INIT_FAILURE; } // If the file on disk is an older database version, bring it up to date. // If the migration fails we return an error to caller and do not commit // the migration. sql::InitStatus migration_status = MigrateOldVersionsAsNeeded(); if (migration_status != sql::INIT_OK) return migration_status; return transaction.Commit() ? sql::INIT_OK : sql::INIT_FAILURE; } bool WebDatabase::SetWebAppImage(const GURL& url, const SkBitmap& image) { // Don't bother with a cached statement since this will be a relatively // infrequent operation. sql::Statement s(db_.GetUniqueStatement( "INSERT OR REPLACE INTO web_app_icons " "(url, width, height, image) VALUES (?, ?, ?, ?)")); if (!s) return false; std::vector image_data; gfx::PNGCodec::EncodeBGRASkBitmap(image, false, &image_data); s.BindString(0, history::HistoryDatabase::GURLToDatabaseURL(url)); s.BindInt(1, image.width()); s.BindInt(2, image.height()); s.BindBlob(3, &image_data.front(), static_cast(image_data.size())); return s.Run(); } bool WebDatabase::GetWebAppImages(const GURL& url, std::vector* images) { sql::Statement s(db_.GetUniqueStatement( "SELECT image FROM web_app_icons WHERE url=?")); if (!s) { NOTREACHED() << "Statement prepare failed"; return false; } s.BindString(0, history::HistoryDatabase::GURLToDatabaseURL(url)); while (s.Step()) { SkBitmap image; int col_bytes = s.ColumnByteLength(0); if (col_bytes > 0) { if (gfx::PNGCodec::Decode( reinterpret_cast(s.ColumnBlob(0)), col_bytes, &image)) { images->push_back(image); } else { // Should only have valid image data in the db. NOTREACHED(); } } } return true; } bool WebDatabase::SetWebAppHasAllImages(const GURL& url, bool has_all_images) { sql::Statement s(db_.GetUniqueStatement( "INSERT OR REPLACE INTO web_apps (url, has_all_images) VALUES (?, ?)")); if (!s) { NOTREACHED() << "Statement prepare failed"; return false; } s.BindString(0, history::HistoryDatabase::GURLToDatabaseURL(url)); s.BindInt(1, has_all_images ? 1 : 0); return s.Run(); } bool WebDatabase::GetWebAppHasAllImages(const GURL& url) { sql::Statement s(db_.GetUniqueStatement( "SELECT has_all_images FROM web_apps WHERE url=?")); if (!s) { NOTREACHED() << "Statement prepare failed"; return false; } s.BindString(0, history::HistoryDatabase::GURLToDatabaseURL(url)); return (s.Step() && s.ColumnInt(0) == 1); } bool WebDatabase::RemoveWebApp(const GURL& url) { sql::Statement delete_s(db_.GetUniqueStatement( "DELETE FROM web_app_icons WHERE url = ?")); if (!delete_s) { NOTREACHED() << "Statement prepare failed"; return false; } delete_s.BindString(0, history::HistoryDatabase::GURLToDatabaseURL(url)); if (!delete_s.Run()) return false; sql::Statement delete_s2(db_.GetUniqueStatement( "DELETE FROM web_apps WHERE url = ?")); if (!delete_s2) { NOTREACHED() << "Statement prepare failed"; return false; } delete_s2.BindString(0, history::HistoryDatabase::GURLToDatabaseURL(url)); return delete_s2.Run(); } bool WebDatabase::RemoveAllTokens() { sql::Statement s(db_.GetUniqueStatement( "DELETE FROM token_service")); if (!s) { NOTREACHED() << "Statement prepare failed"; return false; } return s.Run(); } bool WebDatabase::SetTokenForService(const std::string& service, const std::string& token) { // Don't bother with a cached statement since this will be a relatively // infrequent operation. sql::Statement s(db_.GetUniqueStatement( "INSERT OR REPLACE INTO token_service " "(service, encrypted_token) VALUES (?, ?)")); if (!s) { NOTREACHED() << "Statement prepare failed"; return false; } std::string encrypted_token; bool encrypted = Encryptor::EncryptString(token, &encrypted_token); if (!encrypted) { return false; } s.BindString(0, service); s.BindBlob(1, encrypted_token.data(), static_cast(encrypted_token.length())); return s.Run(); } bool WebDatabase::GetAllTokens(std::map* tokens) { sql::Statement s(db_.GetUniqueStatement( "SELECT service, encrypted_token FROM token_service")); if (!s) { NOTREACHED() << "Statement prepare failed"; return false; } while (s.Step()) { std::string encrypted_token; std::string decrypted_token; std::string service; service = s.ColumnString(0); bool entry_ok = !service.empty() && s.ColumnBlobAsString(1, &encrypted_token); if (entry_ok) { Encryptor::DecryptString(encrypted_token, &decrypted_token); (*tokens)[service] = decrypted_token; } else { NOTREACHED(); return false; } } return true; } bool WebDatabase::InitKeywordsTable() { if (!db_.DoesTableExist("keywords")) { if (!db_.Execute("CREATE TABLE keywords (" "id INTEGER PRIMARY KEY," "short_name VARCHAR NOT NULL," "keyword VARCHAR NOT NULL," "favicon_url VARCHAR NOT NULL," "url VARCHAR NOT NULL," "show_in_default_list INTEGER," "safe_for_autoreplace INTEGER," "originating_url VARCHAR," "date_created INTEGER DEFAULT 0," "usage_count INTEGER DEFAULT 0," "input_encodings VARCHAR," "suggest_url VARCHAR," "prepopulate_id INTEGER DEFAULT 0," "autogenerate_keyword INTEGER DEFAULT 0," "logo_id INTEGER DEFAULT 0," "created_by_policy INTEGER DEFAULT 0," "instant_url VARCHAR)")) { NOTREACHED(); return false; } } return true; } bool WebDatabase::InitLoginsTable() { if (!db_.DoesTableExist("logins")) { if (!db_.Execute("CREATE TABLE logins (" "origin_url VARCHAR NOT NULL, " "action_url VARCHAR, " "username_element VARCHAR, " "username_value VARCHAR, " "password_element VARCHAR, " "password_value BLOB, " "submit_element VARCHAR, " "signon_realm VARCHAR NOT NULL," "ssl_valid INTEGER NOT NULL," "preferred INTEGER NOT NULL," "date_created INTEGER NOT NULL," "blacklisted_by_user INTEGER NOT NULL," "scheme INTEGER NOT NULL," "UNIQUE " "(origin_url, username_element, " "username_value, password_element, " "submit_element, signon_realm))")) { NOTREACHED(); return false; } if (!db_.Execute("CREATE INDEX logins_signon ON logins (signon_realm)")) { NOTREACHED(); return false; } } #if defined(OS_WIN) if (!db_.DoesTableExist("ie7_logins")) { if (!db_.Execute("CREATE TABLE ie7_logins (" "url_hash VARCHAR NOT NULL, " "password_value BLOB, " "date_created INTEGER NOT NULL," "UNIQUE " "(url_hash))")) { NOTREACHED(); return false; } if (!db_.Execute("CREATE INDEX ie7_logins_hash ON " "ie7_logins (url_hash)")) { NOTREACHED(); return false; } } #endif return true; } bool WebDatabase::InitAutofillTable() { 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 WebDatabase::InitAutofillDatesTable() { 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 WebDatabase::InitAutoFillProfilesTable() { if (!db_.DoesTableExist("autofill_profiles")) { if (!db_.Execute("CREATE TABLE autofill_profiles ( " "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)")) { NOTREACHED(); return false; } if (!db_.Execute("CREATE INDEX autofill_profiles_label_index " "ON autofill_profiles (label)")) { NOTREACHED(); return false; } } return true; } bool WebDatabase::InitCreditCardsTable() { if (!db_.DoesTableExist("credit_cards")) { if (!db_.Execute("CREATE TABLE credit_cards ( " "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)")) { NOTREACHED(); return false; } if (!db_.Execute("CREATE INDEX credit_cards_label_index " "ON credit_cards (label)")) { NOTREACHED(); return false; } } return true; } bool WebDatabase::InitWebAppIconsTable() { if (!db_.DoesTableExist("web_app_icons")) { if (!db_.Execute("CREATE TABLE web_app_icons (" "url LONGVARCHAR," "width int," "height int," "image BLOB, UNIQUE (url, width, height))")) { NOTREACHED(); return false; } } return true; } bool WebDatabase::InitWebAppsTable() { if (!db_.DoesTableExist("web_apps")) { if (!db_.Execute("CREATE TABLE web_apps (" "url LONGVARCHAR UNIQUE," "has_all_images INTEGER NOT NULL)")) { NOTREACHED(); return false; } if (!db_.Execute("CREATE INDEX web_apps_url_index ON web_apps (url)")) { NOTREACHED(); return false; } } return true; } bool WebDatabase::InitTokenServiceTable() { if (!db_.DoesTableExist("token_service")) { if (!db_.Execute("CREATE TABLE token_service (" "service VARCHAR PRIMARY KEY NOT NULL," "encrypted_token BLOB)")) { NOTREACHED(); return false; } } return true; } bool WebDatabase::AddKeyword(const TemplateURL& url) { DCHECK(url.id()); // Be sure to change kUrlIdPosition if you add columns sql::Statement s(db_.GetCachedStatement(SQL_FROM_HERE, "INSERT INTO keywords " "(short_name, keyword, favicon_url, url, safe_for_autoreplace, " "originating_url, date_created, usage_count, input_encodings, " "show_in_default_list, suggest_url, prepopulate_id, " "autogenerate_keyword, logo_id, created_by_policy, instant_url, " "id) VALUES " "(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)")); if (!s) { NOTREACHED() << "Statement prepare failed"; return false; } BindURLToStatement(url, &s); s.BindInt64(kUrlIdPosition, url.id()); if (!s.Run()) { NOTREACHED(); return false; } return true; } bool WebDatabase::RemoveKeyword(TemplateURLID id) { DCHECK(id); sql::Statement s(db_.GetUniqueStatement("DELETE FROM keywords WHERE id = ?")); if (!s) { NOTREACHED() << "Statement prepare failed"; return false; } s.BindInt64(0, id); return s.Run(); } bool WebDatabase::GetKeywords(std::vector* urls) { sql::Statement s(db_.GetUniqueStatement( "SELECT id, short_name, keyword, favicon_url, url, " "safe_for_autoreplace, originating_url, date_created, " "usage_count, input_encodings, show_in_default_list, " "suggest_url, prepopulate_id, autogenerate_keyword, logo_id, " "created_by_policy, instant_url " "FROM keywords ORDER BY id ASC")); if (!s) { NOTREACHED() << "Statement prepare failed"; return false; } while (s.Step()) { TemplateURL* template_url = new TemplateURL(); template_url->set_id(s.ColumnInt64(0)); std::string tmp; tmp = s.ColumnString(1); DCHECK(!tmp.empty()); template_url->set_short_name(UTF8ToUTF16(tmp)); template_url->set_keyword(UTF8ToUTF16(s.ColumnString(2))); tmp = s.ColumnString(3); if (!tmp.empty()) template_url->SetFavIconURL(GURL(tmp)); template_url->SetURL(s.ColumnString(4), 0, 0); template_url->set_safe_for_autoreplace(s.ColumnInt(5) == 1); tmp = s.ColumnString(6); if (!tmp.empty()) template_url->set_originating_url(GURL(tmp)); template_url->set_date_created(Time::FromTimeT(s.ColumnInt64(7))); template_url->set_usage_count(s.ColumnInt(8)); std::vector encodings; base::SplitString(s.ColumnString(9), ';', &encodings); template_url->set_input_encodings(encodings); template_url->set_show_in_default_list(s.ColumnInt(10) == 1); template_url->SetSuggestionsURL(s.ColumnString(11), 0, 0); template_url->set_prepopulate_id(s.ColumnInt(12)); template_url->set_autogenerate_keyword(s.ColumnInt(13) == 1); template_url->set_logo_id(s.ColumnInt(14)); template_url->set_created_by_policy(s.ColumnBool(15)); template_url->SetInstantURL(s.ColumnString(16), 0, 0); urls->push_back(template_url); } return s.Succeeded(); } bool WebDatabase::UpdateKeyword(const TemplateURL& url) { DCHECK(url.id()); // Be sure to change kUrlIdPosition if you add columns sql::Statement s(db_.GetUniqueStatement( "UPDATE keywords " "SET short_name=?, keyword=?, favicon_url=?, url=?, " "safe_for_autoreplace=?, originating_url=?, date_created=?, " "usage_count=?, input_encodings=?, show_in_default_list=?, " "suggest_url=?, prepopulate_id=?, autogenerate_keyword=?, " "logo_id=?, created_by_policy=?, instant_url=? WHERE id=?")); if (!s) { NOTREACHED() << "Statement prepare failed"; return false; } BindURLToStatement(url, &s); s.BindInt64(kUrlIdPosition, url.id()); return s.Run(); } bool WebDatabase::SetDefaultSearchProviderID(int64 id) { return meta_table_.SetValue(kDefaultSearchProviderKey, id); } int64 WebDatabase::GetDefaulSearchProviderID() { int64 value = 0; meta_table_.GetValue(kDefaultSearchProviderKey, &value); return value; } bool WebDatabase::SetBuitinKeywordVersion(int version) { return meta_table_.SetValue(kBuiltinKeywordVersion, version); } int WebDatabase::GetBuitinKeywordVersion() { int version = 0; meta_table_.GetValue(kBuiltinKeywordVersion, &version); return version; } bool WebDatabase::AddLogin(const PasswordForm& form) { sql::Statement s(db_.GetUniqueStatement( "INSERT OR REPLACE INTO logins " "(origin_url, action_url, username_element, username_value, " " password_element, password_value, submit_element, " " signon_realm, ssl_valid, preferred, date_created, " " blacklisted_by_user, scheme) " "VALUES " "(?,?,?,?,?,?,?,?,?,?,?,?,?)")); if (!s) { NOTREACHED() << "Statement prepare failed"; return false; } std::string encrypted_password; s.BindString(0, form.origin.spec()); s.BindString(1, form.action.spec()); s.BindString16(2, form.username_element); s.BindString16(3, form.username_value); s.BindString16(4, form.password_element); Encryptor::EncryptString16(form.password_value, &encrypted_password); s.BindBlob(5, encrypted_password.data(), static_cast(encrypted_password.length())); s.BindString16(6, form.submit_element); s.BindString(7, form.signon_realm); s.BindInt(8, form.ssl_valid); s.BindInt(9, form.preferred); s.BindInt64(10, form.date_created.ToTimeT()); s.BindInt(11, form.blacklisted_by_user); s.BindInt(12, form.scheme); if (!s.Run()) { NOTREACHED(); return false; } return true; } bool WebDatabase::UpdateLogin(const PasswordForm& form) { sql::Statement s(db_.GetUniqueStatement( "UPDATE logins SET " "action_url = ?, " "password_value = ?, " "ssl_valid = ?, " "preferred = ? " "WHERE origin_url = ? AND " "username_element = ? AND " "username_value = ? AND " "password_element = ? AND " "signon_realm = ?")); if (!s) { NOTREACHED() << "Statement prepare failed"; return false; } s.BindString(0, form.action.spec()); std::string encrypted_password; Encryptor::EncryptString16(form.password_value, &encrypted_password); s.BindBlob(1, encrypted_password.data(), static_cast(encrypted_password.length())); s.BindInt(2, form.ssl_valid); s.BindInt(3, form.preferred); s.BindString(4, form.origin.spec()); s.BindString16(5, form.username_element); s.BindString16(6, form.username_value); s.BindString16(7, form.password_element); s.BindString(8, form.signon_realm); if (!s.Run()) { NOTREACHED(); return false; } return true; } bool WebDatabase::RemoveLogin(const PasswordForm& form) { // Remove a login by UNIQUE-constrained fields. sql::Statement s(db_.GetUniqueStatement( "DELETE FROM logins WHERE " "origin_url = ? AND " "username_element = ? AND " "username_value = ? AND " "password_element = ? AND " "submit_element = ? AND " "signon_realm = ?")); if (!s) { NOTREACHED() << "Statement prepare failed"; return false; } s.BindString(0, form.origin.spec()); s.BindString16(1, form.username_element); s.BindString16(2, form.username_value); s.BindString16(3, form.password_element); s.BindString16(4, form.submit_element); s.BindString(5, form.signon_realm); if (!s.Run()) { NOTREACHED(); return false; } return true; } bool WebDatabase::RemoveLoginsCreatedBetween(base::Time delete_begin, base::Time delete_end) { sql::Statement s1(db_.GetUniqueStatement( "DELETE FROM logins WHERE " "date_created >= ? AND date_created < ?")); if (!s1) { NOTREACHED() << "Statement 1 prepare failed"; return false; } s1.BindInt64(0, delete_begin.ToTimeT()); s1.BindInt64(1, delete_end.is_null() ? std::numeric_limits::max() : delete_end.ToTimeT()); bool success = s1.Run(); #if defined(OS_WIN) sql::Statement s2(db_.GetUniqueStatement( "DELETE FROM ie7_logins WHERE date_created >= ? AND date_created < ?")); if (!s2) { NOTREACHED() << "Statement 2 prepare failed"; return false; } s2.BindInt64(0, delete_begin.ToTimeT()); s2.BindInt64(1, delete_end.is_null() ? std::numeric_limits::max() : delete_end.ToTimeT()); success = success && s2.Run(); #endif return success; } bool WebDatabase::GetLogins(const PasswordForm& form, std::vector* forms) { DCHECK(forms); sql::Statement s(db_.GetUniqueStatement( "SELECT origin_url, action_url, " "username_element, username_value, " "password_element, password_value, " "submit_element, signon_realm, " "ssl_valid, preferred, " "date_created, blacklisted_by_user, scheme FROM logins " "WHERE signon_realm == ?")); if (!s) { NOTREACHED() << "Statement prepare failed"; return false; } s.BindString(0, form.signon_realm); while (s.Step()) { PasswordForm* new_form = new PasswordForm(); InitPasswordFormFromStatement(new_form, &s); forms->push_back(new_form); } return s.Succeeded(); } bool WebDatabase::GetAllLogins(std::vector* forms, bool include_blacklisted) { DCHECK(forms); std::string stmt = "SELECT origin_url, action_url, " "username_element, username_value, " "password_element, password_value, " "submit_element, signon_realm, ssl_valid, preferred, " "date_created, blacklisted_by_user, scheme FROM logins "; if (!include_blacklisted) stmt.append("WHERE blacklisted_by_user == 0 "); stmt.append("ORDER BY origin_url"); sql::Statement s(db_.GetUniqueStatement(stmt.c_str())); if (!s) { NOTREACHED() << "Statement prepare failed"; return false; } while (s.Step()) { PasswordForm* new_form = new PasswordForm(); InitPasswordFormFromStatement(new_form, &s); forms->push_back(new_form); } return s.Succeeded(); } bool WebDatabase::AddFormFieldValues(const std::vector& elements, std::vector* changes) { return AddFormFieldValuesTime(elements, changes, Time::Now()); } bool WebDatabase::AddFormFieldValuesTime(const std::vector& elements, std::vector* 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 seen_names; bool result = true; for (std::vector::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 WebDatabase::ClearAutofillEmptyValueElements() { sql::Statement s(db_.GetUniqueStatement( "SELECT pair_id FROM autofill WHERE TRIM(value)= \"\"")); if (!s) { NOTREACHED() << "Statement prepare failed"; return false; } std::set ids; while (s.Step()) ids.insert(s.ColumnInt64(0)); bool success = true; for (std::set::const_iterator iter = ids.begin(); iter != ids.end(); ++iter) { if (!RemoveFormElementForID(*iter)) success = false; } return success; } bool WebDatabase::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()); *count = 0; if (s.Step()) { *pair_id = s.ColumnInt64(0); *count = s.ColumnInt(1); } return true; } bool WebDatabase::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 WebDatabase::GetAllAutofillEntries(std::vector* 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* 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; 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; } 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 WebDatabase::GetAutofillTimestamps(const string16& name, const string16& value, std::vector* 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 WebDatabase::UpdateAutofillEntries( const std::vector& 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 WebDatabase::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 WebDatabase::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 WebDatabase::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 WebDatabase::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 WebDatabase::AddFormFieldValue(const FormField& element, std::vector* changes) { return AddFormFieldValueTime(element, changes, base::Time::Now()); } bool WebDatabase::AddFormFieldValueTime(const FormField& element, std::vector* 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 WebDatabase::GetFormValuesForElementName(const string16& name, const string16& prefix, std::vector* 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 WebDatabase::RemoveFormElementsAddedBetween( base::Time delete_begin, base::Time delete_end, std::vector* 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::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 WebDatabase::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::max() : delete_end.ToTimeT()); bool result = s.Run(); if (how_many) *how_many = db_.GetLastChangeCount(); return result; } bool WebDatabase::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 WebDatabase::AddAutoFillProfile(const AutoFillProfile& profile) { sql::Statement s(db_.GetUniqueStatement( "INSERT INTO autofill_profiles" "(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)" "VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)")); if (!s) { NOTREACHED() << "Statement prepare failed"; return false; } BindAutoFillProfileToStatement(profile, &s); if (!s.Run()) { NOTREACHED(); return false; } return s.Succeeded(); } bool WebDatabase::GetAutoFillProfileForLabel(const string16& label, AutoFillProfile** profile) { DCHECK(profile); sql::Statement s(db_.GetUniqueStatement( "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 " "WHERE label = ?")); if (!s) { NOTREACHED() << "Statement prepare failed"; return false; } s.BindString16(0, label); if (!s.Step()) return false; *profile = AutoFillProfileFromStatement(s); return s.Succeeded(); } bool WebDatabase::GetAutoFillProfileForGUID(const std::string& guid, AutoFillProfile** profile) { DCHECK(guid::IsValidGUID(guid)); DCHECK(profile); sql::Statement s(db_.GetUniqueStatement( "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 " "WHERE guid = ?")); if (!s) { NOTREACHED() << "Statement prepare failed"; return false; } s.BindString(0, guid); if (!s.Step()) return false; *profile = AutoFillProfileFromStatement(s); return s.Succeeded(); } bool WebDatabase::GetAutoFillProfiles( std::vector* profiles) { DCHECK(profiles); profiles->clear(); sql::Statement s(db_.GetUniqueStatement( "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")); if (!s) { NOTREACHED() << "Statement prepare failed"; return false; } while (s.Step()) profiles->push_back(AutoFillProfileFromStatement(s)); return s.Succeeded(); } bool WebDatabase::UpdateAutoFillProfile(const AutoFillProfile& profile) { DCHECK(guid::IsValidGUID(profile.guid())); AutoFillProfile* tmp_profile = NULL; if (!GetAutoFillProfileForGUID(profile.guid(), &tmp_profile)) return false; // Preserve appropriate modification dates by not updating unchanged profiles. scoped_ptr old_profile(tmp_profile); if (*old_profile == profile) return true; sql::Statement s(db_.GetUniqueStatement( "UPDATE autofill_profiles " "SET 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=? " "WHERE guid=?")); if (!s) { NOTREACHED() << "Statement prepare failed"; return false; } BindAutoFillProfileToStatement(profile, &s); s.BindString(16, profile.guid()); bool result = s.Run(); DCHECK_GT(db_.GetLastChangeCount(), 0); return result; } bool WebDatabase::RemoveAutoFillProfile(const std::string& guid) { DCHECK(guid::IsValidGUID(guid)); sql::Statement s(db_.GetUniqueStatement( "DELETE FROM autofill_profiles WHERE guid = ?")); if (!s) { NOTREACHED() << "Statement prepare failed"; return false; } s.BindString(0, guid); return s.Run(); } bool WebDatabase::AddCreditCard(const CreditCard& credit_card) { sql::Statement s(db_.GetUniqueStatement( "INSERT INTO credit_cards" "(guid, label, 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 WebDatabase::GetCreditCardForLabel(const string16& label, CreditCard** credit_card) { DCHECK(credit_card); sql::Statement s(db_.GetUniqueStatement( "SELECT guid, label, name_on_card, expiration_month, expiration_year, " "card_number_encrypted, date_modified " "FROM credit_cards " "WHERE label = ?")); if (!s) { NOTREACHED() << "Statement prepare failed"; return false; } s.BindString16(0, label); if (!s.Step()) return false; *credit_card = CreditCardFromStatement(s); return s.Succeeded(); } bool WebDatabase::GetCreditCardForGUID(const std::string& guid, CreditCard** credit_card) { DCHECK(guid::IsValidGUID(guid)); sql::Statement s(db_.GetUniqueStatement( "SELECT guid, label, 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 WebDatabase::GetCreditCards( std::vector* credit_cards) { DCHECK(credit_cards); credit_cards->clear(); sql::Statement s(db_.GetUniqueStatement( "SELECT guid, label, name_on_card, expiration_month, expiration_year, " "card_number_encrypted, date_modified " "FROM credit_cards")); if (!s) { NOTREACHED() << "Statement prepare failed"; return false; } while (s.Step()) credit_cards->push_back(CreditCardFromStatement(s)); return s.Succeeded(); } bool WebDatabase::UpdateCreditCard(const CreditCard& credit_card) { DCHECK(guid::IsValidGUID(credit_card.guid())); CreditCard* tmp_credit_card = NULL; if (!GetCreditCardForGUID(credit_card.guid(), &tmp_credit_card)) return false; // Preserve appropriate modification dates by not updating unchanged cards. scoped_ptr old_credit_card(tmp_credit_card); if (*old_credit_card == credit_card) return true; sql::Statement s(db_.GetUniqueStatement( "UPDATE credit_cards " "SET guid=?, label=?, 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(7, credit_card.guid()); bool result = s.Run(); DCHECK_GT(db_.GetLastChangeCount(), 0); return result; } bool WebDatabase::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 WebDatabase::RemoveAutoFillProfilesAndCreditCardsModifiedBetween( base::Time delete_begin, base::Time delete_end) { 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::max() : delete_end.ToTimeT(); // 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; } // Remove AutoFill profiles 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 WebDatabase::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 WebDatabase::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; } sql::InitStatus WebDatabase::MigrateOldVersionsAsNeeded(){ // Migrate if necessary. int current_version = meta_table_.GetVersionNumber(); switch (current_version) { // Versions 1 - 19 are unhandled. Version numbers greater than // kCurrentVersionNumber should have already been weeded out by the caller. default: // When the version is too old, we return failure error code. The schema // is too out of date to migrate. // There should not be a released product that makes a database too old to // migrate. If we do encounter such a legacy database, we will need a // better solution to handle it (i.e., pop up a dialog to tell the user, // erase all their prefs and start over, etc.). LOG(WARNING) << "Web database version " << current_version << " is too old to handle."; NOTREACHED(); return sql::INIT_FAILURE; case 20: // Add the autogenerate_keyword column. if (!db_.Execute("ALTER TABLE keywords ADD COLUMN autogenerate_keyword " "INTEGER DEFAULT 0")) { LOG(WARNING) << "Unable to update web database to version 21."; NOTREACHED(); return sql::INIT_FAILURE; } meta_table_.SetVersionNumber(21); meta_table_.SetCompatibleVersionNumber( std::min(21, kCompatibleVersionNumber)); // FALL THROUGH case 21: if (!ClearAutofillEmptyValueElements()) { LOG(WARNING) << "Failed to clean-up autofill DB."; NOTREACHED(); return sql::INIT_FAILURE; } meta_table_.SetVersionNumber(22); // No change in the compatibility version number. // FALL THROUGH case 22: // 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. 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."; NOTREACHED(); return sql::INIT_FAILURE; } } 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."; NOTREACHED(); return sql::INIT_FAILURE; } } meta_table_.SetVersionNumber(23); // FALL THROUGH case 23: { // One-time cleanup for Chromium bug 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())) { LOG(WARNING) << "Unable to update web database to version 24."; NOTREACHED(); return sql::INIT_FAILURE; } query = "DELETE FROM autofill WHERE " + autofill_is_too_big; if (!db_.Execute(query.c_str())) { LOG(WARNING) << "Unable to update web database to version 24."; NOTREACHED(); return sql::INIT_FAILURE; } // 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")) { 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())) { LOG(WARNING) << "Unable to update web database to version 24."; NOTREACHED(); return sql::INIT_FAILURE; } } query = "DELETE FROM autofill_profiles WHERE " + autofill_profiles_is_too_big; if (!db_.Execute(query.c_str())) { LOG(WARNING) << "Unable to update web database to version 24."; NOTREACHED(); return sql::INIT_FAILURE; } meta_table_.SetVersionNumber(24); // FALL THROUGH } case 24: // Add the logo_id column if keyword table was not created in this build. if (!db_.Execute("ALTER TABLE keywords ADD COLUMN logo_id " "INTEGER DEFAULT 0")) { LOG(WARNING) << "Unable to update web database to version 25."; NOTREACHED(); return sql::INIT_FAILURE; } meta_table_.SetVersionNumber(25); meta_table_.SetCompatibleVersionNumber( std::min(25, kCompatibleVersionNumber)); // FALL THROUGH case 25: // Add the created_by_policy column. if (!db_.Execute("ALTER TABLE keywords ADD COLUMN created_by_policy " "INTEGER DEFAULT 0")) { LOG(WARNING) << "Unable to update web database to version 26."; NOTREACHED(); return sql::INIT_FAILURE; } meta_table_.SetVersionNumber(26); meta_table_.SetCompatibleVersionNumber( std::min(26, kCompatibleVersionNumber)); // FALL THROUGH case 26: { // 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")) { // 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) { LOG(WARNING) << "Statement prepare failed"; NOTREACHED(); return sql::INIT_FAILURE; } std::map 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) { LOG(WARNING) << "Statement prepare failed"; NOTREACHED(); return sql::INIT_FAILURE; } 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)")) { LOG(WARNING) << "Unable to update web database to version 27."; NOTREACHED(); return sql::INIT_FAILURE; } 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")) { LOG(WARNING) << "Unable to update web database to version 27."; NOTREACHED(); return sql::INIT_FAILURE; } if (!db_.Execute("DROP TABLE credit_cards")) { LOG(WARNING) << "Unable to update web database to version 27."; NOTREACHED(); return sql::INIT_FAILURE; } if (!db_.Execute( "ALTER TABLE credit_cards_temp RENAME TO credit_cards")) { LOG(WARNING) << "Unable to update web database to version 27."; NOTREACHED(); return sql::INIT_FAILURE; } for (std::map::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) { LOG(WARNING) << "Statement prepare failed"; NOTREACHED(); return sql::INIT_FAILURE; } s.BindInt(0, (*iter).second); s.BindInt(1, (*iter).first); if (!s.Run()) { LOG(WARNING) << "Unable to update web database to version 27."; NOTREACHED(); return sql::INIT_FAILURE; } } } meta_table_.SetVersionNumber(27); meta_table_.SetCompatibleVersionNumber( std::min(27, kCompatibleVersionNumber)); // FALL THROUGH } case 27: // Add supports_instant to keywords. if (!db_.Execute("ALTER TABLE keywords ADD COLUMN supports_instant " "INTEGER DEFAULT 0")) { LOG(WARNING) << "Unable to update web database to version 28."; NOTREACHED(); return sql::INIT_FAILURE; } meta_table_.SetVersionNumber(28); meta_table_.SetCompatibleVersionNumber( std::min(28, kCompatibleVersionNumber)); // FALL THROUGH case 28: // Keywords loses the column supports_instant and gets instant_url. if (!db_.Execute("ALTER TABLE keywords ADD COLUMN instant_url " "VARCHAR")) { LOG(WARNING) << "Unable to update web database to version 29."; NOTREACHED(); return sql::INIT_FAILURE; } if (!db_.Execute("CREATE TABLE keywords_temp (" "id INTEGER PRIMARY KEY," "short_name VARCHAR NOT NULL," "keyword VARCHAR NOT NULL," "favicon_url VARCHAR NOT NULL," "url VARCHAR NOT NULL," "show_in_default_list INTEGER," "safe_for_autoreplace INTEGER," "originating_url VARCHAR," "date_created INTEGER DEFAULT 0," "usage_count INTEGER DEFAULT 0," "input_encodings VARCHAR," "suggest_url VARCHAR," "prepopulate_id INTEGER DEFAULT 0," "autogenerate_keyword INTEGER DEFAULT 0," "logo_id INTEGER DEFAULT 0," "created_by_policy INTEGER DEFAULT 0," "instant_url VARCHAR)")) { LOG(WARNING) << "Unable to update web database to version 29."; NOTREACHED(); return sql::INIT_FAILURE; } if (!db_.Execute( "INSERT INTO keywords_temp " "SELECT id, short_name, keyword, favicon_url, url, " "show_in_default_list, safe_for_autoreplace, originating_url, " "date_created, usage_count, input_encodings, suggest_url, " "prepopulate_id, autogenerate_keyword, logo_id, created_by_policy, " "instant_url FROM keywords")) { LOG(WARNING) << "Unable to update web database to version 29."; NOTREACHED(); return sql::INIT_FAILURE; } if (!db_.Execute("DROP TABLE keywords")) { LOG(WARNING) << "Unable to update web database to version 29."; NOTREACHED(); return sql::INIT_FAILURE; } if (!db_.Execute( "ALTER TABLE keywords_temp RENAME TO keywords")) { LOG(WARNING) << "Unable to update web database to version 29."; NOTREACHED(); return sql::INIT_FAILURE; } meta_table_.SetVersionNumber(29); meta_table_.SetCompatibleVersionNumber( std::min(29, kCompatibleVersionNumber)); // FALL THROUGH case 29: // 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")) { LOG(WARNING) << "Unable to update web database to version 30"; NOTREACHED(); return sql::INIT_FAILURE; } sql::Statement s(db_.GetUniqueStatement( "UPDATE autofill_profiles SET date_modified=?")); if (!s) { LOG(WARNING) << "Unable to update web database to version 30."; NOTREACHED(); return sql::INIT_FAILURE; } s.BindInt64(0, Time::Now().ToTimeT()); if (!s.Run()) { LOG(WARNING) << "Unable to update web database to version 30."; NOTREACHED(); return sql::INIT_FAILURE; } } // 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")) { LOG(WARNING) << "Unable to update web database to version 30"; NOTREACHED(); return sql::INIT_FAILURE; } sql::Statement s(db_.GetUniqueStatement( "UPDATE credit_cards SET date_modified=?")); if (!s) { LOG(WARNING) << "Unable to update web database to version 30."; NOTREACHED(); return sql::INIT_FAILURE; } s.BindInt64(0, Time::Now().ToTimeT()); if (!s.Run()) { LOG(WARNING) << "Unable to update web database to version 30."; NOTREACHED(); return sql::INIT_FAILURE; } } meta_table_.SetVersionNumber(30); meta_table_.SetCompatibleVersionNumber( std::min(30, kCompatibleVersionNumber)); // FALL THROUGH case 30: // 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 \"\"")) { LOG(WARNING) << "Unable to update web database to version 30."; NOTREACHED(); return sql::INIT_FAILURE; } // Set all the |guid| fields to valid values. { sql::Statement s(db_.GetUniqueStatement("SELECT unique_id " "FROM autofill_profiles")); if (!s) { LOG(WARNING) << "Unable to update web database to version 30."; NOTREACHED(); return sql::INIT_FAILURE; } while (s.Step()) { sql::Statement update_s( db_.GetUniqueStatement("UPDATE autofill_profiles " "SET guid=? WHERE unique_id=?")); if (!update_s) { LOG(WARNING) << "Unable to update web database to version 30."; NOTREACHED(); return sql::INIT_FAILURE; } update_s.BindString(0, guid::GenerateGUID()); update_s.BindInt(1, s.ColumnInt(0)); if (!update_s.Run()) { LOG(WARNING) << "Unable to update web database to version 30."; NOTREACHED(); return sql::INIT_FAILURE; } } } } // 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 \"\"")) { LOG(WARNING) << "Unable to update web database to version 30."; NOTREACHED(); return sql::INIT_FAILURE; } // Set all the |guid| fields to valid values. { sql::Statement s(db_.GetUniqueStatement("SELECT unique_id " "FROM credit_cards")); if (!s) { LOG(WARNING) << "Unable to update web database to version 30."; NOTREACHED(); return sql::INIT_FAILURE; } while (s.Step()) { sql::Statement update_s( db_.GetUniqueStatement("UPDATE credit_cards " "set guid=? WHERE unique_id=?")); if (!update_s) { LOG(WARNING) << "Unable to update web database to version 30."; NOTREACHED(); return sql::INIT_FAILURE; } update_s.BindString(0, guid::GenerateGUID()); update_s.BindInt(1, s.ColumnInt(0)); if (!update_s.Run()) { LOG(WARNING) << "Unable to update web database to version 30."; NOTREACHED(); return sql::INIT_FAILURE; } } } } meta_table_.SetVersionNumber(31); meta_table_.SetCompatibleVersionNumber( std::min(31, kCompatibleVersionNumber)); // FALL THROUGH case 31: 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)")) { LOG(WARNING) << "Unable to update web database to version 32."; NOTREACHED(); return sql::INIT_FAILURE; } 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")) { LOG(WARNING) << "Unable to update web database to version 32."; NOTREACHED(); return sql::INIT_FAILURE; } if (!db_.Execute("DROP TABLE autofill_profiles")) { LOG(WARNING) << "Unable to update web database to version 32."; NOTREACHED(); return sql::INIT_FAILURE; } if (!db_.Execute( "ALTER TABLE autofill_profiles_temp RENAME TO autofill_profiles")) { LOG(WARNING) << "Unable to update web database to version 32."; NOTREACHED(); return sql::INIT_FAILURE; } } 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)")) { LOG(WARNING) << "Unable to update web database to version 32."; NOTREACHED(); return sql::INIT_FAILURE; } 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")) { LOG(WARNING) << "Unable to update web database to version 32."; NOTREACHED(); return sql::INIT_FAILURE; } if (!db_.Execute("DROP TABLE credit_cards")) { LOG(WARNING) << "Unable to update web database to version 32."; NOTREACHED(); return sql::INIT_FAILURE; } if (!db_.Execute( "ALTER TABLE credit_cards_temp RENAME TO credit_cards")) { LOG(WARNING) << "Unable to update web database to version 32."; NOTREACHED(); return sql::INIT_FAILURE; } } meta_table_.SetVersionNumber(32); meta_table_.SetCompatibleVersionNumber( std::min(32, kCompatibleVersionNumber)); // FALL THROUGH // Add successive versions here. Each should set the version number and // compatible version number as appropriate, then fall through to the next // case. case kCurrentVersionNumber: // No migration needed. return sql::INIT_OK; } }