diff options
author | georgey@chromium.org <georgey@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-10-12 02:36:46 +0000 |
---|---|---|
committer | georgey@chromium.org <georgey@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-10-12 02:36:46 +0000 |
commit | f69c8856f8de9aeb109363da9eca25f781323ff8 (patch) | |
tree | 699413c984eb7aaa4baa0f0afedf918017435e8a /chrome/browser/autofill | |
parent | 005bb5f499ad3282ffb6ad7d14167e1801796ef0 (diff) | |
download | chromium_src-f69c8856f8de9aeb109363da9eca25f781323ff8.zip chromium_src-f69c8856f8de9aeb109363da9eca25f781323ff8.tar.gz chromium_src-f69c8856f8de9aeb109363da9eca25f781323ff8.tar.bz2 |
Import locally saved IE Tolbar Autofill data
BUG=49084
TEST=Should automatically import the data on fresh install of Chrome.
Review URL: http://codereview.chromium.org/3367016
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@62232 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/autofill')
3 files changed, 494 insertions, 0 deletions
diff --git a/chrome/browser/autofill/autofill_ie_toolbar_import_win.cc b/chrome/browser/autofill/autofill_ie_toolbar_import_win.cc new file mode 100644 index 0000000..de151e3 --- /dev/null +++ b/chrome/browser/autofill/autofill_ie_toolbar_import_win.cc @@ -0,0 +1,240 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/autofill/autofill_ie_toolbar_import_win.h" + +#include "base/basictypes.h" +#include "base/registry.h" +#include "base/string16.h" +#include "chrome/browser/autofill/autofill_profile.h" +#include "chrome/browser/autofill/credit_card.h" +#include "chrome/browser/autofill/field_types.h" +#include "chrome/browser/autofill/personal_data_manager.h" +#include "chrome/browser/sync/util/data_encryption.h" + +// Forward declaration. This function is not in unnamed namespace as it +// is referenced in the unittest. +bool ImportCurrentUserProfiles(std::vector<AutoFillProfile>* profiles, + std::vector<CreditCard>* credit_cards); +namespace { + +#if defined(GOOGLE_CHROME_BUILD) +#include "chrome/browser/autofill/internal/autofill_ie_toolbar_decryption.h" +#else // defined(GOOGLE_CHROME_BUILD) +inline std::wstring DecryptCCNumber(const std::wstring& data) { + return std::wstring(); +} +inline bool IsEmptySalt(const std::wstring& salt) { + return false; +} +#endif // defined(GOOGLE_CHROME_BUILD) + +const wchar_t* const kProfileKey = + L"Software\\Google\\Google Toolbar\\4.0\\Autofill\\Profiles"; +const wchar_t* const kCreditCardKey = + L"Software\\Google\\Google Toolbar\\4.0\\Autofill\\Credit Cards"; +const wchar_t* const kPasswordHashValue = L"password_hash"; +const wchar_t* const kSaltValue = L"salt"; + +string16 ReadAndDecryptValue(RegKey* key, const wchar_t* value_name) { + DWORD data_type = REG_BINARY; + DWORD data_size = 0; + if (!key->ReadValue(value_name, NULL, &data_size, &data_type) || + !data_size || data_type != REG_BINARY) + return string16(); + std::vector<uint8> data; + data.resize(data_size); + if (key->ReadValue(value_name, &(data[0]), &data_size, &data_type)) { + std::string out_data; + if (DecryptData(data, &out_data)) { + // The actual data is in UTF16 already. + if (!(out_data.size() & 1) && (out_data.size() > 2) && + !out_data[out_data.size() - 1] && !out_data[out_data.size() - 2]) { + return string16( + reinterpret_cast<const wchar_t *>(out_data.c_str())); + } + } + } + return string16(); +} + +struct { + AutoFillFieldType field_type; + const wchar_t *reg_value_name; +} profile_reg_values[] = { + { NAME_FIRST, L"name_first" }, + { NAME_MIDDLE, L"name_middle" }, + { NAME_LAST, L"name_last" }, + { NAME_SUFFIX, L"name_suffix" }, + { EMAIL_ADDRESS, L"email" }, + { COMPANY_NAME, L"company_name" }, + { PHONE_HOME_NUMBER, L"phone_home_number" }, + { PHONE_HOME_CITY_CODE, L"phone_home_city_code" }, + { PHONE_HOME_COUNTRY_CODE, L"phone_home_country_code" }, + { PHONE_FAX_NUMBER, L"phone_fax_number" }, + { PHONE_FAX_CITY_CODE, L"phone_fax_city_code" }, + { PHONE_FAX_COUNTRY_CODE, L"phone_fax_country_code" }, + { ADDRESS_HOME_LINE1, L"address_home_line1" }, + { ADDRESS_HOME_LINE2, L"address_home_line2" }, + { ADDRESS_HOME_CITY, L"address_home_city" }, + { ADDRESS_HOME_STATE, L"address_home_state" }, + { ADDRESS_HOME_ZIP, L"address_home_zip" }, + { ADDRESS_HOME_COUNTRY, L"address_home_country" }, + { ADDRESS_BILLING_LINE1, L"address_billing_line1" }, + { ADDRESS_BILLING_LINE2, L"address_billing_line2" }, + { ADDRESS_BILLING_CITY, L"address_billing_city" }, + { ADDRESS_BILLING_STATE, L"address_billing_state" }, + { ADDRESS_BILLING_ZIP, L"address_billing_zip" }, + { ADDRESS_BILLING_COUNTRY, L"address_billing_country" }, + { CREDIT_CARD_NAME, L"credit_card_name" }, + { CREDIT_CARD_NUMBER, L"credit_card_number" }, + { CREDIT_CARD_EXP_MONTH, L"credit_card_exp_month" }, + { CREDIT_CARD_EXP_4_DIGIT_YEAR, L"credit_card_exp_4_digit_year" }, + { CREDIT_CARD_TYPE, L"credit_card_type" }, + // We do not import verification code. +}; + +typedef std::map<std::wstring, AutoFillFieldType> RegToFieldMap; + +bool ImportSingleProfile(FormGroup* profile, + RegKey* key, + const RegToFieldMap& reg_to_field ) { + DCHECK(profile != NULL); + if (!key->Valid()) + return false; + + bool has_non_empty_fields = false; + + for (uint32 value_index = 0; value_index < key->ValueCount(); ++value_index) { + std::wstring value_name; + if (!key->ReadName(value_index, &value_name)) + continue; + RegToFieldMap::const_iterator it = reg_to_field.find(value_name); + if (it == reg_to_field.end()) + continue; // This field is not imported. + string16 field_value = ReadAndDecryptValue(key, value_name.c_str()); + if (!field_value.empty()) { + has_non_empty_fields = true; + profile->SetInfo(AutoFillType(it->second), field_value); + } + } + return has_non_empty_fields; +} + +// Imports profiles from the IE toolbar and stores them. Asynchronous +// if PersonalDataManager has not been loaded yet. Deletes itself on completion. +class AutoFillImporter : public PersonalDataManager::Observer { + public: + explicit AutoFillImporter(PersonalDataManager* personal_data_manager) + : personal_data_manager_(personal_data_manager) { + personal_data_manager_->SetObserver(this); + } + + bool ImportProfiles() { + if (!ImportCurrentUserProfiles(&profiles_, &credit_cards_)) { + delete this; + return false; + } + if (personal_data_manager_->IsDataLoaded()) + OnPersonalDataLoaded(); + return true; + } + + // PersonalDataManager::Observer methods: + virtual void OnPersonalDataLoaded() { + if (!profiles_.empty()) + personal_data_manager_->SetProfiles(&profiles_); + if (!credit_cards_.empty()) + personal_data_manager_->SetCreditCards(&credit_cards_); + delete this; + } + + private: + ~AutoFillImporter() { + personal_data_manager_->RemoveObserver(this); + } + + PersonalDataManager* personal_data_manager_; + std::vector<AutoFillProfile> profiles_; + std::vector<CreditCard> credit_cards_; +}; + +} // namespace + +// Imports AutoFill profiles and credit cards from IE Toolbar if present and not +// password protected. Returns true if data is successfully retrieved. False if +// there is no data, data is password protected or error occurred. +bool ImportCurrentUserProfiles(std::vector<AutoFillProfile>* profiles, + std::vector<CreditCard>* credit_cards) { + DCHECK(profiles); + DCHECK(credit_cards); + + // Create a map of possible fields for a quick access. + RegToFieldMap reg_to_field; + for (size_t i = 0; i < arraysize(profile_reg_values); ++i) { + reg_to_field[std::wstring(profile_reg_values[i].reg_value_name)] = + profile_reg_values[i].field_type; + } + + RegistryKeyIterator iterator_profiles(HKEY_CURRENT_USER, kProfileKey); + for (; iterator_profiles.Valid(); ++iterator_profiles) { + std::wstring key_name(kProfileKey); + key_name.append(L"\\"); + key_name.append(iterator_profiles.Name()); + RegKey key(HKEY_CURRENT_USER, key_name.c_str(), KEY_READ); + AutoFillProfile profile; + if (ImportSingleProfile(&profile, &key, reg_to_field)) { + // Combine phones into whole phone #. + string16 phone; + phone = profile.GetFieldText(AutoFillType(PHONE_HOME_COUNTRY_CODE)); + phone.append(profile.GetFieldText(AutoFillType(PHONE_HOME_CITY_CODE))); + phone.append(profile.GetFieldText(AutoFillType(PHONE_HOME_NUMBER))); + profile.SetInfo(AutoFillType(PHONE_HOME_WHOLE_NUMBER), phone); + phone = profile.GetFieldText(AutoFillType(PHONE_FAX_COUNTRY_CODE)); + phone.append(profile.GetFieldText(AutoFillType(PHONE_FAX_CITY_CODE))); + phone.append(profile.GetFieldText(AutoFillType(PHONE_FAX_NUMBER))); + profile.SetInfo(AutoFillType(PHONE_FAX_WHOLE_NUMBER), phone); + profiles->push_back(profile); + } + } + string16 password_hash; + string16 salt; + RegKey cc_key(HKEY_CURRENT_USER, kCreditCardKey, KEY_READ); + if (cc_key.Valid()) { + password_hash = ReadAndDecryptValue(&cc_key, kPasswordHashValue); + salt = ReadAndDecryptValue(&cc_key, kSaltValue); + } + + // We import CC profiles only if they are not password protected. + if (password_hash.empty() && IsEmptySalt(salt)) { + RegistryKeyIterator iterator_cc(HKEY_CURRENT_USER, kCreditCardKey); + for (; iterator_cc.Valid(); ++iterator_cc) { + std::wstring key_name(kCreditCardKey); + key_name.append(L"\\"); + key_name.append(iterator_cc.Name()); + RegKey key(HKEY_CURRENT_USER, key_name.c_str(), KEY_READ); + CreditCard credit_card; + if (ImportSingleProfile(&credit_card, &key, reg_to_field)) { + string16 cc_number = credit_card.GetFieldText( + AutoFillType(CREDIT_CARD_NUMBER)); + + if (!cc_number.empty()) { + // No additional password, and CC# is not empty, decrypt CC#. + cc_number = DecryptCCNumber(cc_number); + } + credit_card.SetInfo(AutoFillType(CREDIT_CARD_NUMBER), cc_number); + if (!cc_number.empty()) + credit_cards->push_back(credit_card); + } + } + } + return (profiles->size() + credit_cards->size()) > 0; +} + +bool ImportAutofillDataWin(PersonalDataManager* pdm) { + AutoFillImporter *importer = new AutoFillImporter(pdm); + // importer will self delete. + return importer->ImportProfiles(); +} + diff --git a/chrome/browser/autofill/autofill_ie_toolbar_import_win.h b/chrome/browser/autofill/autofill_ie_toolbar_import_win.h new file mode 100644 index 0000000..b38da9c --- /dev/null +++ b/chrome/browser/autofill/autofill_ie_toolbar_import_win.h @@ -0,0 +1,23 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_AUTOFILL_AUTOFILL_IE_TOOLBAR_IMPORT_WIN_H_ +#define CHROME_BROWSER_AUTOFILL_AUTOFILL_IE_TOOLBAR_IMPORT_WIN_H_ + +#include <vector> + +// This importer is here and not in chrome/browser/importer/toolbar_importer.cc +// because of the following: +// 1. The data is not saved in profile, but rather in registry, thus it is +// accessed without going through toolbar front end. +// 2. This applies to IE (thus Windows) toolbar only. +// 3. The functionality relevant only to and completely encapsulated in the +// autofill. +// 4. This is completely automated as opposed to Importers, which are explicit. +class PersonalDataManager; + +bool ImportAutofillDataWin(PersonalDataManager* pdm); + +#endif // CHROME_BROWSER_AUTOFILL_AUTOFILL_IE_TOOLBAR_IMPORT_WIN_H_ + diff --git a/chrome/browser/autofill/autofill_ie_toolbar_import_win_unittest.cc b/chrome/browser/autofill/autofill_ie_toolbar_import_win_unittest.cc new file mode 100644 index 0000000..9aa7649 --- /dev/null +++ b/chrome/browser/autofill/autofill_ie_toolbar_import_win_unittest.cc @@ -0,0 +1,231 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/autofill/autofill_ie_toolbar_import_win.h" + +#include "base/basictypes.h" +#include "base/registry.h" +#include "base/string16.h" +#include "chrome/browser/autofill/autofill_profile.h" +#include "chrome/browser/autofill/credit_card.h" +#include "chrome/browser/autofill/field_types.h" +#include "chrome/browser/sync/util/data_encryption.h" +#include "testing/gtest/include/gtest/gtest.h" + +// Defined in autofill_ie_toolbar_import_win.cc. Not exposed in the header file. +bool ImportCurrentUserProfiles(std::vector<AutoFillProfile>* profiles, + std::vector<CreditCard>* credit_cards); + +namespace { + +const wchar_t kUnitTestRegistrySubKey[] = L"SOFTWARE\\Chromium Unit Tests"; +const wchar_t kUnitTestUserOverrideSubKey[] = + L"SOFTWARE\\Chromium Unit Tests\\HKCU Override"; + +const wchar_t* const kProfileKey = + L"Software\\Google\\Google Toolbar\\4.0\\Autofill\\Profiles"; +const wchar_t* const kCreditCardKey = + L"Software\\Google\\Google Toolbar\\4.0\\Autofill\\Credit Cards"; +const wchar_t* const kPasswordHashValue = L"password_hash"; +const wchar_t* const kSaltValue = L"salt"; + +struct ValueDescription { + wchar_t const* const value_name; + wchar_t const* const value; +}; + +ValueDescription profile1[] = { + { L"name_first", L"John" }, + { L"name_middle", L"Herman" }, + { L"name_last", L"Doe" }, + { L"email", L"jdoe@test.com" }, + { L"company_name", L"Testcompany" }, + { L"phone_home_number", L"555-5555" }, + { L"phone_home_city_code", L"444" }, + { L"phone_home_country_code", L"1" }, +}; + +ValueDescription profile2[] = { + { L"name_first", L"Jane" }, + { L"name_last", L"Doe" }, + { L"email", L"janedoe@test.com" }, + { L"company_name", L"Testcompany" }, + { L"phone_fax_number", L"555-6666" }, + { L"phone_fax_city_code", L"777" }, + { L"phone_fax_country_code", L"2" }, +}; + +ValueDescription credit_card[] = { + { L"credit_card_name", L"Tommy Gun" }, + // "12345790087665675" encrypted: + { L"credit_card_number", L"\x18B7\xE586\x459B\x7457\xA066\x3842\x71DA" + L"\x4854\xB906\x9C7C\x50A6\x4376\xFD9D\x1E02" + L"\x790A\x7330\xB77B\xAF32\x93EB\xB84F\xEC8F" + L"\x265B\xD0E1\x4E27\xB758\x7985\xB92F" }, + { L"credit_card_exp_month", L"11" }, + { L"credit_card_exp_4_digit_year", L"2011" }, +}; + +ValueDescription empty_salt = { + L"salt", L"\x1\x2\x3\x4\x5\x6\x7\x8\x9\xA\xB\xC\xD\xE\xF\x10\x11\x12\x13\x14" +}; + +ValueDescription empty_password = { + L"password_hash", L"" +}; + +ValueDescription protected_salt = { + L"salt", L"\x4854\xB906\x9C7C\x50A6\x4376\xFD9D\x1E02" +}; + +ValueDescription protected_password = { + L"password_hash", L"\x18B7\xE586\x459B\x7457\xA066\x3842\x71DA" +}; + +void EncryptAndWrite(RegKey* key, const ValueDescription* value) { + string data; + size_t data_size = (lstrlen(value->value) + 1) * sizeof(wchar_t); + data.resize(data_size); + memcpy(&data[0], value->value, data_size); + + std::vector<uint8> encrypted_data = EncryptData(data); + EXPECT_TRUE(key->WriteValue(value->value_name, &encrypted_data[0], + encrypted_data.size(), REG_BINARY)); +} + +void CreateSubkey(RegKey* key, wchar_t const* subkey_name, + const ValueDescription* values, size_t values_size) { + RegKey subkey; + subkey.Create(key->Handle(), subkey_name, KEY_ALL_ACCESS); + EXPECT_TRUE(subkey.Valid()); + for (size_t i = 0; i < values_size; ++i) + EncryptAndWrite(&subkey, values + i); +} + +} // namespace + +class AutofillIeToolbarImportTest : public testing::Test { + public: + AutofillIeToolbarImportTest(); + + // testing::Test method overrides: + virtual void SetUp(); + virtual void TearDown(); + + private: + RegKey temp_hkcu_hive_key_; + + DISALLOW_COPY_AND_ASSIGN(AutofillIeToolbarImportTest); +}; + +AutofillIeToolbarImportTest::AutofillIeToolbarImportTest() { +} + +void AutofillIeToolbarImportTest::SetUp() { + temp_hkcu_hive_key_.Create(HKEY_CURRENT_USER, + kUnitTestUserOverrideSubKey, + KEY_ALL_ACCESS); + EXPECT_TRUE(temp_hkcu_hive_key_.Valid()); + EXPECT_EQ(ERROR_SUCCESS, RegOverridePredefKey(HKEY_CURRENT_USER, + temp_hkcu_hive_key_.Handle())); +} + +void AutofillIeToolbarImportTest::TearDown() { + EXPECT_EQ(ERROR_SUCCESS, RegOverridePredefKey(HKEY_CURRENT_USER, NULL)); + temp_hkcu_hive_key_.Close(); + RegKey key(HKEY_CURRENT_USER, kUnitTestRegistrySubKey, KEY_ALL_ACCESS); + key.DeleteKey(L""); +} + +TEST_F(AutofillIeToolbarImportTest, TestAutoFillImport) { + RegKey profile_key; + profile_key.Create(HKEY_CURRENT_USER, kProfileKey, KEY_ALL_ACCESS); + EXPECT_TRUE(profile_key.Valid()); + + CreateSubkey(&profile_key, L"0", profile1, arraysize(profile1)); + CreateSubkey(&profile_key, L"1", profile2, arraysize(profile2)); + + RegKey cc_key; + cc_key.Create(HKEY_CURRENT_USER, kCreditCardKey, KEY_ALL_ACCESS); + EXPECT_TRUE(cc_key.Valid()); + CreateSubkey(&cc_key, L"0", credit_card, arraysize(credit_card)); + EncryptAndWrite(&cc_key, &empty_password); + EncryptAndWrite(&cc_key, &empty_salt); + + profile_key.Close(); + cc_key.Close(); + + std::vector<AutoFillProfile> profiles; + std::vector<CreditCard> credit_cards; + EXPECT_TRUE(ImportCurrentUserProfiles(&profiles, &credit_cards)); + ASSERT_EQ(profiles.size(), 2); + // The profiles are read in reverse order. + EXPECT_EQ(profiles[1].GetFieldText(AutoFillType(NAME_FIRST)), + profile1[0].value); + EXPECT_EQ(profiles[1].GetFieldText(AutoFillType(NAME_MIDDLE)), + profile1[1].value); + EXPECT_EQ(profiles[1].GetFieldText(AutoFillType(NAME_LAST)), + profile1[2].value); + EXPECT_EQ(profiles[1].GetFieldText(AutoFillType(EMAIL_ADDRESS)), + profile1[3].value); + EXPECT_EQ(profiles[1].GetFieldText(AutoFillType(COMPANY_NAME)), + profile1[4].value); + EXPECT_EQ(profiles[1].GetFieldText(AutoFillType(PHONE_HOME_COUNTRY_CODE)), + profile1[7].value); + EXPECT_EQ(profiles[1].GetFieldText(AutoFillType(PHONE_HOME_CITY_CODE)), + profile1[6].value); + EXPECT_EQ(profiles[1].GetFieldText(AutoFillType(PHONE_HOME_NUMBER)), + L"5555555"); + EXPECT_EQ(profiles[1].GetFieldText(AutoFillType(PHONE_HOME_WHOLE_NUMBER)), + L"14445555555"); + + EXPECT_EQ(profiles[0].GetFieldText(AutoFillType(NAME_FIRST)), + profile2[0].value); + EXPECT_EQ(profiles[0].GetFieldText(AutoFillType(NAME_LAST)), + profile2[1].value); + EXPECT_EQ(profiles[0].GetFieldText(AutoFillType(EMAIL_ADDRESS)), + profile2[2].value); + EXPECT_EQ(profiles[0].GetFieldText(AutoFillType(COMPANY_NAME)), + profile2[3].value); + EXPECT_EQ(profiles[0].GetFieldText(AutoFillType(PHONE_FAX_COUNTRY_CODE)), + profile2[6].value); + EXPECT_EQ(profiles[0].GetFieldText(AutoFillType(PHONE_FAX_CITY_CODE)), + profile2[5].value); + EXPECT_EQ(profiles[0].GetFieldText(AutoFillType(PHONE_FAX_NUMBER)), + L"5556666"); + EXPECT_EQ(profiles[0].GetFieldText(AutoFillType(PHONE_FAX_WHOLE_NUMBER)), + L"27775556666"); +#if defined(GOOGLE_CHROME_BUILD) + // We have the ability to export credit cards only in chrome build. + ASSERT_EQ(credit_cards.size(), 1); + EXPECT_EQ(credit_cards[0].GetFieldText(AutoFillType(CREDIT_CARD_NAME)), + credit_card[0].value); + EXPECT_EQ(credit_cards[0].GetFieldText(AutoFillType(CREDIT_CARD_NUMBER)), + L"12345790087665675"); + EXPECT_EQ(credit_cards[0].GetFieldText(AutoFillType(CREDIT_CARD_EXP_MONTH)), + credit_card[2].value); + EXPECT_EQ(credit_cards[0].GetFieldText( + AutoFillType(CREDIT_CARD_EXP_4_DIGIT_YEAR)), + credit_card[3].value); + + // Mock password encrypted cc. + cc_key.Open(HKEY_CURRENT_USER, kCreditCardKey, KEY_ALL_ACCESS); + EXPECT_TRUE(cc_key.Valid()); + EncryptAndWrite(&cc_key, &protected_password); + EncryptAndWrite(&cc_key, &protected_salt); + cc_key.Close(); + + profiles.clear(); + credit_cards.clear(); + EXPECT_TRUE(ImportCurrentUserProfiles(&profiles, &credit_cards)); + // Profiles are not protected. + EXPECT_EQ(profiles.size(), 2); + // Credit cards are. + EXPECT_EQ(credit_cards.size(), 0); +#else // defined(GOOGLE_CHROME_BUILD) + // Cannot decrypt CC in non-chrome build. + EXPECT_EQ(credit_cards.size(), 0); +#endif // defined(GOOGLE_CHROME_BUILD) +} + |