From d02de4556a244955abcc7de355a16e06020c91a7 Mon Sep 17 00:00:00 2001 From: "jhawkins@chromium.org" Date: Wed, 6 Jan 2010 20:25:41 +0000 Subject: Add CreditCard, a form group that stores credit card information. BUG=none TEST=none Review URL: http://codereview.chromium.org/518031 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@35645 0039d316-1c4b-4281-b951-d872f2087c98 --- chrome/browser/autofill/credit_card.cc | 397 +++++++++++++++++++++++++++++++++ chrome/browser/autofill/credit_card.h | 125 +++++++++++ 2 files changed, 522 insertions(+) create mode 100644 chrome/browser/autofill/credit_card.cc create mode 100644 chrome/browser/autofill/credit_card.h (limited to 'chrome/browser/autofill') diff --git a/chrome/browser/autofill/credit_card.cc b/chrome/browser/autofill/credit_card.cc new file mode 100644 index 0000000..97264de --- /dev/null +++ b/chrome/browser/autofill/credit_card.cc @@ -0,0 +1,397 @@ +// Copyright (c) 2010 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 "base/basictypes.h" +#include "base/string_util.h" +#include "chrome/browser/autofill/autofill_type.h" +#include "chrome/browser/autofill/field_types.h" + +static const string16 kCreditCardSeparators = ASCIIToUTF16(" -"); + +static const AutoFillFieldType kAutoFillCreditCardTypes[] = { + CREDIT_CARD_NAME, + CREDIT_CARD_NUMBER, + CREDIT_CARD_TYPE, + CREDIT_CARD_EXP_MONTH, + CREDIT_CARD_EXP_4_DIGIT_YEAR, + CREDIT_CARD_VERIFICATION_CODE, +}; + +static const int kAutoFillCreditCardLength = + arraysize(kAutoFillCreditCardTypes); + +CreditCard::CreditCard(const string16& label) + : expiration_month_(0), + expiration_year_(0), + label_(label) { +} + +FormGroup* CreditCard::Clone() const { + return new CreditCard(*this); +} + +void CreditCard::GetPossibleFieldTypes(const string16& text, + FieldTypeSet* possible_types) const { + if (IsNameOnCard(text)) + possible_types->insert(CREDIT_CARD_NAME); + + if (IsCreditCardNumber(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); + + if (IsCardType(text)) + possible_types->insert(CREDIT_CARD_TYPE); + + if (IsVerificationCode(text)) + possible_types->insert(CREDIT_CARD_VERIFICATION_CODE); +} + +void CreditCard::FindInfoMatches(const AutoFillType& type, + const string16& info, + std::vector* matched_text) const { + if (matched_text == NULL) { + DLOG(ERROR) << "NULL matched vector passed in"; + return; + } + + string16 match; + switch (type.field_type()) { + case CREDIT_CARD_NUMBER: { + // Because the credit card number is encrypted and we are not able to do + // comparisons with it we will say that any field that is known to be a + // credit card number field will match all credit card numbers. + string16 text = GetPreviewText(AutoFillType(CREDIT_CARD_NUMBER)); + if (!text.empty()) + matched_text->push_back(text); + break; + } + + case CREDIT_CARD_VERIFICATION_CODE: + break; + + case UNKNOWN_TYPE: + for (int i = 0; i < kAutoFillCreditCardLength; ++i) { + if (FindInfoMatchesHelper(kAutoFillCreditCardTypes[i], info, &match)) + matched_text->push_back(match); + } + break; + + default: + if (FindInfoMatchesHelper(type.field_type(), info, &match)) + matched_text->push_back(match); + break; + } +} + +string16 CreditCard::GetFieldText(const AutoFillType& type) const { + switch (type.field_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 EmptyString16(); + } + + case CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR: { + string16 month = ExpirationMonthAsString(); + string16 year = Expiration4DigitYearAsString(); + if (!month.empty() && !year.empty()) + return month + ASCIIToUTF16("/") + year; + return EmptyString16(); + } + + case CREDIT_CARD_TYPE: + return this->type(); + + case CREDIT_CARD_NUMBER: + return number(); + + case CREDIT_CARD_VERIFICATION_CODE: + return verification_code(); + + default: + // ComputeDataPresentForArray will hit this repeatedly. + return EmptyString16(); + } +} + +string16 CreditCard::GetPreviewText(const AutoFillType& type) const { + switch (type.field_type()) { + case CREDIT_CARD_NUMBER: + return last_four_digits(); + + case CREDIT_CARD_VERIFICATION_CODE: + return ASCIIToUTF16("xxx"); + + default: + return GetFieldText(type); + } +} + +void CreditCard::SetInfo(const AutoFillType& type, const string16& value) { + switch (type.field_type()) { + case CREDIT_CARD_NAME: + set_name_on_card(value); + break; + + case CREDIT_CARD_EXP_MONTH: + SetExpirationMonthFromString(value); + break; + + case CREDIT_CARD_EXP_4_DIGIT_YEAR: + SetExpirationYearFromString(value); + break; + + case CREDIT_CARD_TYPE: + set_type(value); + break; + + case CREDIT_CARD_NUMBER: + set_number(value); + break; + + case CREDIT_CARD_VERIFICATION_CODE: + set_verification_code(value); + break; + + default: + DLOG(ERROR) << "Attempting to set unknown info-type"; + break; + } +} + +string16 CreditCard::ExpirationMonthAsString() const { + if (expiration_month_ == 0) + return EmptyString16(); + + string16 month = 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 EmptyString16(); + + return IntToString16(Expiration4DigitYear()); +} + +string16 CreditCard::Expiration2DigitYearAsString() const { + if (expiration_year_ == 0) + return EmptyString16(); + + return IntToString16(Expiration2DigitYear()); +} + +void CreditCard::SetExpirationMonthFromString(const string16& text) { + int month; + if (!ConvertDate(text, &month)) + return; + + set_expiration_month(month); +} + +void CreditCard::SetExpirationYearFromString(const string16& text) { + int year; + if (!ConvertDate(text, &year)) + return; + + set_expiration_year(year); +} + +void CreditCard::set_expiration_month(int expiration_month) { + if (expiration_month < 0 || expiration_month > 12) + return; + + expiration_month_ = expiration_month; +} + +void CreditCard::set_expiration_year(int expiration_year) { + if (expiration_year != 0 && + (expiration_year < 2006 || expiration_year > 10000)) { + return; + } + + expiration_year_ = expiration_year; +} + +CreditCard::CreditCard(const CreditCard& card) + : number_(card.number_), + name_on_card_(card.name_on_card_), + type_(card.type_), + verification_code_(card.verification_code_), + last_four_digits_(card.last_four_digits_), + expiration_month_(card.expiration_month_), + expiration_year_(card.expiration_year_), + label_(card.label_) { +} + +bool CreditCard::FindInfoMatchesHelper(const AutoFillFieldType& field_type, + const string16& info, + string16* match) const { + if (match == NULL) { + DLOG(ERROR) << "NULL match string passed in"; + return false; + } + + match->clear(); + switch (field_type) { + case CREDIT_CARD_NAME: { + if (StartsWith(name_on_card(), info, false)) + *match = name_on_card(); + break; + } + + case CREDIT_CARD_EXP_MONTH: { + string16 exp_month(ExpirationMonthAsString()); + if (StartsWith(exp_month, info, true)) + *match = exp_month; + } + + case CREDIT_CARD_EXP_2_DIGIT_YEAR: { + string16 exp_year(Expiration2DigitYearAsString()); + if (StartsWith(exp_year, info, true)) + *match = exp_year; + } + + case CREDIT_CARD_EXP_4_DIGIT_YEAR: { + string16 exp_year(Expiration4DigitYearAsString()); + if (StartsWith(exp_year, info, true)) + *match = exp_year; + } + + case CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR: { + string16 exp_date(ExpirationMonthAsString() + ASCIIToUTF16("/") + + Expiration2DigitYearAsString()); + if (StartsWith(exp_date, info, true)) + *match = exp_date; + } + + case CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR: { + string16 exp_date(ExpirationMonthAsString() + ASCIIToUTF16("/") + + Expiration4DigitYearAsString()); + if (StartsWith(exp_date, info, true)) + *match = exp_date; + } + + case CREDIT_CARD_TYPE: { + string16 card_type(this->type()); + if (StartsWith(card_type, info, true)) + *match = card_type; + } + + case CREDIT_CARD_VERIFICATION_CODE: { + if (StartsWith(verification_code(), info, true)) + *match = verification_code(); + } + + default: + break; + } + return !match->empty(); +} + +bool CreditCard::IsNameOnCard(const string16& text) const { + return StringToLowerASCII(text) == StringToLowerASCII(name_on_card_); +} + +bool CreditCard::IsCreditCardNumber(const string16& text) { + string16 number; + TrimString(text, kCreditCardSeparators.c_str(), &number); + + // We use the Luhn formula to validate the number; see + // http://www.beachnet.com/~hstiles/cardtype.html and + // http://www.webopedia.com/TERM/L/Luhn_formula.html. + 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::IsVerificationCode(const string16& text) const { + return StringToLowerASCII(text) == StringToLowerASCII(verification_code_); +} + +bool CreditCard::IsExpirationMonth(const string16& text) const { + int month; + if (!StringToInt(text, &month)) + return false; + + return expiration_month_ == month; +} + +bool CreditCard::Is2DigitExpirationYear(const string16& text) const { + int year; + if (!StringToInt(text, &year)) + return false; + + return year < 100 && (expiration_year_ % 100) == year; +} + +bool CreditCard::Is4DigitExpirationYear(const string16& text) const { + int year; + if (!StringToInt(text, &year)) + return false; + + return expiration_year_ == year; +} + +bool CreditCard::IsCardType(const string16& text) const { + return StringToLowerASCII(text) == StringToLowerASCII(type_); +} + +bool CreditCard::ConvertDate(const string16& date, int* num) const { + if (!date.empty()) { + bool converted = StringToInt(date, num); + DCHECK(converted); + if (!converted) + return false; + } else { + // Clear the value. + *num = 0; + } + + return true; +} diff --git a/chrome/browser/autofill/credit_card.h b/chrome/browser/autofill/credit_card.h new file mode 100644 index 0000000..d686bbc --- /dev/null +++ b/chrome/browser/autofill/credit_card.h @@ -0,0 +1,125 @@ +// Copyright (c) 2010 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. + +#ifndef CHROME_BROWSER_AUTOFILL_CREDIT_CARD_H_ +#define CHROME_BROWSER_AUTOFILL_CREDIT_CARD_H_ + +#include + +#include "base/string16.h" +#include "chrome/browser/autofill/form_group.h" + +// A form group that stores credit card information. +class CreditCard : public FormGroup { + public: + explicit CreditCard(const string16& label); + + // FormGroup implementation: + FormGroup* Clone() const; + virtual void GetPossibleFieldTypes(const string16& text, + FieldTypeSet* possible_types) const; + virtual void FindInfoMatches(const AutoFillType& type, + const string16& info, + std::vector* matched_text) const; + virtual string16 GetFieldText(const AutoFillType& type) const; + virtual string16 GetPreviewText(const AutoFillType& type) const; + virtual void SetInfo(const AutoFillType& type, const string16& value); + string16 Label() const { return label_; } + + // The month and year are zero if not present. + int Expiration4DigitYear() const { return expiration_year_; } + int Expiration2DigitYear() const { return expiration_year_ % 100; } + string16 ExpirationMonthAsString() const; + string16 Expiration4DigitYearAsString() const; + string16 Expiration2DigitYearAsString() const; + + // Sets |expiration_month_| to the integer conversion of |text|. + void SetExpirationMonthFromString(const string16& text); + + // Sets |expiration_year_| to the integer conversion of |text|. + void SetExpirationYearFromString(const string16& text); + + string16 number() const { return number_; } + string16 name_on_card() const { return name_on_card_; } + string16 type() const { return type_; } + string16 verification_code() const { return verification_code_; } + string16 last_four_digits() const { return last_four_digits_; } + int expiration_month() const { return expiration_month_; } + int expiration_year() const { return expiration_year_; } + + void set_number(const string16& number) { number_ = number; } + void set_name_on_card(const string16& name_on_card) { + name_on_card_ = name_on_card; + } + void set_type(const string16& type) { type_ = type; } + void set_verification_code(const string16& verification_code) { + verification_code_ = verification_code; + } + void set_last_four_digits(const string16& last_four_digits) { + last_four_digits_ = last_four_digits; + } + + // These setters verify that the month and year are within appropriate + // ranges. + void set_expiration_month(int expiration_month); + void set_expiration_year(int expiration_year); + + private: + explicit CreditCard(const CreditCard& card); + void operator=(const CreditCard& card); + + // A helper function for FindInfoMatches that only handles matching the info + // with the requested field type. + bool FindInfoMatchesHelper(const AutoFillFieldType& field_type, + const string16& info, + string16* match) const; + + // Returns true if |text| matches the name on the card. The comparison is + // case-insensitive. + bool IsNameOnCard(const string16& text) const; + + // Uses the Luhn formula to validate the credit card number in |text|. + static bool IsCreditCardNumber(const string16& text); + + // Returns true if |text| matches the expiration month of the card. + bool IsExpirationMonth(const string16& text) const; + + // Returns true if |text| matches the CVV of the card. The comparison is + // case-insensitive. + bool IsVerificationCode(const string16& text) const; + + // Returns true if the integer value of |text| matches the 2-digit expiration + // year. + bool Is2DigitExpirationYear(const string16& text) const; + + // Returns true if the integer value of |text| matches the 4-digit expiration + // year. + bool Is4DigitExpirationYear(const string16& text) const; + + // Returns true if |text| matches the type of the card. The comparison is + // case-insensitive. + bool IsCardType(const string16& text) const; + + // Converts |date| to an integer form. Returns true if the conversion + // succeeded. + bool ConvertDate(const string16& date, int* num) const; + + // The credit card values. + string16 number_; // The encrypted credit card number. + string16 name_on_card_; // The cardholder's name. + string16 type_; // The type of the card. + string16 verification_code_; // The CVV. + + // Stores the last four digits of the credit card number. + string16 last_four_digits_; + + // These members are zero if not present. + int expiration_month_; + int expiration_year_; + + // This is the display name of the card set by the user, e.g., Amazon Visa. + string16 label_; +}; + +#endif // CHROME_BROWSER_AUTOFILL_CREDIT_CARD_H_ -- cgit v1.1