Home | History | Annotate | Download | only in browser
      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 
     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
    300