普通文本  |  559行  |  15.32 KB

// Copyright (c) 2011 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 "chrome/browser/autofill/credit_card.h"

#include <stddef.h>
#include <string>

#include "base/basictypes.h"
#include "base/logging.h"
#include "base/string16.h"
#include "base/string_number_conversions.h"
#include "base/string_split.h"
#include "base/string_util.h"
#include "base/utf_string_conversions.h"
#include "chrome/browser/autofill/autofill_type.h"
#include "chrome/browser/autofill/field_types.h"
#include "chrome/browser/autofill/form_field.h"
#include "chrome/common/guid.h"
#include "grit/generated_resources.h"
#include "ui/base/l10n/l10n_util.h"

namespace {

const char16 kCreditCardObfuscationSymbol = '*';

const AutofillFieldType kAutofillCreditCardTypes[] = {
  CREDIT_CARD_NAME,
  CREDIT_CARD_NUMBER,
  CREDIT_CARD_TYPE,
  CREDIT_CARD_EXP_MONTH,
  CREDIT_CARD_EXP_4_DIGIT_YEAR,
};

const int kAutofillCreditCardLength = arraysize(kAutofillCreditCardTypes);

std::string GetCreditCardType(const string16& number) {
  // Don't check for a specific type if this is not a credit card number.
  if (!CreditCard::IsValidCreditCardNumber(number))
    return kGenericCard;

  // Credit card number specifications taken from:
  // http://en.wikipedia.org/wiki/Credit_card_numbers and
  // http://www.beachnet.com/~hstiles/cardtype.html
  // Card Type              Prefix(es)                      Length
  // ---------------------------------------------------------------
  // Visa                   4                               13,16
  // American Express       34,37                           15
  // Diners Club            300-305,2014,2149,36,           14,15
  // Discover Card          6011,65                         16
  // JCB                    3                               16
  // JCB                    2131,1800                       15
  // MasterCard             51-55                           16
  // Solo (debit card)      6334,6767                       16,18,19

  // We need at least 4 digits to work with.
  if (number.length() < 4)
    return kGenericCard;

  int first_four_digits = 0;
  if (!base::StringToInt(number.substr(0, 4), &first_four_digits))
    return kGenericCard;

  int first_three_digits = first_four_digits / 10;
  int first_two_digits = first_three_digits / 10;
  int first_digit = first_two_digits / 10;

  switch (number.length()) {
    case 13:
      if (first_digit == 4)
        return kVisaCard;

      break;
    case 14:
      if (first_three_digits >= 300 && first_three_digits <=305)
        return kDinersCard;

      if (first_digit == 36)
        return kDinersCard;

      break;
    case 15:
      if (first_two_digits == 34 || first_two_digits == 37)
        return kAmericanExpressCard;

      if (first_four_digits == 2131 || first_four_digits == 1800)
        return kJCBCard;

      if (first_four_digits == 2014 || first_four_digits == 2149)
        return kDinersCard;

      break;
    case 16:
      if (first_four_digits == 6011 || first_two_digits == 65)
        return kDiscoverCard;

      if (first_four_digits == 6334 || first_four_digits == 6767)
        return kSoloCard;

      if (first_two_digits >= 51 && first_two_digits <= 55)
        return kMasterCard;

      if (first_digit == 3)
        return kJCBCard;

      if (first_digit == 4)
        return kVisaCard;

      break;
    case 18:
    case 19:
      if (first_four_digits == 6334 || first_four_digits == 6767)
        return kSoloCard;

      break;
  }

  return kGenericCard;
}

bool ConvertDate(const string16& date, int* num) {
  if (!date.empty()) {
    bool converted = base::StringToInt(date, num);
    DCHECK(converted);
    if (!converted)
      return false;
  } else {
    // Clear the value.
    *num = 0;
  }

  return true;
}

}  // namespace

CreditCard::CreditCard(const std::string& guid)
    : type_(kGenericCard),
      expiration_month_(0),
      expiration_year_(0),
      guid_(guid) {
}

