• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2011 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 "chrome/browser/autofill/credit_card.h"
6 
7 #include <stddef.h>
8 #include <string>
9 
10 #include "base/basictypes.h"
11 #include "base/logging.h"
12 #include "base/string16.h"
13 #include "base/string_number_conversions.h"
14 #include "base/string_split.h"
15 #include "base/string_util.h"
16 #include "base/utf_string_conversions.h"
17 #include "chrome/browser/autofill/autofill_type.h"
18 #include "chrome/browser/autofill/field_types.h"
19 #include "chrome/browser/autofill/form_field.h"
20 #include "chrome/common/guid.h"
21 #include "grit/generated_resources.h"
22 #include "ui/base/l10n/l10n_util.h"
23 
24 namespace {
25 
26 const char16 kCreditCardObfuscationSymbol = '*';
27 
28 const AutofillFieldType kAutofillCreditCardTypes[] = {
29   CREDIT_CARD_NAME,
30   CREDIT_CARD_NUMBER,
31   CREDIT_CARD_TYPE,
32   CREDIT_CARD_EXP_MONTH,
33   CREDIT_CARD_EXP_4_DIGIT_YEAR,
34 };
35 
36 const int kAutofillCreditCardLength = arraysize(kAutofillCreditCardTypes);
37 
GetCreditCardType(const string16 & number)38 std::string GetCreditCardType(const string16& number) {
39   // Don't check for a specific type if this is not a credit card number.
40   if (!CreditCard::IsValidCreditCardNumber(number))
41     return kGenericCard;
42 
43   // Credit card number specifications taken from:
44   // http://en.wikipedia.org/wiki/Credit_card_numbers and
45   // http://www.beachnet.com/~hstiles/cardtype.html
46   // Card Type              Prefix(es)                      Length
47   // ---------------------------------------------------------------
48   // Visa                   4                               13,16
49   // American Express       34,37                           15
50   // Diners Club            300-305,2014,2149,36,           14,15
51   // Discover Card          6011,65                         16
52   // JCB                    3                               16
53   // JCB                    2131,1800                       15
54   // MasterCard             51-55                           16
55   // Solo (debit card)      6334,6767                       16,18,19
56 
57   // We need at least 4 digits to work with.
58   if (number.length() < 4)
59     return kGenericCard;
60 
61   int first_four_digits = 0;
62   if (!base::StringToInt(number.substr(0, 4), &first_four_digits))
63     return kGenericCard;
64 
65   int first_three_digits = first_four_digits / 10;
66   int first_two_digits = first_three_digits / 10;
67   int first_digit = first_two_digits / 10;
68 
69   switch (number.length()) {
70     case 13:
71       if (first_digit == 4)
72         return kVisaCard;
73 
74       break;
75     case 14:
76       if (first_three_digits >= 300 && first_three_digits <=305)
77         return kDinersCard;
78 
79       if (first_digit == 36)
80         return kDinersCard;
81 
82       break;
83     case 15:
84       if (first_two_digits == 34 || first_two_digits == 37)
85         return kAmericanExpressCard;
86 
87       if (first_four_digits == 2131 || first_four_digits == 1800)
88         return kJCBCard;
89 
90       if (first_four_digits == 2014 || first_four_digits == 2149)
91         return kDinersCard;
92 
93       break;
94     case 16:
95       if (first_four_digits == 6011 || first_two_digits == 65)
96         return kDiscoverCard;
97 
98       if (first_four_digits == 6334 || first_four_digits == 6767)
99         return kSoloCard;
100 
101       if (first_two_digits >= 51 && first_two_digits <= 55)
102         return kMasterCard;
103 
104       if (first_digit == 3)
105         return kJCBCard;
106 
107       if (first_digit == 4)
108         return kVisaCard;
109 
110       break;
111     case 18:
112     case 19:
113       if (first_four_digits == 6334 || first_four_digits == 6767)
114         return kSoloCard;
115 
116       break;
117   }
118 
119   return kGenericCard;
120 }
121 
ConvertDate(const string16 & date,int * num)122 bool ConvertDate(const string16& date, int* num) {
123   if (!date.empty()) {
124     bool converted = base::StringToInt(date, num);
125     DCHECK(converted);
126     if (!converted)
127       return false;
128   } else {
129     // Clear the value.
130     *num = 0;
131   }
132 
133   return true;
134 }
135 
136 }  // namespace
137 
CreditCard(const std::string & guid)138 CreditCard::CreditCard(const std::string& guid)
139     : type_(kGenericCard),
140       expiration_month_(0),
141       expiration_year_(0),
142       guid_(guid) {
143 }
144 
CreditCard()145 CreditCard::CreditCard()
146     : type_(kGenericCard),
147       expiration_month_(0),
148       expiration_year_(0),
149       guid_(guid::GenerateGUID()) {
150 }
151 
CreditCard(const CreditCard & credit_card)152 CreditCard::CreditCard(const CreditCard& credit_card) : FormGroup() {
153   operator=(credit_card);
154 }
155 
~CreditCard()156 CreditCard::~CreditCard() {}
157 
GetPossibleFieldTypes(const string16 & text,FieldTypeSet * possible_types) const158 void CreditCard::GetPossibleFieldTypes(const string16& text,
159                                        FieldTypeSet* possible_types) const {
160   if (IsNameOnCard(text))
161     possible_types->insert(CREDIT_CARD_NAME);
162 
163   if (IsNumber(text))
164     possible_types->insert(CREDIT_CARD_NUMBER);
165 
166   if (IsExpirationMonth(text))
167     possible_types->insert(CREDIT_CARD_EXP_MONTH);
168 
169   if (Is2DigitExpirationYear(text))
170     possible_types->insert(CREDIT_CARD_EXP_2_DIGIT_YEAR);
171 
172   if (Is4DigitExpirationYear(text))
173     possible_types->insert(CREDIT_CARD_EXP_4_DIGIT_YEAR);
174 }
175 
GetAvailableFieldTypes(FieldTypeSet * available_types) const176 void CreditCard::GetAvailableFieldTypes(FieldTypeSet* available_types) const {
177   DCHECK(available_types);
178 
179   if (!name_on_card_.empty())
180     available_types->insert(CREDIT_CARD_NAME);
181 
182   if (!number_.empty())
183     available_types->insert(CREDIT_CARD_NUMBER);
184 
185   if (!ExpirationMonthAsString().empty())
186     available_types->insert(CREDIT_CARD_EXP_MONTH);
187 
188   if (!Expiration2DigitYearAsString().empty())
189     available_types->insert(CREDIT_CARD_EXP_2_DIGIT_YEAR);
190 
191   if (!Expiration4DigitYearAsString().empty())
192     available_types->insert(CREDIT_CARD_EXP_4_DIGIT_YEAR);
193 }
194 
GetInfo(AutofillFieldType type) const195 string16 CreditCard::GetInfo(AutofillFieldType type) const {
196   switch (type) {
197     case CREDIT_CARD_NAME:
198       return name_on_card_;
199 
200     case CREDIT_CARD_EXP_MONTH:
201       return ExpirationMonthAsString();
202 
203     case CREDIT_CARD_EXP_2_DIGIT_YEAR:
204       return Expiration2DigitYearAsString();
205 
206     case CREDIT_CARD_EXP_4_DIGIT_YEAR:
207       return Expiration4DigitYearAsString();
208 
209     case CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR: {
210       string16 month = ExpirationMonthAsString();
211       string16 year = Expiration2DigitYearAsString();
212       if (!month.empty() && !year.empty())
213         return month + ASCIIToUTF16("/") + year;
214       return string16();
215     }
216 
217     case CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR: {
218       string16 month = ExpirationMonthAsString();
219       string16 year = Expiration4DigitYearAsString();
220       if (!month.empty() && !year.empty())
221         return month + ASCIIToUTF16("/") + year;
222       return string16();
223     }
224 
225     case CREDIT_CARD_TYPE:
226       // We don't handle this case.
227       return string16();
228 
229     case CREDIT_CARD_NUMBER:
230       return number();
231 
232     case CREDIT_CARD_VERIFICATION_CODE:
233       NOTREACHED();
234       return string16();
235 
236     default:
237       // ComputeDataPresentForArray will hit this repeatedly.
238       return string16();
239   }
240 }
241 
SetInfo(AutofillFieldType type,const string16 & value)242 void CreditCard::SetInfo(AutofillFieldType type, const string16& value) {
243   switch (type) {
244     case CREDIT_CARD_NAME:
245       name_on_card_ = value;
246       break;
247 
248     case CREDIT_CARD_EXP_MONTH:
249       SetExpirationMonthFromString(value);
250       break;
251 
252     case CREDIT_CARD_EXP_2_DIGIT_YEAR:
253       // This is a read-only attribute.
254       break;
255 
256     case CREDIT_CARD_EXP_4_DIGIT_YEAR:
257       SetExpirationYearFromString(value);
258       break;
259 
260     case CREDIT_CARD_TYPE:
261       // We determine the type based on the number.
262       break;
263 
264     case CREDIT_CARD_NUMBER: {
265       // Don't change the real value if the input is an obfuscated string.
266       if (value.size() > 0 && value[0] != kCreditCardObfuscationSymbol)
267         SetNumber(value);
268       break;
269     }
270 
271     case CREDIT_CARD_VERIFICATION_CODE:
272       NOTREACHED();
273       break;
274 
275     default:
276       NOTREACHED() << "Attempting to set unknown info-type " << type;
277       break;
278   }
279 }
280 
Label() const281 const string16 CreditCard::Label() const {
282   string16 label;
283   if (number().empty())
284     return name_on_card_;  // No CC number, return name only.
285 
286   string16 obfuscated_cc_number = ObfuscatedNumber();
287   if (!expiration_month_ || !expiration_year_)
288     return obfuscated_cc_number;  // No expiration date set.
289 
290   // TODO(georgey): Internationalize date.
291   string16 formatted_date(ExpirationMonthAsString());
292   formatted_date.append(ASCIIToUTF16("/"));
293   formatted_date.append(Expiration4DigitYearAsString());
294 
295 #ifndef ANDROID
296   label = l10n_util::GetStringFUTF16(IDS_CREDIT_CARD_NUMBER_PREVIEW_FORMAT,
297                                      obfuscated_cc_number,
298                                      formatted_date);
299 #endif
300   return label;
301 }
302 
SetInfoForMonthInputType(const string16 & value)303 void CreditCard::SetInfoForMonthInputType(const string16& value) {
304   // Check if |text| is "yyyy-mm" format first, and check normal month format.
305   if (!autofill::MatchString(value, UTF8ToUTF16("^[0-9]{4}-[0-9]{1,2}$")))
306     return;
307 
308   std::vector<string16> year_month;
309   base::SplitString(value, L'-', &year_month);
310   DCHECK_EQ((int)year_month.size(), 2);
311   int num = 0;
312   bool converted = false;
313   converted = base::StringToInt(year_month[0], &num);
314   DCHECK(converted);
315   SetExpirationYear(num);
316   converted = base::StringToInt(year_month[1], &num);
317   DCHECK(converted);
318   SetExpirationMonth(num);
319 }
320 
ObfuscatedNumber() const321 string16 CreditCard::ObfuscatedNumber() const {
322   // If the number is shorter than four digits, there's no need to obfuscate it.
323   if (number_.size() < 4)
324     return number_;
325 
326   string16 number = StripSeparators(number_);
327   string16 result(number.size() - 4, kCreditCardObfuscationSymbol);
328   result.append(LastFourDigits());
329 
330   return result;
331 }
332 
LastFourDigits() const333 string16 CreditCard::LastFourDigits() const {
334   static const size_t kNumLastDigits = 4;
335 
336   string16 number = StripSeparators(number_);
337   if (number.size() < kNumLastDigits)
338     return string16();
339 
340   return number.substr(number.size() - kNumLastDigits, kNumLastDigits);
341 }
342 
operator =(const CreditCard & credit_card)343 void CreditCard::operator=(const CreditCard& credit_card) {
344   if (this == &credit_card)
345     return;
346 
347   number_ = credit_card.number_;
348   name_on_card_ = credit_card.name_on_card_;
349   type_ = credit_card.type_;
350   expiration_month_ = credit_card.expiration_month_;
351   expiration_year_ = credit_card.expiration_year_;
352   guid_ = credit_card.guid_;
353 }
354 
Compare(const CreditCard & credit_card) const355 int CreditCard::Compare(const CreditCard& credit_card) const {
356   // The following CreditCard field types are the only types we store in the
357   // WebDB so far, so we're only concerned with matching these types in the
358   // credit card.
359   const AutofillFieldType types[] = { CREDIT_CARD_NAME,
360                                       CREDIT_CARD_NUMBER,
361                                       CREDIT_CARD_EXP_MONTH,
362                                       CREDIT_CARD_EXP_4_DIGIT_YEAR };
363   for (size_t index = 0; index < arraysize(types); ++index) {
364     int comparison = GetInfo(types[index]).compare(
365         credit_card.GetInfo(types[index]));
366     if (comparison != 0)
367       return comparison;
368   }
369 
370   return 0;
371 }
372 
operator ==(const CreditCard & credit_card) const373 bool CreditCard::operator==(const CreditCard& credit_card) const {
374   if (guid_ != credit_card.guid_)
375     return false;
376 
377   return Compare(credit_card) == 0;
378 }
379 
operator !=(const CreditCard & credit_card) const380 bool CreditCard::operator!=(const CreditCard& credit_card) const {
381   return !operator==(credit_card);
382 }
383 
384 // static
StripSeparators(const string16 & number)385 const string16 CreditCard::StripSeparators(const string16& number) {
386   const char16 kSeparators[] = {'-', ' ', '\0'};
387   string16 stripped;
388   RemoveChars(number, kSeparators, &stripped);
389   return stripped;
390 }
391 
392 // static
IsValidCreditCardNumber(const string16 & text)393 bool CreditCard::IsValidCreditCardNumber(const string16& text) {
394   string16 number = StripSeparators(text);
395 
396   // Credit card numbers are at most 19 digits in length [1]. 12 digits seems to
397   // be a fairly safe lower-bound [2].
398   // [1] http://www.merriampark.com/anatomycc.htm
399   // [2] http://en.wikipedia.org/wiki/Bank_card_number
400   const size_t kMinCreditCardDigits = 12;
401   const size_t kMaxCreditCardDigits = 19;
402   if (number.size() < kMinCreditCardDigits ||
403       number.size() > kMaxCreditCardDigits)
404     return false;
405 
406   // Use the Luhn formula [3] to validate the number.
407   // [3] http://en.wikipedia.org/wiki/Luhn_algorithm
408   int sum = 0;
409   bool odd = false;
410   string16::reverse_iterator iter;
411   for (iter = number.rbegin(); iter != number.rend(); ++iter) {
412     if (!IsAsciiDigit(*iter))
413       return false;
414 
415     int digit = *iter - '0';
416     if (odd) {
417       digit *= 2;
418       sum += digit / 10 + digit % 10;
419     } else {
420       sum += digit;
421     }
422     odd = !odd;
423   }
424 
425   return (sum % 10) == 0;
426 }
427 
IsEmpty() const428 bool CreditCard::IsEmpty() const {
429   FieldTypeSet types;
430   GetAvailableFieldTypes(&types);
431   return types.empty();
432 }
433 
ExpirationMonthAsString() const434 string16 CreditCard::ExpirationMonthAsString() const {
435   if (expiration_month_ == 0)
436     return string16();
437 
438   string16 month = base::IntToString16(expiration_month_);
439   if (expiration_month_ >= 10)
440     return month;
441 
442   string16 zero = ASCIIToUTF16("0");
443   zero.append(month);
444   return zero;
445 }
446 
Expiration4DigitYearAsString() const447 string16 CreditCard::Expiration4DigitYearAsString() const {
448   if (expiration_year_ == 0)
449     return string16();
450 
451   return base::IntToString16(Expiration4DigitYear());
452 }
453 
Expiration2DigitYearAsString() const454 string16 CreditCard::Expiration2DigitYearAsString() const {
455   if (expiration_year_ == 0)
456     return string16();
457 
458   return base::IntToString16(Expiration2DigitYear());
459 }
460 
SetExpirationMonthFromString(const string16 & text)461 void CreditCard::SetExpirationMonthFromString(const string16& text) {
462   int month;
463   if (!ConvertDate(text, &month))
464     return;
465 
466   SetExpirationMonth(month);
467 }
468 
SetExpirationYearFromString(const string16 & text)469 void CreditCard::SetExpirationYearFromString(const string16& text) {
470   int year;
471   if (!ConvertDate(text, &year))
472     return;
473 
474   SetExpirationYear(year);
475 }
476 
SetNumber(const string16 & number)477 void CreditCard::SetNumber(const string16& number) {
478   number_ = number;
479   type_ = GetCreditCardType(StripSeparators(number_));
480 }
481 
SetExpirationMonth(int expiration_month)482 void CreditCard::SetExpirationMonth(int expiration_month) {
483   if (expiration_month < 0 || expiration_month > 12)
484     return;
485 
486   expiration_month_ = expiration_month;
487 }
488 
SetExpirationYear(int expiration_year)489 void CreditCard::SetExpirationYear(int expiration_year) {
490   if (expiration_year != 0 &&
491       (expiration_year < 2006 || expiration_year > 10000)) {
492     return;
493   }
494 
495   expiration_year_ = expiration_year;
496 }
497 
IsNumber(const string16 & text) const498 bool CreditCard::IsNumber(const string16& text) const {
499   return StripSeparators(text) == StripSeparators(number_);
500 }
501 
IsNameOnCard(const string16 & text) const502 bool CreditCard::IsNameOnCard(const string16& text) const {
503   return StringToLowerASCII(text) == StringToLowerASCII(name_on_card_);
504 }
505 
IsExpirationMonth(const string16 & text) const506 bool CreditCard::IsExpirationMonth(const string16& text) const {
507   int month;
508   if (!base::StringToInt(text, &month))
509     return false;
510 
511   return expiration_month_ == month;
512 }
513 
Is2DigitExpirationYear(const string16 & text) const514 bool CreditCard::Is2DigitExpirationYear(const string16& text) const {
515   int year;
516   if (!base::StringToInt(text, &year))
517     return false;
518 
519   return year < 100 && (expiration_year_ % 100) == year;
520 }
521 
Is4DigitExpirationYear(const string16 & text) const522 bool CreditCard::Is4DigitExpirationYear(const string16& text) const {
523   int year;
524   if (!base::StringToInt(text, &year))
525     return false;
526 
527   return expiration_year_ == year;
528 }
529 
530 // So we can compare CreditCards with EXPECT_EQ().
operator <<(std::ostream & os,const CreditCard & credit_card)531 std::ostream& operator<<(std::ostream& os, const CreditCard& credit_card) {
532   return os
533       << UTF16ToUTF8(credit_card.Label())
534       << " "
535       << credit_card.guid()
536       << " "
537       << UTF16ToUTF8(credit_card.GetInfo(CREDIT_CARD_NAME))
538       << " "
539       << UTF16ToUTF8(credit_card.GetInfo(CREDIT_CARD_TYPE))
540       << " "
541       << UTF16ToUTF8(credit_card.GetInfo(CREDIT_CARD_NUMBER))
542       << " "
543       << UTF16ToUTF8(credit_card.GetInfo(CREDIT_CARD_EXP_MONTH))
544       << " "
545       << UTF16ToUTF8(credit_card.GetInfo(CREDIT_CARD_EXP_4_DIGIT_YEAR));
546 }
547 
548 // These values must match the values in WebKitClientImpl in webkit/glue. We
549 // send these strings to WK, which then asks WebKitClientImpl to load the image
550 // data.
551 const char* const kAmericanExpressCard = "americanExpressCC";
552 const char* const kDinersCard = "dinersCC";
553 const char* const kDiscoverCard = "discoverCC";
554 const char* const kGenericCard = "genericCC";
555 const char* const kJCBCard = "jcbCC";
556 const char* const kMasterCard = "masterCardCC";
557 const char* const kSoloCard = "soloCC";
558 const char* const kVisaCard = "visaCC";
559