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