phone_number_i18n.cc revision 23730a6e56a168d1879203e4b3819bb36e3d8f1f
1// Copyright 2013 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "components/autofill/core/browser/phone_number_i18n.h" 6 7#include "base/basictypes.h" 8#include "base/logging.h" 9#include "base/strings/string_number_conversions.h" 10#include "base/strings/string_util.h" 11#include "base/strings/stringprintf.h" 12#include "base/strings/utf_string_conversions.h" 13#include "components/autofill/core/browser/autofill_country.h" 14#include "third_party/libphonenumber/src/phonenumber_api.h" 15 16using i18n::phonenumbers::PhoneNumber; 17using i18n::phonenumbers::PhoneNumberUtil; 18 19namespace autofill { 20 21namespace { 22 23std::string SanitizeRegion(const std::string& region, 24 const std::string& app_locale) { 25 if (region.length() == 2) 26 return region; 27 28 return AutofillCountry::CountryCodeForLocale(app_locale); 29} 30 31// Returns true if |phone_number| is valid. 32bool IsValidPhoneNumber(const PhoneNumber& phone_number) { 33 PhoneNumberUtil* phone_util = PhoneNumberUtil::GetInstance(); 34 if (!phone_util->IsPossibleNumber(phone_number)) 35 return false; 36 37 // Verify that the number has a valid area code (that in some cases could be 38 // empty) for the parsed country code. Also verify that this is a valid 39 // number (for example, in the US 1234567 is not valid, because numbers do not 40 // start with 1). 41 if (!phone_util->IsValidNumber(phone_number)) 42 return false; 43 44 return true; 45} 46 47// Formats the given |number| as a human-readable string, and writes the result 48// into |formatted_number|. Also, normalizes the formatted number, and writes 49// that result into |normalized_number|. This function should only be called 50// with numbers already known to be valid, i.e. validation should be done prior 51// to calling this function. Note that the |country_code|, which determines 52// whether to format in the national or in the international format, is passed 53// in explicitly, as |number| might have an implicit country code set, even 54// though the original input lacked a country code. 55void FormatValidatedNumber(const PhoneNumber& number, 56 const base::string16& country_code, 57 base::string16* formatted_number, 58 base::string16* normalized_number) { 59 PhoneNumberUtil::PhoneNumberFormat format = 60 country_code.empty() ? 61 PhoneNumberUtil::NATIONAL : 62 PhoneNumberUtil::INTERNATIONAL; 63 64 PhoneNumberUtil* phone_util = PhoneNumberUtil::GetInstance(); 65 std::string processed_number; 66 phone_util->Format(number, format, &processed_number); 67 68 if (formatted_number) 69 *formatted_number = base::UTF8ToUTF16(processed_number); 70 71 if (normalized_number) { 72 phone_util->NormalizeDigitsOnly(&processed_number); 73 *normalized_number = base::UTF8ToUTF16(processed_number); 74 } 75} 76 77} // namespace 78 79namespace i18n { 80 81// Parses the number stored in |value| as it should be interpreted in the given 82// |default_region|, and stores the results into the remaining arguments. 83// The |default_region| should be sanitized prior to calling this function. 84bool ParsePhoneNumber(const base::string16& value, 85 const std::string& default_region, 86 base::string16* country_code, 87 base::string16* city_code, 88 base::string16* number, 89 std::string* inferred_region, 90 PhoneNumber* i18n_number) { 91 country_code->clear(); 92 city_code->clear(); 93 number->clear(); 94 *i18n_number = PhoneNumber(); 95 96 std::string number_text(base::UTF16ToUTF8(value)); 97 98 // Parse phone number based on the region. 99 PhoneNumberUtil* phone_util = PhoneNumberUtil::GetInstance(); 100 101 // The |default_region| should already be sanitized. 102 DCHECK_EQ(2U, default_region.size()); 103 if (phone_util->Parse(number_text, default_region, i18n_number) != 104 PhoneNumberUtil::NO_PARSING_ERROR) { 105 return false; 106 } 107 108 if (!IsValidPhoneNumber(*i18n_number)) 109 return false; 110 111 std::string national_significant_number; 112 phone_util->GetNationalSignificantNumber(*i18n_number, 113 &national_significant_number); 114 115 int area_length = phone_util->GetLengthOfGeographicalAreaCode(*i18n_number); 116 int destination_length = 117 phone_util->GetLengthOfNationalDestinationCode(*i18n_number); 118 // Some phones have a destination code in lieu of area code: mobile operators 119 // in Europe, toll and toll-free numbers in USA, etc. From our point of view 120 // these two types of codes are the same. 121 if (destination_length > area_length) 122 area_length = destination_length; 123 124 std::string area_code; 125 std::string subscriber_number; 126 if (area_length > 0) { 127 area_code = national_significant_number.substr(0, area_length); 128 subscriber_number = national_significant_number.substr(area_length); 129 } else { 130 subscriber_number = national_significant_number; 131 } 132 *number = base::UTF8ToUTF16(subscriber_number); 133 *city_code = base::UTF8ToUTF16(area_code); 134 *country_code = base::string16(); 135 136 phone_util->NormalizeDigitsOnly(&number_text); 137 base::string16 normalized_number(base::UTF8ToUTF16(number_text)); 138 139 // Check if parsed number has a country code that was not inferred from the 140 // region. 141 if (i18n_number->has_country_code()) { 142 *country_code = base::UTF8ToUTF16( 143 base::StringPrintf("%d", i18n_number->country_code())); 144 if (normalized_number.length() <= national_significant_number.length() && 145 !StartsWith(normalized_number, *country_code, 146 true /* case_sensitive */)) { 147 country_code->clear(); 148 } 149 } 150 151 // The region might be different from what we started with. 152 phone_util->GetRegionCodeForNumber(*i18n_number, inferred_region); 153 154 return true; 155} 156 157base::string16 NormalizePhoneNumber(const base::string16& value, 158 const std::string& region) { 159 DCHECK_EQ(2u, region.size()); 160 base::string16 country_code, unused_city_code, unused_number; 161 std::string unused_region; 162 PhoneNumber phone_number; 163 if (!ParsePhoneNumber(value, region, &country_code, &unused_city_code, 164 &unused_number, &unused_region, &phone_number)) { 165 return base::string16(); // Parsing failed - do not store phone. 166 } 167 168 base::string16 normalized_number; 169 FormatValidatedNumber(phone_number, country_code, NULL, &normalized_number); 170 return normalized_number; 171} 172 173bool ConstructPhoneNumber(const base::string16& country_code, 174 const base::string16& city_code, 175 const base::string16& number, 176 const std::string& region, 177 base::string16* whole_number) { 178 DCHECK_EQ(2u, region.size()); 179 whole_number->clear(); 180 181 base::string16 unused_country_code, unused_city_code, unused_number; 182 std::string unused_region; 183 PhoneNumber phone_number; 184 if (!ParsePhoneNumber(country_code + city_code + number, region, 185 &unused_country_code, &unused_city_code, &unused_number, 186 &unused_region, &phone_number)) { 187 return false; 188 } 189 190 FormatValidatedNumber(phone_number, country_code, whole_number, NULL); 191 return true; 192} 193 194bool PhoneNumbersMatch(const base::string16& number_a, 195 const base::string16& number_b, 196 const std::string& raw_region, 197 const std::string& app_locale) { 198 // Sanitize the provided |raw_region| before trying to use it for parsing. 199 const std::string region = SanitizeRegion(raw_region, app_locale); 200 201 PhoneNumberUtil* phone_util = PhoneNumberUtil::GetInstance(); 202 203 // Parse phone numbers based on the region 204 PhoneNumber i18n_number1; 205 if (phone_util->Parse( 206 base::UTF16ToUTF8(number_a), region.c_str(), &i18n_number1) != 207 PhoneNumberUtil::NO_PARSING_ERROR) { 208 return false; 209 } 210 211 PhoneNumber i18n_number2; 212 if (phone_util->Parse( 213 base::UTF16ToUTF8(number_b), region.c_str(), &i18n_number2) != 214 PhoneNumberUtil::NO_PARSING_ERROR) { 215 return false; 216 } 217 218 switch (phone_util->IsNumberMatch(i18n_number1, i18n_number2)) { 219 case PhoneNumberUtil::INVALID_NUMBER: 220 case PhoneNumberUtil::NO_MATCH: 221 return false; 222 case PhoneNumberUtil::SHORT_NSN_MATCH: 223 return false; 224 case PhoneNumberUtil::NSN_MATCH: 225 case PhoneNumberUtil::EXACT_MATCH: 226 return true; 227 } 228 229 NOTREACHED(); 230 return false; 231} 232 233PhoneObject::PhoneObject(const base::string16& number, 234 const std::string& region) { 235 DCHECK_EQ(2u, region.size()); 236 // TODO(isherman): Autofill profiles should always have a |region| set, but in 237 // some cases it should be marked as implicit. Otherwise, phone numbers 238 // might behave differently when they are synced across computers: 239 // [ http://crbug.com/100845 ]. Once the bug is fixed, add a DCHECK here to 240 // verify. 241 242 scoped_ptr<PhoneNumber> i18n_number(new PhoneNumber); 243 if (ParsePhoneNumber(number, region, &country_code_, &city_code_, &number_, 244 ®ion_, i18n_number.get())) { 245 // The phone number was successfully parsed, so store the parsed version. 246 // The formatted and normalized versions will be set on the first call to 247 // the coresponding methods. 248 i18n_number_ = i18n_number.Pass(); 249 } else { 250 // Parsing failed. Store passed phone "as is" into |whole_number_|. 251 whole_number_ = number; 252 } 253} 254 255PhoneObject::PhoneObject(const PhoneObject& other) { *this = other; } 256 257PhoneObject::PhoneObject() {} 258 259PhoneObject::~PhoneObject() {} 260 261const base::string16& PhoneObject::GetFormattedNumber() const { 262 if (i18n_number_ && formatted_number_.empty()) { 263 FormatValidatedNumber(*i18n_number_, country_code_, &formatted_number_, 264 &whole_number_); 265 } 266 267 return formatted_number_; 268} 269 270base::string16 PhoneObject::GetNationallyFormattedNumber() const { 271 base::string16 formatted = whole_number_; 272 if (i18n_number_) 273 FormatValidatedNumber(*i18n_number_, base::string16(), &formatted, NULL); 274 275 return formatted; 276} 277 278const base::string16& PhoneObject::GetWholeNumber() const { 279 if (i18n_number_ && whole_number_.empty()) { 280 FormatValidatedNumber(*i18n_number_, country_code_, &formatted_number_, 281 &whole_number_); 282 } 283 284 return whole_number_; 285} 286 287PhoneObject& PhoneObject::operator=(const PhoneObject& other) { 288 if (this == &other) 289 return *this; 290 291 region_ = other.region_; 292 293 if (other.i18n_number_.get()) 294 i18n_number_.reset(new PhoneNumber(*other.i18n_number_)); 295 else 296 i18n_number_.reset(); 297 298 country_code_ = other.country_code_; 299 city_code_ = other.city_code_; 300 number_ = other.number_; 301 302 formatted_number_ = other.formatted_number_; 303 whole_number_ = other.whole_number_; 304 305 return *this; 306} 307 308} // namespace i18n 309} // namespace autofill 310