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 16 using i18n::phonenumbers::PhoneNumber; 17 using i18n::phonenumbers::PhoneNumberUtil; 18 19 namespace autofill { 20 21 namespace { 22 23 std::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. 32 bool 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. 55 void 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 = UTF8ToUTF16(processed_number); 70 71 if (normalized_number) { 72 phone_util->NormalizeDigitsOnly(&processed_number); 73 *normalized_number = UTF8ToUTF16(processed_number); 74 } 75 } 76 77 } // namespace 78 79 namespace i18n { 80 81 // Parses the number stored in |value| as it should be interpreted in the given 82 // |region|, and stores the results into the remaining arguments. The |region| 83 // should be sanitized prior to calling this function. 84 bool ParsePhoneNumber(const base::string16& value, 85 const std::string& region, 86 base::string16* country_code, 87 base::string16* city_code, 88 base::string16* number, 89 PhoneNumber* i18n_number) { 90 country_code->clear(); 91 city_code->clear(); 92 number->clear(); 93 *i18n_number = PhoneNumber(); 94 95 std::string number_text(UTF16ToUTF8(value)); 96 97 // Parse phone number based on the region. 98 PhoneNumberUtil* phone_util = PhoneNumberUtil::GetInstance(); 99 100 // The |region| should already be sanitized. 101 DCHECK_EQ(2U, region.size()); 102 if (phone_util->Parse(number_text, region.c_str(), i18n_number) != 103 PhoneNumberUtil::NO_PARSING_ERROR) { 104 return false; 105 } 106 107 if (!IsValidPhoneNumber(*i18n_number)) 108 return false; 109 110 std::string national_significant_number; 111 phone_util->GetNationalSignificantNumber(*i18n_number, 112 &national_significant_number); 113 114 int area_length = phone_util->GetLengthOfGeographicalAreaCode(*i18n_number); 115 int destination_length = 116 phone_util->GetLengthOfNationalDestinationCode(*i18n_number); 117 // Some phones have a destination code in lieu of area code: mobile operators 118 // in Europe, toll and toll-free numbers in USA, etc. From our point of view 119 // these two types of codes are the same. 120 if (destination_length > area_length) 121 area_length = destination_length; 122 123 std::string area_code; 124 std::string subscriber_number; 125 if (area_length > 0) { 126 area_code = national_significant_number.substr(0, area_length); 127 subscriber_number = national_significant_number.substr(area_length); 128 } else { 129 subscriber_number = national_significant_number; 130 } 131 *number = UTF8ToUTF16(subscriber_number); 132 *city_code = UTF8ToUTF16(area_code); 133 *country_code = base::string16(); 134 135 phone_util->NormalizeDigitsOnly(&number_text); 136 base::string16 normalized_number(UTF8ToUTF16(number_text)); 137 138 // Check if parsed number has a country code that was not inferred from the 139 // region. 140 if (i18n_number->has_country_code()) { 141 *country_code = UTF8ToUTF16( 142 base::StringPrintf("%d", i18n_number->country_code())); 143 if (normalized_number.length() <= national_significant_number.length() && 144 !StartsWith(normalized_number, *country_code, 145 true /* case_sensitive */)) { 146 country_code->clear(); 147 } 148 } 149 150 return true; 151 } 152 153 base::string16 NormalizePhoneNumber(const base::string16& value, 154 const std::string& region) { 155 DCHECK_EQ(2u, region.size()); 156 base::string16 country_code; 157 base::string16 unused_city_code; 158 base::string16 unused_number; 159 PhoneNumber phone_number; 160 if (!ParsePhoneNumber(value, region, &country_code, &unused_city_code, 161 &unused_number, &phone_number)) { 162 return base::string16(); // Parsing failed - do not store phone. 163 } 164 165 base::string16 normalized_number; 166 FormatValidatedNumber(phone_number, country_code, NULL, &normalized_number); 167 return normalized_number; 168 } 169 170 bool ConstructPhoneNumber(const base::string16& country_code, 171 const base::string16& city_code, 172 const base::string16& number, 173 const std::string& region, 174 base::string16* whole_number) { 175 DCHECK_EQ(2u, region.size()); 176 whole_number->clear(); 177 178 base::string16 unused_country_code; 179 base::string16 unused_city_code; 180 base::string16 unused_number; 181 PhoneNumber phone_number; 182 if (!ParsePhoneNumber(country_code + city_code + number, region, 183 &unused_country_code, &unused_city_code, &unused_number, 184 &phone_number)) { 185 return false; 186 } 187 188 FormatValidatedNumber(phone_number, country_code, whole_number, NULL); 189 return true; 190 } 191 192 bool PhoneNumbersMatch(const base::string16& number_a, 193 const base::string16& number_b, 194 const std::string& raw_region, 195 const std::string& app_locale) { 196 // Sanitize the provided |raw_region| before trying to use it for parsing. 197 const std::string region = SanitizeRegion(raw_region, app_locale); 198 199 PhoneNumberUtil* phone_util = PhoneNumberUtil::GetInstance(); 200 201 // Parse phone numbers based on the region 202 PhoneNumber i18n_number1; 203 if (phone_util->Parse(UTF16ToUTF8(number_a), region.c_str(), &i18n_number1) != 204 PhoneNumberUtil::NO_PARSING_ERROR) { 205 return false; 206 } 207 208 PhoneNumber i18n_number2; 209 if (phone_util->Parse(UTF16ToUTF8(number_b), region.c_str(), &i18n_number2) != 210 PhoneNumberUtil::NO_PARSING_ERROR) { 211 return false; 212 } 213 214 switch (phone_util->IsNumberMatch(i18n_number1, i18n_number2)) { 215 case PhoneNumberUtil::INVALID_NUMBER: 216 case PhoneNumberUtil::NO_MATCH: 217 return false; 218 case PhoneNumberUtil::SHORT_NSN_MATCH: 219 return false; 220 case PhoneNumberUtil::NSN_MATCH: 221 case PhoneNumberUtil::EXACT_MATCH: 222 return true; 223 } 224 225 NOTREACHED(); 226 return false; 227 } 228 229 PhoneObject::PhoneObject(const base::string16& number, 230 const std::string& region) 231 : region_(region) { 232 DCHECK_EQ(2u, region.size()); 233 // TODO(isherman): Autofill profiles should always have a |region| set, but in 234 // some cases it should be marked as implicit. Otherwise, phone numbers 235 // might behave differently when they are synced across computers: 236 // [ http://crbug.com/100845 ]. Once the bug is fixed, add a DCHECK here to 237 // verify. 238 239 scoped_ptr<PhoneNumber> i18n_number(new PhoneNumber); 240 if (ParsePhoneNumber(number, region_, &country_code_, &city_code_, &number_, 241 i18n_number.get())) { 242 // The phone number was successfully parsed, so store the parsed version. 243 // The formatted and normalized versions will be set on the first call to 244 // the coresponding methods. 245 i18n_number_.reset(i18n_number.release()); 246 } else { 247 // Parsing failed. Store passed phone "as is" into |whole_number_|. 248 whole_number_ = number; 249 } 250 } 251 252 PhoneObject::PhoneObject(const PhoneObject& other) { *this = other; } 253 254 PhoneObject::PhoneObject() {} 255 256 PhoneObject::~PhoneObject() { 257 } 258 259 base::string16 PhoneObject::GetFormattedNumber() const { 260 if (i18n_number_ && formatted_number_.empty()) { 261 FormatValidatedNumber(*i18n_number_, country_code_, &formatted_number_, 262 &whole_number_); 263 } 264 265 return formatted_number_; 266 } 267 268 base::string16 PhoneObject::GetWholeNumber() const { 269 if (i18n_number_ && whole_number_.empty()) { 270 FormatValidatedNumber(*i18n_number_, country_code_, &formatted_number_, 271 &whole_number_); 272 } 273 274 return whole_number_; 275 } 276 277 PhoneObject& PhoneObject::operator=(const PhoneObject& other) { 278 if (this == &other) 279 return *this; 280 281 region_ = other.region_; 282 283 if (other.i18n_number_.get()) 284 i18n_number_.reset(new PhoneNumber(*other.i18n_number_)); 285 else 286 i18n_number_.reset(); 287 288 country_code_ = other.country_code_; 289 city_code_ = other.city_code_; 290 number_ = other.number_; 291 292 formatted_number_ = other.formatted_number_; 293 whole_number_ = other.whole_number_; 294 295 return *this; 296 } 297 298 } // namespace i18n 299 } // namespace autofill 300