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