• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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>
VectorsAreEqual(const std::vector<T * > & a,const std::vector<T * > & b)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
TypeFromString(const std::string & type_string)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
StatusFromString(const std::string & status_string)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 
DisplayStringFromType(WalletItems::MaskedInstrument::Type type)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 
MaskedInstrument(const base::string16 & descriptive_name,const WalletItems::MaskedInstrument::Type & type,const std::vector<base::string16> & supported_currencies,const base::string16 & last_four_digits,int expiration_month,int expiration_year,scoped_ptr<Address> address,const WalletItems::MaskedInstrument::Status & status,const std::string & object_id)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 
~MaskedInstrument()123 WalletItems::MaskedInstrument::~MaskedInstrument() {}
124 
125 scoped_ptr<WalletItems::MaskedInstrument>
CreateMaskedInstrument(const base::DictionaryValue & dictionary)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, &currency))
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 
operator ==(const WalletItems::MaskedInstrument & other) const205 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 
operator !=(const WalletItems::MaskedInstrument & other) const236 bool WalletItems::MaskedInstrument::operator!=(
237     const WalletItems::MaskedInstrument& other) const {
238   return !(*this == other);
239 }
240 
GetInstrumentById(const std::string & object_id) const241 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 
HasRequiredAction(RequiredAction action) const254 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 
SupportsCard(const base::string16 & card_number,base::string16 * message) const261 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 
ObfuscatedGaiaId() const285 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 
DisplayName() const292 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 
DisplayNameDetail() const301 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 
TypeAndLastFourDigits() const310 base::string16 WalletItems::MaskedInstrument::TypeAndLastFourDigits() const {
311   // TODO(dbeam): i18n.
312   return DisplayStringFromType(type_) + ASCIIToUTF16(" - ") +
313          last_four_digits();
314 }
315 
CardIcon() const316 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 
GetInfo(const AutofillType & type,const std::string & app_locale) const346 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 
~LegalDocument()375 WalletItems::LegalDocument::~LegalDocument() {}
376 
377 scoped_ptr<WalletItems::LegalDocument>
CreateLegalDocument(const base::DictionaryValue & dictionary)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>
CreatePrivacyPolicyDocument()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 
operator ==(const LegalDocument & other) const402 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 
operator !=(const LegalDocument & other) const408 bool WalletItems::LegalDocument::operator!=(const LegalDocument& other) const {
409   return !(*this == other);
410 }
411 
LegalDocument(const std::string & id,const base::string16 & display_name)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 
LegalDocument(const GURL & url,const base::string16 & display_name)418 WalletItems::LegalDocument::LegalDocument(const GURL& url,
419                                           const base::string16& display_name)
420     : url_(url),
421       display_name_(display_name) {}
422 
WalletItems(const std::vector<RequiredAction> & required_actions,const std::string & google_transaction_id,const std::string & default_instrument_id,const std::string & default_address_id,AmexPermission amex_permission)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 
~WalletItems()435 WalletItems::~WalletItems() {}
436 
437 scoped_ptr<WalletItems>
CreateWalletItems(const base::DictionaryValue & dictionary)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 
AddAccount(scoped_ptr<GaiaAccount> account)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 
operator ==(const WalletItems & other) const572 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 
operator !=(const WalletItems & other) const589 bool WalletItems::operator!=(const WalletItems& other) const {
590   return !(*this == other);
591 }
592 
593 }  // namespace wallet
594 }  // namespace autofill
595