CreditCard::CreditCard()
    : type_(kGenericCard),
      expiration_month_(0),
      expiration_year_(0),
      guid_(guid::GenerateGUID()) {
}

CreditCard::CreditCard(const CreditCard& credit_card) : FormGroup() {
  operator=(credit_card);
}

CreditCard::~CreditCard() {}

void CreditCard::GetPossibleFieldTypes(const string16& text,
                                       FieldTypeSet* possible_types) const {
  if (IsNameOnCard(text))
    possible_types->insert(CREDIT_CARD_NAME);

  if (IsNumber(text))
    possible_types->insert(CREDIT_CARD_NUMBER);

  if (IsExpirationMonth(text))
    possible_types->insert(CREDIT_CARD_EXP_MONTH);

  if (Is2DigitExpirationYear(text))
    possible_types->insert(CREDIT_CARD_EXP_2_DIGIT_YEAR);

  if (Is4DigitExpirationYear(text))
    possible_types->insert(CREDIT_CARD_EXP_4_DIGIT_YEAR);
}

void CreditCard::GetAvailableFieldTypes(FieldTypeSet* available_types) const {
  DCHECK(available_types);

  if (!name_on_card_.empty())
    available_types->insert(CREDIT_CARD_NAME);

  if (!number_.empty())
    available_types->insert(CREDIT_CARD_NUMBER);

  if (!ExpirationMonthAsString().empty())
    available_types->insert(CREDIT_CARD_EXP_MONTH);

  if (!Expiration2DigitYearAsString().empty())
    available_types->insert(CREDIT_CARD_EXP_2_DIGIT_YEAR);

  if (!Expiration4DigitYearAsString().empty())
    available_types->insert(CREDIT_CARD_EXP_4_DIGIT_YEAR);
}

string16 CreditCard::GetInfo(AutofillFieldType type) const {
  switch (type) {
    case CREDIT_CARD_NAME:
      return name_on_card_;

    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: {
      string16 month = ExpirationMonthAsString();
      string16 year = Expiration2DigitYearAsString();
      if (!month.empty() && !year.empty())
        return month + ASCIIToUTF16("/") + year;
      return string16();
    }

    case CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR: {
      string16 month = ExpirationMonthAsString();
      string16 year = Expiration4DigitYearAsString();
      if (!month.empty() && !year.empty())
        return month + ASCIIToUTF16("/") + year;
      return string16();
    }

    case CREDIT_CARD_TYPE:
      // We don't handle this case.
      return string16();

    case CREDIT_CARD_NUMBER:
      return number();

    case CREDIT_CARD_VERIFICATION_CODE:
      NOTREACHED();
      return string16();

    default:
      // ComputeDataPresentForArray will hit this repeatedly.
      return string16();
  }
}

void CreditCard::SetInfo(AutofillFieldType type, const string16& value) {
  switch (type) {
    case CREDIT_CARD_NAME:
      name_on_card_ = value;
      break;

    case CREDIT_CARD_EXP_MONTH:
      SetExpirationMonthFromString(value);
      break;

    case CREDIT_CARD_EXP_2_DIGIT_YEAR:
      // This is a read-only attribute.
      break;

    case CREDIT_CARD_EXP_4_DIGIT_YEAR:
      SetExpirationYearFromString(value);
      break;

    case CREDIT_CARD_TYPE:
      // We determine the type based on the 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:
      NOTREACHED();
      break;

    default:
      NOTREACHED() << "Attempting to set unknown info-type " << type;
      break;
  }
}

