credit_card.cc revision 4a5e2dc747d50c653511c68ccb2cfbfb740bd5a7
1// Copyright (c) 2010 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 <string>
8
9#ifndef ANDROID
10// FIXME: Need l10n on Android?
11#include "app/l10n_util.h"
12#endif
13
14#include "base/basictypes.h"
15#include "base/string_util.h"
16#include "base/string_number_conversions.h"
17#include "base/string16.h"
18#include "base/utf_string_conversions.h"
19#include "chrome/browser/autofill/autofill_type.h"
20#include "chrome/browser/autofill/field_types.h"
21#include "chrome/browser/guid.h"
22#ifndef ANDROID
23#include "grit/generated_resources.h"
24#endif
25
26namespace {
27
28const char* kCreditCardObfuscationString = "************";
29
30const AutoFillFieldType kAutoFillCreditCardTypes[] = {
31  CREDIT_CARD_NAME,
32  CREDIT_CARD_NUMBER,
33  CREDIT_CARD_TYPE,
34  CREDIT_CARD_EXP_MONTH,
35  CREDIT_CARD_EXP_4_DIGIT_YEAR,
36};
37
38const int kAutoFillCreditCardLength = arraysize(kAutoFillCreditCardTypes);
39
40// These values must match the values in WebKitClientImpl in webkit/glue. We
41// send these strings to WK, which then asks WebKitClientImpl to load the image
42// data.
43const char* const kAmericanExpressCard = "americanExpressCC";
44const char* const kDinersCard = "dinersCC";
45const char* const kDiscoverCard = "discoverCC";
46const char* const kGenericCard = "genericCC";
47const char* const kJCBCard = "jcbCC";
48const char* const kMasterCard = "masterCardCC";
49const char* const kSoloCard = "soloCC";
50const char* const kVisaCard = "visaCC";
51
52std::string GetCreditCardType(const string16& number) {
53  // Credit card number specifications taken from:
54  // http://en.wikipedia.org/wiki/Credit_card_numbers and
55  // http://www.beachnet.com/~hstiles/cardtype.html
56  // Card Type              Prefix(es)                      Length
57  // ---------------------------------------------------------------
58  // Visa                   4                               13,16
59  // American Express       34,37                           15
60  // Diners Club            300-305,2014,2149,36,           14,15
61  // Discover Card          6011,65                         16
62  // JCB                    3                               16
63  // JCB                    2131,1800                       15
64  // MasterCard             51-55                           16
65  // Solo (debit card)      6334,6767                       16,18,19
66
67  // We need at least 4 digits to work with.
68  if (number.length() < 4)
69    return kGenericCard;
70
71  int first_four_digits = 0;
72  if (!base::StringToInt(number.substr(0, 4), &first_four_digits))
73    return kGenericCard;
74
75  int first_three_digits = first_four_digits / 10;
76  int first_two_digits = first_three_digits / 10;
77  int first_digit = first_two_digits / 10;
78
79  switch (number.length()) {
80    case 13:
81      if (first_digit == 4)
82        return kVisaCard;
83
84      break;
85    case 14:
86      if (first_three_digits >= 300 && first_three_digits <=305)
87        return kDinersCard;
88
89      if (first_digit == 36)
90        return kDinersCard;
91
92      break;
93    case 15:
94      if (first_two_digits == 34 || first_two_digits == 37)
95        return kAmericanExpressCard;
96
97      if (first_four_digits == 2131 || first_four_digits == 1800)
98        return kJCBCard;
99
100      if (first_four_digits == 2014 || first_four_digits == 2149)
101        return kDinersCard;
102
103      break;
104    case 16:
105      if (first_four_digits == 6011 || first_two_digits == 65)
106        return kDiscoverCard;
107
108      if (first_four_digits == 6334 || first_four_digits == 6767)
109        return kSoloCard;
110
111      if (first_two_digits >= 51 && first_two_digits <= 55)
112        return kMasterCard;
113
114      if (first_digit == 3)
115        return kJCBCard;
116
117      if (first_digit == 4)
118        return kVisaCard;
119
120      break;
121    case 18:
122    case 19:
123      if (first_four_digits == 6334 || first_four_digits == 6767)
124        return kSoloCard;
125
126      break;
127  }
128
129  return kGenericCard;
130}
131
132// Return a version of |number| that has had any non-digit values removed.
133const string16 RemoveNonAsciiDigits(const string16& number) {
134  string16 stripped;
135  for (size_t i = 0; i < number.size(); ++i) {
136    if (IsAsciiDigit(number[i]))
137      stripped.append(1, number[i]);
138  }
139  return stripped;
140}
141
142}  // namespace
143
144CreditCard::CreditCard(const std::string& guid)
145    : expiration_month_(0),
146      expiration_year_(0),
147      guid_(guid) {
148}
149
150CreditCard::CreditCard()
151    : expiration_month_(0),
152      expiration_year_(0),
153      guid_(guid::GenerateGUID()) {
154}
155
156CreditCard::CreditCard(const CreditCard& credit_card) : FormGroup() {
157  operator=(credit_card);
158}
159
160CreditCard::~CreditCard() {}
161
162FormGroup* CreditCard::Clone() const {
163  return new CreditCard(*this);
164}
165
166void CreditCard::GetPossibleFieldTypes(const string16& text,
167                                       FieldTypeSet* possible_types) const {
168  if (IsNameOnCard(text))
169    possible_types->insert(CREDIT_CARD_NAME);
170
171  if (IsCreditCardNumber(text))
172    possible_types->insert(CREDIT_CARD_NUMBER);
173
174  if (IsExpirationMonth(text))
175    possible_types->insert(CREDIT_CARD_EXP_MONTH);
176
177  if (Is2DigitExpirationYear(text))
178    possible_types->insert(CREDIT_CARD_EXP_2_DIGIT_YEAR);
179
180  if (Is4DigitExpirationYear(text))
181    possible_types->insert(CREDIT_CARD_EXP_4_DIGIT_YEAR);
182}
183
184void CreditCard::GetAvailableFieldTypes(FieldTypeSet* available_types) const {
185  DCHECK(available_types);
186
187  if (!name_on_card().empty())
188    available_types->insert(CREDIT_CARD_NAME);
189
190  if (!ExpirationMonthAsString().empty())
191    available_types->insert(CREDIT_CARD_EXP_MONTH);
192
193  if (!Expiration2DigitYearAsString().empty())
194    available_types->insert(CREDIT_CARD_EXP_2_DIGIT_YEAR);
195
196  if (!Expiration4DigitYearAsString().empty())
197    available_types->insert(CREDIT_CARD_EXP_4_DIGIT_YEAR);
198
199  if (!number().empty())
200    available_types->insert(CREDIT_CARD_NUMBER);
201}
202
203void CreditCard::FindInfoMatches(const AutoFillType& type,
204                                 const string16& info,
205                                 std::vector<string16>* matched_text) const {
206  DCHECK(matched_text);
207
208  string16 match;
209  switch (type.field_type()) {
210    case CREDIT_CARD_NUMBER: {
211      // Because the credit card number is encrypted and we are not able to do
212      // comparisons with it we will say that any field that is known to be a
213      // credit card number field will match all credit card numbers.
214      string16 text = GetPreviewText(AutoFillType(CREDIT_CARD_NUMBER));
215      if (!text.empty())
216        matched_text->push_back(text);
217      break;
218    }
219
220    case CREDIT_CARD_VERIFICATION_CODE:
221      NOTREACHED();
222      break;
223
224    case UNKNOWN_TYPE:
225      for (int i = 0; i < kAutoFillCreditCardLength; ++i) {
226        if (FindInfoMatchesHelper(kAutoFillCreditCardTypes[i], info, &match))
227          matched_text->push_back(match);
228      }
229      break;
230
231    default:
232      if (FindInfoMatchesHelper(type.field_type(), info, &match))
233        matched_text->push_back(match);
234      break;
235  }
236}
237
238string16 CreditCard::GetFieldText(const AutoFillType& type) const {
239  switch (type.field_type()) {
240    case CREDIT_CARD_NAME:
241      return name_on_card();
242
243    case CREDIT_CARD_EXP_MONTH:
244      return ExpirationMonthAsString();
245
246    case CREDIT_CARD_EXP_2_DIGIT_YEAR:
247      return Expiration2DigitYearAsString();
248
249    case CREDIT_CARD_EXP_4_DIGIT_YEAR:
250      return Expiration4DigitYearAsString();
251
252    case CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR: {
253      string16 month = ExpirationMonthAsString();
254      string16 year = Expiration2DigitYearAsString();
255      if (!month.empty() && !year.empty())
256        return month + ASCIIToUTF16("/") + year;
257      return string16();
258    }
259
260    case CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR: {
261      string16 month = ExpirationMonthAsString();
262      string16 year = Expiration4DigitYearAsString();
263      if (!month.empty() && !year.empty())
264        return month + ASCIIToUTF16("/") + year;
265      return string16();
266    }
267
268    case CREDIT_CARD_TYPE:
269      // We don't handle this case.
270      return string16();
271
272    case CREDIT_CARD_NUMBER:
273      return number();
274
275    case CREDIT_CARD_VERIFICATION_CODE:
276      NOTREACHED();
277      return string16();
278
279    default:
280      // ComputeDataPresentForArray will hit this repeatedly.
281      return string16();
282  }
283}
284
285string16 CreditCard::GetPreviewText(const AutoFillType& type) const {
286  switch (type.field_type()) {
287    case CREDIT_CARD_NUMBER:
288      return last_four_digits();
289
290    case CREDIT_CARD_VERIFICATION_CODE:
291      NOTREACHED();
292      return string16();
293
294    default:
295      return GetFieldText(type);
296  }
297}
298
299void CreditCard::SetInfo(const AutoFillType& type, const string16& value) {
300  switch (type.field_type()) {
301    case CREDIT_CARD_NAME:
302      set_name_on_card(value);
303      break;
304
305    case CREDIT_CARD_EXP_MONTH:
306      SetExpirationMonthFromString(value);
307      break;
308
309    case CREDIT_CARD_EXP_2_DIGIT_YEAR:
310      // This is a read-only attribute.
311      break;
312
313    case CREDIT_CARD_EXP_4_DIGIT_YEAR:
314      SetExpirationYearFromString(value);
315      break;
316
317    case CREDIT_CARD_TYPE:
318      // We determine the type based on the number.
319      break;
320
321    case CREDIT_CARD_NUMBER: {
322      if (StartsWith(value, ASCIIToUTF16(kCreditCardObfuscationString), true)) {
323        // this is an obfuscated string. Do not change the real value.
324        break;
325      }
326      set_number(value);
327      set_type(ASCIIToUTF16(GetCreditCardType(number())));
328      // Update last four digits as well.
329      if (value.length() > 4)
330        set_last_four_digits(value.substr(value.length() - 4));
331      else
332        set_last_four_digits(string16());
333    }  break;
334
335    case CREDIT_CARD_VERIFICATION_CODE:
336      NOTREACHED();
337      break;
338
339    default:
340      DLOG(ERROR) << "Attempting to set unknown info-type "
341                  << type.field_type();
342      break;
343  }
344}
345
346string16 CreditCard::ObfuscatedNumber() const {
347  if (number().empty())
348    return string16();  // No CC number, means empty preview.
349  string16 result(ASCIIToUTF16(kCreditCardObfuscationString));
350  result.append(last_four_digits());
351
352  return result;
353}
354
355string16 CreditCard::PreviewSummary() const {
356#ifdef ANDROID
357  // TODO: Hook up credit card support on Android.
358  // What is the Android UX for autofill previews?
359  // This has to be #if #else #endif as we can't
360  // compile the Chromium code that uses l10n functions.
361  return string16(ASCIIToUTF16("1234 5678 9123 4567"));
362#else
363  string16 preview;
364  if (number().empty())
365    return preview;  // No CC number, means empty preview.
366  string16 obfuscated_cc_number = ObfuscatedNumber();
367  if (!expiration_month() || !expiration_year())
368    return obfuscated_cc_number;  // No expiration date set.
369  // TODO(georgey): Internationalize date.
370  string16 formatted_date(ExpirationMonthAsString());
371  formatted_date.append(ASCIIToUTF16("/"));
372  formatted_date.append(Expiration4DigitYearAsString());
373
374  preview = l10n_util::GetStringFUTF16(
375      IDS_CREDIT_CARD_NUMBER_PREVIEW_FORMAT,
376      obfuscated_cc_number,
377      formatted_date);
378  return preview;
379#endif
380}
381
382string16 CreditCard::LastFourDigits() const {
383  static const size_t kNumLastDigits = 4;
384
385  if (number().size() < kNumLastDigits)
386    return string16();
387
388  return number().substr(number().size() - kNumLastDigits, kNumLastDigits);
389}
390
391void CreditCard::operator=(const CreditCard& credit_card) {
392  number_ = credit_card.number_;
393  name_on_card_ = credit_card.name_on_card_;
394  type_ = credit_card.type_;
395  last_four_digits_ = credit_card.last_four_digits_;
396  expiration_month_ = credit_card.expiration_month_;
397  expiration_year_ = credit_card.expiration_year_;
398  label_ = credit_card.label_;
399  guid_ = credit_card.guid_;
400}
401
402int CreditCard::Compare(const CreditCard& credit_card) const {
403  // The following CreditCard field types are the only types we store in the
404  // WebDB so far, so we're only concerned with matching these types in the
405  // credit card.
406  const AutoFillFieldType types[] = { CREDIT_CARD_NAME,
407                                      CREDIT_CARD_NUMBER,
408                                      CREDIT_CARD_EXP_MONTH,
409                                      CREDIT_CARD_EXP_4_DIGIT_YEAR };
410  for (size_t index = 0; index < arraysize(types); ++index) {
411    int comparison = GetFieldText(AutoFillType(types[index])).compare(
412        credit_card.GetFieldText(AutoFillType(types[index])));
413    if (comparison != 0)
414      return comparison;
415  }
416
417  return 0;
418}
419
420bool CreditCard::operator==(const CreditCard& credit_card) const {
421  if (label_ != credit_card.label_ || guid_ != credit_card.guid_)
422    return false;
423
424  return Compare(credit_card) == 0;
425}
426
427bool CreditCard::operator!=(const CreditCard& credit_card) const {
428  return !operator==(credit_card);
429}
430
431// Use the Luhn formula to validate the number.
432// static
433bool CreditCard::IsCreditCardNumber(const string16& text) {
434  string16 number = RemoveNonAsciiDigits(text);
435
436  if (number.empty())
437    return false;
438
439  int sum = 0;
440  bool odd = false;
441  string16::reverse_iterator iter;
442  for (iter = number.rbegin(); iter != number.rend(); ++iter) {
443    if (!IsAsciiDigit(*iter))
444      return false;
445
446    int digit = *iter - '0';
447    if (odd) {
448      digit *= 2;
449      sum += digit / 10 + digit % 10;
450    } else {
451      sum += digit;
452    }
453    odd = !odd;
454  }
455
456  return (sum % 10) == 0;
457}
458
459bool CreditCard::IsEmpty() const {
460  FieldTypeSet types;
461  GetAvailableFieldTypes(&types);
462  return types.empty();
463}
464
465string16 CreditCard::ExpirationMonthAsString() const {
466  if (expiration_month_ == 0)
467    return string16();
468
469  string16 month = base::IntToString16(expiration_month_);
470  if (expiration_month_ >= 10)
471    return month;
472
473  string16 zero = ASCIIToUTF16("0");
474  zero.append(month);
475  return zero;
476}
477
478string16 CreditCard::Expiration4DigitYearAsString() const {
479  if (expiration_year_ == 0)
480    return string16();
481
482  return base::IntToString16(Expiration4DigitYear());
483}
484
485string16 CreditCard::Expiration2DigitYearAsString() const {
486  if (expiration_year_ == 0)
487    return string16();
488
489  return base::IntToString16(Expiration2DigitYear());
490}
491
492void CreditCard::SetExpirationMonthFromString(const string16& text) {
493  int month;
494  if (!ConvertDate(text, &month))
495    return;
496
497  set_expiration_month(month);
498}
499
500void CreditCard::SetExpirationYearFromString(const string16& text) {
501  int year;
502  if (!ConvertDate(text, &year))
503    return;
504
505  set_expiration_year(year);
506}
507
508void CreditCard::set_number(const string16& number) {
509  number_ = RemoveNonAsciiDigits(number);
510}
511
512void CreditCard::set_expiration_month(int expiration_month) {
513  if (expiration_month < 0 || expiration_month > 12)
514    return;
515
516  expiration_month_ = expiration_month;
517}
518
519void CreditCard::set_expiration_year(int expiration_year) {
520  if (expiration_year != 0 &&
521      (expiration_year < 2006 || expiration_year > 10000)) {
522    return;
523  }
524
525  expiration_year_ = expiration_year;
526}
527
528bool CreditCard::FindInfoMatchesHelper(const AutoFillFieldType& field_type,
529                                       const string16& info,
530                                       string16* match) const {
531  DCHECK(match);
532
533  match->clear();
534  switch (field_type) {
535    case CREDIT_CARD_NAME: {
536      if (StartsWith(name_on_card(), info, false))
537        *match = name_on_card();
538      break;
539    }
540
541    case CREDIT_CARD_EXP_MONTH: {
542      string16 exp_month(ExpirationMonthAsString());
543      if (StartsWith(exp_month, info, true))
544        *match = exp_month;
545      break;
546    }
547
548    case CREDIT_CARD_EXP_2_DIGIT_YEAR: {
549      string16 exp_year(Expiration2DigitYearAsString());
550      if (StartsWith(exp_year, info, true))
551        *match = exp_year;
552      break;
553    }
554
555    case CREDIT_CARD_EXP_4_DIGIT_YEAR: {
556      string16 exp_year(Expiration4DigitYearAsString());
557      if (StartsWith(exp_year, info, true))
558        *match = exp_year;
559      break;
560    }
561
562    case CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR: {
563      string16 exp_date(ExpirationMonthAsString() + ASCIIToUTF16("/") +
564                        Expiration2DigitYearAsString());
565      if (StartsWith(exp_date, info, true))
566        *match = exp_date;
567      break;
568    }
569
570    case CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR: {
571      string16 exp_date(ExpirationMonthAsString() + ASCIIToUTF16("/") +
572                        Expiration4DigitYearAsString());
573      if (StartsWith(exp_date, info, true))
574        *match = exp_date;
575      break;
576    }
577
578    case CREDIT_CARD_TYPE:
579      // We don't handle this case.
580      break;
581
582    case CREDIT_CARD_VERIFICATION_CODE:
583      NOTREACHED();
584      break;
585
586    default:
587      break;
588  }
589  return !match->empty();
590}
591
592bool CreditCard::IsNameOnCard(const string16& text) const {
593  return StringToLowerASCII(text) == StringToLowerASCII(name_on_card_);
594}
595
596bool CreditCard::IsExpirationMonth(const string16& text) const {
597  int month;
598  if (!base::StringToInt(text, &month))
599    return false;
600
601  return expiration_month_ == month;
602}
603
604bool CreditCard::Is2DigitExpirationYear(const string16& text) const {
605  int year;
606  if (!base::StringToInt(text, &year))
607    return false;
608
609  return year < 100 && (expiration_year_ % 100) == year;
610}
611
612bool CreditCard::Is4DigitExpirationYear(const string16& text) const {
613  int year;
614  if (!base::StringToInt(text, &year))
615    return false;
616
617  return expiration_year_ == year;
618}
619
620bool CreditCard::ConvertDate(const string16& date, int* num) const {
621  if (!date.empty()) {
622    bool converted = base::StringToInt(date, num);
623    DCHECK(converted);
624    if (!converted)
625      return false;
626  } else {
627    // Clear the value.
628    *num = 0;
629  }
630
631  return true;
632}
633
634// So we can compare CreditCards with EXPECT_EQ().
635std::ostream& operator<<(std::ostream& os, const CreditCard& credit_card) {
636  return os
637      << UTF16ToUTF8(credit_card.Label())
638      << " "
639      << credit_card.guid()
640      << " "
641      << UTF16ToUTF8(credit_card.GetFieldText(AutoFillType(CREDIT_CARD_NAME)))
642      << " "
643      << UTF16ToUTF8(credit_card.GetFieldText(AutoFillType(CREDIT_CARD_TYPE)))
644      << " "
645      << UTF16ToUTF8(credit_card.GetFieldText(AutoFillType(CREDIT_CARD_NUMBER)))
646      << " "
647      << UTF16ToUTF8(credit_card.GetFieldText(
648             AutoFillType(CREDIT_CARD_EXP_MONTH)))
649      << " "
650      << UTF16ToUTF8(credit_card.GetFieldText(
651             AutoFillType(CREDIT_CARD_EXP_4_DIGIT_YEAR)));
652}
653