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