const string16 CreditCard::Label() const {
  string16 label;
  if (number().empty())
    return name_on_card_;  // No CC number, return name only.

  string16 obfuscated_cc_number = ObfuscatedNumber();
  if (!expiration_month_ || !expiration_year_)
    return obfuscated_cc_number;  // No expiration date set.

  // TODO(georgey): Internationalize date.
  string16 formatted_date(ExpirationMonthAsString());
  formatted_date.append(ASCIIToUTF16("/"));
  formatted_date.append(Expiration4DigitYearAsString());

#ifndef ANDROID
  label = l10n_util::GetStringFUTF16(IDS_CREDIT_CARD_NUMBER_PREVIEW_FORMAT,
                                     obfuscated_cc_number,
                                     formatted_date);
#endif
  return label;
}

void CreditCard::SetInfoForMonthInputType(const string16& value) {
  // Check if |text| is "yyyy-mm" format first, and check normal month format.
  if (!autofill::MatchString(value, UTF8ToUTF16("^[0-9]{4}-[0-9]{1,2}$")))
    return;

  std::vector<string16> year_month;
  base::SplitString(value, L'-', &year_month);
  DCHECK_EQ((int)year_month.size(), 2);
  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);
}

string16 CreditCard::ObfuscatedNumber() const {
  // If the number is shorter than four digits, there's no need to obfuscate it.
  if (number_.size() < 4)
    return number_;

  string16 number = StripSeparators(number_);
  string16 result(number.size() - 4, kCreditCardObfuscationSymbol);
  result.append(LastFourDigits());

  return result;
}

string16 CreditCard::LastFourDigits() const {
  static const size_t kNumLastDigits = 4;

  string16 number = StripSeparators(number_);
  if (number.size() < kNumLastDigits)
    return string16();

  return number.substr(number.size() - kNumLastDigits, kNumLastDigits);
}

void CreditCard::operator=(const CreditCard& credit_card) {
  if (this == &credit_card)
    return;

  number_ = credit_card.number_;
  name_on_card_ = credit_card.name_on_card_;
  type_ = credit_card.type_;
  expiration_month_ = credit_card.expiration_month_;
  expiration_year_ = credit_card.expiration_year_;
  guid_ = credit_card.guid_;
}

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 AutofillFieldType types[] = { CREDIT_CARD_NAME,
                                      CREDIT_CARD_NUMBER,
                                      CREDIT_CARD_EXP_MONTH,
                                      CREDIT_CARD_EXP_4_DIGIT_YEAR };
  for (size_t index = 0; index < arraysize(types); ++index) {
    int comparison = GetInfo(types[index]).compare(
        credit_card.GetInfo(types[index]));
    if (comparison != 0)
      return comparison;
  }

  return 0;
}

bool CreditCard::operator==(const CreditCard& credit_card) const {
  if (guid_ != credit_card.guid_)
    return false;

  return Compare(credit_card) == 0;
}

bool CreditCard::operator!=(const CreditCard& credit_card) const {
  return !operator==(credit_card);
}

// static
const string16 CreditCard::StripSeparators(const string16& number) {
  const char16 kSeparators[] = {'-', ' ', '\0'};
  string16 stripped;
  RemoveChars(number, kSeparators, &stripped);
  return stripped;
}

// static
bool CreditCard::IsValidCreditCardNumber(const string16& text) {
  string16 number = StripSeparators(text);

  // Credit card numbers are at most 19 digits in length [1]. 12 digits seems to
  // be a fairly safe lower-bound [2].
  // [1] http://www.merriampark.com/anatomycc.htm
  // [2] http://en.wikipedia.org/wiki/Bank_card_number
  const size_t kMinCreditCardDigits = 12;
  const size_t kMaxCreditCardDigits = 19;
  if (number.size() < kMinCreditCardDigits ||
      number.size() > kMaxCreditCardDigits)
    return false;

  // Use the Luhn formula [3] to validate the number.
  // [3] http://en.wikipedia.org/wiki/Luhn_algorithm
  int sum = 0;
  bool odd = false;
  string16::reverse_iterator iter;
  for (iter = number.rbegin(); iter != number.rend(); ++iter) {
    if (!IsAsciiDigit(*iter))
      return false;

    int digit = *iter - '0';
    if (odd) {
      digit *= 2;
      sum += digit / 10 + digit % 10;
    } else {
      sum += digit;
    }
    odd = !odd;
  }

  return (sum % 10) == 0;
}

