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