blob: 139dca405e105cda2e1ffd7ba503b7e3674f828b [file] [log] [blame]
// 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 <stddef.h>
#include <stdint.h>
#include <algorithm>
#include <ostream>
#include <string>
#include "base/guid.h"
#include "base/i18n/rtl.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_field.h"
#include "components/autofill/core/browser/autofill_metadata.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_constants.h"
#include "components/autofill/core/common/autofill_features.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 {
// Unicode characters used in card number obfuscation:
// - 0x2022 - Bullet.
// - 0x2006 - SIX-PER-EM SPACE (small space between bullets).
// - 0x2060 - WORD-JOINER (makes obfuscated string undivisible).
const base::char16 kMidlineEllipsis[] = {
0x2022, 0x2060, 0x2006, 0x2060, 0x2022, 0x2060, 0x2006, 0x2060, 0x2022,
0x2060, 0x2006, 0x2060, 0x2022, 0x2060, 0x2006, 0x2060, 0};
namespace {
const base::char16 kCreditCardObfuscationSymbol = '*';
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
namespace internal {
base::string16 GetObfuscatedStringForCardDigits(const base::string16& digits) {
base::string16 obfuscated_string = base::string16(kMidlineEllipsis) + digits;
base::i18n::WrapStringWithLTRFormatting(&obfuscated_string);
return obfuscated_string;
}
} // namespace internal
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
// Determine the network for the given |number| by going from the longest
// (most specific) prefix to the shortest (most general) prefix.
base::string16 stripped_number = CreditCard::StripSeparators(number);
// Check for prefixes of length 6.
if (stripped_number.size() >= 6) {
int first_six_digits = 0;
if (!base::StringToInt(stripped_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;
}
// Check for prefixes of length 4.
if (stripped_number.size() >= 4) {
int first_four_digits = 0;
if (!base::StringToInt(stripped_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 3.
if (stripped_number.size() >= 3) {
int first_three_digits = 0;
if (!base::StringToInt(stripped_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 2.
if (stripped_number.size() >= 2) {
int first_two_digits = 0;
if (!base::StringToInt(stripped_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 1.
if (stripped_number.empty())
return kGenericCard;
if (stripped_number[0] == '4')
return kVisaCard;
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_;
}
AutofillMetadata CreditCard::GetMetadata() const {
AutofillMetadata metadata = AutofillDataModel::GetMetadata();
metadata.id = (record_type_ == LOCAL_CARD ? guid() : server_id_);
metadata.billing_address_id = billing_address_id_;
return metadata;
}
bool CreditCard::SetMetadata(const AutofillMetadata metadata) {
// Make sure the ids matches.
if (metadata.id != (record_type_ == LOCAL_CARD ? guid() : server_id_))
return false;
if (!AutofillDataModel::SetMetadata(metadata))
return false;
billing_address_id_ = metadata.billing_address_id;
return true;
}
bool CreditCard::IsDeletable() const {
return AutofillDataModel::IsDeletable() &&
IsExpired(AutofillClock::Now() - kDisusedDataModelDeletionTimeDelta);
}
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_NAME_FIRST:
temp_card_first_name_ = value;
if (!temp_card_last_name_.empty()) {
SetNameOnCardFromSeparateParts();
}
break;
case CREDIT_CARD_NAME_LAST:
temp_card_last_name_ = value;
if (!temp_card_first_name_.empty()) {
SetNameOnCardFromSeparateParts();
}
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<base::StringPiece16> 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_;
temp_card_first_name_ = credit_card.temp_card_first_name_;
temp_card_last_name_ = credit_card.temp_card_last_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, and the imported card's expiration date
// is not empty, update the expiration date.
if (this->IsExpired(AutofillClock::Now()) &&
!imported_card.IsExpired(AutofillClock::Now()) &&
(name_on_card_ == imported_card.name_on_card_) &&
(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_;
// If |imported_card| has an expiration date, overwrite |this|'s expiration
// date with its value.
if (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_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;
}
if (!HasSameNumberAs(credit_card)) {
return number().compare(credit_card.number());
}
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<int>(server_status_) <
static_cast<int>(credit_card.server_status_))
return -1;
if (static_cast<int>(server_status_) >
static_cast<int>(credit_card.server_status_))
return 1;
// Do not distinguish masked server cards from full server cards as this is
// not needed and not desired - we want to identify masked server card from
// sync with the (potential) full server card stored locally.
if (record_type_ == LOCAL_CARD && credit_card.record_type_ != LOCAL_CARD)
return -1;
if (record_type_ != LOCAL_CARD && credit_card.record_type_ == LOCAL_CARD)
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() &&
record_type() == credit_card.record_type() &&
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<base::string16> 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<base::string16> 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<base::string16, base::string16> 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<base::string16, base::string16> 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::ObfuscatedLastFourDigits() const {
return internal::GetObfuscatedStringForCardDigits(LastFourDigits());
}
base::string16 CreditCard::NetworkAndLastFourDigits() const {
const base::string16 network = NetworkForDisplay();
// TODO(crbug.com/734197): truncate network.
const base::string16 digits = LastFourDigits();
if (digits.empty())
return network;
// TODO(estade): i18n?
const base::string16 obfuscated_string =
internal::GetObfuscatedStringForCardDigits(digits);
return network.empty() ? obfuscated_string
: network + ASCIIToUTF16(" ") + obfuscated_string;
}
base::string16 CreditCard::BankNameAndLastFourDigits() const {
const base::string16 digits = LastFourDigits();
// TODO(crbug.com/734197): truncate bank name.
if (digits.empty())
return ASCIIToUTF16(bank_name_);
const base::string16 obfuscated_string =
internal::GetObfuscatedStringForCardDigits(digits);
return bank_name_.empty() ? obfuscated_string
: ASCIIToUTF16(bank_name_) + ASCIIToUTF16(" ") +
obfuscated_string;
}
base::string16 CreditCard::NetworkOrBankNameAndLastFourDigits() const {
return bank_name_.empty() ? NetworkAndLastFourDigits()
: BankNameAndLastFourDigits();
}
base::string16
CreditCard::NetworkOrBankNameLastFourDigitsAndDescriptiveExpiration(
const std::string& app_locale) const {
return l10n_util::GetStringFUTF16(
IDS_AUTOFILL_CREDIT_CARD_TWO_LINE_LABEL_FROM_NAME,
NetworkOrBankNameAndLastFourDigits(),
GetInfo(AutofillType(CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR), app_locale));
}
base::string16 CreditCard::DescriptiveExpiration(
const std::string& app_locale) const {
return l10n_util::GetStringFUTF16(
IDS_AUTOFILL_CREDIT_CARD_TWO_LINE_LABEL_FROM_CARD_NUMBER,
GetInfo(AutofillType(CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR), app_locale));
}
base::string16 CreditCard::AbbreviatedExpirationDateForDisplay(
bool with_prefix) const {
base::string16 month = ExpirationMonthAsString();
base::string16 year = Expiration2DigitYearAsString();
if (month.empty() || year.empty())
return base::string16();
return l10n_util::GetStringFUTF16(
with_prefix ? IDS_AUTOFILL_CREDIT_CARD_EXPIRATION_DATE_ABBR
: IDS_AUTOFILL_CREDIT_CARD_EXPIRATION_DATE_ABBR_V2,
month, year);
}
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());
}
bool CreditCard::HasFirstAndLastName() const {
return !temp_card_first_name_.empty() && !temp_card_last_name_.empty() &&
!name_on_card_.empty();
}
bool CreditCard::HasNameOnCard() const {
return !name_on_card_.empty();
}
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.record_type() == CreditCard::LOCAL_CARD
? credit_card.guid()
: base::HexEncode(credit_card.server_id().data(),
credit_card.server_id().size()))
<< " " << 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))
<< " " << credit_card.bank_name() << " "
<< " " << credit_card.record_type() << " "
<< credit_card.use_count() << " " << credit_card.use_date() << " "
<< credit_card.billing_address_id();
}
void CreditCard::SetNameOnCardFromSeparateParts() {
DCHECK(name_on_card_.empty() && !temp_card_first_name_.empty() &&
!temp_card_last_name_.empty());
name_on_card_ =
temp_card_first_name_ + base::UTF8ToUTF16(" ") + temp_card_last_name_;
}
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