bool CreditCard::IsEmpty() const {
  FieldTypeSet types;
  GetAvailableFieldTypes(&types);
  return types.empty();
}

string16 CreditCard::ExpirationMonthAsString() const {
  if (expiration_month_ == 0)
    return string16();

  string16 month = base::IntToString16(expiration_month_);
  if (expiration_month_ >= 10)
    return month;

  string16 zero = ASCIIToUTF16("0");
  zero.append(month);
  return zero;
}

string16 CreditCard::Expiration4DigitYearAsString() const {
  if (expiration_year_ == 0)
    return string16();

  return base::IntToString16(Expiration4DigitYear());
}

string16 CreditCard::Expiration2DigitYearAsString() const {
  if (expiration_year_ == 0)
    return string16();

  return base::IntToString16(Expiration2DigitYear());
}

void CreditCard::SetExpirationMonthFromString(const string16& text) {
  int month;
  if (!ConvertDate(text, &month))
    return;

  SetExpirationMonth(month);
}

void CreditCard::SetExpirationYearFromString(const string16& text) {
  int year;
  if (!ConvertDate(text, &year))
    return;

  SetExpirationYear(year);
}

void CreditCard::SetNumber(const string16& number) {
  number_ = number;
  type_ = GetCreditCardType(StripSeparators(number_));
}

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 != 0 &&
      (expiration_year < 2006 || expiration_year > 10000)) {
    return;
  }

  expiration_year_ = expiration_year;
}

bool CreditCard::IsNumber(const string16& text) const {
  return StripSeparators(text) == StripSeparators(number_);
}

bool CreditCard::IsNameOnCard(const string16& text) const {
  return StringToLowerASCII(text) == StringToLowerASCII(name_on_card_);
}

bool CreditCard::IsExpirationMonth(const string16& text) const {
  int month;
  if (!base::StringToInt(text, &month))
    return false;

  return expiration_month_ == month;
}

bool CreditCard::Is2DigitExpirationYear(const string16& text) const {
  int year;
  if (!base::StringToInt(text, &year))
    return false;

  return year < 100 && (expiration_year_ % 100) == year;
}

bool CreditCard::Is4DigitExpirationYear(const string16& text) const {
  int year;
  if (!base::StringToInt(text, &year))
    return false;

  return expiration_year_ == year;
}

// So we can compare CreditCards with EXPECT_EQ().
std::ostream& operator<<(std::ostream& os, const CreditCard& credit_card) {
  return os
      << UTF16ToUTF8(credit_card.Label())
      << " "
      << credit_card.guid()
      << " "
      << UTF16ToUTF8(credit_card.GetInfo(CREDIT_CARD_NAME))
      << " "
      << UTF16ToUTF8(credit_card.GetInfo(CREDIT_CARD_TYPE))
      << " "
      << UTF16ToUTF8(credit_card.GetInfo(CREDIT_CARD_NUMBER))
      << " "
      << UTF16ToUTF8(credit_card.GetInfo(CREDIT_CARD_EXP_MONTH))
      << " "
      << UTF16ToUTF8(credit_card.GetInfo(CREDIT_CARD_EXP_4_DIGIT_YEAR));
}

// These values must match the values in WebKitClientImpl in webkit/glue. We
// send these strings to WK, which then asks WebKitClientImpl to load the image
// data.
const char* const kAmericanExpressCard = "americanExpressCC";
const char* const kDinersCard = "dinersCC";
const char* const kDiscoverCard = "discoverCC";
const char* const kGenericCard = "genericCC";
const char* const kJCBCard = "jcbCC";
const char* const kMasterCard = "masterCardCC";
const char* const kSoloCard = "soloCC";
const char* const kVisaCard = "visaCC";