diff options
author | andybons@chromium.org <andybons@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-04-01 13:34:53 +0000 |
---|---|---|
committer | andybons@chromium.org <andybons@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-04-01 13:34:53 +0000 |
commit | 9268173603c6ee2b9292fe53017fd27a444307b6 (patch) | |
tree | 400fbb906d2a2f7f264261c2ad2873a61ffda1db /chrome | |
parent | 0414411f67979897882bbad4f7b5f218c30c1f76 (diff) | |
download | chromium_src-9268173603c6ee2b9292fe53017fd27a444307b6.zip chromium_src-9268173603c6ee2b9292fe53017fd27a444307b6.tar.gz chromium_src-9268173603c6ee2b9292fe53017fd27a444307b6.tar.bz2 |
Split out the rest of the tables within WebDatabase.
Logins, WebApps and TokenService
BUG=none
TEST=WebDatabaseMigrationTest,LoginsTableTest,WebAppsTableTest,TokenServiceTableTest
Review URL: http://codereview.chromium.org/6670122
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@80150 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome')
18 files changed, 1354 insertions, 1135 deletions
diff --git a/chrome/browser/webdata/autofill_table_unittest.cc b/chrome/browser/webdata/autofill_table_unittest.cc index 22d2ad5..e3f90f6 100644 --- a/chrome/browser/webdata/autofill_table_unittest.cc +++ b/chrome/browser/webdata/autofill_table_unittest.cc @@ -678,7 +678,7 @@ TEST_F(AutofillTableTest, AutofillProfile) { ASSERT_TRUE(db.GetAutofillTable()->GetAutofillProfile( home_profile.guid(), &db_profile)); EXPECT_EQ(home_profile, *db_profile); - sql::Statement s_home(db.db_.GetUniqueStatement( + sql::Statement s_home(db.GetSQLConnection()->GetUniqueStatement( "SELECT date_modified " "FROM autofill_profiles WHERE guid=?")); s_home.BindString(0, home_profile.guid()); @@ -704,7 +704,7 @@ TEST_F(AutofillTableTest, AutofillProfile) { ASSERT_TRUE(db.GetAutofillTable()->GetAutofillProfile( billing_profile.guid(), &db_profile)); EXPECT_EQ(billing_profile, *db_profile); - sql::Statement s_billing(db.db_.GetUniqueStatement( + sql::Statement s_billing(db.GetSQLConnection()->GetUniqueStatement( "SELECT date_modified FROM autofill_profiles WHERE guid=?")); s_billing.BindString(0, billing_profile.guid()); ASSERT_TRUE(s_billing); @@ -723,7 +723,7 @@ TEST_F(AutofillTableTest, AutofillProfile) { ASSERT_TRUE(db.GetAutofillTable()->GetAutofillProfile( billing_profile.guid(), &db_profile)); EXPECT_EQ(billing_profile, *db_profile); - sql::Statement s_billing_updated(db.db_.GetUniqueStatement( + sql::Statement s_billing_updated(db.GetSQLConnection()->GetUniqueStatement( "SELECT date_modified FROM autofill_profiles WHERE guid=?")); s_billing_updated.BindString(0, billing_profile.guid()); ASSERT_TRUE(s_billing_updated); @@ -756,7 +756,7 @@ TEST_F(AutofillTableTest, AutofillProfile) { ASSERT_TRUE(db.GetAutofillTable()->GetAutofillProfile( billing_profile.guid(), &db_profile)); EXPECT_EQ(billing_profile, *db_profile); - sql::Statement s_billing_updated_2(db.db_.GetUniqueStatement( + sql::Statement s_billing_updated_2(db.GetSQLConnection()->GetUniqueStatement( "SELECT date_modified FROM autofill_profiles WHERE guid=?")); s_billing_updated_2.BindString(0, billing_profile.guid()); ASSERT_TRUE(s_billing_updated_2); @@ -1096,7 +1096,7 @@ TEST_F(AutofillTableTest, CreditCard) { ASSERT_TRUE(db.GetAutofillTable()->GetCreditCard(work_creditcard.guid(), &db_creditcard)); EXPECT_EQ(work_creditcard, *db_creditcard); - sql::Statement s_work(db.db_.GetUniqueStatement( + sql::Statement s_work(db.GetSQLConnection()->GetUniqueStatement( "SELECT guid, name_on_card, expiration_month, expiration_year, " "card_number_encrypted, date_modified " "FROM credit_cards WHERE guid=?")); @@ -1122,7 +1122,7 @@ TEST_F(AutofillTableTest, CreditCard) { ASSERT_TRUE(db.GetAutofillTable()->GetCreditCard(target_creditcard.guid(), &db_creditcard)); EXPECT_EQ(target_creditcard, *db_creditcard); - sql::Statement s_target(db.db_.GetUniqueStatement( + sql::Statement s_target(db.GetSQLConnection()->GetUniqueStatement( "SELECT guid, name_on_card, expiration_month, expiration_year, " "card_number_encrypted, date_modified " "FROM credit_cards WHERE guid=?")); @@ -1142,7 +1142,7 @@ TEST_F(AutofillTableTest, CreditCard) { ASSERT_TRUE(db.GetAutofillTable()->GetCreditCard(target_creditcard.guid(), &db_creditcard)); EXPECT_EQ(target_creditcard, *db_creditcard); - sql::Statement s_target_updated(db.db_.GetUniqueStatement( + sql::Statement s_target_updated(db.GetSQLConnection()->GetUniqueStatement( "SELECT guid, name_on_card, expiration_month, expiration_year, " "card_number_encrypted, date_modified " "FROM credit_cards WHERE guid=?")); @@ -1184,7 +1184,7 @@ TEST_F(AutofillTableTest, UpdateAutofillProfile) { // Set a mocked value for the profile's creation time. const time_t mock_creation_date = Time::Now().ToTimeT() - 13; - sql::Statement s_mock_creation_date(db.db_.GetUniqueStatement( + sql::Statement s_mock_creation_date(db.GetSQLConnection()->GetUniqueStatement( "UPDATE autofill_profiles SET date_modified = ?")); ASSERT_TRUE(s_mock_creation_date); s_mock_creation_date.BindInt64(0, mock_creation_date); @@ -1196,7 +1196,7 @@ TEST_F(AutofillTableTest, UpdateAutofillProfile) { &tmp_profile)); scoped_ptr<AutofillProfile> db_profile(tmp_profile); EXPECT_EQ(profile, *db_profile); - sql::Statement s_original(db.db_.GetUniqueStatement( + sql::Statement s_original(db.GetSQLConnection()->GetUniqueStatement( "SELECT date_modified FROM autofill_profiles")); ASSERT_TRUE(s_original); ASSERT_TRUE(s_original.Step()); @@ -1213,7 +1213,7 @@ TEST_F(AutofillTableTest, UpdateAutofillProfile) { &tmp_profile)); db_profile.reset(tmp_profile); EXPECT_EQ(profile, *db_profile); - sql::Statement s_updated(db.db_.GetUniqueStatement( + sql::Statement s_updated(db.GetSQLConnection()->GetUniqueStatement( "SELECT date_modified FROM autofill_profiles")); ASSERT_TRUE(s_updated); ASSERT_TRUE(s_updated.Step()); @@ -1222,8 +1222,9 @@ TEST_F(AutofillTableTest, UpdateAutofillProfile) { // Set a mocked value for the profile's modification time. const time_t mock_modification_date = Time::Now().ToTimeT() - 7; - sql::Statement s_mock_modification_date(db.db_.GetUniqueStatement( - "UPDATE autofill_profiles SET date_modified = ?")); + sql::Statement s_mock_modification_date( + db.GetSQLConnection()->GetUniqueStatement( + "UPDATE autofill_profiles SET date_modified = ?")); ASSERT_TRUE(s_mock_modification_date); s_mock_modification_date.BindInt64(0, mock_modification_date); ASSERT_TRUE(s_mock_modification_date.Run()); @@ -1237,7 +1238,7 @@ TEST_F(AutofillTableTest, UpdateAutofillProfile) { &tmp_profile)); db_profile.reset(tmp_profile); EXPECT_EQ(profile, *db_profile); - sql::Statement s_unchanged(db.db_.GetUniqueStatement( + sql::Statement s_unchanged(db.GetSQLConnection()->GetUniqueStatement( "SELECT date_modified FROM autofill_profiles")); ASSERT_TRUE(s_unchanged); ASSERT_TRUE(s_unchanged.Step()); @@ -1259,7 +1260,7 @@ TEST_F(AutofillTableTest, UpdateCreditCard) { // Set a mocked value for the credit card's creation time. const time_t mock_creation_date = Time::Now().ToTimeT() - 13; - sql::Statement s_mock_creation_date(db.db_.GetUniqueStatement( + sql::Statement s_mock_creation_date(db.GetSQLConnection()->GetUniqueStatement( "UPDATE credit_cards SET date_modified = ?")); ASSERT_TRUE(s_mock_creation_date); s_mock_creation_date.BindInt64(0, mock_creation_date); @@ -1271,7 +1272,7 @@ TEST_F(AutofillTableTest, UpdateCreditCard) { &tmp_credit_card)); scoped_ptr<CreditCard> db_credit_card(tmp_credit_card); EXPECT_EQ(credit_card, *db_credit_card); - sql::Statement s_original(db.db_.GetUniqueStatement( + sql::Statement s_original(db.GetSQLConnection()->GetUniqueStatement( "SELECT date_modified FROM credit_cards")); ASSERT_TRUE(s_original); ASSERT_TRUE(s_original.Step()); @@ -1288,7 +1289,7 @@ TEST_F(AutofillTableTest, UpdateCreditCard) { &tmp_credit_card)); db_credit_card.reset(tmp_credit_card); EXPECT_EQ(credit_card, *db_credit_card); - sql::Statement s_updated(db.db_.GetUniqueStatement( + sql::Statement s_updated(db.GetSQLConnection()->GetUniqueStatement( "SELECT date_modified FROM credit_cards")); ASSERT_TRUE(s_updated); ASSERT_TRUE(s_updated.Step()); @@ -1297,8 +1298,9 @@ TEST_F(AutofillTableTest, UpdateCreditCard) { // Set a mocked value for the credit card's modification time. const time_t mock_modification_date = Time::Now().ToTimeT() - 7; - sql::Statement s_mock_modification_date(db.db_.GetUniqueStatement( - "UPDATE credit_cards SET date_modified = ?")); + sql::Statement s_mock_modification_date( + db.GetSQLConnection()->GetUniqueStatement( + "UPDATE credit_cards SET date_modified = ?")); ASSERT_TRUE(s_mock_modification_date); s_mock_modification_date.BindInt64(0, mock_modification_date); ASSERT_TRUE(s_mock_modification_date.Run()); @@ -1312,7 +1314,7 @@ TEST_F(AutofillTableTest, UpdateCreditCard) { &tmp_credit_card)); db_credit_card.reset(tmp_credit_card); EXPECT_EQ(credit_card, *db_credit_card); - sql::Statement s_unchanged(db.db_.GetUniqueStatement( + sql::Statement s_unchanged(db.GetSQLConnection()->GetUniqueStatement( "SELECT date_modified FROM credit_cards")); ASSERT_TRUE(s_unchanged); ASSERT_TRUE(s_unchanged.Step()); @@ -1325,7 +1327,7 @@ TEST_F(AutofillTableTest, RemoveAutofillProfilesAndCreditCardsModifiedBetween) { ASSERT_EQ(sql::INIT_OK, db.Init(file_)); // Populate the autofill_profiles and credit_cards tables. - ASSERT_TRUE(db.db_.Execute( + ASSERT_TRUE(db.GetSQLConnection()->Execute( "INSERT INTO autofill_profiles (guid, date_modified) " "VALUES('00000000-0000-0000-0000-000000000000', 11);" "INSERT INTO autofill_profiles (guid, date_modified) " @@ -1354,8 +1356,9 @@ TEST_F(AutofillTableTest, RemoveAutofillProfilesAndCreditCardsModifiedBetween) { // Remove all entries modified in the bounded time range [17,41). db.GetAutofillTable()->RemoveAutofillProfilesAndCreditCardsModifiedBetween( Time::FromTimeT(17), Time::FromTimeT(41)); - sql::Statement s_autofill_profiles_bounded(db.db_.GetUniqueStatement( - "SELECT date_modified FROM autofill_profiles")); + sql::Statement s_autofill_profiles_bounded( + db.GetSQLConnection()->GetUniqueStatement( + "SELECT date_modified FROM autofill_profiles")); ASSERT_TRUE(s_autofill_profiles_bounded); ASSERT_TRUE(s_autofill_profiles_bounded.Step()); EXPECT_EQ(11, s_autofill_profiles_bounded.ColumnInt64(0)); @@ -1366,8 +1369,9 @@ TEST_F(AutofillTableTest, RemoveAutofillProfilesAndCreditCardsModifiedBetween) { ASSERT_TRUE(s_autofill_profiles_bounded.Step()); EXPECT_EQ(61, s_autofill_profiles_bounded.ColumnInt64(0)); EXPECT_FALSE(s_autofill_profiles_bounded.Step()); - sql::Statement s_credit_cards_bounded(db.db_.GetUniqueStatement( - "SELECT date_modified FROM credit_cards")); + sql::Statement s_credit_cards_bounded( + db.GetSQLConnection()->GetUniqueStatement( + "SELECT date_modified FROM credit_cards")); ASSERT_TRUE(s_credit_cards_bounded); ASSERT_TRUE(s_credit_cards_bounded.Step()); EXPECT_EQ(47, s_credit_cards_bounded.ColumnInt64(0)); @@ -1380,16 +1384,18 @@ TEST_F(AutofillTableTest, RemoveAutofillProfilesAndCreditCardsModifiedBetween) { // Remove all entries modified on or after time 51 (unbounded range). db.GetAutofillTable()->RemoveAutofillProfilesAndCreditCardsModifiedBetween( Time::FromTimeT(51), Time()); - sql::Statement s_autofill_profiles_unbounded(db.db_.GetUniqueStatement( - "SELECT date_modified FROM autofill_profiles")); + sql::Statement s_autofill_profiles_unbounded( + db.GetSQLConnection()->GetUniqueStatement( + "SELECT date_modified FROM autofill_profiles")); ASSERT_TRUE(s_autofill_profiles_unbounded); ASSERT_TRUE(s_autofill_profiles_unbounded.Step()); EXPECT_EQ(11, s_autofill_profiles_unbounded.ColumnInt64(0)); ASSERT_TRUE(s_autofill_profiles_unbounded.Step()); EXPECT_EQ(41, s_autofill_profiles_unbounded.ColumnInt64(0)); EXPECT_FALSE(s_autofill_profiles_unbounded.Step()); - sql::Statement s_credit_cards_unbounded(db.db_.GetUniqueStatement( - "SELECT date_modified FROM credit_cards")); + sql::Statement s_credit_cards_unbounded( + db.GetSQLConnection()->GetUniqueStatement( + "SELECT date_modified FROM credit_cards")); ASSERT_TRUE(s_credit_cards_unbounded); ASSERT_TRUE(s_credit_cards_unbounded.Step()); EXPECT_EQ(47, s_credit_cards_unbounded.ColumnInt64(0)); @@ -1398,12 +1404,14 @@ TEST_F(AutofillTableTest, RemoveAutofillProfilesAndCreditCardsModifiedBetween) { // Remove all remaining entries. db.GetAutofillTable()->RemoveAutofillProfilesAndCreditCardsModifiedBetween( Time(), Time()); - sql::Statement s_autofill_profiles_empty(db.db_.GetUniqueStatement( - "SELECT date_modified FROM autofill_profiles")); + sql::Statement s_autofill_profiles_empty( + db.GetSQLConnection()->GetUniqueStatement( + "SELECT date_modified FROM autofill_profiles")); ASSERT_TRUE(s_autofill_profiles_empty); EXPECT_FALSE(s_autofill_profiles_empty.Step()); - sql::Statement s_credit_cards_empty(db.db_.GetUniqueStatement( - "SELECT date_modified FROM credit_cards")); + sql::Statement s_credit_cards_empty( + db.GetSQLConnection()->GetUniqueStatement( + "SELECT date_modified FROM credit_cards")); ASSERT_TRUE(s_credit_cards_empty); EXPECT_FALSE(s_credit_cards_empty.Step()); } diff --git a/chrome/browser/webdata/logins_table.cc b/chrome/browser/webdata/logins_table.cc new file mode 100644 index 0000000..cfa28aa --- /dev/null +++ b/chrome/browser/webdata/logins_table.cc @@ -0,0 +1,296 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/webdata/logins_table.h" + +#include <limits> +#include <string> + +#include "app/sql/statement.h" +#include "base/logging.h" +#include "chrome/browser/password_manager/encryptor.h" +#include "webkit/glue/password_form.h" + +using webkit_glue::PasswordForm; + +namespace { + +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 = base::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); +} + +} // anonymous namespace + +bool LoginsTable::Init() { + 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 LoginsTable::IsSyncable() { + return true; +} + +bool LoginsTable::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 LoginsTable::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 LoginsTable::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 LoginsTable::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 LoginsTable::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 LoginsTable::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(); +} diff --git a/chrome/browser/webdata/logins_table.h b/chrome/browser/webdata/logins_table.h new file mode 100644 index 0000000..db8cb98 --- /dev/null +++ b/chrome/browser/webdata/logins_table.h @@ -0,0 +1,99 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_WEBDATA_LOGINS_TABLE_H_ +#define CHROME_BROWSER_WEBDATA_LOGINS_TABLE_H_ +#pragma once + +#include <vector> + +#include "chrome/browser/webdata/web_database_table.h" + +namespace base { +class Time; +} + +namespace webkit_glue { +struct PasswordForm; +} + +#if defined(OS_WIN) +struct IE7PasswordInfo; +#endif + +// This class manages the logins table within the SQLite database passed to the +// constructor. It expects the following schemas: +// +// Note: The database stores time in seconds, UTC. +// +// 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. +// +class LoginsTable : public WebDatabaseTable { + public: + LoginsTable(sql::Connection* db, sql::MetaTable* meta_table) + : WebDatabaseTable(db, meta_table) {} + virtual ~LoginsTable() {} + virtual bool Init(); + virtual bool IsSyncable(); + + // Adds |form| to the list of remembered password forms. + bool AddLogin(const webkit_glue::PasswordForm& form); + +#if defined(OS_WIN) + // Adds |info| to the list of imported passwords from ie7/ie8. + bool AddIE7Login(const IE7PasswordInfo& info); + + // Removes |info| from the list of imported passwords from ie7/ie8. + bool RemoveIE7Login(const IE7PasswordInfo& info); + + // Return the ie7/ie8 login matching |info|. + bool GetIE7Login(const IE7PasswordInfo& info, IE7PasswordInfo* result); +#endif + + // Updates remembered password form. + bool UpdateLogin(const webkit_glue::PasswordForm& form); + + // Removes |form| from the list of remembered password forms. + bool RemoveLogin(const webkit_glue::PasswordForm& form); + + // Removes all logins created from |delete_begin| onwards (inclusive) and + // before |delete_end|. You may use a null Time value to do an unbounded + // delete in either direction. + bool RemoveLoginsCreatedBetween(base::Time delete_begin, + base::Time delete_end); + + // Loads a list of matching password forms into the specified vector |forms|. + // The list will contain all possibly relevant entries to the observed |form|, + // including blacklisted matches. + bool GetLogins(const webkit_glue::PasswordForm& form, + std::vector<webkit_glue::PasswordForm*>* forms); + + // Loads the complete list of password forms into the specified vector |forms| + // if include_blacklisted is true, otherwise only loads those which are + // actually autofill-able; i.e haven't been blacklisted by the user selecting + // the 'Never for this site' button. + bool GetAllLogins(std::vector<webkit_glue::PasswordForm*>* forms, + bool include_blacklisted); + + private: + DISALLOW_COPY_AND_ASSIGN(LoginsTable); +}; + +#endif // CHROME_BROWSER_WEBDATA_LOGINS_TABLE_H_ diff --git a/chrome/browser/webdata/logins_table_unittest.cc b/chrome/browser/webdata/logins_table_unittest.cc new file mode 100644 index 0000000..ad8aac7 --- /dev/null +++ b/chrome/browser/webdata/logins_table_unittest.cc @@ -0,0 +1,278 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/file_util.h" +#include "base/path_service.h" +#include "base/string_number_conversions.h" +#include "base/time.h" +#include "base/utf_string_conversions.h" +#include "chrome/browser/webdata/web_database.h" +#include "chrome/common/chrome_paths.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "webkit/glue/password_form.h" + +using base::Time; +using base::TimeDelta; +using webkit_glue::PasswordForm; + +class LoginsTableTest : public testing::Test { + public: + LoginsTableTest() {} + virtual ~LoginsTableTest() {} + + protected: + virtual void SetUp() { + PathService::Get(chrome::DIR_TEST_DATA, &file_); + const std::string test_db = "TestWebDatabase" + + base::Int64ToString(Time::Now().ToTimeT()) + + ".db"; + file_ = file_.AppendASCII(test_db); + file_util::Delete(file_, false); + } + + virtual void TearDown() { + file_util::Delete(file_, false); + } + + FilePath file_; + + private: + DISALLOW_COPY_AND_ASSIGN(LoginsTableTest); +}; + +TEST_F(LoginsTableTest, Logins) { + WebDatabase db; + + ASSERT_EQ(sql::INIT_OK, db.Init(file_)); + + std::vector<PasswordForm*> result; + + // Verify the database is empty. + EXPECT_TRUE(db.GetLoginsTable()->GetAllLogins(&result, true)); + EXPECT_EQ(0U, result.size()); + + // Example password form. + PasswordForm form; + form.origin = GURL("http://www.google.com/accounts/LoginAuth"); + form.action = GURL("http://www.google.com/accounts/Login"); + form.username_element = ASCIIToUTF16("Email"); + form.username_value = ASCIIToUTF16("test@gmail.com"); + form.password_element = ASCIIToUTF16("Passwd"); + form.password_value = ASCIIToUTF16("test"); + form.submit_element = ASCIIToUTF16("signIn"); + form.signon_realm = "http://www.google.com/"; + form.ssl_valid = false; + form.preferred = false; + form.scheme = PasswordForm::SCHEME_HTML; + + // Add it and make sure it is there. + EXPECT_TRUE(db.GetLoginsTable()->AddLogin(form)); + EXPECT_TRUE(db.GetLoginsTable()->GetAllLogins(&result, true)); + EXPECT_EQ(1U, result.size()); + delete result[0]; + result.clear(); + + // Match against an exact copy. + EXPECT_TRUE(db.GetLoginsTable()->GetLogins(form, &result)); + EXPECT_EQ(1U, result.size()); + delete result[0]; + result.clear(); + + // The example site changes... + PasswordForm form2(form); + form2.origin = GURL("http://www.google.com/new/accounts/LoginAuth"); + form2.submit_element = ASCIIToUTF16("reallySignIn"); + + // Match against an inexact copy + EXPECT_TRUE(db.GetLoginsTable()->GetLogins(form2, &result)); + EXPECT_EQ(1U, result.size()); + delete result[0]; + result.clear(); + + // Uh oh, the site changed origin & action URL's all at once! + PasswordForm form3(form2); + form3.action = GURL("http://www.google.com/new/accounts/Login"); + + // signon_realm is the same, should match. + EXPECT_TRUE(db.GetLoginsTable()->GetLogins(form3, &result)); + EXPECT_EQ(1U, result.size()); + delete result[0]; + result.clear(); + + // Imagine the site moves to a secure server for login. + PasswordForm form4(form3); + form4.signon_realm = "https://www.google.com/"; + form4.ssl_valid = true; + + // We have only an http record, so no match for this. + EXPECT_TRUE(db.GetLoginsTable()->GetLogins(form4, &result)); + EXPECT_EQ(0U, result.size()); + + // Let's imagine the user logs into the secure site. + EXPECT_TRUE(db.GetLoginsTable()->AddLogin(form4)); + EXPECT_TRUE(db.GetLoginsTable()->GetAllLogins(&result, true)); + EXPECT_EQ(2U, result.size()); + delete result[0]; + delete result[1]; + result.clear(); + + // Now the match works + EXPECT_TRUE(db.GetLoginsTable()->GetLogins(form4, &result)); + EXPECT_EQ(1U, result.size()); + delete result[0]; + result.clear(); + + // The user chose to forget the original but not the new. + EXPECT_TRUE(db.GetLoginsTable()->RemoveLogin(form)); + EXPECT_TRUE(db.GetLoginsTable()->GetAllLogins(&result, true)); + EXPECT_EQ(1U, result.size()); + delete result[0]; + result.clear(); + + // The old form wont match the new site (http vs https). + EXPECT_TRUE(db.GetLoginsTable()->GetLogins(form, &result)); + EXPECT_EQ(0U, result.size()); + + // The user's request for the HTTPS site is intercepted + // by an attacker who presents an invalid SSL cert. + PasswordForm form5(form4); + form5.ssl_valid = 0; + + // It will match in this case. + EXPECT_TRUE(db.GetLoginsTable()->GetLogins(form5, &result)); + EXPECT_EQ(1U, result.size()); + delete result[0]; + result.clear(); + + // User changes his password. + PasswordForm form6(form5); + form6.password_value = ASCIIToUTF16("test6"); + form6.preferred = true; + + // We update, and check to make sure it matches the + // old form, and there is only one record. + EXPECT_TRUE(db.GetLoginsTable()->UpdateLogin(form6)); + // matches + EXPECT_TRUE(db.GetLoginsTable()->GetLogins(form5, &result)); + EXPECT_EQ(1U, result.size()); + delete result[0]; + result.clear(); + // Only one record. + EXPECT_TRUE(db.GetLoginsTable()->GetAllLogins(&result, true)); + EXPECT_EQ(1U, result.size()); + // password element was updated. + EXPECT_EQ(form6.password_value, result[0]->password_value); + // Preferred login. + EXPECT_TRUE(form6.preferred); + delete result[0]; + result.clear(); + + // Make sure everything can disappear. + EXPECT_TRUE(db.GetLoginsTable()->RemoveLogin(form4)); + EXPECT_TRUE(db.GetLoginsTable()->GetAllLogins(&result, true)); + EXPECT_EQ(0U, result.size()); +} + +static bool AddTimestampedLogin(WebDatabase* db, std::string url, + const std::string& unique_string, + const Time& time) { + // Example password form. + PasswordForm form; + form.origin = GURL(url + std::string("/LoginAuth")); + form.username_element = ASCIIToUTF16(unique_string); + form.username_value = ASCIIToUTF16(unique_string); + form.password_element = ASCIIToUTF16(unique_string); + form.submit_element = ASCIIToUTF16("signIn"); + form.signon_realm = url; + form.date_created = time; + return db->GetLoginsTable()->AddLogin(form); +} + +static void ClearResults(std::vector<PasswordForm*>* results) { + for (size_t i = 0; i < results->size(); ++i) { + delete (*results)[i]; + } + results->clear(); +} + +TEST_F(LoginsTableTest, ClearPrivateData_SavedPasswords) { + WebDatabase db; + + ASSERT_EQ(sql::INIT_OK, db.Init(file_)); + + std::vector<PasswordForm*> result; + + // Verify the database is empty. + EXPECT_TRUE(db.GetLoginsTable()->GetAllLogins(&result, true)); + EXPECT_EQ(0U, result.size()); + + Time now = Time::Now(); + TimeDelta one_day = TimeDelta::FromDays(1); + + // Create one with a 0 time. + EXPECT_TRUE(AddTimestampedLogin(&db, "1", "foo1", Time())); + // Create one for now and +/- 1 day. + EXPECT_TRUE(AddTimestampedLogin(&db, "2", "foo2", now - one_day)); + EXPECT_TRUE(AddTimestampedLogin(&db, "3", "foo3", now)); + EXPECT_TRUE(AddTimestampedLogin(&db, "4", "foo4", now + one_day)); + + // Verify inserts worked. + EXPECT_TRUE(db.GetLoginsTable()->GetAllLogins(&result, true)); + EXPECT_EQ(4U, result.size()); + ClearResults(&result); + + // Delete everything from today's date and on. + db.GetLoginsTable()->RemoveLoginsCreatedBetween(now, Time()); + + // Should have deleted half of what we inserted. + EXPECT_TRUE(db.GetLoginsTable()->GetAllLogins(&result, true)); + EXPECT_EQ(2U, result.size()); + ClearResults(&result); + + // Delete with 0 date (should delete all). + db.GetLoginsTable()->RemoveLoginsCreatedBetween(Time(), Time()); + + // Verify nothing is left. + EXPECT_TRUE(db.GetLoginsTable()->GetAllLogins(&result, true)); + EXPECT_EQ(0U, result.size()); +} + +TEST_F(LoginsTableTest, BlacklistedLogins) { + WebDatabase db; + + ASSERT_EQ(sql::INIT_OK, db.Init(file_)); + std::vector<PasswordForm*> result; + + // Verify the database is empty. + EXPECT_TRUE(db.GetLoginsTable()->GetAllLogins(&result, true)); + ASSERT_EQ(0U, result.size()); + + // Save a form as blacklisted. + PasswordForm form; + form.origin = GURL("http://www.google.com/accounts/LoginAuth"); + form.action = GURL("http://www.google.com/accounts/Login"); + form.username_element = ASCIIToUTF16("Email"); + form.password_element = ASCIIToUTF16("Passwd"); + form.submit_element = ASCIIToUTF16("signIn"); + form.signon_realm = "http://www.google.com/"; + form.ssl_valid = false; + form.preferred = true; + form.blacklisted_by_user = true; + form.scheme = PasswordForm::SCHEME_HTML; + EXPECT_TRUE(db.GetLoginsTable()->AddLogin(form)); + + // Get all non-blacklisted logins (should be none). + EXPECT_TRUE(db.GetLoginsTable()->GetAllLogins(&result, false)); + ASSERT_EQ(0U, result.size()); + + // GetLogins should give the blacklisted result. + EXPECT_TRUE(db.GetLoginsTable()->GetLogins(form, &result)); + EXPECT_EQ(1U, result.size()); + ClearResults(&result); + + // So should GetAll including blacklisted. + EXPECT_TRUE(db.GetLoginsTable()->GetAllLogins(&result, true)); + EXPECT_EQ(1U, result.size()); + ClearResults(&result); +} diff --git a/chrome/browser/webdata/web_database_win.cc b/chrome/browser/webdata/logins_table_win.cc index d9d3e19..3445f42 100644 --- a/chrome/browser/webdata/web_database_win.cc +++ b/chrome/browser/webdata/logins_table_win.cc @@ -1,8 +1,8 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Copyright (c) 2011 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "chrome/browser/webdata/web_database.h" +#include "chrome/browser/webdata/logins_table.h" #include "app/sql/statement.h" #include "base/logging.h" @@ -10,13 +10,13 @@ #include "base/utf_string_conversions.h" #include "chrome/browser/password_manager/ie7_password.h" -bool WebDatabase::AddIE7Login(const IE7PasswordInfo& info) { - sql::Statement s(db_.GetUniqueStatement( +bool LoginsTable::AddIE7Login(const IE7PasswordInfo& info) { + sql::Statement s(db_->GetUniqueStatement( "INSERT OR REPLACE INTO ie7_logins " "(url_hash, password_value, date_created) " "VALUES (?,?,?)")); if (!s) { - NOTREACHED() << db_.GetErrorMessage(); + NOTREACHED() << db_->GetErrorMessage(); return false; } @@ -31,12 +31,12 @@ bool WebDatabase::AddIE7Login(const IE7PasswordInfo& info) { return true; } -bool WebDatabase::RemoveIE7Login(const IE7PasswordInfo& info) { +bool LoginsTable::RemoveIE7Login(const IE7PasswordInfo& info) { // Remove a login by UNIQUE-constrained fields. - sql::Statement s(db_.GetUniqueStatement( + sql::Statement s(db_->GetUniqueStatement( "DELETE FROM ie7_logins WHERE url_hash = ?")); if (!s) { - NOTREACHED() << db_.GetErrorMessage(); + NOTREACHED() << db_->GetErrorMessage(); return false; } s.BindString(0, WideToUTF8(info.url_hash)); @@ -48,14 +48,14 @@ bool WebDatabase::RemoveIE7Login(const IE7PasswordInfo& info) { return true; } -bool WebDatabase::GetIE7Login(const IE7PasswordInfo& info, +bool LoginsTable::GetIE7Login(const IE7PasswordInfo& info, IE7PasswordInfo* result) { DCHECK(result); - sql::Statement s(db_.GetUniqueStatement( + sql::Statement s(db_->GetUniqueStatement( "SELECT password_value, date_created FROM ie7_logins " "WHERE url_hash == ? ")); if (!s) { - NOTREACHED() << db_.GetErrorMessage(); + NOTREACHED() << db_->GetErrorMessage(); return false; } diff --git a/chrome/browser/webdata/token_service_table.cc b/chrome/browser/webdata/token_service_table.cc new file mode 100644 index 0000000..8567112 --- /dev/null +++ b/chrome/browser/webdata/token_service_table.cc @@ -0,0 +1,92 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/webdata/token_service_table.h" + +#include <map> +#include <string> + +#include "app/sql/statement.h" +#include "base/logging.h" +#include "chrome/browser/password_manager/encryptor.h" + +bool TokenServiceTable::Init() { + 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 TokenServiceTable::IsSyncable() { + return true; +} + +bool TokenServiceTable::RemoveAllTokens() { + sql::Statement s(db_->GetUniqueStatement( + "DELETE FROM token_service")); + if (!s) { + NOTREACHED() << "Statement prepare failed"; + return false; + } + + return s.Run(); +} + +bool TokenServiceTable::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 TokenServiceTable::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; +} + diff --git a/chrome/browser/webdata/token_service_table.h b/chrome/browser/webdata/token_service_table.h new file mode 100644 index 0000000..db80fd6 --- /dev/null +++ b/chrome/browser/webdata/token_service_table.h @@ -0,0 +1,38 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_WEBDATA_TOKEN_SERVICE_TABLE_H_ +#define CHROME_BROWSER_WEBDATA_TOKEN_SERVICE_TABLE_H_ +#pragma once + +#include "chrome/browser/webdata/web_database_table.h" + +class TokenServiceTable : public WebDatabaseTable { + public: + TokenServiceTable(sql::Connection* db, sql::MetaTable* meta_table) + : WebDatabaseTable(db, meta_table) {} + virtual ~TokenServiceTable() {} + virtual bool Init(); + virtual bool IsSyncable(); + + // Remove all tokens previously set with SetTokenForService. + bool RemoveAllTokens(); + + // Retrieves all tokens previously set with SetTokenForService. + // Returns true if there were tokens and we decrypted them, + // false if there was a failure somehow + bool GetAllTokens(std::map<std::string, std::string>* tokens); + + // Store a token in the token_service table. Stored encrypted. May cause + // a mac keychain popup. + // True if we encrypted a token and stored it, false otherwise. + bool SetTokenForService(const std::string& service, + const std::string& token); + + private: + DISALLOW_COPY_AND_ASSIGN(TokenServiceTable); +}; + + +#endif // CHROME_BROWSER_WEBDATA_TOKEN_SERVICE_TABLE_H_ diff --git a/chrome/browser/webdata/token_service_table_unittest.cc b/chrome/browser/webdata/token_service_table_unittest.cc new file mode 100644 index 0000000..b167822 --- /dev/null +++ b/chrome/browser/webdata/token_service_table_unittest.cc @@ -0,0 +1,100 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/file_util.h" +#include "base/path_service.h" +#include "base/string_number_conversions.h" +#include "base/time.h" +#include "chrome/browser/webdata/web_database.h" +#include "chrome/common/chrome_paths.h" +#include "testing/gtest/include/gtest/gtest.h" + +using base::Time; + +class TokenServiceTableTest : public testing::Test { + public: + TokenServiceTableTest() {} + virtual ~TokenServiceTableTest() {} + + protected: + virtual void SetUp() { + PathService::Get(chrome::DIR_TEST_DATA, &file_); + const std::string test_db = "TestWebDatabase" + + base::Int64ToString(Time::Now().ToTimeT()) + + ".db"; + file_ = file_.AppendASCII(test_db); + file_util::Delete(file_, false); + } + + virtual void TearDown() { + file_util::Delete(file_, false); + } + + FilePath file_; + + private: + DISALLOW_COPY_AND_ASSIGN(TokenServiceTableTest); +}; + +TEST_F(TokenServiceTableTest, TokenServiceGetAllRemoveAll) { + WebDatabase db; + ASSERT_EQ(sql::INIT_OK, db.Init(file_)); + + std::map<std::string, std::string> out_map; + std::string service; + std::string service2; + service = "testservice"; + service2 = "othertestservice"; + + EXPECT_TRUE(db.GetTokenServiceTable()->GetAllTokens(&out_map)); + EXPECT_TRUE(out_map.empty()); + + // Check that get all tokens works + EXPECT_TRUE(db.GetTokenServiceTable()->SetTokenForService(service, + "pepperoni")); + EXPECT_TRUE(db.GetTokenServiceTable()->SetTokenForService(service2, "steak")); + EXPECT_TRUE(db.GetTokenServiceTable()->GetAllTokens(&out_map)); + EXPECT_EQ(out_map.find(service)->second, "pepperoni"); + EXPECT_EQ(out_map.find(service2)->second, "steak"); + out_map.clear(); + + // Purge + EXPECT_TRUE(db.GetTokenServiceTable()->RemoveAllTokens()); + EXPECT_TRUE(db.GetTokenServiceTable()->GetAllTokens(&out_map)); + EXPECT_TRUE(out_map.empty()); + + // Check that you can still add it back in + EXPECT_TRUE(db.GetTokenServiceTable()->SetTokenForService(service, "cheese")); + EXPECT_TRUE(db.GetTokenServiceTable()->GetAllTokens(&out_map)); + EXPECT_EQ(out_map.find(service)->second, "cheese"); +} + +TEST_F(TokenServiceTableTest, TokenServiceGetSet) { + WebDatabase db; + ASSERT_EQ(sql::INIT_OK, db.Init(file_)); + + std::map<std::string, std::string> out_map; + std::string service; + service = "testservice"; + + EXPECT_TRUE(db.GetTokenServiceTable()->GetAllTokens(&out_map)); + EXPECT_TRUE(out_map.empty()); + + EXPECT_TRUE(db.GetTokenServiceTable()->SetTokenForService(service, + "pepperoni")); + EXPECT_TRUE(db.GetTokenServiceTable()->GetAllTokens(&out_map)); + EXPECT_EQ(out_map.find(service)->second, "pepperoni"); + out_map.clear(); + + // try blanking it - won't remove it from the db though! + EXPECT_TRUE(db.GetTokenServiceTable()->SetTokenForService(service, "")); + EXPECT_TRUE(db.GetTokenServiceTable()->GetAllTokens(&out_map)); + EXPECT_EQ(out_map.find(service)->second, ""); + out_map.clear(); + + // try mutating it + EXPECT_TRUE(db.GetTokenServiceTable()->SetTokenForService(service, "ham")); + EXPECT_TRUE(db.GetTokenServiceTable()->GetAllTokens(&out_map)); + EXPECT_EQ(out_map.find(service)->second, "ham"); +} diff --git a/chrome/browser/webdata/web_apps_table.cc b/chrome/browser/webdata/web_apps_table.cc new file mode 100644 index 0000000..2b8812c --- /dev/null +++ b/chrome/browser/webdata/web_apps_table.cc @@ -0,0 +1,141 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/webdata/web_apps_table.h" + +#include "app/sql/statement.h" +#include "base/logging.h" +#include "chrome/browser/history/history_database.h" +#include "googleurl/src/gurl.h" +#include "third_party/skia/include/core/SkBitmap.h" +#include "ui/gfx/codec/png_codec.h" + +bool WebAppsTable::Init() { + return (InitWebAppIconsTable() && InitWebAppsTable()); +} + +bool WebAppsTable::IsSyncable() { + return true; +} + +bool WebAppsTable::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 WebAppsTable::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 WebAppsTable::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 WebAppsTable::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 WebAppsTable::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 WebAppsTable::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 WebAppsTable::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(); +} + diff --git a/chrome/browser/webdata/web_apps_table.h b/chrome/browser/webdata/web_apps_table.h new file mode 100644 index 0000000..b39ac9c --- /dev/null +++ b/chrome/browser/webdata/web_apps_table.h @@ -0,0 +1,54 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_WEBDATA_WEB_APPS_TABLE_H_ +#define CHROME_BROWSER_WEBDATA_WEB_APPS_TABLE_H_ +#pragma once + +#include <vector> + +#include "chrome/browser/webdata/web_database_table.h" + +class GURL; +class SkBitmap; + +// This class manages the WebApps tables within the SQLite database passed to +// the constructor. It expects the following schema: +// +// Note: The database stores time in seconds, UTC. +// +// web_apps +// url URL of the web app. +// has_all_images Do we have all the images? +// +// web_app_icons +// url URL of the web app. +// width Width of the image. +// height Height of the image. +// image PNG encoded image data. +// +class WebAppsTable : public WebDatabaseTable { + public: + WebAppsTable(sql::Connection* db, sql::MetaTable* meta_table) + : WebDatabaseTable(db, meta_table) {} + virtual ~WebAppsTable() {} + virtual bool Init(); + virtual bool IsSyncable(); + + bool SetWebAppImage(const GURL& url, const SkBitmap& image); + bool GetWebAppImages(const GURL& url, std::vector<SkBitmap>* images); + + bool SetWebAppHasAllImages(const GURL& url, bool has_all_images); + bool GetWebAppHasAllImages(const GURL& url); + + bool RemoveWebApp(const GURL& url); + + private: + bool InitWebAppIconsTable(); + bool InitWebAppsTable(); + + DISALLOW_COPY_AND_ASSIGN(WebAppsTable); +}; + +#endif // CHROME_BROWSER_WEBDATA_WEB_APPS_TABLE_H_ diff --git a/chrome/browser/webdata/web_apps_table_unittest.cc b/chrome/browser/webdata/web_apps_table_unittest.cc new file mode 100644 index 0000000..5cbd3e6 --- /dev/null +++ b/chrome/browser/webdata/web_apps_table_unittest.cc @@ -0,0 +1,133 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/file_util.h" +#include "base/path_service.h" +#include "base/string_number_conversions.h" +#include "base/time.h" +#include "chrome/browser/webdata/web_database.h" +#include "chrome/common/chrome_paths.h" +#include "googleurl/src/gurl.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/skia/include/core/SkBitmap.h" + +using base::Time; + +class WebAppsTableTest : public testing::Test { + public: + WebAppsTableTest() {} + virtual ~WebAppsTableTest() {} + + protected: + virtual void SetUp() { + PathService::Get(chrome::DIR_TEST_DATA, &file_); + const std::string test_db = "TestWebDatabase" + + base::Int64ToString(Time::Now().ToTimeT()) + + ".db"; + file_ = file_.AppendASCII(test_db); + file_util::Delete(file_, false); + } + + virtual void TearDown() { + file_util::Delete(file_, false); + } + + FilePath file_; + + private: + DISALLOW_COPY_AND_ASSIGN(WebAppsTableTest); +}; + + +TEST_F(WebAppsTableTest, WebAppHasAllImages) { + WebDatabase db; + + ASSERT_EQ(sql::INIT_OK, db.Init(file_)); + GURL url("http://google.com/"); + + // Initial value for unknown web app should be false. + EXPECT_FALSE(db.GetWebAppsTable()->GetWebAppHasAllImages(url)); + + // Set the value and make sure it took. + EXPECT_TRUE(db.GetWebAppsTable()->SetWebAppHasAllImages(url, true)); + EXPECT_TRUE(db.GetWebAppsTable()->GetWebAppHasAllImages(url)); + + // Remove the app and make sure value reverts to default. + EXPECT_TRUE(db.GetWebAppsTable()->RemoveWebApp(url)); + EXPECT_FALSE(db.GetWebAppsTable()->GetWebAppHasAllImages(url)); +} + +TEST_F(WebAppsTableTest, WebAppImages) { + WebDatabase db; + + ASSERT_EQ(sql::INIT_OK, db.Init(file_)); + GURL url("http://google.com/"); + + // Web app should initially have no images. + std::vector<SkBitmap> images; + ASSERT_TRUE(db.GetWebAppsTable()->GetWebAppImages(url, &images)); + ASSERT_EQ(0U, images.size()); + + // Add an image. + SkBitmap image; + image.setConfig(SkBitmap::kARGB_8888_Config, 16, 16); + image.allocPixels(); + image.eraseColor(SK_ColorBLACK); + ASSERT_TRUE(db.GetWebAppsTable()->SetWebAppImage(url, image)); + + // Make sure we get the image back. + ASSERT_TRUE(db.GetWebAppsTable()->GetWebAppImages(url, &images)); + ASSERT_EQ(1U, images.size()); + ASSERT_EQ(16, images[0].width()); + ASSERT_EQ(16, images[0].height()); + + // Add another 16x16 image and make sure it replaces the original. + image.setConfig(SkBitmap::kARGB_8888_Config, 16, 16); + image.allocPixels(); + image.eraseColor(SK_ColorBLACK); + + // Set some random pixels so that we can identify the image. We don't use + // transparent images because of pre-multiplication rounding errors. + SkColor test_pixel_1 = 0xffccbbaa; + SkColor test_pixel_2 = 0xffaabbaa; + SkColor test_pixel_3 = 0xff339966; + image.getAddr32(0, 1)[0] = test_pixel_1; + image.getAddr32(0, 1)[1] = test_pixel_2; + image.getAddr32(0, 1)[2] = test_pixel_3; + + ASSERT_TRUE(db.GetWebAppsTable()->SetWebAppImage(url, image)); + images.clear(); + ASSERT_TRUE(db.GetWebAppsTable()->GetWebAppImages(url, &images)); + ASSERT_EQ(1U, images.size()); + ASSERT_EQ(16, images[0].width()); + ASSERT_EQ(16, images[0].height()); + images[0].lockPixels(); + ASSERT_TRUE(images[0].getPixels() != NULL); + ASSERT_EQ(test_pixel_1, images[0].getAddr32(0, 1)[0]); + ASSERT_EQ(test_pixel_2, images[0].getAddr32(0, 1)[1]); + ASSERT_EQ(test_pixel_3, images[0].getAddr32(0, 1)[2]); + images[0].unlockPixels(); + + // Add another image at a bigger size. + image.setConfig(SkBitmap::kARGB_8888_Config, 32, 32); + image.allocPixels(); + image.eraseColor(SK_ColorBLACK); + ASSERT_TRUE(db.GetWebAppsTable()->SetWebAppImage(url, image)); + + // Make sure we get both images back. + images.clear(); + ASSERT_TRUE(db.GetWebAppsTable()->GetWebAppImages(url, &images)); + ASSERT_EQ(2U, images.size()); + if (images[0].width() == 16) { + ASSERT_EQ(16, images[0].width()); + ASSERT_EQ(16, images[0].height()); + ASSERT_EQ(32, images[1].width()); + ASSERT_EQ(32, images[1].height()); + } else { + ASSERT_EQ(32, images[0].width()); + ASSERT_EQ(32, images[0].height()); + ASSERT_EQ(16, images[1].width()); + ASSERT_EQ(16, images[1].height()); + } +} diff --git a/chrome/browser/webdata/web_data_service.cc b/chrome/browser/webdata/web_data_service.cc index 8e4483c..122aadf 100644 --- a/chrome/browser/webdata/web_data_service.cc +++ b/chrome/browser/webdata/web_data_service.cc @@ -743,7 +743,8 @@ void WebDataService::SetWebAppImageImpl( GenericRequest2<GURL, SkBitmap>* request) { InitializeDatabaseIfNecessary(); if (db_ && !request->IsCancelled()) { - db_->SetWebAppImage(request->GetArgument1(), request->GetArgument2()); + db_->GetWebAppsTable()->SetWebAppImage( + request->GetArgument1(), request->GetArgument2()); ScheduleCommit(); } request->RequestComplete(); @@ -753,8 +754,8 @@ void WebDataService::SetWebAppHasAllImagesImpl( GenericRequest2<GURL, bool>* request) { InitializeDatabaseIfNecessary(); if (db_ && !request->IsCancelled()) { - db_->SetWebAppHasAllImages(request->GetArgument1(), - request->GetArgument2()); + db_->GetWebAppsTable()->SetWebAppHasAllImages(request->GetArgument1(), + request->GetArgument2()); ScheduleCommit(); } request->RequestComplete(); @@ -763,7 +764,7 @@ void WebDataService::SetWebAppHasAllImagesImpl( void WebDataService::RemoveWebAppImpl(GenericRequest<GURL>* request) { InitializeDatabaseIfNecessary(); if (db_ && !request->IsCancelled()) { - db_->RemoveWebApp(request->GetArgument()); + db_->GetWebAppsTable()->RemoveWebApp(request->GetArgument()); ScheduleCommit(); } request->RequestComplete(); @@ -773,8 +774,10 @@ void WebDataService::GetWebAppImagesImpl(GenericRequest<GURL>* request) { InitializeDatabaseIfNecessary(); if (db_ && !request->IsCancelled()) { WDAppImagesResult result; - result.has_all_images = db_->GetWebAppHasAllImages(request->GetArgument()); - db_->GetWebAppImages(request->GetArgument(), &result.images); + result.has_all_images = + db_->GetWebAppsTable()->GetWebAppHasAllImages(request->GetArgument()); + db_->GetWebAppsTable()->GetWebAppImages( + request->GetArgument(), &result.images); request->SetResult( new WDResult<WDAppImagesResult>(WEB_APP_IMAGES, result)); } @@ -792,7 +795,7 @@ void WebDataService::RemoveAllTokensImpl( GenericRequest<std::string>* request) { InitializeDatabaseIfNecessary(); if (db_ && !request->IsCancelled()) { - if (db_->RemoveAllTokens()) { + if (db_->GetTokenServiceTable()->RemoveAllTokens()) { ScheduleCommit(); } } @@ -803,8 +806,8 @@ void WebDataService::SetTokenForServiceImpl( GenericRequest2<std::string, std::string>* request) { InitializeDatabaseIfNecessary(); if (db_ && !request->IsCancelled()) { - if (db_->SetTokenForService(request->GetArgument1(), - request->GetArgument2())) { + if (db_->GetTokenServiceTable()->SetTokenForService( + request->GetArgument1(), request->GetArgument2())) { ScheduleCommit(); } } @@ -817,7 +820,7 @@ void WebDataService::GetAllTokensImpl( InitializeDatabaseIfNecessary(); if (db_ && !request->IsCancelled()) { std::map<std::string, std::string> map; - db_->GetAllTokens(&map); + db_->GetTokenServiceTable()->GetAllTokens(&map); request->SetResult( new WDResult<std::map<std::string, std::string> >(TOKEN_RESULT, map)); } @@ -833,7 +836,7 @@ void WebDataService::GetAllTokensImpl( void WebDataService::AddLoginImpl(GenericRequest<PasswordForm>* request) { InitializeDatabaseIfNecessary(); if (db_ && !request->IsCancelled()) { - if (db_->AddLogin(request->GetArgument())) + if (db_->GetLoginsTable()->AddLogin(request->GetArgument())) ScheduleCommit(); } request->RequestComplete(); @@ -842,7 +845,7 @@ void WebDataService::AddLoginImpl(GenericRequest<PasswordForm>* request) { void WebDataService::UpdateLoginImpl(GenericRequest<PasswordForm>* request) { InitializeDatabaseIfNecessary(); if (db_ && !request->IsCancelled()) { - if (db_->UpdateLogin(request->GetArgument())) + if (db_->GetLoginsTable()->UpdateLogin(request->GetArgument())) ScheduleCommit(); } request->RequestComplete(); @@ -851,7 +854,7 @@ void WebDataService::UpdateLoginImpl(GenericRequest<PasswordForm>* request) { void WebDataService::RemoveLoginImpl(GenericRequest<PasswordForm>* request) { InitializeDatabaseIfNecessary(); if (db_ && !request->IsCancelled()) { - if (db_->RemoveLogin(request->GetArgument())) + if (db_->GetLoginsTable()->RemoveLogin(request->GetArgument())) ScheduleCommit(); } request->RequestComplete(); @@ -861,9 +864,10 @@ void WebDataService::RemoveLoginsCreatedBetweenImpl( GenericRequest2<Time, Time>* request) { InitializeDatabaseIfNecessary(); if (db_ && !request->IsCancelled()) { - if (db_->RemoveLoginsCreatedBetween(request->GetArgument1(), - request->GetArgument2())) + if (db_->GetLoginsTable()->RemoveLoginsCreatedBetween( + request->GetArgument1(), request->GetArgument2())) { ScheduleCommit(); + } } request->RequestComplete(); } @@ -872,7 +876,7 @@ void WebDataService::GetLoginsImpl(GenericRequest<PasswordForm>* request) { InitializeDatabaseIfNecessary(); if (db_ && !request->IsCancelled()) { std::vector<PasswordForm*> forms; - db_->GetLogins(request->GetArgument(), &forms); + db_->GetLoginsTable()->GetLogins(request->GetArgument(), &forms); request->SetResult( new WDResult<std::vector<PasswordForm*> >(PASSWORD_RESULT, forms)); } @@ -883,7 +887,7 @@ void WebDataService::GetAutofillableLoginsImpl(WebDataRequest* request) { InitializeDatabaseIfNecessary(); if (db_ && !request->IsCancelled()) { std::vector<PasswordForm*> forms; - db_->GetAllLogins(&forms, false); + db_->GetLoginsTable()->GetAllLogins(&forms, false); request->SetResult( new WDResult<std::vector<PasswordForm*> >(PASSWORD_RESULT, forms)); } @@ -894,7 +898,7 @@ void WebDataService::GetBlacklistLoginsImpl(WebDataRequest* request) { InitializeDatabaseIfNecessary(); if (db_ && !request->IsCancelled()) { std::vector<PasswordForm*> all_forms; - db_->GetAllLogins(&all_forms, true); + db_->GetLoginsTable()->GetAllLogins(&all_forms, true); std::vector<PasswordForm*> blacklist_forms; for (std::vector<PasswordForm*>::iterator i = all_forms.begin(); i != all_forms.end(); ++i) { diff --git a/chrome/browser/webdata/web_data_service_win.cc b/chrome/browser/webdata/web_data_service_win.cc index 1c4f307..3167c82 100644 --- a/chrome/browser/webdata/web_data_service_win.cc +++ b/chrome/browser/webdata/web_data_service_win.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Copyright (c) 2011 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -40,7 +40,7 @@ WebDataService::Handle WebDataService::GetIE7Login( void WebDataService::AddIE7LoginImpl(GenericRequest<IE7PasswordInfo>* request) { if (db_ && !request->IsCancelled()) { - if (db_->AddIE7Login(request->GetArgument())) + if (db_->GetLoginsTable()->AddIE7Login(request->GetArgument())) ScheduleCommit(); } request->RequestComplete(); @@ -49,7 +49,7 @@ void WebDataService::AddIE7LoginImpl(GenericRequest<IE7PasswordInfo>* request) { void WebDataService::RemoveIE7LoginImpl( GenericRequest<IE7PasswordInfo>* request) { if (db_ && !request->IsCancelled()) { - if (db_->RemoveIE7Login(request->GetArgument())) + if (db_->GetLoginsTable()->RemoveIE7Login(request->GetArgument())) ScheduleCommit(); } request->RequestComplete(); @@ -59,7 +59,7 @@ void WebDataService::GetIE7LoginImpl( GenericRequest<IE7PasswordInfo>* request) { if (db_ && !request->IsCancelled()) { IE7PasswordInfo result; - db_->GetIE7Login(request->GetArgument(), &result); + db_->GetLoginsTable()->GetIE7Login(request->GetArgument(), &result); request->SetResult( new WDResult<IE7PasswordInfo>(PASSWORD_IE7_RESULT, result)); } diff --git a/chrome/browser/webdata/web_database.cc b/chrome/browser/webdata/web_database.cc index 5fa4413..c3fe4d0 100644 --- a/chrome/browser/webdata/web_database.cc +++ b/chrome/browser/webdata/web_database.cc @@ -17,60 +17,11 @@ #include "chrome/browser/autofill/autofill_type.h" #include "chrome/browser/autofill/personal_data_manager.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/webdata/autofill_util.h" -#include "chrome/browser/webdata/keyword_table.h" #include "chrome/common/guid.h" #include "content/common/notification_service.h" -#include "third_party/skia/include/core/SkBitmap.h" -#include "ui/gfx/codec/png_codec.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 -// -//////////////////////////////////////////////////////////////////////////////// -// -// Schema -// Note: The database stores time in seconds, UTC. -// -// 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. -// -// 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; -using webkit_glue::PasswordForm; namespace { @@ -80,38 +31,6 @@ namespace { const int kCurrentVersionNumber = 36; const int kCompatibleVersionNumber = 36; -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(dhollowa): Find a common place for this. It is duplicated in // personal_data_manager.cc. template<typename T> @@ -119,7 +38,7 @@ T* address_of(T& v) { return &v; } -} // namespace +} // anonymous namespace WebDatabase::WebDatabase() {} @@ -141,6 +60,22 @@ KeywordTable* WebDatabase::GetKeywordTable() { return keyword_table_.get(); } +LoginsTable* WebDatabase::GetLoginsTable() { + return logins_table_.get(); +} + +TokenServiceTable* WebDatabase::GetTokenServiceTable() { + return token_service_table_.get(); +} + +WebAppsTable* WebDatabase::GetWebAppsTable() { + return web_apps_table_.get(); +} + +sql::Connection* WebDatabase::GetSQLConnection() { + return &db_; +} + 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. @@ -182,11 +117,14 @@ sql::InitStatus WebDatabase::Init(const FilePath& db_name) { // Create the tables. autofill_table_.reset(new AutofillTable(&db_, &meta_table_)); keyword_table_.reset(new KeywordTable(&db_, &meta_table_)); + logins_table_.reset(new LoginsTable(&db_, &meta_table_)); + token_service_table_.reset(new TokenServiceTable(&db_, &meta_table_)); + web_apps_table_.reset(new WebAppsTable(&db_, &meta_table_)); // Initialize the tables. if (!keyword_table_->Init() || !autofill_table_->Init() || - !InitLoginsTable() || !InitWebAppIconsTable() || !InitWebAppsTable() || - !InitTokenServiceTable()) { + !logins_table_->Init() || !web_apps_table_->Init() || + !token_service_table_->Init()) { LOG(WARNING) << "Unable to initialize the web database."; return sql::INIT_FAILURE; } @@ -201,441 +139,6 @@ sql::InitStatus WebDatabase::Init(const FilePath& db_name) { 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::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::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::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(); -} - sql::InitStatus WebDatabase::MigrateOldVersionsAsNeeded() { // Migrate if necessary. int current_version = meta_table_.GetVersionNumber(); diff --git a/chrome/browser/webdata/web_database.h b/chrome/browser/webdata/web_database.h index ee69165..e743f38 100644 --- a/chrome/browser/webdata/web_database.h +++ b/chrome/browser/webdata/web_database.h @@ -6,44 +6,26 @@ #define CHROME_BROWSER_WEBDATA_WEB_DATABASE_H_ #pragma once -#include <vector> - #include "app/sql/connection.h" #include "app/sql/init_status.h" #include "app/sql/meta_table.h" #include "base/memory/scoped_ptr.h" #include "chrome/browser/webdata/autofill_table.h" #include "chrome/browser/webdata/keyword_table.h" +#include "chrome/browser/webdata/logins_table.h" +#include "chrome/browser/webdata/token_service_table.h" +#include "chrome/browser/webdata/web_apps_table.h" -class AutofillTableTest; class FilePath; -class GURL; class NotificationService; -class SkBitmap; - -namespace base { -class Time; -} - -namespace webkit_glue { -struct PasswordForm; -} -#if defined(OS_WIN) -struct IE7PasswordInfo; -#endif - -//////////////////////////////////////////////////////////////////////////////// -// -// A Sqlite database instance to store all the meta data we have about web pages -// -//////////////////////////////////////////////////////////////////////////////// +// This class manages a SQLite database that stores various web page meta data. class WebDatabase { public: WebDatabase(); virtual ~WebDatabase(); - // Initialize the database given a name. The name defines where the sqlite + // Initialize the database given a name. The name defines where the SQLite // file is. If this returns an error code, no other method should be called. sql::InitStatus Init(const FilePath& db_name); @@ -53,108 +35,14 @@ class WebDatabase { virtual AutofillTable* GetAutofillTable(); virtual KeywordTable* GetKeywordTable(); + virtual LoginsTable* GetLoginsTable(); + virtual TokenServiceTable* GetTokenServiceTable(); + virtual WebAppsTable* GetWebAppsTable(); - ////////////////////////////////////////////////////////////////////////////// - // - // Password manager support - // - ////////////////////////////////////////////////////////////////////////////// - - // Adds |form| to the list of remembered password forms. - bool AddLogin(const webkit_glue::PasswordForm& form); - -#if defined(OS_WIN) - // Adds |info| to the list of imported passwords from ie7/ie8. - bool AddIE7Login(const IE7PasswordInfo& info); - - // Removes |info| from the list of imported passwords from ie7/ie8. - bool RemoveIE7Login(const IE7PasswordInfo& info); - - // Return the ie7/ie8 login matching |info|. - bool GetIE7Login(const IE7PasswordInfo& info, IE7PasswordInfo* result); -#endif - - // Updates remembered password form. - bool UpdateLogin(const webkit_glue::PasswordForm& form); - - // Removes |form| from the list of remembered password forms. - bool RemoveLogin(const webkit_glue::PasswordForm& form); - - // Removes all logins created from |delete_begin| onwards (inclusive) and - // before |delete_end|. You may use a null Time value to do an unbounded - // delete in either direction. - bool RemoveLoginsCreatedBetween(base::Time delete_begin, - base::Time delete_end); - - // Loads a list of matching password forms into the specified vector |forms|. - // The list will contain all possibly relevant entries to the observed |form|, - // including blacklisted matches. - bool GetLogins(const webkit_glue::PasswordForm& form, - std::vector<webkit_glue::PasswordForm*>* forms); - - // Loads the complete list of password forms into the specified vector |forms| - // if include_blacklisted is true, otherwise only loads those which are - // actually autofill-able; i.e haven't been blacklisted by the user selecting - // the 'Never for this site' button. - bool GetAllLogins(std::vector<webkit_glue::PasswordForm*>* forms, - bool include_blacklisted); - - ////////////////////////////////////////////////////////////////////////////// - // - // Web Apps - // - ////////////////////////////////////////////////////////////////////////////// - - bool SetWebAppImage(const GURL& url, const SkBitmap& image); - bool GetWebAppImages(const GURL& url, std::vector<SkBitmap>* images); - - bool SetWebAppHasAllImages(const GURL& url, bool has_all_images); - bool GetWebAppHasAllImages(const GURL& url); - - bool RemoveWebApp(const GURL& url); - - ////////////////////////////////////////////////////////////////////////////// - // - // Token Service - // - ////////////////////////////////////////////////////////////////////////////// - - // Remove all tokens previously set with SetTokenForService. - bool RemoveAllTokens(); - - // Retrieves all tokens previously set with SetTokenForService. - // Returns true if there were tokens and we decrypted them, - // false if there was a failure somehow - bool GetAllTokens(std::map<std::string, std::string>* tokens); - - // Store a token in the token_service table. Stored encrypted. May cause - // a mac keychain popup. - // True if we encrypted a token and stored it, false otherwise. - bool SetTokenForService(const std::string& service, - const std::string& token); + // Exposed for testing only. + sql::Connection* GetSQLConnection(); private: - // TODO(andybons): Remove this in the next refactor round. - FRIEND_TEST_ALL_PREFIXES(AutofillTableTest, Autofill); - FRIEND_TEST_ALL_PREFIXES(AutofillTableTest, Autofill_AddChanges); - FRIEND_TEST_ALL_PREFIXES(AutofillTableTest, Autofill_RemoveBetweenChanges); - - FRIEND_TEST_ALL_PREFIXES(AutofillTableTest, Autofill_UpdateDontReplace); - FRIEND_TEST_ALL_PREFIXES(AutofillTableTest, Autofill_AddFormFieldValues); - FRIEND_TEST_ALL_PREFIXES(AutofillTableTest, AutofillProfile); - FRIEND_TEST_ALL_PREFIXES(AutofillTableTest, UpdateAutofillProfile); - FRIEND_TEST_ALL_PREFIXES(AutofillTableTest, AutofillProfileTrash); - FRIEND_TEST_ALL_PREFIXES(AutofillTableTest, AutofillProfileTrashInteraction); - FRIEND_TEST_ALL_PREFIXES(AutofillTableTest, - RemoveAutofillProfilesAndCreditCardsModifiedBetween); - FRIEND_TEST_ALL_PREFIXES(AutofillTableTest, CreditCard); - FRIEND_TEST_ALL_PREFIXES(AutofillTableTest, UpdateCreditCard); - - bool InitLoginsTable(); - bool InitTokenServiceTable(); - bool InitWebAppIconsTable(); - bool InitWebAppsTable(); - // Used by |Init()| to migration database schema from older versions to // current version. sql::InitStatus MigrateOldVersionsAsNeeded(); @@ -164,6 +52,9 @@ class WebDatabase { scoped_ptr<AutofillTable> autofill_table_; scoped_ptr<KeywordTable> keyword_table_; + scoped_ptr<LoginsTable> logins_table_; + scoped_ptr<TokenServiceTable> token_service_table_; + scoped_ptr<WebAppsTable> web_apps_table_; scoped_ptr<NotificationService> notification_service_; diff --git a/chrome/browser/webdata/web_database_unittest.cc b/chrome/browser/webdata/web_database_migration_unittest.cc index 9939284..0d2a365 100644 --- a/chrome/browser/webdata/web_database_unittest.cc +++ b/chrome/browser/webdata/web_database_migration_unittest.cc @@ -2,17 +2,11 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include <list> -#include <map> -#include <set> #include <string> -#include <utility> -#include <vector> #include "app/sql/statement.h" #include "base/file_util.h" #include "base/memory/scoped_temp_dir.h" -#include "base/path_service.h" #include "base/stl_util-inl.h" #include "base/string16.h" #include "base/string_number_conversions.h" @@ -22,7 +16,6 @@ #include "chrome/browser/autofill/autofill_profile.h" #include "chrome/browser/autofill/autofill_type.h" #include "chrome/browser/autofill/credit_card.h" -#include "chrome/browser/password_manager/encryptor.h" #include "chrome/browser/webdata/autofill_change.h" #include "chrome/browser/webdata/autofill_entry.h" #include "chrome/browser/webdata/web_database.h" @@ -30,12 +23,8 @@ #include "chrome/common/guid.h" #include "chrome/test/ui_test_utils.h" #include "testing/gtest/include/gtest/gtest.h" -#include "third_party/skia/include/core/SkBitmap.h" -#include "webkit/glue/password_form.h" using base::Time; -using base::TimeDelta; -using webkit_glue::PasswordForm; namespace { @@ -160,422 +149,6 @@ void CreditCard32FromStatement(const sql::Statement& s, } // anonymous namespace -class WebDatabaseTest : public testing::Test { - public: - WebDatabaseTest() {} - virtual ~WebDatabaseTest() {} - - protected: - virtual void SetUp() { -#if defined(OS_MACOSX) - Encryptor::UseMockKeychain(true); -#endif - PathService::Get(chrome::DIR_TEST_DATA, &file_); - const std::string test_db = "TestWebDatabase" + - base::Int64ToString(Time::Now().ToTimeT()) + - ".db"; - file_ = file_.AppendASCII(test_db); - file_util::Delete(file_, false); - } - - virtual void TearDown() { - file_util::Delete(file_, false); - } - - FilePath file_; - - private: - DISALLOW_COPY_AND_ASSIGN(WebDatabaseTest); -}; - -TEST_F(WebDatabaseTest, Logins) { - WebDatabase db; - - ASSERT_EQ(sql::INIT_OK, db.Init(file_)); - - std::vector<PasswordForm*> result; - - // Verify the database is empty. - EXPECT_TRUE(db.GetAllLogins(&result, true)); - EXPECT_EQ(0U, result.size()); - - // Example password form. - PasswordForm form; - form.origin = GURL("http://www.google.com/accounts/LoginAuth"); - form.action = GURL("http://www.google.com/accounts/Login"); - form.username_element = ASCIIToUTF16("Email"); - form.username_value = ASCIIToUTF16("test@gmail.com"); - form.password_element = ASCIIToUTF16("Passwd"); - form.password_value = ASCIIToUTF16("test"); - form.submit_element = ASCIIToUTF16("signIn"); - form.signon_realm = "http://www.google.com/"; - form.ssl_valid = false; - form.preferred = false; - form.scheme = PasswordForm::SCHEME_HTML; - - // Add it and make sure it is there. - EXPECT_TRUE(db.AddLogin(form)); - EXPECT_TRUE(db.GetAllLogins(&result, true)); - EXPECT_EQ(1U, result.size()); - delete result[0]; - result.clear(); - - // Match against an exact copy. - EXPECT_TRUE(db.GetLogins(form, &result)); - EXPECT_EQ(1U, result.size()); - delete result[0]; - result.clear(); - - // The example site changes... - PasswordForm form2(form); - form2.origin = GURL("http://www.google.com/new/accounts/LoginAuth"); - form2.submit_element = ASCIIToUTF16("reallySignIn"); - - // Match against an inexact copy - EXPECT_TRUE(db.GetLogins(form2, &result)); - EXPECT_EQ(1U, result.size()); - delete result[0]; - result.clear(); - - // Uh oh, the site changed origin & action URL's all at once! - PasswordForm form3(form2); - form3.action = GURL("http://www.google.com/new/accounts/Login"); - - // signon_realm is the same, should match. - EXPECT_TRUE(db.GetLogins(form3, &result)); - EXPECT_EQ(1U, result.size()); - delete result[0]; - result.clear(); - - // Imagine the site moves to a secure server for login. - PasswordForm form4(form3); - form4.signon_realm = "https://www.google.com/"; - form4.ssl_valid = true; - - // We have only an http record, so no match for this. - EXPECT_TRUE(db.GetLogins(form4, &result)); - EXPECT_EQ(0U, result.size()); - - // Let's imagine the user logs into the secure site. - EXPECT_TRUE(db.AddLogin(form4)); - EXPECT_TRUE(db.GetAllLogins(&result, true)); - EXPECT_EQ(2U, result.size()); - delete result[0]; - delete result[1]; - result.clear(); - - // Now the match works - EXPECT_TRUE(db.GetLogins(form4, &result)); - EXPECT_EQ(1U, result.size()); - delete result[0]; - result.clear(); - - // The user chose to forget the original but not the new. - EXPECT_TRUE(db.RemoveLogin(form)); - EXPECT_TRUE(db.GetAllLogins(&result, true)); - EXPECT_EQ(1U, result.size()); - delete result[0]; - result.clear(); - - // The old form wont match the new site (http vs https). - EXPECT_TRUE(db.GetLogins(form, &result)); - EXPECT_EQ(0U, result.size()); - - // The user's request for the HTTPS site is intercepted - // by an attacker who presents an invalid SSL cert. - PasswordForm form5(form4); - form5.ssl_valid = 0; - - // It will match in this case. - EXPECT_TRUE(db.GetLogins(form5, &result)); - EXPECT_EQ(1U, result.size()); - delete result[0]; - result.clear(); - - // User changes his password. - PasswordForm form6(form5); - form6.password_value = ASCIIToUTF16("test6"); - form6.preferred = true; - - // We update, and check to make sure it matches the - // old form, and there is only one record. - EXPECT_TRUE(db.UpdateLogin(form6)); - // matches - EXPECT_TRUE(db.GetLogins(form5, &result)); - EXPECT_EQ(1U, result.size()); - delete result[0]; - result.clear(); - // Only one record. - EXPECT_TRUE(db.GetAllLogins(&result, true)); - EXPECT_EQ(1U, result.size()); - // password element was updated. - EXPECT_EQ(form6.password_value, result[0]->password_value); - // Preferred login. - EXPECT_TRUE(form6.preferred); - delete result[0]; - result.clear(); - - // Make sure everything can disappear. - EXPECT_TRUE(db.RemoveLogin(form4)); - EXPECT_TRUE(db.GetAllLogins(&result, true)); - EXPECT_EQ(0U, result.size()); -} - -static bool AddTimestampedLogin(WebDatabase* db, std::string url, - const std::string& unique_string, - const Time& time) { - // Example password form. - PasswordForm form; - form.origin = GURL(url + std::string("/LoginAuth")); - form.username_element = ASCIIToUTF16(unique_string); - form.username_value = ASCIIToUTF16(unique_string); - form.password_element = ASCIIToUTF16(unique_string); - form.submit_element = ASCIIToUTF16("signIn"); - form.signon_realm = url; - form.date_created = time; - return db->AddLogin(form); -} - -static void ClearResults(std::vector<PasswordForm*>* results) { - for (size_t i = 0; i < results->size(); ++i) { - delete (*results)[i]; - } - results->clear(); -} - -TEST_F(WebDatabaseTest, ClearPrivateData_SavedPasswords) { - WebDatabase db; - - ASSERT_EQ(sql::INIT_OK, db.Init(file_)); - - std::vector<PasswordForm*> result; - - // Verify the database is empty. - EXPECT_TRUE(db.GetAllLogins(&result, true)); - EXPECT_EQ(0U, result.size()); - - Time now = Time::Now(); - TimeDelta one_day = TimeDelta::FromDays(1); - - // Create one with a 0 time. - EXPECT_TRUE(AddTimestampedLogin(&db, "1", "foo1", Time())); - // Create one for now and +/- 1 day. - EXPECT_TRUE(AddTimestampedLogin(&db, "2", "foo2", now - one_day)); - EXPECT_TRUE(AddTimestampedLogin(&db, "3", "foo3", now)); - EXPECT_TRUE(AddTimestampedLogin(&db, "4", "foo4", now + one_day)); - - // Verify inserts worked. - EXPECT_TRUE(db.GetAllLogins(&result, true)); - EXPECT_EQ(4U, result.size()); - ClearResults(&result); - - // Delete everything from today's date and on. - db.RemoveLoginsCreatedBetween(now, Time()); - - // Should have deleted half of what we inserted. - EXPECT_TRUE(db.GetAllLogins(&result, true)); - EXPECT_EQ(2U, result.size()); - ClearResults(&result); - - // Delete with 0 date (should delete all). - db.RemoveLoginsCreatedBetween(Time(), Time()); - - // Verify nothing is left. - EXPECT_TRUE(db.GetAllLogins(&result, true)); - EXPECT_EQ(0U, result.size()); -} - -TEST_F(WebDatabaseTest, BlacklistedLogins) { - WebDatabase db; - - ASSERT_EQ(sql::INIT_OK, db.Init(file_)); - std::vector<PasswordForm*> result; - - // Verify the database is empty. - EXPECT_TRUE(db.GetAllLogins(&result, true)); - ASSERT_EQ(0U, result.size()); - - // Save a form as blacklisted. - PasswordForm form; - form.origin = GURL("http://www.google.com/accounts/LoginAuth"); - form.action = GURL("http://www.google.com/accounts/Login"); - form.username_element = ASCIIToUTF16("Email"); - form.password_element = ASCIIToUTF16("Passwd"); - form.submit_element = ASCIIToUTF16("signIn"); - form.signon_realm = "http://www.google.com/"; - form.ssl_valid = false; - form.preferred = true; - form.blacklisted_by_user = true; - form.scheme = PasswordForm::SCHEME_HTML; - EXPECT_TRUE(db.AddLogin(form)); - - // Get all non-blacklisted logins (should be none). - EXPECT_TRUE(db.GetAllLogins(&result, false)); - ASSERT_EQ(0U, result.size()); - - // GetLogins should give the blacklisted result. - EXPECT_TRUE(db.GetLogins(form, &result)); - EXPECT_EQ(1U, result.size()); - ClearResults(&result); - - // So should GetAll including blacklisted. - EXPECT_TRUE(db.GetAllLogins(&result, true)); - EXPECT_EQ(1U, result.size()); - ClearResults(&result); -} - -TEST_F(WebDatabaseTest, WebAppHasAllImages) { - WebDatabase db; - - ASSERT_EQ(sql::INIT_OK, db.Init(file_)); - GURL url("http://google.com/"); - - // Initial value for unknown web app should be false. - EXPECT_FALSE(db.GetWebAppHasAllImages(url)); - - // Set the value and make sure it took. - EXPECT_TRUE(db.SetWebAppHasAllImages(url, true)); - EXPECT_TRUE(db.GetWebAppHasAllImages(url)); - - // Remove the app and make sure value reverts to default. - EXPECT_TRUE(db.RemoveWebApp(url)); - EXPECT_FALSE(db.GetWebAppHasAllImages(url)); -} - -TEST_F(WebDatabaseTest, WebAppImages) { - WebDatabase db; - - ASSERT_EQ(sql::INIT_OK, db.Init(file_)); - GURL url("http://google.com/"); - - // Web app should initially have no images. - std::vector<SkBitmap> images; - ASSERT_TRUE(db.GetWebAppImages(url, &images)); - ASSERT_EQ(0U, images.size()); - - // Add an image. - SkBitmap image; - image.setConfig(SkBitmap::kARGB_8888_Config, 16, 16); - image.allocPixels(); - image.eraseColor(SK_ColorBLACK); - ASSERT_TRUE(db.SetWebAppImage(url, image)); - - // Make sure we get the image back. - ASSERT_TRUE(db.GetWebAppImages(url, &images)); - ASSERT_EQ(1U, images.size()); - ASSERT_EQ(16, images[0].width()); - ASSERT_EQ(16, images[0].height()); - - // Add another 16x16 image and make sure it replaces the original. - image.setConfig(SkBitmap::kARGB_8888_Config, 16, 16); - image.allocPixels(); - image.eraseColor(SK_ColorBLACK); - - // Set some random pixels so that we can identify the image. We don't use - // transparent images because of pre-multiplication rounding errors. - SkColor test_pixel_1 = 0xffccbbaa; - SkColor test_pixel_2 = 0xffaabbaa; - SkColor test_pixel_3 = 0xff339966; - image.getAddr32(0, 1)[0] = test_pixel_1; - image.getAddr32(0, 1)[1] = test_pixel_2; - image.getAddr32(0, 1)[2] = test_pixel_3; - - ASSERT_TRUE(db.SetWebAppImage(url, image)); - images.clear(); - ASSERT_TRUE(db.GetWebAppImages(url, &images)); - ASSERT_EQ(1U, images.size()); - ASSERT_EQ(16, images[0].width()); - ASSERT_EQ(16, images[0].height()); - images[0].lockPixels(); - ASSERT_TRUE(images[0].getPixels() != NULL); - ASSERT_EQ(test_pixel_1, images[0].getAddr32(0, 1)[0]); - ASSERT_EQ(test_pixel_2, images[0].getAddr32(0, 1)[1]); - ASSERT_EQ(test_pixel_3, images[0].getAddr32(0, 1)[2]); - images[0].unlockPixels(); - - // Add another image at a bigger size. - image.setConfig(SkBitmap::kARGB_8888_Config, 32, 32); - image.allocPixels(); - image.eraseColor(SK_ColorBLACK); - ASSERT_TRUE(db.SetWebAppImage(url, image)); - - // Make sure we get both images back. - images.clear(); - ASSERT_TRUE(db.GetWebAppImages(url, &images)); - ASSERT_EQ(2U, images.size()); - if (images[0].width() == 16) { - ASSERT_EQ(16, images[0].width()); - ASSERT_EQ(16, images[0].height()); - ASSERT_EQ(32, images[1].width()); - ASSERT_EQ(32, images[1].height()); - } else { - ASSERT_EQ(32, images[0].width()); - ASSERT_EQ(32, images[0].height()); - ASSERT_EQ(16, images[1].width()); - ASSERT_EQ(16, images[1].height()); - } -} - -TEST_F(WebDatabaseTest, TokenServiceGetAllRemoveAll) { - WebDatabase db; - ASSERT_EQ(sql::INIT_OK, db.Init(file_)); - - std::map<std::string, std::string> out_map; - std::string service; - std::string service2; - service = "testservice"; - service2 = "othertestservice"; - - EXPECT_TRUE(db.GetAllTokens(&out_map)); - EXPECT_TRUE(out_map.empty()); - - // Check that get all tokens works - EXPECT_TRUE(db.SetTokenForService(service, "pepperoni")); - EXPECT_TRUE(db.SetTokenForService(service2, "steak")); - EXPECT_TRUE(db.GetAllTokens(&out_map)); - EXPECT_EQ(out_map.find(service)->second, "pepperoni"); - EXPECT_EQ(out_map.find(service2)->second, "steak"); - out_map.clear(); - - // Purge - EXPECT_TRUE(db.RemoveAllTokens()); - EXPECT_TRUE(db.GetAllTokens(&out_map)); - EXPECT_TRUE(out_map.empty()); - - // Check that you can still add it back in - EXPECT_TRUE(db.SetTokenForService(service, "cheese")); - EXPECT_TRUE(db.GetAllTokens(&out_map)); - EXPECT_EQ(out_map.find(service)->second, "cheese"); -} - -TEST_F(WebDatabaseTest, TokenServiceGetSet) { - WebDatabase db; - ASSERT_EQ(sql::INIT_OK, db.Init(file_)); - - std::map<std::string, std::string> out_map; - std::string service; - service = "testservice"; - - EXPECT_TRUE(db.GetAllTokens(&out_map)); - EXPECT_TRUE(out_map.empty()); - - EXPECT_TRUE(db.SetTokenForService(service, "pepperoni")); - EXPECT_TRUE(db.GetAllTokens(&out_map)); - EXPECT_EQ(out_map.find(service)->second, "pepperoni"); - out_map.clear(); - - // try blanking it - won't remove it from the db though! - EXPECT_TRUE(db.SetTokenForService(service, "")); - EXPECT_TRUE(db.GetAllTokens(&out_map)); - EXPECT_EQ(out_map.find(service)->second, ""); - out_map.clear(); - - // try mutating it - EXPECT_TRUE(db.SetTokenForService(service, "ham")); - EXPECT_TRUE(db.GetAllTokens(&out_map)); - EXPECT_EQ(out_map.find(service)->second, "ham"); -} - // The WebDatabaseMigrationTest encapsulates testing of database migrations. // Specifically, these tests are intended to exercise any schema changes in // the WebDatabase and data migrations that occur in diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi index 73b3708..3d5a560 100644 --- a/chrome/chrome_browser.gypi +++ b/chrome/chrome_browser.gypi @@ -3314,12 +3314,18 @@ 'browser/webdata/autofill_util.h', 'browser/webdata/keyword_table.cc', 'browser/webdata/keyword_table.h', + 'browser/webdata/logins_table.cc', + 'browser/webdata/logins_table.h', + 'browser/webdata/logins_table_win.cc', + 'browser/webdata/token_service_table.cc', + 'browser/webdata/token_service_table.h', + 'browser/webdata/web_apps_table.cc', + 'browser/webdata/web_apps_table.h', 'browser/webdata/web_data_service.cc', 'browser/webdata/web_data_service.h', 'browser/webdata/web_data_service_win.cc', 'browser/webdata/web_database.cc', 'browser/webdata/web_database.h', - 'browser/webdata/web_database_win.cc', 'browser/webdata/web_database_table.h', # These files are generated by GRIT. diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi index fd7c836..aa4ef48 100644 --- a/chrome/chrome_tests.gypi +++ b/chrome/chrome_tests.gypi @@ -1755,9 +1755,12 @@ 'browser/web_applications/web_app_unittest.cc', 'browser/webdata/autofill_table_unittest.cc', 'browser/webdata/keyword_table_unittest.cc', + 'browser/webdata/logins_table_unittest.cc', + 'browser/webdata/token_service_table_unittest.cc', + 'browser/webdata/web_apps_table_unittest.cc', 'browser/webdata/web_data_service_test_util.h', 'browser/webdata/web_data_service_unittest.cc', - 'browser/webdata/web_database_unittest.cc', + 'browser/webdata/web_database_migration_unittest.cc', 'browser/web_resource/promo_resource_service_unittest.cc', 'common/bzip2_unittest.cc', 'common/child_process_logging_mac_unittest.mm', |