1 /*
2  * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 #include "phone_number_format.h"
16 #include <cctype>
17 #include <dlfcn.h>
18 #include <regex>
19 #include "unicode/localebuilder.h"
20 #include "locale_config.h"
21 #include "unicode/locid.h"
22 #include "i18n_hilog.h"
23 #include "map"
24 #include "new"
25 #include "set"
26 #include "securec.h"
27 #include "string"
28 #include "taboo_utils.h"
29 #include "utility"
30 #include "utils.h"
31 
32 namespace OHOS {
33 namespace Global {
34 namespace I18n {
35 const int RECV_CHAR_LEN = 128;
36 using i18n::phonenumbers::PhoneNumberUtil;
37 void* PhoneNumberFormat::dynamicHandler = nullptr;
38 std::mutex PhoneNumberFormat::phoneMutex;
39 std::mutex PhoneNumberFormat::AS_YOU_TYPE_FORMAT_MUTEX;
40 size_t PhoneNumberFormat::MAX_NUMBER_LENGTH = 30;
41 std::map<char, char> PhoneNumberFormat::VALID_PHONE_NUMBER_CHARS {
42     { '+', '+' },
43     { ' ', ' ' },
44     { '*', '*' },
45     { '-', '-' },
46     { '#', '#' },
47     { '(', '(' },
48     { ')', ')' },
49     { ';', ';' },
50     { ',', ',' },
51     { 'A', '2' },
52     { 'B', '2' },
53     { 'C', '2' },
54     { 'D', '3' },
55     { 'E', '3' },
56     { 'F', '3' },
57     { 'G', '4' },
58     { 'H', '4' },
59     { 'I', '4' },
60     { 'J', '5' },
61     { 'K', '5' },
62     { 'L', '5' },
63     { 'M', '6' },
64     { 'N', '6' },
65     { 'O', '6' },
66     { 'P', '7' },
67     { 'Q', '7' },
68     { 'R', '7' },
69     { 'S', '7' },
70     { 'T', '8' },
71     { 'U', '8' },
72     { 'V', '8' },
73     { 'W', '9' },
74     { 'X', '9' },
75     { 'Y', '9' },
76     { 'Z', '9' },
77     { 'a', '2' },
78     { 'b', '2' },
79     { 'c', '2' },
80     { 'd', '3' },
81     { 'e', '3' },
82     { 'f', '3' },
83     { 'g', '4' },
84     { 'h', '4' },
85     { 'i', '4' },
86     { 'j', '5' },
87     { 'k', '5' },
88     { 'l', '5' },
89     { 'm', '6' },
90     { 'n', '6' },
91     { 'o', '6' },
92     { 'p', '7' },
93     { 'q', '7' },
94     { 'r', '7' },
95     { 's', '7' },
96     { 't', '8' },
97     { 'u', '8' },
98     { 'v', '8' },
99     { 'w', '9' },
100     { 'x', '9' },
101     { 'y', '9' },
102     { 'z', '9' },
103 };
104 
PhoneNumberFormat(const std::string & countryTag,const std::map<std::string,std::string> & options)105 PhoneNumberFormat::PhoneNumberFormat(const std::string &countryTag,
106                                      const std::map<std::string, std::string> &options)
107 {
108     util = PhoneNumberUtil::GetInstance();
109     if (LocaleConfig::IsValidRegion(countryTag)) {
110         country = countryTag;
111     } else {
112         icu::Locale locale = icu::Locale::createFromName(countryTag.c_str());
113         country = locale.getCountry();
114     }
115     std::string type = "";
116     auto search = options.find("type");
117     if (search != options.end()) {
118         type = search->second;
119     }
120 
121     std::map<std::string, PhoneNumberUtil::PhoneNumberFormat> type2PhoneNumberFormat = {
122         {"E164", PhoneNumberUtil::PhoneNumberFormat::E164},
123         {"RFC3966", PhoneNumberUtil::PhoneNumberFormat::RFC3966},
124         {"INTERNATIONAL", PhoneNumberUtil::PhoneNumberFormat::INTERNATIONAL},
125         {"NATIONAL", PhoneNumberUtil::PhoneNumberFormat::NATIONAL}
126     };
127 
128     std::set<std::string> validType = {"E164", "RFC3966", "INTERNATIONAL", "NATIONAL"};
129     if (validType.find(type) != validType.end()) {
130         withOptions = true;
131         phoneNumberFormat = type2PhoneNumberFormat[type];
132     } else {
133         phoneNumberFormat = PhoneNumberUtil::PhoneNumberFormat::NATIONAL;
134     }
135     if (type.compare("TYPING") == 0) {
136         formatter = std::unique_ptr<AsYouTypeFormatter>(util->GetAsYouTypeFormatter(country));
137     }
138 }
139 
~PhoneNumberFormat()140 PhoneNumberFormat::~PhoneNumberFormat()
141 {
142 }
143 
CloseDynamicHandler()144 void PhoneNumberFormat::CloseDynamicHandler()
145 {
146     std::lock_guard<std::mutex> phoneLock(phoneMutex);
147     if (dynamicHandler != nullptr) {
148         dlclose(dynamicHandler);
149     }
150     dynamicHandler = nullptr;
151 }
152 
CreateInstance(const std::string & countryTag,const std::map<std::string,std::string> & options)153 std::unique_ptr<PhoneNumberFormat> PhoneNumberFormat::CreateInstance(const std::string &countryTag,
154     const std::map<std::string, std::string> &options)
155 {
156     std::unique_ptr<PhoneNumberFormat> phoneNumberFormat = std::make_unique<PhoneNumberFormat>(countryTag, options);
157     if (phoneNumberFormat->GetPhoneNumberUtil() == nullptr) {
158         return nullptr;
159     }
160     return phoneNumberFormat;
161 }
162 
GetPhoneNumberUtil()163 PhoneNumberUtil* PhoneNumberFormat::GetPhoneNumberUtil()
164 {
165     return util;
166 }
167 
isValidPhoneNumber(const std::string & number) const168 bool PhoneNumberFormat::isValidPhoneNumber(const std::string &number) const
169 {
170     i18n::phonenumbers::PhoneNumber phoneNumber;
171     PhoneNumberUtil::ErrorType type = util->Parse(number, country, &phoneNumber);
172     if (type != PhoneNumberUtil::ErrorType::NO_PARSING_ERROR) {
173         return false;
174     }
175     return util->IsValidNumber(phoneNumber);
176 }
177 
format(const std::string & number)178 std::string PhoneNumberFormat::format(const std::string &number)
179 {
180     std::string formatted_number;
181     if (formatter != nullptr) {
182         formatted_number = GetAsYouTypeFormatResult(number);
183         return PseudoLocalizationProcessor(formatted_number);
184     }
185     i18n::phonenumbers::PhoneNumber phoneNumber;
186     PhoneNumberUtil::ErrorType type = util->ParseAndKeepRawInput(number, country, &phoneNumber);
187     if (type != PhoneNumberUtil::ErrorType::NO_PARSING_ERROR) {
188         return PseudoLocalizationProcessor("");
189     }
190     const std::string prefix = "106";
191     if (number.compare(0, prefix.length(), prefix) == 0) {
192         util->FormatInOriginalFormat(phoneNumber, country, &formatted_number);
193     } else {
194         util->Format(phoneNumber, phoneNumberFormat, &formatted_number);
195     }
196     return PseudoLocalizationProcessor(formatted_number);
197 }
198 
GetAsYouTypeFormatResult(const std::string & number)199 std::string PhoneNumberFormat::GetAsYouTypeFormatResult(const std::string &number)
200 {
201     std::lock_guard<std::mutex> formatLock(AS_YOU_TYPE_FORMAT_MUTEX);
202     if (formatter == nullptr || number.length() > MAX_NUMBER_LENGTH) {
203         return number;
204     }
205     std::regex pattern("[^\\d]");
206     std::string phoneNumber = std::regex_replace(number, pattern, "");
207     if (lastFormatNumber.length() > 0 && phoneNumber.length() == lastFormatNumber.length() + 1) {
208         if (phoneNumber.compare(0, lastFormatNumber.length(), lastFormatNumber) != 0) {
209             return FormatAllInputNumber(number, phoneNumber);
210         }
211         char lastChar = *(phoneNumber.rbegin());
212         std::string result;
213         this->lastFormatNumber = phoneNumber;
214         return formatter->InputDigit(lastChar, &result);
215     }
216 
217     return FormatAllInputNumber(number, phoneNumber);
218 }
219 
FormatAllInputNumber(const std::string & originalNumber,std::string & replacedNumber)220 std::string PhoneNumberFormat::FormatAllInputNumber(const std::string &originalNumber,
221                                                     std::string &replacedNumber)
222 {
223     formatter->Clear();
224     this->lastFormatNumber = replacedNumber;
225     std::string result;
226     std::string formattedNumber;
227     for (size_t i = 0; i < originalNumber.length(); i++) {
228         char c = originalNumber.at(i);
229         auto iter = VALID_PHONE_NUMBER_CHARS.find(c);
230         if (isdigit(c)) {
231             formattedNumber = formatter->InputDigit(c, &result);
232         } else if (iter != VALID_PHONE_NUMBER_CHARS.end()) {
233             char replacedChar = VALID_PHONE_NUMBER_CHARS[c];
234             formattedNumber = formatter->InputDigit(replacedChar, &result);
235         }
236     }
237     return formattedNumber;
238 }
239 
getLocationName(const std::string & number,const std::string & locale)240 std::string PhoneNumberFormat::getLocationName(
241     const std::string &number, const std::string &locale)
242 {
243     // Combine country and locale parameters
244     UErrorCode status = U_ZERO_ERROR;
245     icu::Locale displayLocale = icu::Locale::createFromName(locale.c_str());
246     displayLocale.addLikelySubtags(status);
247     icu::LocaleBuilder builder = icu::LocaleBuilder().setRegion(country);
248     builder.setLanguage(displayLocale.getLanguage());
249     builder.setScript(displayLocale.getScript());
250     icu::Locale phoneLocale = builder.build(status);
251 
252     i18n::phonenumbers::PhoneNumber phoneNumber;
253     PhoneNumberUtil::ErrorType type = util->Parse(number, phoneLocale.getCountry(), &phoneNumber);
254     if (type != PhoneNumberUtil::ErrorType::NO_PARSING_ERROR) {
255         return "";
256     }
257     std::string regionCode;
258     util->GetRegionCodeForNumber(phoneNumber, &regionCode);
259 
260     std::string locName = getPhoneLocationName(number, phoneLocale.getName(), locale, regionCode);
261 
262     icu::LocaleBuilder regionbuilder = icu::LocaleBuilder().setRegion(regionCode);
263     icu::Locale regionLocale = builder.build(status);
264     icu::UnicodeString displayRegion;
265     regionLocale.getDisplayCountry(displayLocale, displayRegion);
266     std::string displayCountryName;
267     displayRegion.toUTF8String(displayCountryName);
268 
269     // Check if region name is a country name
270     if (locName.compare(displayCountryName) == 0) {
271         if (getBlockedRegionName(regionCode, displayLocale.getLanguage())) {
272             return "";
273         }
274 
275         TabooUtils taboo;
276         return taboo.ReplaceCountryName(regionCode, locale, displayCountryName);
277     }
278 
279     // Process the city name
280     std::string formatted_number;
281     util->Format(phoneNumber, PhoneNumberUtil::PhoneNumberFormat::E164, &formatted_number);
282     return getCityName(locale, formatted_number.substr(1), locName);
283 }
284 
getPhoneLocationName(const std::string & number,const std::string & phoneLocale,const std::string & displayLocale,const std::string & regionCode)285 std::string PhoneNumberFormat::getPhoneLocationName(
286     const std::string& number, const std::string& phoneLocale,
287     const std::string& displayLocale, const std::string& regionCode)
288 {
289     OpenHandler();
290     std::lock_guard<std::mutex> phoneLock(phoneMutex);
291     std::string locName;
292     if (dynamicHandler) {
293         HILOG_ERROR_I18N("LocationNameFunc Init");
294         ExposeLocationName locationNameFunc = reinterpret_cast<ExposeLocationName>(
295             dlsym(dynamicHandler, "exposeLocationName"));
296         if (locationNameFunc) {
297             // The function uses the same locale for phone and display.
298             char recvArr[RECV_CHAR_LEN];
299             const char* numberArr = number.c_str();
300             const char* phoneLocaleArr = phoneLocale.c_str();
301             locationNameFunc(numberArr, phoneLocaleArr, recvArr, RECV_CHAR_LEN);
302             locName = recvArr;
303         }
304     }
305     return locName;
306 }
307 
OpenHandler()308 void PhoneNumberFormat::OpenHandler()
309 {
310     if (dynamicHandler == nullptr) {
311         HILOG_INFO_I18N("DynamicHandler init.");
312         std::lock_guard<std::mutex> phoneLock(phoneMutex);
313         if (dynamicHandler == nullptr) {
314             HILOG_INFO_I18N("DynamicHandler lock init.");
315 #ifndef SUPPORT_ASAN
316             const char* geocodingSO = "libgeocoding.z.so";
317 #else
318             const char* geocodingSO = "system/asan/lib64/platformsdk/libgeocoding.z.so";
319 #endif
320             HILOG_INFO_I18N("DynamicHandler lock init.");
321             dynamicHandler = dlopen(geocodingSO, RTLD_NOW);
322         }
323     }
324 }
325 
getBlockedRegionName(const std::string & regionCode,const std::string & language)326 bool PhoneNumberFormat::getBlockedRegionName(
327     const std::string& regionCode, const std::string& language)
328 {
329     void *i18nUtilHandle = dlopen("libi18n_util.z.so", RTLD_NOW);
330     if (i18nUtilHandle == nullptr) {
331         return false;
332     }
333     GetBlockedRegionName getBlockedRegionName = (GetBlockedRegionName)dlsym(i18nUtilHandle, "GetBlockedRegionName");
334     bool ret = false;
335     if (getBlockedRegionName) {
336         ret = getBlockedRegionName(language.c_str(), regionCode.c_str());
337     }
338     dlclose(i18nUtilHandle);
339     i18nUtilHandle = nullptr;
340     return ret;
341 }
342 
getCityName(const std::string & language,const std::string & phonenumber,const std::string & locationName)343 std::string PhoneNumberFormat::getCityName(
344     const std::string& language, const std::string& phonenumber,
345     const std::string& locationName)
346 {
347     char recvArr[RECV_CHAR_LEN];
348     int err = strcpy_s(recvArr, RECV_CHAR_LEN, locationName.c_str());
349     if (err != 0) {
350         return locationName;
351     }
352     void *i18nUtilHandle = dlopen("libi18n_util.z.so", RTLD_NOW);
353     if (i18nUtilHandle == nullptr) {
354         return locationName;
355     }
356     GetBlockedPhoneLocationName getBlockedPhoneLocationName =
357         (GetBlockedPhoneLocationName)dlsym(i18nUtilHandle, "GetBlockedPhoneLocationName");
358     if (getBlockedPhoneLocationName) {
359         getBlockedPhoneLocationName(language.c_str(), phonenumber.c_str(), recvArr);
360     }
361     if (locationName.compare(recvArr) != 0) {
362         dlclose(i18nUtilHandle);
363         i18nUtilHandle = nullptr;
364         return "";
365     }
366 
367     GetReplacedPhoneLocationName getReplacedPhoneLocationName =
368         (GetReplacedPhoneLocationName)dlsym(i18nUtilHandle, "GetReplacedPhoneLocationName");
369     if (getReplacedPhoneLocationName) {
370         getReplacedPhoneLocationName(language.c_str(), phonenumber.c_str(), recvArr);
371     }
372     dlclose(i18nUtilHandle);
373     i18nUtilHandle = nullptr;
374     std::string ret(recvArr);
375     return ret;
376 }
377 } // namespace I18n
378 } // namespace Global
379 } // namespace OHOS
380