OLD | NEW |
| (Empty) |
1 // Copyright (c) 2011 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/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/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 | |
OLD | NEW |