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
24namespace {
25
26const char16 kCreditCardObfuscationSymbol = '*';
27
28const 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
36const int kAutofillCreditCardLength = arraysize(kAutofillCreditCardTypes);
37
38std::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
122bool 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
138CreditCard::CreditCard(const std::string& guid)
139    : type_(kGenericCard),
140      expiration_month_(0),
141      expiration_year_(0),
142      guid_(guid) {
143}
144
145CreditCard::CreditCard()
146    : type_(kGenericCard),
147      expiration_month_(0),
148      expiration_year_(0),
149      guid_(guid::GenerateGUID()) {
150}
151
152CreditCard::CreditCard(const CreditCard& credit_card) : FormGroup() {
153  operator=(credit_card);
154}
155
156CreditCard::~CreditCard() {}
157
158void 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
176void 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
195string16 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
242void 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
281const 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
303void 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
321string16 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
333string16 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
343void 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
355int 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
373bool 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
380bool CreditCard::operator!=(const CreditCard& credit_card) const {
381  return !operator==(credit_card);
382}
383
384// static
385const 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
393bool 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
428bool CreditCard::IsEmpty() const {
429  FieldTypeSet types;
430  GetAvailableFieldTypes(&types);
431  return types.empty();
432}
433
434string16 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
447string16 CreditCard::Expiration4DigitYearAsString() const {
448  if (expiration_year_ == 0)
449    return string16();
450
451  return base::IntToString16(Expiration4DigitYear());
452}
453
454string16 CreditCard::Expiration2DigitYearAsString() const {
455  if (expiration_year_ == 0)
456    return string16();
457
458  return base::IntToString16(Expiration2DigitYear());
459}
460
461void CreditCard::SetExpirationMonthFromString(const string16& text) {
462  int month;
463  if (!ConvertDate(text, &month))
464    return;
465
466  SetExpirationMonth(month);
467}
468
469void CreditCard::SetExpirationYearFromString(const string16& text) {
470  int year;
471  if (!ConvertDate(text, &year))
472    return;
473
474  SetExpirationYear(year);
475}
476
477void CreditCard::SetNumber(const string16& number) {
478  number_ = number;
479  type_ = GetCreditCardType(StripSeparators(number_));
480}
481
482void CreditCard::SetExpirationMonth(int expiration_month) {
483  if (expiration_month < 0 || expiration_month > 12)
484    return;
485
486  expiration_month_ = expiration_month;
487}
488
489void 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
498bool CreditCard::IsNumber(const string16& text) const {
499  return StripSeparators(text) == StripSeparators(number_);
500}
501
502bool CreditCard::IsNameOnCard(const string16& text) const {
503  return StringToLowerASCII(text) == StringToLowerASCII(name_on_card_);
504}
505
506bool 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
514bool 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
522bool 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().
531std::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.
551const char* const kAmericanExpressCard = "americanExpressCC";
552const char* const kDinersCard = "dinersCC";
553const char* const kDiscoverCard = "discoverCC";
554const char* const kGenericCard = "genericCC";
555const char* const kJCBCard = "jcbCC";
556const char* const kMasterCard = "masterCardCC";
557const char* const kSoloCard = "soloCC";
558const char* const kVisaCard = "visaCC";
559