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  std::string region_code;
69  phone_util->GetRegionCodeForNumber(number, &region_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
91namespace 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.
96bool 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
162base::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
178bool 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
199bool 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
238PhoneObject::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                       &region_, 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
260PhoneObject::PhoneObject(const PhoneObject& other) { *this = other; }
261
262PhoneObject::PhoneObject() {}
263
264PhoneObject::~PhoneObject() {}
265
266const 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
275base::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
283const 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
292PhoneObject& 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