1 // Copyright 2013 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "components/autofill/content/browser/wallet/wallet_items.h" 6 7 #include "base/logging.h" 8 #include "base/strings/string_number_conversions.h" 9 #include "base/strings/utf_string_conversions.h" 10 #include "base/values.h" 11 #include "components/autofill/content/browser/wallet/gaia_account.h" 12 #include "components/autofill/core/browser/autofill_type.h" 13 #include "components/autofill/core/browser/credit_card.h" 14 #include "grit/component_strings.h" 15 #include "grit/webkit_resources.h" 16 #include "ui/base/l10n/l10n_util.h" 17 #include "ui/base/resource/resource_bundle.h" 18 #include "ui/gfx/image/image.h" 19 #include "url/gurl.h" 20 21 namespace autofill { 22 namespace wallet { 23 24 namespace { 25 26 const char kLegalDocumentUrl[] = 27 "https://wallet.google.com/legaldocument?docId="; 28 const char kPrivacyNoticeUrl[] = "https://wallet.google.com/files/privacy.html"; 29 30 // TODO(estade): move to base/. 31 template<class T> 32 bool VectorsAreEqual(const std::vector<T*>& a, const std::vector<T*>& b) { 33 if (a.size() != b.size()) 34 return false; 35 36 for (size_t i = 0; i < a.size(); ++i) { 37 if (*a[i] != *b[i]) 38 return false; 39 } 40 41 return true; 42 } 43 44 WalletItems::MaskedInstrument::Type 45 TypeFromString(const std::string& type_string) { 46 if (type_string == "VISA") 47 return WalletItems::MaskedInstrument::VISA; 48 if (type_string == "MASTER_CARD") 49 return WalletItems::MaskedInstrument::MASTER_CARD; 50 if (type_string == "AMEX") 51 return WalletItems::MaskedInstrument::AMEX; 52 if (type_string == "DISCOVER") 53 return WalletItems::MaskedInstrument::DISCOVER; 54 if (type_string == "SOLO") 55 return WalletItems::MaskedInstrument::SOLO; 56 if (type_string == "MAESTRO") 57 return WalletItems::MaskedInstrument::MAESTRO; 58 if (type_string == "SWITCH") 59 return WalletItems::MaskedInstrument::SWITCH; 60 return WalletItems::MaskedInstrument::UNKNOWN; 61 } 62 63 WalletItems::MaskedInstrument::Status 64 StatusFromString(const std::string& status_string) { 65 if (status_string == "AMEX_NOT_SUPPORTED") 66 return WalletItems::MaskedInstrument::AMEX_NOT_SUPPORTED; 67 if (status_string == "PENDING") 68 return WalletItems::MaskedInstrument::PENDING; 69 if (status_string == "VALID") 70 return WalletItems::MaskedInstrument::VALID; 71 if (status_string == "DECLINED") 72 return WalletItems::MaskedInstrument::DECLINED; 73 if (status_string == "DISABLED_FOR_THIS_MERCHANT") 74 return WalletItems::MaskedInstrument::DISABLED_FOR_THIS_MERCHANT; 75 if (status_string == "UNSUPPORTED_COUNTRY") 76 return WalletItems::MaskedInstrument::UNSUPPORTED_COUNTRY; 77 if (status_string == "EXPIRED") 78 return WalletItems::MaskedInstrument::EXPIRED; 79 if (status_string == "BILLING_INCOMPLETE") 80 return WalletItems::MaskedInstrument::BILLING_INCOMPLETE; 81 return WalletItems::MaskedInstrument::INAPPLICABLE; 82 } 83 84 base::string16 DisplayStringFromType(WalletItems::MaskedInstrument::Type type) { 85 switch (type) { 86 case WalletItems::MaskedInstrument::AMEX: 87 return CreditCard::TypeForDisplay(kAmericanExpressCard); 88 case WalletItems::MaskedInstrument::DISCOVER: 89 return CreditCard::TypeForDisplay(kDiscoverCard); 90 case WalletItems::MaskedInstrument::MASTER_CARD: 91 return CreditCard::TypeForDisplay(kMasterCard); 92 case WalletItems::MaskedInstrument::VISA: 93 return CreditCard::TypeForDisplay(kVisaCard); 94 default: 95 return CreditCard::TypeForDisplay(kGenericCard); 96 } 97 } 98 99 } // anonymous namespace 100 101 WalletItems::MaskedInstrument::MaskedInstrument( 102 const base::string16& descriptive_name, 103 const WalletItems::MaskedInstrument::Type& type, 104 const std::vector<base::string16>& supported_currencies, 105 const base::string16& last_four_digits, 106 int expiration_month, 107 int expiration_year, 108 scoped_ptr<Address> address, 109 const WalletItems::MaskedInstrument::Status& status, 110 const std::string& object_id) 111 : descriptive_name_(descriptive_name), 112 type_(type), 113 supported_currencies_(supported_currencies), 114 last_four_digits_(last_four_digits), 115 expiration_month_(expiration_month), 116 expiration_year_(expiration_year), 117 address_(address.Pass()), 118 status_(status), 119 object_id_(object_id) { 120 DCHECK(address_); 121 } 122 123 WalletItems::MaskedInstrument::~MaskedInstrument() {} 124 125 scoped_ptr<WalletItems::MaskedInstrument> 126 WalletItems::MaskedInstrument::CreateMaskedInstrument( 127 const base::DictionaryValue& dictionary) { 128 std::string type_string; 129 Type type; 130 if (dictionary.GetString("type", &type_string)) { 131 type = TypeFromString(type_string); 132 } else { 133 DLOG(ERROR) << "Response from Google Wallet missing card type"; 134 return scoped_ptr<MaskedInstrument>(); 135 } 136 137 base::string16 last_four_digits; 138 if (!dictionary.GetString("last_four_digits", &last_four_digits)) { 139 DLOG(ERROR) << "Response from Google Wallet missing last four digits"; 140 return scoped_ptr<MaskedInstrument>(); 141 } 142 143 std::string status_string; 144 Status status; 145 if (dictionary.GetString("status", &status_string)) { 146 status = StatusFromString(status_string); 147 } else { 148 DLOG(ERROR) << "Response from Google Wallet missing status"; 149 return scoped_ptr<MaskedInstrument>(); 150 } 151 152 std::string object_id; 153 if (!dictionary.GetString("object_id", &object_id)) { 154 DLOG(ERROR) << "Response from Google Wallet missing object id"; 155 return scoped_ptr<MaskedInstrument>(); 156 } 157 158 const DictionaryValue* address_dict; 159 if (!dictionary.GetDictionary("billing_address", &address_dict)) { 160 DLOG(ERROR) << "Response from Google wallet missing address"; 161 return scoped_ptr<MaskedInstrument>(); 162 } 163 scoped_ptr<Address> address = Address::CreateDisplayAddress(*address_dict); 164 165 if (!address) { 166 DLOG(ERROR) << "Response from Google wallet contained malformed address"; 167 return scoped_ptr<MaskedInstrument>(); 168 } 169 170 std::vector<base::string16> supported_currencies; 171 const ListValue* supported_currency_list; 172 if (dictionary.GetList("supported_currency", &supported_currency_list)) { 173 for (size_t i = 0; i < supported_currency_list->GetSize(); ++i) { 174 base::string16 currency; 175 if (supported_currency_list->GetString(i, ¤cy)) 176 supported_currencies.push_back(currency); 177 } 178 } else { 179 DVLOG(1) << "Response from Google Wallet missing supported currency"; 180 } 181 182 int expiration_month; 183 if (!dictionary.GetInteger("expiration_month", &expiration_month)) 184 DVLOG(1) << "Response from Google Wallet missing expiration month"; 185 186 int expiration_year; 187 if (!dictionary.GetInteger("expiration_year", &expiration_year)) 188 DVLOG(1) << "Response from Google Wallet missing expiration year"; 189 190 base::string16 descriptive_name; 191 if (!dictionary.GetString("descriptive_name", &descriptive_name)) 192 DVLOG(1) << "Response from Google Wallet missing descriptive name"; 193 194 return scoped_ptr<MaskedInstrument>(new MaskedInstrument(descriptive_name, 195 type, 196 supported_currencies, 197 last_four_digits, 198 expiration_month, 199 expiration_year, 200 address.Pass(), 201 status, 202 object_id)); 203 } 204 205 bool WalletItems::MaskedInstrument::operator==( 206 const WalletItems::MaskedInstrument& other) const { 207 if (descriptive_name_ != other.descriptive_name_) 208 return false; 209 if (type_ != other.type_) 210 return false; 211 if (supported_currencies_ != other.supported_currencies_) 212 return false; 213 if (last_four_digits_ != other.last_four_digits_) 214 return false; 215 if (expiration_month_ != other.expiration_month_) 216 return false; 217 if (expiration_year_ != other.expiration_year_) 218 return false; 219 if (address_) { 220 if (other.address_) { 221 if (*address_ != *other.address_) 222 return false; 223 } else { 224 return false; 225 } 226 } else if (other.address_) { 227 return false; 228 } 229 if (status_ != other.status_) 230 return false; 231 if (object_id_ != other.object_id_) 232 return false; 233 return true; 234 } 235 236 bool WalletItems::MaskedInstrument::operator!=( 237 const WalletItems::MaskedInstrument& other) const { 238 return !(*this == other); 239 } 240 241 const WalletItems::MaskedInstrument* WalletItems::GetInstrumentById( 242 const std::string& object_id) const { 243 if (object_id.empty()) 244 return NULL; 245 246 for (size_t i = 0; i < instruments_.size(); ++i) { 247 if (instruments_[i]->object_id() == object_id) 248 return instruments_[i]; 249 } 250 251 return NULL; 252 } 253 254 bool WalletItems::HasRequiredAction(RequiredAction action) const { 255 DCHECK(ActionAppliesToWalletItems(action)); 256 return std::find(required_actions_.begin(), 257 required_actions_.end(), 258 action) != required_actions_.end(); 259 } 260 261 bool WalletItems::SupportsCard(const base::string16& card_number, 262 base::string16* message) const { 263 std::string card_type = CreditCard::GetCreditCardType(card_number); 264 265 if (card_type == kVisaCard || 266 card_type == kMasterCard || 267 card_type == kDiscoverCard) { 268 return true; 269 } 270 271 if (card_type == kAmericanExpressCard) { 272 if (amex_permission_ == AMEX_ALLOWED) 273 return true; 274 275 *message = l10n_util::GetStringUTF16( 276 IDS_AUTOFILL_CREDIT_CARD_NOT_SUPPORTED_BY_WALLET_FOR_MERCHANT); 277 return false; 278 } 279 280 *message = l10n_util::GetStringUTF16( 281 IDS_AUTOFILL_CREDIT_CARD_NOT_SUPPORTED_BY_WALLET); 282 return false; 283 } 284 285 std::string WalletItems::ObfuscatedGaiaId() const { 286 if (active_account_index_ >= gaia_accounts_.size()) 287 return std::string(); 288 289 return gaia_accounts_[active_account_index_]->obfuscated_id(); 290 } 291 292 base::string16 WalletItems::MaskedInstrument::DisplayName() const { 293 #if defined(OS_ANDROID) 294 // TODO(aruslan): improve this stub implementation. 295 return descriptive_name(); 296 #else 297 return descriptive_name(); 298 #endif 299 } 300 301 base::string16 WalletItems::MaskedInstrument::DisplayNameDetail() const { 302 #if defined(OS_ANDROID) 303 // TODO(aruslan): improve this stub implementation. 304 return address().DisplayName(); 305 #else 306 return base::string16(); 307 #endif 308 } 309 310 base::string16 WalletItems::MaskedInstrument::TypeAndLastFourDigits() const { 311 // TODO(dbeam): i18n. 312 return DisplayStringFromType(type_) + ASCIIToUTF16(" - ") + 313 last_four_digits(); 314 } 315 316 const gfx::Image& WalletItems::MaskedInstrument::CardIcon() const { 317 int idr = 0; 318 switch (type_) { 319 case AMEX: 320 idr = IDR_AUTOFILL_CC_AMEX; 321 break; 322 323 case DISCOVER: 324 idr = IDR_AUTOFILL_CC_DISCOVER; 325 break; 326 327 case MASTER_CARD: 328 idr = IDR_AUTOFILL_CC_MASTERCARD; 329 break; 330 331 case VISA: 332 idr = IDR_AUTOFILL_CC_VISA; 333 break; 334 335 case SOLO: 336 case MAESTRO: 337 case SWITCH: 338 case UNKNOWN: 339 idr = IDR_AUTOFILL_CC_GENERIC; 340 break; 341 } 342 343 return ResourceBundle::GetSharedInstance().GetImageNamed(idr); 344 } 345 346 base::string16 WalletItems::MaskedInstrument::GetInfo( 347 const AutofillType& type, 348 const std::string& app_locale) const { 349 if (type.group() != CREDIT_CARD) 350 return address().GetInfo(type, app_locale); 351 352 switch (type.GetStorableType()) { 353 case CREDIT_CARD_NAME: 354 return address().recipient_name(); 355 356 case CREDIT_CARD_NUMBER: 357 return DisplayName(); 358 359 case CREDIT_CARD_EXP_4_DIGIT_YEAR: 360 return base::IntToString16(expiration_year()); 361 362 case CREDIT_CARD_VERIFICATION_CODE: 363 break; 364 365 case CREDIT_CARD_TYPE: 366 return DisplayStringFromType(type_); 367 368 default: 369 NOTREACHED(); 370 } 371 372 return base::string16(); 373 } 374 375 WalletItems::LegalDocument::~LegalDocument() {} 376 377 scoped_ptr<WalletItems::LegalDocument> 378 WalletItems::LegalDocument::CreateLegalDocument( 379 const base::DictionaryValue& dictionary) { 380 std::string id; 381 if (!dictionary.GetString("legal_document_id", &id)) { 382 DLOG(ERROR) << "Response from Google Wallet missing legal document id"; 383 return scoped_ptr<LegalDocument>(); 384 } 385 386 base::string16 display_name; 387 if (!dictionary.GetString("display_name", &display_name)) { 388 DLOG(ERROR) << "Response from Google Wallet missing display name"; 389 return scoped_ptr<LegalDocument>(); 390 } 391 392 return scoped_ptr<LegalDocument>(new LegalDocument(id, display_name)); 393 } 394 395 scoped_ptr<WalletItems::LegalDocument> 396 WalletItems::LegalDocument::CreatePrivacyPolicyDocument() { 397 return scoped_ptr<LegalDocument>(new LegalDocument( 398 GURL(kPrivacyNoticeUrl), 399 l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_PRIVACY_POLICY_LINK))); 400 } 401 402 bool WalletItems::LegalDocument::operator==(const LegalDocument& other) const { 403 return id_ == other.id_ && 404 url_ == other.url_ && 405 display_name_ == other.display_name_; 406 } 407 408 bool WalletItems::LegalDocument::operator!=(const LegalDocument& other) const { 409 return !(*this == other); 410 } 411 412 WalletItems::LegalDocument::LegalDocument(const std::string& id, 413 const base::string16& display_name) 414 : id_(id), 415 url_(kLegalDocumentUrl + id), 416 display_name_(display_name) {} 417 418 WalletItems::LegalDocument::LegalDocument(const GURL& url, 419 const base::string16& display_name) 420 : url_(url), 421 display_name_(display_name) {} 422 423 WalletItems::WalletItems(const std::vector<RequiredAction>& required_actions, 424 const std::string& google_transaction_id, 425 const std::string& default_instrument_id, 426 const std::string& default_address_id, 427 AmexPermission amex_permission) 428 : required_actions_(required_actions), 429 google_transaction_id_(google_transaction_id), 430 default_instrument_id_(default_instrument_id), 431 default_address_id_(default_address_id), 432 active_account_index_(std::numeric_limits<size_t>::max()), 433 amex_permission_(amex_permission) {} 434 435 WalletItems::~WalletItems() {} 436 437 scoped_ptr<WalletItems> 438 WalletItems::CreateWalletItems(const base::DictionaryValue& dictionary) { 439 std::vector<RequiredAction> required_action; 440 const ListValue* required_action_list; 441 if (dictionary.GetList("required_action", &required_action_list)) { 442 for (size_t i = 0; i < required_action_list->GetSize(); ++i) { 443 std::string action_string; 444 if (required_action_list->GetString(i, &action_string)) { 445 RequiredAction action = ParseRequiredActionFromString(action_string); 446 if (!ActionAppliesToWalletItems(action)) { 447 DLOG(ERROR) << "Response from Google wallet with bad required action:" 448 " \"" << action_string << "\""; 449 return scoped_ptr<WalletItems>(); 450 } 451 required_action.push_back(action); 452 } 453 } 454 } else { 455 DVLOG(1) << "Response from Google wallet missing required actions"; 456 } 457 458 std::string google_transaction_id; 459 if (!dictionary.GetString("google_transaction_id", &google_transaction_id) && 460 required_action.empty()) { 461 DLOG(ERROR) << "Response from Google wallet missing google transaction id"; 462 return scoped_ptr<WalletItems>(); 463 } 464 465 std::string default_instrument_id; 466 if (!dictionary.GetString("default_instrument_id", &default_instrument_id)) 467 DVLOG(1) << "Response from Google wallet missing default instrument id"; 468 469 std::string default_address_id; 470 if (!dictionary.GetString("default_address_id", &default_address_id)) 471 DVLOG(1) << "Response from Google wallet missing default_address_id"; 472 473 // obfuscated_gaia_id is deprecated. 474 475 bool amex_disallowed = true; 476 if (!dictionary.GetBoolean("amex_disallowed", &amex_disallowed)) 477 DVLOG(1) << "Response from Google wallet missing the amex_disallowed field"; 478 AmexPermission amex_permission = 479 amex_disallowed ? AMEX_DISALLOWED : AMEX_ALLOWED; 480 481 scoped_ptr<WalletItems> wallet_items(new WalletItems(required_action, 482 google_transaction_id, 483 default_instrument_id, 484 default_address_id, 485 amex_permission)); 486 std::vector<std::string> gaia_accounts; 487 const base::ListValue* gaia_profiles; 488 if (dictionary.GetList("gaia_profile", &gaia_profiles)) { 489 for (size_t i = 0; i < gaia_profiles->GetSize(); ++i) { 490 const base::DictionaryValue* account_dict; 491 std::string email; 492 if (!gaia_profiles->GetDictionary(i, &account_dict)) 493 continue; 494 495 scoped_ptr<GaiaAccount> gaia_account( 496 GaiaAccount::Create(*account_dict)); 497 if (gaia_account) 498 wallet_items->AddAccount(gaia_account.Pass()); 499 } 500 } else { 501 DVLOG(1) << "Response from Google wallet missing GAIA accounts"; 502 } 503 504 const ListValue* legal_docs; 505 if (dictionary.GetList("required_legal_document", &legal_docs)) { 506 for (size_t i = 0; i < legal_docs->GetSize(); ++i) { 507 const DictionaryValue* legal_doc_dict; 508 if (legal_docs->GetDictionary(i, &legal_doc_dict)) { 509 scoped_ptr<LegalDocument> legal_doc( 510 LegalDocument::CreateLegalDocument(*legal_doc_dict)); 511 if (legal_doc) 512 wallet_items->AddLegalDocument(legal_doc.Pass()); 513 else 514 return scoped_ptr<WalletItems>(); 515 } 516 } 517 518 if (!legal_docs->empty()) { 519 // Always append the privacy policy link as well. 520 wallet_items->AddLegalDocument( 521 LegalDocument::CreatePrivacyPolicyDocument()); 522 } 523 } else { 524 DVLOG(1) << "Response from Google wallet missing legal docs"; 525 } 526 527 const ListValue* instruments; 528 if (dictionary.GetList("instrument", &instruments)) { 529 for (size_t i = 0; i < instruments->GetSize(); ++i) { 530 const DictionaryValue* instrument_dict; 531 if (instruments->GetDictionary(i, &instrument_dict)) { 532 scoped_ptr<MaskedInstrument> instrument( 533 MaskedInstrument::CreateMaskedInstrument(*instrument_dict)); 534 if (instrument) 535 wallet_items->AddInstrument(instrument.Pass()); 536 } 537 } 538 } else { 539 DVLOG(1) << "Response from Google wallet missing instruments"; 540 } 541 542 const ListValue* addresses; 543 if (dictionary.GetList("address", &addresses)) { 544 for (size_t i = 0; i < addresses->GetSize(); ++i) { 545 const DictionaryValue* address_dict; 546 if (addresses->GetDictionary(i, &address_dict)) { 547 scoped_ptr<Address> address( 548 Address::CreateAddressWithID(*address_dict)); 549 if (address) 550 wallet_items->AddAddress(address.Pass()); 551 } 552 } 553 } else { 554 DVLOG(1) << "Response from Google wallet missing addresses"; 555 } 556 557 return wallet_items.Pass(); 558 } 559 560 void WalletItems::AddAccount(scoped_ptr<GaiaAccount> account) { 561 if (account->index() != gaia_accounts_.size()) { 562 DVLOG(1) << "Tried to add account out of order"; 563 return; 564 } 565 566 if (account->is_active()) 567 active_account_index_ = account->index(); 568 569 gaia_accounts_.push_back(account.release()); 570 } 571 572 bool WalletItems::operator==(const WalletItems& other) const { 573 return google_transaction_id_ == other.google_transaction_id_ && 574 default_instrument_id_ == other.default_instrument_id_ && 575 default_address_id_ == other.default_address_id_ && 576 required_actions_ == other.required_actions_ && 577 // This check is technically redundant, but is useful for tests. 578 ObfuscatedGaiaId() == other.ObfuscatedGaiaId() && 579 active_account_index() == other.active_account_index() && 580 VectorsAreEqual<GaiaAccount>(gaia_accounts(), 581 other.gaia_accounts()) && 582 VectorsAreEqual<MaskedInstrument>(instruments(), 583 other.instruments()) && 584 VectorsAreEqual<Address>(addresses(), other.addresses()) && 585 VectorsAreEqual<LegalDocument>(legal_documents(), 586 other.legal_documents()); 587 } 588 589 bool WalletItems::operator!=(const WalletItems& other) const { 590 return !(*this == other); 591 } 592 593 } // namespace wallet 594 } // namespace autofill 595