phone_number_i18n.cc revision 4e180b6a0b4720a9b8e9e959a882386f690f08ff
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 = 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
79namespace 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.
84bool 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
153base::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
170bool 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
192bool 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
229PhoneObject::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
252PhoneObject::PhoneObject(const PhoneObject& other) { *this = other; }
253
254PhoneObject::PhoneObject() {}
255
256PhoneObject::~PhoneObject() {}
257
258const base::string16& PhoneObject::GetFormattedNumber() const {
259  if (i18n_number_ && formatted_number_.empty()) {
260    FormatValidatedNumber(*i18n_number_, country_code_, &formatted_number_,
261                          &whole_number_);
262  }
263
264  return formatted_number_;
265}
266
267base::string16 PhoneObject::GetNationallyFormattedNumber() const {
268  base::string16 formatted = whole_number_;
269  if (i18n_number_)
270    FormatValidatedNumber(*i18n_number_, base::string16(), &formatted, NULL);
271
272  return formatted;
273}
274
275const base::string16& PhoneObject::GetWholeNumber() const {
276  if (i18n_number_ && whole_number_.empty()) {
277    FormatValidatedNumber(*i18n_number_, country_code_, &formatted_number_,
278                          &whole_number_);
279  }
280
281  return whole_number_;
282}
283
284PhoneObject& PhoneObject::operator=(const PhoneObject& other) {
285  if (this == &other)
286    return *this;
287
288  region_ = other.region_;
289
290  if (other.i18n_number_.get())
291    i18n_number_.reset(new PhoneNumber(*other.i18n_number_));
292  else
293    i18n_number_.reset();
294
295  country_code_ = other.country_code_;
296  city_code_ = other.city_code_;
297  number_ = other.number_;
298
299  formatted_number_ = other.formatted_number_;
300  whole_number_ = other.whole_number_;
301
302  return *this;
303}
304
305}  // namespace i18n
306}  // namespace autofill
307