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