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