| // 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 <algorithm> |
| #include <limits> |
| #include <set> |
| #include <string> |
| |
| #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/guid.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/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<Tuple3<int64, string16, string16> > 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, WideToUTF8(url.short_name())); |
| s->BindString(1, WideToUTF8(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<PasswordForm::Scheme>(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<int>(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<unsigned char> 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<int>(image_data.size())); |
| return s.Run(); |
| } |
| |
| bool WebDatabase::GetWebAppImages(const GURL& url, |
| std::vector<SkBitmap>* 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<const unsigned char*>(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<int>(encrypted_token.length())); |
| return s.Run(); |
| } |
| |
| bool WebDatabase::GetAllTokens(std::map<std::string, std::string>* 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<TemplateURL*>* 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(UTF8ToWide(tmp)); |
| |
| template_url->set_keyword(UTF8ToWide(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<std::string> 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<int>(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<int>(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<int64>::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<int64>::max() : |
| delete_end.ToTimeT()); |
| success = success && s2.Run(); |
| #endif |
| |
| return success; |
| } |
| |
| bool WebDatabase::GetLogins(const PasswordForm& form, |
| std::vector<PasswordForm*>* 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<PasswordForm*>* 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<FormField>& elements, |
| std::vector<AutofillChange>* changes) { |
| return AddFormFieldValuesTime(elements, changes, Time::Now()); |
| } |
| |
| bool WebDatabase::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 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<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 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<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 WebDatabase::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 WebDatabase::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 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<AutofillChange>* changes) { |
| return AddFormFieldValueTime(element, changes, base::Time::Now()); |
| } |
| |
| bool WebDatabase::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 WebDatabase::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 WebDatabase::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 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<int64>::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<AutoFillProfile*>* 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())); |
| 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<CreditCard*>* 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())); |
| 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<time_t>::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<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) { |
| 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<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) { |
| 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; |
| } |
| } |