// 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/phone_number_i18n.h" #include "base/basictypes.h" #include "base/logging.h" #include "base/stringprintf.h" #include "base/string_number_conversions.h" #include "base/utf_string_conversions.h" #include "chrome/browser/autofill/autofill_country.h" #include "third_party/libphonenumber/cpp/src/phonenumberutil.h" namespace { std::string SanitizeLocaleCode(const std::string& locale_code) { if (locale_code.length() == 2) return locale_code; return AutofillCountry::CountryCodeForLocale( AutofillCountry::ApplicationLocale()); } i18n::phonenumbers::PhoneNumberUtil::PhoneNumberFormat UtilsTypeToPhoneLibType( autofill_i18n::FullPhoneFormat phone_format) { switch (phone_format) { case autofill_i18n::E164: return i18n::phonenumbers::PhoneNumberUtil::E164; case autofill_i18n::INTERNATIONAL: return i18n::phonenumbers::PhoneNumberUtil::INTERNATIONAL; case autofill_i18n::NATIONAL: return i18n::phonenumbers::PhoneNumberUtil::NATIONAL; case autofill_i18n::RFC3966: return i18n::phonenumbers::PhoneNumberUtil::RFC3966; default: NOTREACHED(); } return i18n::phonenumbers::PhoneNumberUtil::NATIONAL; } bool ParsePhoneNumberInternal(const string16& value, const std::string& locale, string16* country_code, string16* city_code, string16* number, i18n::phonenumbers::PhoneNumber* i18n_number) { DCHECK(number); DCHECK(city_code); DCHECK(country_code); DCHECK(i18n_number); number->clear(); city_code->clear(); country_code->clear(); std::string number_text(UTF16ToUTF8(value)); // Parse phone number based on the locale. i18n::phonenumbers::PhoneNumberUtil* phone_util = i18n::phonenumbers::PhoneNumberUtil::GetInstance(); DCHECK(phone_util); if (phone_util->Parse(number_text, SanitizeLocaleCode(locale).c_str(), i18n_number) != i18n::phonenumbers::PhoneNumberUtil::NO_PARSING_ERROR) { return false; } i18n::phonenumbers::PhoneNumberUtil::ValidationResult validation = phone_util->IsPossibleNumberWithReason(*i18n_number); if (validation != i18n::phonenumbers::PhoneNumberUtil::IS_POSSIBLE) return false; // This verifies that number has a valid area code (that in some cases could // be empty) for parsed country code. Also verifies that this is a valid // number (in US 1234567 is not valid, because numbers do not start with 1). if (!phone_util->IsValidNumber(*i18n_number)) return false; std::string national_significant_number; phone_util->GetNationalSignificantNumber(*i18n_number, &national_significant_number); std::string area_code; std::string subscriber_number; int area_length = phone_util->GetLengthOfGeographicalAreaCode(*i18n_number); int destination_length = phone_util->GetLengthOfNationalDestinationCode(*i18n_number); // Some phones have a destination code in lieu of area code: mobile operators // in Europe, toll and toll-free numbers in USA, etc. From our point of view // these two types of codes are the same. if (destination_length > area_length) area_length = destination_length; if (area_length > 0) { area_code = national_significant_number.substr(0, area_length); subscriber_number = national_significant_number.substr(area_length); } else { subscriber_number = national_significant_number; } *number = UTF8ToUTF16(subscriber_number); *city_code = UTF8ToUTF16(area_code); *country_code = string16(); i18n::phonenumbers::PhoneNumberUtil::NormalizeDigitsOnly(&number_text); string16 normalized_number(UTF8ToUTF16(number_text)); // Check if parsed number has country code and it was not inferred from the // locale. if (i18n_number->has_country_code()) { *country_code = UTF8ToUTF16( base::StringPrintf("%d", i18n_number->country_code())); if (normalized_number.length() <= national_significant_number.length() && (normalized_number.length() < country_code->length() || normalized_number.compare(0, country_code->length(), *country_code))) { country_code->clear(); } } return true; } } // namespace namespace autofill_i18n { string16 NormalizePhoneNumber(const string16& value, std::string const& locale) { string16 number; string16 city_code; string16 country_code; string16 result; // Full number - parse it, split it and re-combine into canonical form. if (!ParsePhoneNumber(value, locale, &country_code, &city_code, &number)) return string16(); // Parsing failed - do not store phone. if (!autofill_i18n::ConstructPhoneNumber( country_code, city_code, number, locale, (country_code.empty() ? autofill_i18n::NATIONAL : autofill_i18n::INTERNATIONAL), &result)) { // Reconstruction failed - do not store phone. return string16(); } std::string result_utf8(UTF16ToUTF8(result)); i18n::phonenumbers::PhoneNumberUtil::NormalizeDigitsOnly(&result_utf8); return UTF8ToUTF16(result_utf8); } bool ParsePhoneNumber(const string16& value, const std::string& locale, string16* country_code, string16* city_code, string16* number) { i18n::phonenumbers::PhoneNumber i18n_number; return ParsePhoneNumberInternal(value, locale, country_code, city_code, number, &i18n_number); } bool ConstructPhoneNumber(const string16& country_code, const string16& city_code, const string16& number, const std::string& locale, FullPhoneFormat phone_format, string16* whole_number) { DCHECK(whole_number); whole_number->clear(); std::string normalized_number(UTF16ToUTF8(city_code)); normalized_number.append(UTF16ToUTF8(number)); i18n::phonenumbers::PhoneNumberUtil::NormalizeDigitsOnly(&normalized_number); int64 number_int = 0; if (!base::StringToInt64(normalized_number, &number_int) || !number_int) return false; i18n::phonenumbers::PhoneNumber i18n_number; i18n_number.set_national_number(static_cast(number_int)); i18n::phonenumbers::PhoneNumberUtil* phone_util = i18n::phonenumbers::PhoneNumberUtil::GetInstance(); DCHECK(phone_util); int country_int = phone_util->GetCountryCodeForRegion( std::string(SanitizeLocaleCode(locale).c_str())); if (!country_code.empty() && !base::StringToInt(country_code, &country_int)) return false; if (country_int) i18n_number.set_country_code(country_int); i18n::phonenumbers::PhoneNumberUtil::ValidationResult validation = phone_util->IsPossibleNumberWithReason(i18n_number); if (validation != i18n::phonenumbers::PhoneNumberUtil::IS_POSSIBLE) return false; // This verifies that number has a valid area code (that in some cases could // be empty) for parsed country code. Also verifies that this is a valid // number (in US 1234567 is not valid, because numbers do not start with 1). if (!phone_util->IsValidNumber(i18n_number)) return false; std::string formatted_number; phone_util->Format(i18n_number, UtilsTypeToPhoneLibType(phone_format), &formatted_number); *whole_number = UTF8ToUTF16(formatted_number); return true; } string16 FormatPhone(const string16& phone, const std::string& locale, FullPhoneFormat phone_format) { std::string number_text(UTF16ToUTF8(phone)); i18n::phonenumbers::PhoneNumberUtil::NormalizeDigitsOnly(&number_text); // Parse phone number based on the locale i18n::phonenumbers::PhoneNumber i18n_number; i18n::phonenumbers::PhoneNumberUtil* phone_util = i18n::phonenumbers::PhoneNumberUtil::GetInstance(); DCHECK(phone_util); if (phone_util->Parse(number_text, SanitizeLocaleCode(locale).c_str(), &i18n_number) != i18n::phonenumbers::PhoneNumberUtil::NO_PARSING_ERROR) { return string16(); } std::string formatted_number; phone_util->Format(i18n_number, UtilsTypeToPhoneLibType(phone_format), &formatted_number); return UTF8ToUTF16(formatted_number); } PhoneMatch ComparePhones(const string16& phone1, const string16& phone2, const std::string& locale) { std::string number_text(UTF16ToUTF8(phone1)); // Parse phone number based on the locale i18n::phonenumbers::PhoneNumber i18n_number1; i18n::phonenumbers::PhoneNumberUtil* phone_util = i18n::phonenumbers::PhoneNumberUtil::GetInstance(); DCHECK(phone_util); if (phone_util->Parse(number_text, SanitizeLocaleCode(locale).c_str(), &i18n_number1) != i18n::phonenumbers::PhoneNumberUtil::NO_PARSING_ERROR) { return PHONES_NOT_EQUAL; } number_text = UTF16ToUTF8(phone2); // Parse phone number based on the locale i18n::phonenumbers::PhoneNumber i18n_number2; if (phone_util->Parse(number_text, SanitizeLocaleCode(locale).c_str(), &i18n_number2) != i18n::phonenumbers::PhoneNumberUtil::NO_PARSING_ERROR) { return PHONES_NOT_EQUAL; } switch (phone_util->IsNumberMatch(i18n_number1, i18n_number2)) { case i18n::phonenumbers::PhoneNumberUtil::INVALID_NUMBER: case i18n::phonenumbers::PhoneNumberUtil::NO_MATCH: return PHONES_NOT_EQUAL; case i18n::phonenumbers::PhoneNumberUtil::SHORT_NSN_MATCH: return PHONES_SUBMATCH; case i18n::phonenumbers::PhoneNumberUtil::NSN_MATCH: case i18n::phonenumbers::PhoneNumberUtil::EXACT_MATCH: return PHONES_EQUAL; default: NOTREACHED(); } return PHONES_NOT_EQUAL; } bool PhoneNumbersMatch(const string16& number_a, const string16& number_b, const std::string& country_code) { return ComparePhones(number_a, number_b, country_code) == PHONES_EQUAL; } PhoneObject::PhoneObject(const string16& number, const std::string& locale) : locale_(SanitizeLocaleCode(locale)), i18n_number_(NULL) { scoped_ptr i18n_number(new i18n::phonenumbers::PhoneNumber); if (ParsePhoneNumberInternal(number, locale_, &country_code_, &city_code_, &number_, i18n_number.get())) { // Phone successfully parsed - set |i18n_number_| object, |whole_number_| // will be set on the first call to GetWholeNumber(). i18n_number_.reset(i18n_number.release()); } else { // Parsing failed. Store passed phone "as is" into |whole_number_|. whole_number_ = number; } } PhoneObject::PhoneObject(const PhoneObject& other) : i18n_number_(NULL) { *this = other; } PhoneObject::PhoneObject() : i18n_number_(NULL) { } PhoneObject::~PhoneObject() { } string16 PhoneObject::GetWholeNumber() const { if (i18n_number_.get() && whole_number_.empty()) { i18n::phonenumbers::PhoneNumberUtil::PhoneNumberFormat format = i18n::phonenumbers::PhoneNumberUtil::INTERNATIONAL; if (country_code_.empty()) format = i18n::phonenumbers::PhoneNumberUtil::NATIONAL; std::string formatted_number; i18n::phonenumbers::PhoneNumberUtil* phone_util = i18n::phonenumbers::PhoneNumberUtil::GetInstance(); phone_util->Format(*i18n_number_, format, &formatted_number); whole_number_ = UTF8ToUTF16(formatted_number); } return whole_number_; } PhoneMatch PhoneObject::ComparePhones(const string16& phone_number) const { PhoneObject phone(phone_number, locale_); if (!i18n_number_.get() || !phone.i18n_number_.get()) { if (GetWholeNumber().empty()) return PHONES_NOT_EQUAL; return (GetWholeNumber() == phone.GetWholeNumber()) ? PHONES_EQUAL : PHONES_NOT_EQUAL; } i18n::phonenumbers::PhoneNumberUtil* phone_util = i18n::phonenumbers::PhoneNumberUtil::GetInstance(); switch (phone_util->IsNumberMatch(*i18n_number_, *(phone.i18n_number_))) { case i18n::phonenumbers::PhoneNumberUtil::INVALID_NUMBER: case i18n::phonenumbers::PhoneNumberUtil::NO_MATCH: return PHONES_NOT_EQUAL; case i18n::phonenumbers::PhoneNumberUtil::SHORT_NSN_MATCH: return PHONES_SUBMATCH; case i18n::phonenumbers::PhoneNumberUtil::NSN_MATCH: case i18n::phonenumbers::PhoneNumberUtil::EXACT_MATCH: return PHONES_EQUAL; default: NOTREACHED(); } return PHONES_NOT_EQUAL; } PhoneObject& PhoneObject::operator=(const PhoneObject& other) { if (this == &other) return *this; country_code_ = other.country_code_; city_code_ = other.city_code_; number_ = other.number_; locale_ = other.locale_; if (other.i18n_number_.get()) { i18n_number_.reset(new i18n::phonenumbers::PhoneNumber( *other.i18n_number_)); } return *this; } } // namespace autofill_i18n