credit_card.cc revision 944a57ef65ab1c9e826dddae33fb347063d87fbc
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/common/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
346const string16 CreditCard::Label() const {
347  return label_;
348}
349
350string16 CreditCard::ObfuscatedNumber() const {
351  if (number().empty())
352    return string16();  // No CC number, means empty preview.
353  string16 result(ASCIIToUTF16(kCreditCardObfuscationString));
354  result.append(last_four_digits());
355
356  return result;
357}
358
359string16 CreditCard::PreviewSummary() const {
360#ifdef ANDROID
361  // TODO: Hook up credit card support on Android.
362  // What is the Android UX for autofill previews?
363  // This has to be #if #else #endif as we can't
364  // compile the Chromium code that uses l10n functions.
365  return string16(ASCIIToUTF16("1234 5678 9123 4567"));
366#else
367  string16 preview;
368  if (number().empty())
369    return preview;  // No CC number, means empty preview.
370  string16 obfuscated_cc_number = ObfuscatedNumber();
371  if (!expiration_month() || !expiration_year())
372    return obfuscated_cc_number;  // No expiration date set.
373  // TODO(georgey): Internationalize date.
374  string16 formatted_date(ExpirationMonthAsString());
375  formatted_date.append(ASCIIToUTF16("/"));
376  formatted_date.append(Expiration4DigitYearAsString());
377
378  preview = l10n_util::GetStringFUTF16(
379      IDS_CREDIT_CARD_NUMBER_PREVIEW_FORMAT,
380      obfuscated_cc_number,
381      formatted_date);
382  return preview;
383#endif
384}
385
386string16 CreditCard::LastFourDigits() const {
387  static const size_t kNumLastDigits = 4;
388
389  if (number().size() < kNumLastDigits)
390    return string16();
391
392  return number().substr(number().size() - kNumLastDigits, kNumLastDigits);
393}
394
395void CreditCard::operator=(const CreditCard& credit_card) {
396  if (this == &credit_card)
397    return;
398
399  number_ = credit_card.number_;
400  name_on_card_ = credit_card.name_on_card_;
401  type_ = credit_card.type_;
402  last_four_digits_ = credit_card.last_four_digits_;
403  expiration_month_ = credit_card.expiration_month_;
404  expiration_year_ = credit_card.expiration_year_;
405  label_ = credit_card.label_;
406  guid_ = credit_card.guid_;
407}
408
409int CreditCard::Compare(const CreditCard& credit_card) const {
410  // The following CreditCard field types are the only types we store in the
411  // WebDB so far, so we're only concerned with matching these types in the
412  // credit card.
413  const AutoFillFieldType types[] = { CREDIT_CARD_NAME,
414                                      CREDIT_CARD_NUMBER,
415                                      CREDIT_CARD_EXP_MONTH,
416                                      CREDIT_CARD_EXP_4_DIGIT_YEAR };
417  for (size_t index = 0; index < arraysize(types); ++index) {
418    int comparison = GetFieldText(AutoFillType(types[index])).compare(
419        credit_card.GetFieldText(AutoFillType(types[index])));
420    if (comparison != 0)
421      return comparison;
422  }
423
424  return 0;
425}
426
427bool CreditCard::operator==(const CreditCard& credit_card) const {
428  if (label_ != credit_card.label_ || guid_ != credit_card.guid_)
429    return false;
430
431  return Compare(credit_card) == 0;
432}
433
434bool CreditCard::operator!=(const CreditCard& credit_card) const {
435  return !operator==(credit_card);
436}
437
438// Use the Luhn formula to validate the number.
439// static
440bool CreditCard::IsCreditCardNumber(const string16& text) {
441  string16 number = RemoveNonAsciiDigits(text);
442
443  if (number.empty())
444    return false;
445
446  int sum = 0;
447  bool odd = false;
448  string16::reverse_iterator iter;
449  for (iter = number.rbegin(); iter != number.rend(); ++iter) {
450    if (!IsAsciiDigit(*iter))
451      return false;
452
453    int digit = *iter - '0';
454    if (odd) {
455      digit *= 2;
456      sum += digit / 10 + digit % 10;
457    } else {
458      sum += digit;
459    }
460    odd = !odd;
461  }
462
463  return (sum % 10) == 0;
464}
465
466bool CreditCard::IsEmpty() const {
467  FieldTypeSet types;
468  GetAvailableFieldTypes(&types);
469  return types.empty();
470}
471
472string16 CreditCard::ExpirationMonthAsString() const {
473  if (expiration_month_ == 0)
474    return string16();
475
476  string16 month = base::IntToString16(expiration_month_);
477  if (expiration_month_ >= 10)
478    return month;
479
480  string16 zero = ASCIIToUTF16("0");
481  zero.append(month);
482  return zero;
483}
484
485string16 CreditCard::Expiration4DigitYearAsString() const {
486  if (expiration_year_ == 0)
487    return string16();
488
489  return base::IntToString16(Expiration4DigitYear());
490}
491
492string16 CreditCard::Expiration2DigitYearAsString() const {
493  if (expiration_year_ == 0)
494    return string16();
495
496  return base::IntToString16(Expiration2DigitYear());
497}
498
499void CreditCard::SetExpirationMonthFromString(const string16& text) {
500  int month;
501  if (!ConvertDate(text, &month))
502    return;
503
504  set_expiration_month(month);
505}
506
507void CreditCard::SetExpirationYearFromString(const string16& text) {
508  int year;
509  if (!ConvertDate(text, &year))
510    return;
511
512  set_expiration_year(year);
513}
514
515void CreditCard::set_number(const string16& number) {
516  number_ = RemoveNonAsciiDigits(number);
517}
518
519void CreditCard::set_expiration_month(int expiration_month) {
520  if (expiration_month < 0 || expiration_month > 12)
521    return;
522
523  expiration_month_ = expiration_month;
524}
525
526void CreditCard::set_expiration_year(int expiration_year) {
527  if (expiration_year != 0 &&
528      (expiration_year < 2006 || expiration_year > 10000)) {
529    return;
530  }
531
532  expiration_year_ = expiration_year;
533}
534
535bool CreditCard::FindInfoMatchesHelper(const AutoFillFieldType& field_type,
536                                       const string16& info,
537                                       string16* match) const {
538  DCHECK(match);
539
540  match->clear();
541  switch (field_type) {
542    case CREDIT_CARD_NAME: {
543      if (StartsWith(name_on_card(), info, false))
544        *match = name_on_card();
545      break;
546    }
547
548    case CREDIT_CARD_EXP_MONTH: {
549      string16 exp_month(ExpirationMonthAsString());
550      if (StartsWith(exp_month, info, true))
551        *match = exp_month;
552      break;
553    }
554
555    case CREDIT_CARD_EXP_2_DIGIT_YEAR: {
556      string16 exp_year(Expiration2DigitYearAsString());
557      if (StartsWith(exp_year, info, true))
558        *match = exp_year;
559      break;
560    }
561
562    case CREDIT_CARD_EXP_4_DIGIT_YEAR: {
563      string16 exp_year(Expiration4DigitYearAsString());
564      if (StartsWith(exp_year, info, true))
565        *match = exp_year;
566      break;
567    }
568
569    case CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR: {
570      string16 exp_date(ExpirationMonthAsString() + ASCIIToUTF16("/") +
571                        Expiration2DigitYearAsString());
572      if (StartsWith(exp_date, info, true))
573        *match = exp_date;
574      break;
575    }
576
577    case CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR: {
578      string16 exp_date(ExpirationMonthAsString() + ASCIIToUTF16("/") +
579                        Expiration4DigitYearAsString());
580      if (StartsWith(exp_date, info, true))
581        *match = exp_date;
582      break;
583    }
584
585    case CREDIT_CARD_TYPE:
586      // We don't handle this case.
587      break;
588
589    case CREDIT_CARD_VERIFICATION_CODE:
590      NOTREACHED();
591      break;
592
593    default:
594      break;
595  }
596  return !match->empty();
597}
598
599bool CreditCard::IsNameOnCard(const string16& text) const {
600  return StringToLowerASCII(text) == StringToLowerASCII(name_on_card_);
601}
602
603bool CreditCard::IsExpirationMonth(const string16& text) const {
604  int month;
605  if (!base::StringToInt(text, &month))
606    return false;
607
608  return expiration_month_ == month;
609}
610
611bool CreditCard::Is2DigitExpirationYear(const string16& text) const {
612  int year;
613  if (!base::StringToInt(text, &year))
614    return false;
615
616  return year < 100 && (expiration_year_ % 100) == year;
617}
618
619bool CreditCard::Is4DigitExpirationYear(const string16& text) const {
620  int year;
621  if (!base::StringToInt(text, &year))
622    return false;
623
624  return expiration_year_ == year;
625}
626
627bool CreditCard::ConvertDate(const string16& date, int* num) const {
628  if (!date.empty()) {
629    bool converted = base::StringToInt(date, num);
630    DCHECK(converted);
631    if (!converted)
632      return false;
633  } else {
634    // Clear the value.
635    *num = 0;
636  }
637
638  return true;
639}
640
641// So we can compare CreditCards with EXPECT_EQ().
642std::ostream& operator<<(std::ostream& os, const CreditCard& credit_card) {
643  return os
644      << UTF16ToUTF8(credit_card.Label())
645      << " "
646      << credit_card.guid()
647      << " "
648      << UTF16ToUTF8(credit_card.GetFieldText(AutoFillType(CREDIT_CARD_NAME)))
649      << " "
650      << UTF16ToUTF8(credit_card.GetFieldText(AutoFillType(CREDIT_CARD_TYPE)))
651      << " "
652      << UTF16ToUTF8(credit_card.GetFieldText(AutoFillType(CREDIT_CARD_NUMBER)))
653      << " "
654      << UTF16ToUTF8(credit_card.GetFieldText(
655             AutoFillType(CREDIT_CARD_EXP_MONTH)))
656      << " "
657      << UTF16ToUTF8(credit_card.GetFieldText(
658             AutoFillType(CREDIT_CARD_EXP_4_DIGIT_YEAR)));
659}
660