// Copyright 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "components/autofill/core/browser/credit_card.h" #include #include #include #include #include #include "base/guid.h" #include "base/i18n/string_search.h" #include "base/i18n/time_formatting.h" #include "base/i18n/unicodestring.h" #include "base/logging.h" #include "base/macros.h" #include "base/metrics/histogram_macros.h" #include "base/strings/string16.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_piece.h" #include "base/strings/string_split.h" #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" #include "base/time/time.h" #include "build/build_config.h" #include "components/autofill/core/browser/autofill_data_util.h" #include "components/autofill/core/browser/autofill_experiments.h" #include "components/autofill/core/browser/autofill_field.h" #include "components/autofill/core/browser/autofill_type.h" #include "components/autofill/core/browser/validation.h" #include "components/autofill/core/common/autofill_clock.h" #include "components/autofill/core/common/autofill_regexes.h" #include "components/autofill/core/common/form_field_data.h" #include "components/grit/components_scaled_resources.h" #include "components/strings/grit/components_strings.h" #include "third_party/icu/source/common/unicode/uloc.h" #include "third_party/icu/source/i18n/unicode/dtfmtsym.h" #include "ui/base/l10n/l10n_util.h" using base::ASCIIToUTF16; namespace autofill { const base::char16 kMidlineEllipsis[] = { 0x0020, 0x0020, 0x2022, 0x2006, 0x2022, 0x2006, 0x2022, 0x2006, 0x2022, 0x2006, 0 }; namespace { const base::char16 kCreditCardObfuscationSymbol = '*'; // Time format pattern for short month name (3 digits) and day in month (2 // digits), e.g. in en-US locale, it can be used to generate "Feb 02". const char kTimeFormatPatternNoYearShortMonthDate[] = "MMMdd"; bool ConvertYear(const base::string16& year, int* num) { // If the |year| is empty, clear the stored value. if (year.empty()) { *num = 0; return true; } // Try parsing the |year| as a number. if (base::StringToInt(year, num)) return true; *num = 0; return false; } base::string16 NetworkForFill(const std::string& network) { if (network == kAmericanExpressCard) return l10n_util::GetStringUTF16(IDS_AUTOFILL_CC_AMEX); if (network == kDinersCard) return l10n_util::GetStringUTF16(IDS_AUTOFILL_CC_DINERS); if (network == kDiscoverCard) return l10n_util::GetStringUTF16(IDS_AUTOFILL_CC_DISCOVER); if (network == kEloCard) return l10n_util::GetStringUTF16(IDS_AUTOFILL_CC_ELO); if (network == kJCBCard) return l10n_util::GetStringUTF16(IDS_AUTOFILL_CC_JCB); if (network == kMasterCard) return l10n_util::GetStringUTF16(IDS_AUTOFILL_CC_MASTERCARD); if (network == kMirCard) return l10n_util::GetStringUTF16(IDS_AUTOFILL_CC_MIR); if (network == kUnionPay) return l10n_util::GetStringUTF16(IDS_AUTOFILL_CC_UNION_PAY); if (network == kVisaCard) return l10n_util::GetStringUTF16(IDS_AUTOFILL_CC_VISA); // If you hit this DCHECK, the above list of cases needs to be updated to // include a new card. DCHECK_EQ(kGenericCard, network); return base::string16(); } // Returns the last four digits of the credit card |number| (fewer if there are // not enough characters in |number|). base::string16 GetLastFourDigits(const base::string16& number) { static const size_t kNumLastDigits = 4; base::string16 stripped = CreditCard::StripSeparators(number); if (stripped.size() <= kNumLastDigits) return stripped; return stripped.substr(stripped.size() - kNumLastDigits, kNumLastDigits); } } // namespace CreditCard::CreditCard(const std::string& guid, const std::string& origin) : AutofillDataModel(guid, origin), record_type_(LOCAL_CARD), card_type_(CARD_TYPE_UNKNOWN), network_(kGenericCard), expiration_month_(0), expiration_year_(0), server_status_(OK) {} CreditCard::CreditCard(RecordType type, const std::string& server_id) : CreditCard() { DCHECK(type == MASKED_SERVER_CARD || type == FULL_SERVER_CARD); record_type_ = type; server_id_ = server_id; } CreditCard::CreditCard() : CreditCard(base::GenerateGUID(), std::string()) {} CreditCard::CreditCard(const CreditCard& credit_card) : CreditCard() { operator=(credit_card); } CreditCard::~CreditCard() {} // static const base::string16 CreditCard::StripSeparators(const base::string16& number) { base::string16 stripped; base::RemoveChars(number, ASCIIToUTF16("- "), &stripped); return stripped; } // static base::string16 CreditCard::NetworkForDisplay(const std::string& network) { if (kGenericCard == network) return l10n_util::GetStringUTF16(IDS_AUTOFILL_CC_GENERIC); if (kAmericanExpressCard == network) return l10n_util::GetStringUTF16(IDS_AUTOFILL_CC_AMEX_SHORT); return ::autofill::NetworkForFill(network); } // static int CreditCard::IconResourceId(const std::string& network) { if (network == kAmericanExpressCard) return IDR_AUTOFILL_CC_AMEX; if (network == kDinersCard) return IDR_AUTOFILL_CC_DINERS; if (network == kDiscoverCard) return IDR_AUTOFILL_CC_DISCOVER; if (network == kEloCard) return IDR_AUTOFILL_CC_ELO; if (network == kJCBCard) return IDR_AUTOFILL_CC_JCB; if (network == kMasterCard) return IDR_AUTOFILL_CC_MASTERCARD; if (network == kMirCard) return IDR_AUTOFILL_CC_MIR; if (network == kUnionPay) return IDR_AUTOFILL_CC_UNIONPAY; if (network == kVisaCard) return IDR_AUTOFILL_CC_VISA; // If you hit this DCHECK, the above list of cases needs to be updated to // include a new card. DCHECK_EQ(kGenericCard, network); return IDR_AUTOFILL_CC_GENERIC; } // static const char* CreditCard::GetCardNetwork(const base::string16& number) { // Credit card number specifications taken from: // https://en.wikipedia.org/wiki/Payment_card_number, // http://www.regular-expressions.info/creditcard.html, // https://developer.ean.com/general-info/valid-card-types, // http://www.bincodes.com/, and // http://www.fraudpractice.com/FL-binCC.html. // (Last updated: May 29, 2017) // // Card Type Prefix(es) Length // -------------------------------------------------------------------------- // Visa 4 13,16,19 // American Express 34,37 15 // Diners Club 300-305,309,36,38-39 14 // Discover Card 6011,644-649,65 16 // Elo 431274,451416,5067,5090,627780,636297 16 // JCB 3528-3589 16 // Mastercard 2221-2720, 51-55 16 // MIR 2200-2204 16 // UnionPay 62 16-19 // Check for prefixes of length 1. if (number.empty()) return kGenericCard; if (number[0] == '4') return kVisaCard; // Check for prefixes of length 2. if (number.size() < 2) return kGenericCard; int first_two_digits = 0; if (!base::StringToInt(number.substr(0, 2), &first_two_digits)) return kGenericCard; if (first_two_digits == 34 || first_two_digits == 37) return kAmericanExpressCard; if (first_two_digits == 36 || first_two_digits == 38 || first_two_digits == 39) return kDinersCard; if (first_two_digits >= 51 && first_two_digits <= 55) return kMasterCard; if (first_two_digits == 62) return kUnionPay; if (first_two_digits == 65) return kDiscoverCard; // Check for prefixes of length 3. if (number.size() < 3) return kGenericCard; int first_three_digits = 0; if (!base::StringToInt(number.substr(0, 3), &first_three_digits)) return kGenericCard; if ((first_three_digits >= 300 && first_three_digits <= 305) || first_three_digits == 309) return kDinersCard; if (first_three_digits >= 644 && first_three_digits <= 649) return kDiscoverCard; // Check for prefixes of length 4. if (number.size() < 4) return kGenericCard; int first_four_digits = 0; if (!base::StringToInt(number.substr(0, 4), &first_four_digits)) return kGenericCard; if (first_four_digits >= 2200 && first_four_digits <= 2204) return kMirCard; if (first_four_digits >= 2221 && first_four_digits <= 2720) return kMasterCard; if (first_four_digits >= 3528 && first_four_digits <= 3589) return kJCBCard; if (first_four_digits == 5067 || first_four_digits == 5090) return kEloCard; if (first_four_digits == 6011) return kDiscoverCard; // Check for prefixes of length 6. if (number.size() < 6) return kGenericCard; int first_six_digits = 0; if (!base::StringToInt(number.substr(0, 6), &first_six_digits)) return kGenericCard; if (first_six_digits == 431274 || first_six_digits == 451416 || first_six_digits == 627780 || first_six_digits == 636297) return kEloCard; return kGenericCard; } void CreditCard::SetNetworkForMaskedCard(base::StringPiece network) { DCHECK_EQ(MASKED_SERVER_CARD, record_type()); network_ = network.as_string(); } void CreditCard::SetServerStatus(ServerStatus status) { DCHECK_NE(LOCAL_CARD, record_type()); server_status_ = status; } CreditCard::ServerStatus CreditCard::GetServerStatus() const { DCHECK_NE(LOCAL_CARD, record_type()); return server_status_; } base::string16 CreditCard::GetRawInfo(ServerFieldType type) const { DCHECK_EQ(CREDIT_CARD, AutofillType(type).group()); switch (type) { case CREDIT_CARD_NAME_FULL: return name_on_card_; case CREDIT_CARD_NAME_FIRST: return data_util::SplitName(name_on_card_).given; case CREDIT_CARD_NAME_LAST: return data_util::SplitName(name_on_card_).family; case CREDIT_CARD_EXP_MONTH: return ExpirationMonthAsString(); case CREDIT_CARD_EXP_2_DIGIT_YEAR: return Expiration2DigitYearAsString(); case CREDIT_CARD_EXP_4_DIGIT_YEAR: return Expiration4DigitYearAsString(); case CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR: { base::string16 month = ExpirationMonthAsString(); base::string16 year = Expiration2DigitYearAsString(); if (!month.empty() && !year.empty()) return month + ASCIIToUTF16("/") + year; return base::string16(); } case CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR: { base::string16 month = ExpirationMonthAsString(); base::string16 year = Expiration4DigitYearAsString(); if (!month.empty() && !year.empty()) return month + ASCIIToUTF16("/") + year; return base::string16(); } case CREDIT_CARD_TYPE: return NetworkForFill(); case CREDIT_CARD_NUMBER: return number_; case CREDIT_CARD_VERIFICATION_CODE: // Chrome doesn't store credit card verification codes. return base::string16(); default: // ComputeDataPresentForArray will hit this repeatedly. return base::string16(); } } void CreditCard::SetRawInfo(ServerFieldType type, const base::string16& value) { DCHECK_EQ(CREDIT_CARD, AutofillType(type).group()); switch (type) { case CREDIT_CARD_NAME_FULL: name_on_card_ = value; break; case CREDIT_CARD_EXP_MONTH: SetExpirationMonthFromString(value, std::string()); break; case CREDIT_CARD_EXP_2_DIGIT_YEAR: SetExpirationYearFromString(value); break; case CREDIT_CARD_EXP_4_DIGIT_YEAR: SetExpirationYearFromString(value); break; case CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR: SetExpirationDateFromString(value); break; case CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR: SetExpirationDateFromString(value); break; case CREDIT_CARD_TYPE: // This is a read-only attribute, determined by the credit card number. break; case CREDIT_CARD_NUMBER: { // Don't change the real value if the input is an obfuscated string. if (value.size() > 0 && value[0] != kCreditCardObfuscationSymbol) SetNumber(value); break; } case CREDIT_CARD_VERIFICATION_CODE: // Chrome doesn't store the credit card verification code. break; default: NOTREACHED() << "Attempting to set unknown info-type " << type; break; } } void CreditCard::GetMatchingTypes(const base::string16& text, const std::string& app_locale, ServerFieldTypeSet* matching_types) const { FormGroup::GetMatchingTypes(text, app_locale, matching_types); base::string16 card_number = GetInfo(AutofillType(CREDIT_CARD_NUMBER), app_locale); if (!card_number.empty()) { // We only have the last four digits for masked cards, so match against // that if |this| is a masked card. bool numbers_match = record_type_ == MASKED_SERVER_CARD ? GetLastFourDigits(text) == LastFourDigits() : StripSeparators(text) == card_number; if (numbers_match) matching_types->insert(CREDIT_CARD_NUMBER); } int month; if (ConvertMonth(text, app_locale, &month) && month == expiration_month_) { matching_types->insert(CREDIT_CARD_EXP_MONTH); } } void CreditCard::SetInfoForMonthInputType(const base::string16& value) { // Check if |text| is "yyyy-mm" format first, and check normal month format. if (!MatchesPattern(value, base::UTF8ToUTF16("^[0-9]{4}-[0-9]{1,2}$"))) return; std::vector year_month = base::SplitStringPiece( value, ASCIIToUTF16("-"), base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL); DCHECK_EQ(2u, year_month.size()); int num = 0; bool converted = false; converted = base::StringToInt(year_month[0], &num); DCHECK(converted); SetExpirationYear(num); converted = base::StringToInt(year_month[1], &num); DCHECK(converted); SetExpirationMonth(num); } void CreditCard::SetExpirationMonth(int expiration_month) { if (expiration_month < 0 || expiration_month > 12) return; expiration_month_ = expiration_month; } void CreditCard::SetExpirationYear(int expiration_year) { // If |expiration_year| is beyond this millenium, or more than 2 digits but // before the current millenium (e.g. "545", "1995"), return. What is left are // values like "45" or "2018". if (expiration_year > 2999 || (expiration_year > 99 && expiration_year < 2000)) return; // Will normalize 2-digit years to the 4-digit version. if (expiration_year > 0 && expiration_year < 100) { base::Time::Exploded now_exploded; AutofillClock::Now().LocalExplode(&now_exploded); expiration_year += (now_exploded.year / 100) * 100; } expiration_year_ = expiration_year; } void CreditCard::operator=(const CreditCard& credit_card) { set_use_count(credit_card.use_count()); set_use_date(credit_card.use_date()); set_modification_date(credit_card.modification_date()); if (this == &credit_card) return; record_type_ = credit_card.record_type_; card_type_ = credit_card.card_type_; number_ = credit_card.number_; name_on_card_ = credit_card.name_on_card_; network_ = credit_card.network_; expiration_month_ = credit_card.expiration_month_; expiration_year_ = credit_card.expiration_year_; server_id_ = credit_card.server_id_; server_status_ = credit_card.server_status_; billing_address_id_ = credit_card.billing_address_id_; bank_name_ = credit_card.bank_name_; set_guid(credit_card.guid()); set_origin(credit_card.origin()); } bool CreditCard::UpdateFromImportedCard(const CreditCard& imported_card, const std::string& app_locale) { if (this->GetInfo(AutofillType(CREDIT_CARD_NUMBER), app_locale) != imported_card.GetInfo(AutofillType(CREDIT_CARD_NUMBER), app_locale)) { return false; } // Heuristically aggregated data should never overwrite verified data, with // the exception of expired verified cards. Instead, discard any heuristically // aggregated credit cards that disagree with explicitly entered data, so that // the UI is not cluttered with duplicate cards. if (this->IsVerified() && !imported_card.IsVerified()) { // If the original card is expired and the imported card is not, and the // name on the cards are identical, update the expiration date. if (this->IsExpired(AutofillClock::Now()) && !imported_card.IsExpired(AutofillClock::Now()) && (name_on_card_ == imported_card.name_on_card_)) { DCHECK(imported_card.expiration_month_ && imported_card.expiration_year_); expiration_month_ = imported_card.expiration_month_; expiration_year_ = imported_card.expiration_year_; } return true; } set_origin(imported_card.origin()); // Note that the card number is intentionally not updated, so as to preserve // any formatting (i.e. separator characters). Since the card number is not // updated, there is no reason to update the card type, either. if (!imported_card.name_on_card_.empty()) name_on_card_ = imported_card.name_on_card_; // The expiration date for |imported_card| should always be set. DCHECK(imported_card.expiration_month_ && imported_card.expiration_year_); expiration_month_ = imported_card.expiration_month_; expiration_year_ = imported_card.expiration_year_; return true; } int CreditCard::Compare(const CreditCard& credit_card) const { // The following CreditCard field types are the only types we store in the // WebDB so far, so we're only concerned with matching these types in the // credit card. const ServerFieldType types[] = {CREDIT_CARD_NAME_FULL, CREDIT_CARD_NUMBER, CREDIT_CARD_EXP_MONTH, CREDIT_CARD_EXP_4_DIGIT_YEAR}; for (ServerFieldType type : types) { int comparison = GetRawInfo(type).compare(credit_card.GetRawInfo(type)); if (comparison != 0) return comparison; } int comparison = server_id_.compare(credit_card.server_id_); if (comparison != 0) return comparison; comparison = billing_address_id_.compare(credit_card.billing_address_id_); if (comparison != 0) return comparison; comparison = bank_name_.compare(credit_card.bank_name_); if (comparison != 0) return comparison; if (static_cast(server_status_) < static_cast(credit_card.server_status_)) return -1; if (static_cast(server_status_) > static_cast(credit_card.server_status_)) return 1; if (static_cast(record_type_) < static_cast(credit_card.record_type_)) return -1; if (static_cast(record_type_) > static_cast(credit_card.record_type_)) return 1; return 0; } bool CreditCard::IsLocalDuplicateOfServerCard(const CreditCard& other) const { if (record_type() != LOCAL_CARD || other.record_type() == LOCAL_CARD) return false; // If |this| is only a partial card, i.e. some fields are missing, assume // those fields match. if ((!name_on_card_.empty() && name_on_card_ != other.name_on_card_) || (expiration_month_ != 0 && expiration_month_ != other.expiration_month_) || (expiration_year_ != 0 && expiration_year_ != other.expiration_year_) || (!billing_address_id_.empty() && billing_address_id_ != other.billing_address_id_)) { return false; } if (number_.empty()) return true; return HasSameNumberAs(other); } bool CreditCard::HasSameNumberAs(const CreditCard& other) const { // For masked cards, this is the best we can do to compare card numbers. if (record_type() == MASKED_SERVER_CARD || other.record_type() == MASKED_SERVER_CARD) { return NetworkAndLastFourDigits() == other.NetworkAndLastFourDigits(); } return StripSeparators(number_) == StripSeparators(other.number_); } bool CreditCard::operator==(const CreditCard& credit_card) const { return guid() == credit_card.guid() && origin() == credit_card.origin() && Compare(credit_card) == 0; } bool CreditCard::operator!=(const CreditCard& credit_card) const { return !operator==(credit_card); } bool CreditCard::IsEmpty(const std::string& app_locale) const { ServerFieldTypeSet types; GetNonEmptyTypes(app_locale, &types); return types.empty(); } bool CreditCard::IsValid() const { return HasValidCardNumber() && HasValidExpirationDate(); } bool CreditCard::HasValidCardNumber() const { return IsValidCreditCardNumber(number_); } bool CreditCard::HasValidExpirationDate() const { return IsValidCreditCardExpirationDate(expiration_year_, expiration_month_, AutofillClock::Now()); } bool CreditCard::SetExpirationMonthFromString(const base::string16& text, const std::string& app_locale) { base::string16 trimmed; base::TrimWhitespace(text, base::TRIM_ALL, &trimmed); int month = 0; if (!ConvertMonth(trimmed, app_locale, &month)) return false; SetExpirationMonth(month); return true; } void CreditCard::SetExpirationYearFromString(const base::string16& text) { int year; if (!ConvertYear(text, &year)) return; SetExpirationYear(year); } void CreditCard::SetExpirationDateFromString(const base::string16& text) { // Check that |text| fits the supported patterns: mmyy, mmyyyy, m-yy, // mm-yy, m-yyyy and mm-yyyy. Note that myy and myyyy matched by this pattern // but are not supported (ambiguous). Separators: -, / and |. if (!MatchesPattern(text, base::UTF8ToUTF16("^[0-9]{1,2}[-/|]?[0-9]{2,4}$"))) return; base::string16 month; base::string16 year; // Check for a separator. base::string16 found_separator; const std::vector kSeparators{ ASCIIToUTF16("-"), ASCIIToUTF16("/"), ASCIIToUTF16("|")}; for (const base::string16& separator : kSeparators) { if (text.find(separator) != base::string16::npos) { found_separator = separator; break; } } if (!found_separator.empty()) { std::vector month_year = base::SplitString( text, found_separator, base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL); DCHECK_EQ(2u, month_year.size()); month = month_year[0]; year = month_year[1]; } else if (text.size() % 2 == 0) { // If there are no separators, the supported formats are mmyy and mmyyyy. month = text.substr(0, 2); year = text.substr(2); } else { // Odd number of digits with no separator is too ambiguous. return; } int num = 0; bool converted = false; converted = base::StringToInt(month, &num); DCHECK(converted); SetExpirationMonth(num); converted = base::StringToInt(year, &num); DCHECK(converted); SetExpirationYear(num); } const std::pair CreditCard::LabelPieces() const { base::string16 label; // No CC number, return name only. if (number().empty()) return std::make_pair(name_on_card_, base::string16()); base::string16 obfuscated_cc_number = NetworkOrBankNameAndLastFourDigits(); // No expiration date set. if (!expiration_month_ || !expiration_year_) return std::make_pair(obfuscated_cc_number, base::string16()); base::string16 formatted_date = ExpirationDateForDisplay(); base::string16 separator = l10n_util::GetStringUTF16(IDS_AUTOFILL_ADDRESS_SUMMARY_SEPARATOR); return std::make_pair(obfuscated_cc_number, separator + formatted_date); } const base::string16 CreditCard::Label() const { std::pair pieces = LabelPieces(); return pieces.first + pieces.second; } base::string16 CreditCard::LastFourDigits() const { return GetLastFourDigits(number_); } base::string16 CreditCard::NetworkForDisplay() const { return CreditCard::NetworkForDisplay(network_); } base::string16 CreditCard::NetworkAndLastFourDigits() const { base::string16 network = NetworkForDisplay(); // TODO(crbug.com/734197): truncate network. base::string16 digits = LastFourDigits(); if (digits.empty()) return network; // TODO(estade): i18n? return network + base::string16(kMidlineEllipsis) + digits; } base::string16 CreditCard::BankNameAndLastFourDigits() const { base::string16 digits = LastFourDigits(); // TODO(crbug.com/734197): truncate bank name. if (digits.empty()) return ASCIIToUTF16(bank_name_); return ASCIIToUTF16(bank_name_) + base::string16(kMidlineEllipsis) + digits; } base::string16 CreditCard::NetworkOrBankNameAndLastFourDigits() const { if (IsAutofillCreditCardBankNameDisplayExperimentEnabled() && !bank_name_.empty()) { return BankNameAndLastFourDigits(); } return NetworkAndLastFourDigits(); } base::string16 CreditCard::AbbreviatedExpirationDateForDisplay() const { base::string16 month = ExpirationMonthAsString(); base::string16 year = Expiration2DigitYearAsString(); return month.empty() || year.empty() ? base::string16() : l10n_util::GetStringFUTF16( IDS_AUTOFILL_CREDIT_CARD_EXPIRATION_DATE_ABBR, month, year); } base::string16 CreditCard::GetLastUsedDateForDisplay( const std::string& app_locale) const { bool show_expiration_date = ShowExpirationDateInAutofillCreditCardLastUsedDate(); DCHECK_LT(0U, use_count()); // use_count() is initialized as 1 when the card is just added. if (use_count() == 1U) { return show_expiration_date ? l10n_util::GetStringFUTF16( IDS_AUTOFILL_CREDIT_CARD_EXP_AND_ADDED_DATE, GetInfo(AutofillType(CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR), app_locale), base::TimeFormatWithPattern( use_date(), kTimeFormatPatternNoYearShortMonthDate)) : l10n_util::GetStringFUTF16( IDS_AUTOFILL_CREDIT_CARD_ADDED_DATE, base::TimeFormatWithPattern( use_date(), kTimeFormatPatternNoYearShortMonthDate)); } // use_count() > 1 when the card has been used in autofill. // If the card was last used in autofill more than a year ago, // display "last used over a year ago" without showing date detail. if ((AutofillClock::Now() - use_date()).InDays() > 365) { return show_expiration_date ? l10n_util::GetStringFUTF16( IDS_AUTOFILL_CREDIT_CARD_EXP_AND_LAST_USED_YEAR_AGO, GetInfo(AutofillType(CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR), app_locale)) : l10n_util::GetStringUTF16( IDS_AUTOFILL_CREDIT_CARD_LAST_USED_YEAR_AGO); } // If the card was last used in autofill within a year, show date information. return show_expiration_date ? l10n_util::GetStringFUTF16( IDS_AUTOFILL_CREDIT_CARD_EXP_AND_LAST_USED_DATE, GetInfo(AutofillType(CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR), app_locale), base::TimeFormatWithPattern( use_date(), kTimeFormatPatternNoYearShortMonthDate)) : l10n_util::GetStringFUTF16( IDS_AUTOFILL_CREDIT_CARD_LAST_USED_DATE, base::TimeFormatWithPattern( use_date(), kTimeFormatPatternNoYearShortMonthDate)); } base::string16 CreditCard::ExpirationDateForDisplay() const { base::string16 formatted_date(ExpirationMonthAsString()); formatted_date.append(ASCIIToUTF16("/")); formatted_date.append(Expiration4DigitYearAsString()); return formatted_date; } base::string16 CreditCard::ExpirationMonthAsString() const { if (expiration_month_ == 0) return base::string16(); base::string16 month = base::IntToString16(expiration_month_); if (expiration_month_ >= 10) return month; base::string16 zero = ASCIIToUTF16("0"); zero.append(month); return zero; } base::string16 CreditCard::Expiration4DigitYearAsString() const { if (expiration_year_ == 0) return base::string16(); return base::IntToString16(Expiration4DigitYear()); } base::string16 CreditCard::Expiration2DigitYearAsString() const { if (expiration_year_ == 0) return base::string16(); return base::IntToString16(Expiration2DigitYear()); } void CreditCard::GetSupportedTypes(ServerFieldTypeSet* supported_types) const { supported_types->insert(CREDIT_CARD_NAME_FULL); supported_types->insert(CREDIT_CARD_NAME_FIRST); supported_types->insert(CREDIT_CARD_NAME_LAST); supported_types->insert(CREDIT_CARD_NUMBER); supported_types->insert(CREDIT_CARD_TYPE); supported_types->insert(CREDIT_CARD_EXP_MONTH); supported_types->insert(CREDIT_CARD_EXP_2_DIGIT_YEAR); supported_types->insert(CREDIT_CARD_EXP_4_DIGIT_YEAR); supported_types->insert(CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR); supported_types->insert(CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR); } base::string16 CreditCard::GetInfoImpl(const AutofillType& type, const std::string& app_locale) const { ServerFieldType storable_type = type.GetStorableType(); if (storable_type == CREDIT_CARD_NUMBER) { // Web pages should never actually be filled by a masked server card, // but this function is used at the preview stage. if (record_type() == MASKED_SERVER_CARD) return NetworkOrBankNameAndLastFourDigits(); return StripSeparators(number_); } return GetRawInfo(storable_type); } bool CreditCard::SetInfoImpl(const AutofillType& type, const base::string16& value, const std::string& app_locale) { ServerFieldType storable_type = type.GetStorableType(); if (storable_type == CREDIT_CARD_EXP_MONTH) return SetExpirationMonthFromString(value, app_locale); if (storable_type == CREDIT_CARD_NUMBER) SetRawInfo(storable_type, StripSeparators(value)); else SetRawInfo(storable_type, value); return true; } base::string16 CreditCard::NetworkForFill() const { return ::autofill::NetworkForFill(network_); } void CreditCard::SetNumber(const base::string16& number) { number_ = number; // Set the type based on the card number, but only for full numbers, not // when we have masked cards from the server (last 4 digits). if (record_type_ != MASKED_SERVER_CARD) network_ = GetCardNetwork(StripSeparators(number_)); } void CreditCard::RecordAndLogUse() { UMA_HISTOGRAM_COUNTS_1000("Autofill.DaysSinceLastUse.CreditCard", (AutofillClock::Now() - use_date()).InDays()); RecordUse(); } // static bool CreditCard::ConvertMonth(const base::string16& month, const std::string& app_locale, int* num) { if (month.empty()) return false; // Try parsing the |month| as a number (this doesn't require |app_locale|). if (base::StringToInt(month, num)) return true; if (app_locale.empty()) return false; // Otherwise, try parsing the |month| as a named month, e.g. "January" or // "Jan" in the user's locale. UErrorCode status = U_ZERO_ERROR; icu::Locale locale(app_locale.c_str()); icu::DateFormatSymbols date_format_symbols(locale, status); DCHECK(status == U_ZERO_ERROR || status == U_USING_FALLBACK_WARNING || status == U_USING_DEFAULT_WARNING); // Full months (January, Janvier, etc.) int32_t num_months; const icu::UnicodeString* months = date_format_symbols.getMonths(num_months); for (int32_t i = 0; i < num_months; ++i) { const base::string16 icu_month( base::i18n::UnicodeStringToString16(months[i])); // We look for the ICU-defined month in |month|. if (base::i18n::StringSearchIgnoringCaseAndAccents(icu_month, month, nullptr, nullptr)) { *num = i + 1; // Adjust from 0-indexed to 1-indexed. return true; } } // Abbreviated months (jan., janv., fév.) Some abbreviations have . at the end // (e.g., "janv." in French). The period is removed. months = date_format_symbols.getShortMonths(num_months); base::string16 trimmed_month; base::TrimString(month, ASCIIToUTF16("."), &trimmed_month); for (int32_t i = 0; i < num_months; ++i) { base::string16 icu_month(base::i18n::UnicodeStringToString16(months[i])); base::TrimString(icu_month, ASCIIToUTF16("."), &icu_month); // We look for the ICU-defined month in |trimmed_month|. if (base::i18n::StringSearchIgnoringCaseAndAccents(icu_month, trimmed_month, nullptr, nullptr)) { *num = i + 1; // Adjust from 0-indexed to 1-indexed. return true; } } return false; } bool CreditCard::IsExpired(const base::Time& current_time) const { return !IsValidCreditCardExpirationDate(expiration_year_, expiration_month_, current_time); } bool CreditCard::ShouldUpdateExpiration(const base::Time& current_time) const { // Local cards always have OK server status. DCHECK(server_status_ == OK || record_type_ != LOCAL_CARD); return server_status_ == EXPIRED || IsExpired(current_time); } // So we can compare CreditCards with EXPECT_EQ(). std::ostream& operator<<(std::ostream& os, const CreditCard& credit_card) { return os << base::UTF16ToUTF8(credit_card.Label()) << " " << credit_card.guid() << " " << credit_card.origin() << " " << base::UTF16ToUTF8(credit_card.GetRawInfo(CREDIT_CARD_NAME_FULL)) << " " << base::UTF16ToUTF8(credit_card.GetRawInfo(CREDIT_CARD_TYPE)) << " " << base::UTF16ToUTF8(credit_card.GetRawInfo(CREDIT_CARD_NUMBER)) << " " << base::UTF16ToUTF8(credit_card.GetRawInfo(CREDIT_CARD_EXP_MONTH)) << " " << base::UTF16ToUTF8( credit_card.GetRawInfo(CREDIT_CARD_EXP_4_DIGIT_YEAR)); } const char kAmericanExpressCard[] = "americanExpressCC"; const char kDinersCard[] = "dinersCC"; const char kDiscoverCard[] = "discoverCC"; const char kEloCard[] = "eloCC"; const char kGenericCard[] = "genericCC"; const char kJCBCard[] = "jcbCC"; const char kMasterCard[] = "masterCardCC"; const char kMirCard[] = "mirCC"; const char kUnionPay[] = "unionPayCC"; const char kVisaCard[] = "visaCC"; } // namespace autofill