// Copyright (c) 2012 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 "components/autofill/browser/wallet/wallet_items.h" #include "base/logging.h" #include "base/strings/string_number_conversions.h" #include "base/strings/utf_string_conversions.h" #include "base/values.h" #include "components/autofill/browser/autofill_type.h" #include "components/autofill/browser/credit_card.h" #include "googleurl/src/gurl.h" #include "grit/component_resources.h" #include "grit/webkit_resources.h" #include "ui/base/l10n/l10n_util.h" #include "ui/base/resource/resource_bundle.h" #include "ui/gfx/image/image.h" namespace autofill { namespace wallet { namespace { const char kLegalDocumentUrl[] = "https://wallet.google.com/legaldocument?docId="; const char kPrivacyNoticeUrl[] = "https://wallet.google.com/files/privacy.html"; // TODO(estade): move to base/. template bool VectorsAreEqual(const std::vector& a, const std::vector& b) { if (a.size() != b.size()) return false; for (size_t i = 0; i < a.size(); ++i) { if (*a[i] != *b[i]) return false; } return true; } WalletItems::MaskedInstrument::Type TypeFromString(const std::string& type_string) { if (type_string == "VISA") return WalletItems::MaskedInstrument::VISA; if (type_string == "MASTER_CARD") return WalletItems::MaskedInstrument::MASTER_CARD; if (type_string == "AMEX") return WalletItems::MaskedInstrument::AMEX; if (type_string == "DISCOVER") return WalletItems::MaskedInstrument::DISCOVER; if (type_string == "SOLO") return WalletItems::MaskedInstrument::SOLO; if (type_string == "MAESTRO") return WalletItems::MaskedInstrument::MAESTRO; if (type_string == "SWITCH") return WalletItems::MaskedInstrument::SWITCH; return WalletItems::MaskedInstrument::UNKNOWN; } WalletItems::MaskedInstrument::Status StatusFromString(const std::string& status_string) { if (status_string == "AMEX_NOT_SUPPORTED") return WalletItems::MaskedInstrument::AMEX_NOT_SUPPORTED; if (status_string == "PENDING") return WalletItems::MaskedInstrument::PENDING; if (status_string == "VALID") return WalletItems::MaskedInstrument::VALID; if (status_string == "DECLINED") return WalletItems::MaskedInstrument::DECLINED; if (status_string == "DISABLED_FOR_THIS_MERCHANT") return WalletItems::MaskedInstrument::DISABLED_FOR_THIS_MERCHANT; if (status_string == "UNSUPPORTED_COUNTRY") return WalletItems::MaskedInstrument::UNSUPPORTED_COUNTRY; if (status_string == "EXPIRED") return WalletItems::MaskedInstrument::EXPIRED; if (status_string == "BILLING_INCOMPLETE") return WalletItems::MaskedInstrument::BILLING_INCOMPLETE; return WalletItems::MaskedInstrument::INAPPLICABLE; } } // anonymous namespace WalletItems::MaskedInstrument::MaskedInstrument( const base::string16& descriptive_name, const WalletItems::MaskedInstrument::Type& type, const std::vector& supported_currencies, const base::string16& last_four_digits, int expiration_month, int expiration_year, scoped_ptr
address, const WalletItems::MaskedInstrument::Status& status, const std::string& object_id) : descriptive_name_(descriptive_name), type_(type), supported_currencies_(supported_currencies), last_four_digits_(last_four_digits), expiration_month_(expiration_month), expiration_year_(expiration_year), address_(address.Pass()), status_(status), object_id_(object_id) { DCHECK(address_.get()); } WalletItems::MaskedInstrument::~MaskedInstrument() {} scoped_ptr WalletItems::MaskedInstrument::CreateMaskedInstrument( const base::DictionaryValue& dictionary) { std::string type_string; Type type; if (dictionary.GetString("type", &type_string)) { type = TypeFromString(type_string); } else { DLOG(ERROR) << "Response from Google Wallet missing card type"; return scoped_ptr(); } base::string16 last_four_digits; if (!dictionary.GetString("last_four_digits", &last_four_digits)) { DLOG(ERROR) << "Response from Google Wallet missing last four digits"; return scoped_ptr(); } std::string status_string; Status status; if (dictionary.GetString("status", &status_string)) { status = StatusFromString(status_string); } else { DLOG(ERROR) << "Response from Google Wallet missing status"; return scoped_ptr(); } std::string object_id; if (!dictionary.GetString("object_id", &object_id)) { DLOG(ERROR) << "Response from Google Wallet missing object id"; return scoped_ptr(); } const DictionaryValue* address_dict; if (!dictionary.GetDictionary("billing_address", &address_dict)) { DLOG(ERROR) << "Response from Google wallet missing address"; return scoped_ptr(); } scoped_ptr
address = Address::CreateDisplayAddress(*address_dict); if (!address.get()) { DLOG(ERROR) << "Response from Google wallet contained malformed address"; return scoped_ptr(); } std::vector supported_currencies; const ListValue* supported_currency_list; if (dictionary.GetList("supported_currency", &supported_currency_list)) { for (size_t i = 0; i < supported_currency_list->GetSize(); ++i) { base::string16 currency; if (supported_currency_list->GetString(i, ¤cy)) supported_currencies.push_back(currency); } } else { DVLOG(1) << "Response from Google Wallet missing supported currency"; } int expiration_month; if (!dictionary.GetInteger("expiration_month", &expiration_month)) DVLOG(1) << "Response from Google Wallet missing expiration month"; int expiration_year; if (!dictionary.GetInteger("expiration_year", &expiration_year)) DVLOG(1) << "Response from Google Wallet missing expiration year"; base::string16 descriptive_name; if (!dictionary.GetString("descriptive_name", &descriptive_name)) DVLOG(1) << "Response from Google Wallet missing descriptive name"; return scoped_ptr(new MaskedInstrument(descriptive_name, type, supported_currencies, last_four_digits, expiration_month, expiration_year, address.Pass(), status, object_id)); } bool WalletItems::MaskedInstrument::operator==( const WalletItems::MaskedInstrument& other) const { if (descriptive_name_ != other.descriptive_name_) return false; if (type_ != other.type_) return false; if (supported_currencies_ != other.supported_currencies_) return false; if (last_four_digits_ != other.last_four_digits_) return false; if (expiration_month_ != other.expiration_month_) return false; if (expiration_year_ != other.expiration_year_) return false; if (address_.get()) { if (other.address_.get()) { if (*address_.get() != *other.address_.get()) return false; } else { return false; } } else if (other.address_.get()) { return false; } if (status_ != other.status_) return false; if (object_id_ != other.object_id_) return false; return true; } bool WalletItems::MaskedInstrument::operator!=( const WalletItems::MaskedInstrument& other) const { return !(*this == other); } const WalletItems::MaskedInstrument* WalletItems::GetInstrumentById( const std::string& object_id) const { if (object_id.empty()) return NULL; for (size_t i = 0; i < instruments_.size(); ++i) { if (instruments_[i]->object_id() == object_id) return instruments_[i]; } return NULL; } bool WalletItems::HasRequiredAction(RequiredAction action) const { DCHECK(ActionAppliesToWalletItems(action)); return std::find(required_actions_.begin(), required_actions_.end(), action) != required_actions_.end(); } base::string16 WalletItems::MaskedInstrument::DisplayName() const { #if defined(OS_ANDROID) // TODO(aruslan): improve this stub implementation. return descriptive_name(); #else return descriptive_name(); #endif } base::string16 WalletItems::MaskedInstrument::DisplayNameDetail() const { #if defined(OS_ANDROID) // TODO(aruslan): improve this stub implementation. return address().DisplayName(); #else return base::string16(); #endif } base::string16 WalletItems::MaskedInstrument::TypeAndLastFourDigits() const { base::string16 display_type; if (type_ == AMEX) display_type = CreditCard::TypeForDisplay(kAmericanExpressCard); else if (type_ == DISCOVER) display_type = CreditCard::TypeForDisplay(kDiscoverCard); else if (type_ == MASTER_CARD) display_type = CreditCard::TypeForDisplay(kMasterCard); else if (type_ == SOLO) display_type = CreditCard::TypeForDisplay(kSoloCard); else if (type_ == VISA) display_type = CreditCard::TypeForDisplay(kVisaCard); else display_type = CreditCard::TypeForDisplay(kGenericCard); // TODO(dbeam): i18n. return display_type + ASCIIToUTF16(" - ") + last_four_digits(); } const gfx::Image& WalletItems::MaskedInstrument::CardIcon() const { int idr = 0; switch (type_) { case AMEX: idr = IDR_AUTOFILL_CC_AMEX; break; case DISCOVER: idr = IDR_AUTOFILL_CC_DISCOVER; break; case MASTER_CARD: idr = IDR_AUTOFILL_CC_MASTERCARD; break; case SOLO: idr = IDR_AUTOFILL_CC_SOLO; break; case VISA: idr = IDR_AUTOFILL_CC_VISA; break; case MAESTRO: case SWITCH: case UNKNOWN: idr = IDR_AUTOFILL_CC_GENERIC; break; } return ResourceBundle::GetSharedInstance().GetImageNamed(idr); } base::string16 WalletItems::MaskedInstrument::GetInfo( AutofillFieldType type, const std::string& app_locale) const { if (AutofillType(type).group() != AutofillType::CREDIT_CARD) return address().GetInfo(type, app_locale); switch (type) { case CREDIT_CARD_NAME: return address().recipient_name(); case CREDIT_CARD_NUMBER: return DisplayName(); case CREDIT_CARD_EXP_4_DIGIT_YEAR: return base::IntToString16(expiration_year()); case CREDIT_CARD_VERIFICATION_CODE: break; default: NOTREACHED(); } return base::string16(); } WalletItems::LegalDocument::~LegalDocument() {} scoped_ptr WalletItems::LegalDocument::CreateLegalDocument( const base::DictionaryValue& dictionary) { std::string id; if (!dictionary.GetString("legal_document_id", &id)) { DLOG(ERROR) << "Response from Google Wallet missing legal document id"; return scoped_ptr(); } base::string16 display_name; if (!dictionary.GetString("display_name", &display_name)) { DLOG(ERROR) << "Response from Google Wallet missing display name"; return scoped_ptr(); } return scoped_ptr(new LegalDocument(id, display_name)); } scoped_ptr WalletItems::LegalDocument::CreatePrivacyPolicyDocument() { return scoped_ptr(new LegalDocument( GURL(kPrivacyNoticeUrl), l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_PRIVACY_POLICY_LINK))); } bool WalletItems::LegalDocument::operator==(const LegalDocument& other) const { return id_ == other.id_ && url_ == other.url_ && display_name_ == other.display_name_; } bool WalletItems::LegalDocument::operator!=(const LegalDocument& other) const { return !(*this == other); } WalletItems::LegalDocument::LegalDocument(const std::string& id, const base::string16& display_name) : id_(id), url_(kLegalDocumentUrl + id), display_name_(display_name) {} WalletItems::LegalDocument::LegalDocument(const GURL& url, const base::string16& display_name) : url_(url), display_name_(display_name) {} WalletItems::WalletItems(const std::vector& required_actions, const std::string& google_transaction_id, const std::string& default_instrument_id, const std::string& default_address_id, const std::string& obfuscated_gaia_id) : required_actions_(required_actions), google_transaction_id_(google_transaction_id), default_instrument_id_(default_instrument_id), default_address_id_(default_address_id), obfuscated_gaia_id_(obfuscated_gaia_id) {} WalletItems::~WalletItems() {} scoped_ptr WalletItems::CreateWalletItems(const base::DictionaryValue& dictionary) { std::vector required_action; const ListValue* required_action_list; if (dictionary.GetList("required_action", &required_action_list)) { for (size_t i = 0; i < required_action_list->GetSize(); ++i) { std::string action_string; if (required_action_list->GetString(i, &action_string)) { RequiredAction action = ParseRequiredActionFromString(action_string); if (!ActionAppliesToWalletItems(action)) { DLOG(ERROR) << "Response from Google wallet with bad required action:" " \"" << action_string << "\""; return scoped_ptr(); } required_action.push_back(action); } } } else { DVLOG(1) << "Response from Google wallet missing required actions"; } std::string google_transaction_id; if (!dictionary.GetString("google_transaction_id", &google_transaction_id) && required_action.empty()) { DLOG(ERROR) << "Response from Google wallet missing google transaction id"; return scoped_ptr(); } std::string default_instrument_id; if (!dictionary.GetString("default_instrument_id", &default_instrument_id)) DVLOG(1) << "Response from Google wallet missing default instrument id"; std::string default_address_id; if (!dictionary.GetString("default_address_id", &default_address_id)) DVLOG(1) << "Response from Google wallet missing default_address_id"; std::string obfuscated_gaia_id; if (!dictionary.GetString("obfuscated_gaia_id", &obfuscated_gaia_id)) DVLOG(1) << "Response from Google wallet missing obfuscated gaia id"; scoped_ptr wallet_items(new WalletItems(required_action, google_transaction_id, default_instrument_id, default_address_id, obfuscated_gaia_id)); const ListValue* legal_docs; if (dictionary.GetList("required_legal_document", &legal_docs)) { for (size_t i = 0; i < legal_docs->GetSize(); ++i) { const DictionaryValue* legal_doc_dict; if (legal_docs->GetDictionary(i, &legal_doc_dict)) { scoped_ptr legal_doc( LegalDocument::CreateLegalDocument(*legal_doc_dict)); if (legal_doc.get()) { wallet_items->AddLegalDocument(legal_doc.Pass()); } else { DLOG(ERROR) << "Malformed legal document in response from " "Google wallet"; return scoped_ptr(); } } } if (!legal_docs->empty()) { // Always append the privacy policy link as well. wallet_items->AddLegalDocument( LegalDocument::CreatePrivacyPolicyDocument()); } } else { DVLOG(1) << "Response from Google wallet missing legal docs"; } const ListValue* instruments; if (dictionary.GetList("instrument", &instruments)) { for (size_t i = 0; i < instruments->GetSize(); ++i) { const DictionaryValue* instrument_dict; if (instruments->GetDictionary(i, &instrument_dict)) { scoped_ptr instrument( MaskedInstrument::CreateMaskedInstrument(*instrument_dict)); if (instrument.get()) wallet_items->AddInstrument(instrument.Pass()); else DLOG(ERROR) << "Malformed instrument in response from Google Wallet"; } } } else { DVLOG(1) << "Response from Google wallet missing instruments"; } const ListValue* addresses; if (dictionary.GetList("address", &addresses)) { for (size_t i = 0; i < addresses->GetSize(); ++i) { const DictionaryValue* address_dict; if (addresses->GetDictionary(i, &address_dict)) { scoped_ptr
address( Address::CreateAddressWithID(*address_dict)); if (address.get()) wallet_items->AddAddress(address.Pass()); else DLOG(ERROR) << "Malformed address in response from Google Wallet"; } } } else { DVLOG(1) << "Response from Google wallet missing addresses"; } return wallet_items.Pass(); } bool WalletItems::operator==(const WalletItems& other) const { return google_transaction_id_ == other.google_transaction_id_ && default_instrument_id_ == other.default_instrument_id_ && default_address_id_ == other.default_address_id_ && required_actions_ == other.required_actions_ && obfuscated_gaia_id_ == other.obfuscated_gaia_id_ && VectorsAreEqual(instruments(), other.instruments()) && VectorsAreEqual
(addresses(), other.addresses()) && VectorsAreEqual(legal_documents(), other.legal_documents()); } bool WalletItems::operator!=(const WalletItems& other) const { return !(*this == other); } } // namespace wallet } // namespace autofill