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 && util != nullptr) {
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 == nullptr || 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 if (util == nullptr) {
171 return false;
172 }
173 i18n::phonenumbers::PhoneNumber phoneNumber;
174 PhoneNumberUtil::ErrorType type = util->Parse(number, country, &phoneNumber);
175 if (type != PhoneNumberUtil::ErrorType::NO_PARSING_ERROR) {
176 return false;
177 }
178 return util->IsValidNumber(phoneNumber);
179 }
180
format(const std::string & number)181 std::string PhoneNumberFormat::format(const std::string &number)
182 {
183 std::string formattedNumber;
184 if (formatter != nullptr) {
185 formattedNumber = GetAsYouTypeFormatResult(number);
186 return PseudoLocalizationProcessor(formattedNumber);
187 }
188 i18n::phonenumbers::PhoneNumber phoneNumber;
189 if (util == nullptr) {
190 HILOG_ERROR_I18N("PhoneNumberFormat::format: util is nullptr.");
191 return PseudoLocalizationProcessor("");
192 }
193 PhoneNumberUtil::ErrorType type = util->ParseAndKeepRawInput(number, country, &phoneNumber);
194 if (type != PhoneNumberUtil::ErrorType::NO_PARSING_ERROR) {
195 return PseudoLocalizationProcessor("");
196 }
197 const std::string prefix = "106";
198 if (number.compare(0, prefix.length(), prefix) == 0) {
199 util->FormatInOriginalFormat(phoneNumber, country, &formattedNumber);
200 } else {
201 util->Format(phoneNumber, phoneNumberFormat, &formattedNumber);
202 }
203 std::string usingNumberingSystem;
204 LocaleConfig::GetUsingNumberingSystem(usingNumberingSystem);
205 icu::UnicodeString replaceDigit = LocaleConfig::GetNumberingSystemDigit(usingNumberingSystem).c_str();
206 if (replaceDigit.isEmpty() || usingNumberingSystem.compare("latn") == 0) {
207 return PseudoLocalizationProcessor(formattedNumber);
208 }
209 icu::UnicodeString replaceNumber;
210 for (auto& number : formattedNumber) {
211 if (number <= '9' && number >= '0') {
212 replaceNumber += replaceDigit.char32At(number - '0');
213 continue;
214 } else {
215 replaceNumber += number;
216 }
217 }
218 if (usingNumberingSystem.compare("arabext") == 0 || usingNumberingSystem.compare("arab") == 0) {
219 replaceNumber = "\u202d" + replaceNumber + "\u202c";
220 }
221 std::string result;
222 replaceNumber.toUTF8String(result);
223 return PseudoLocalizationProcessor(result);
224 }
225
GetAsYouTypeFormatResult(const std::string & number)226 std::string PhoneNumberFormat::GetAsYouTypeFormatResult(const std::string &number)
227 {
228 std::lock_guard<std::mutex> formatLock(AS_YOU_TYPE_FORMAT_MUTEX);
229 if (formatter == nullptr || number.length() > MAX_NUMBER_LENGTH) {
230 return number;
231 }
232 std::regex pattern("[^\\d]");
233 std::string phoneNumber = std::regex_replace(number, pattern, "");
234 if (lastFormatNumber.length() > 0 && phoneNumber.length() == lastFormatNumber.length() + 1) {
235 if (phoneNumber.compare(0, lastFormatNumber.length(), lastFormatNumber) != 0) {
236 return FormatAllInputNumber(number, phoneNumber);
237 }
238 char lastChar = *(phoneNumber.rbegin());
239 std::string result;
240 this->lastFormatNumber = phoneNumber;
241 return formatter->InputDigit(lastChar, &result);
242 }
243
244 return FormatAllInputNumber(number, phoneNumber);
245 }
246
FormatAllInputNumber(const std::string & originalNumber,std::string & replacedNumber)247 std::string PhoneNumberFormat::FormatAllInputNumber(const std::string &originalNumber,
248 std::string &replacedNumber)
249 {
250 if (formatter == nullptr) {
251 HILOG_ERROR_I18N("PhoneNumberFormat::FormatAllInputNumber: formatter is nullptr.");
252 return "";
253 }
254 formatter->Clear();
255 this->lastFormatNumber = replacedNumber;
256 std::string result;
257 std::string formattedNumber;
258 for (size_t i = 0; i < originalNumber.length(); i++) {
259 char c = originalNumber.at(i);
260 auto iter = VALID_PHONE_NUMBER_CHARS.find(c);
261 if (isdigit(c)) {
262 formattedNumber = formatter->InputDigit(c, &result);
263 } else if (iter != VALID_PHONE_NUMBER_CHARS.end()) {
264 char replacedChar = VALID_PHONE_NUMBER_CHARS[c];
265 formattedNumber = formatter->InputDigit(replacedChar, &result);
266 }
267 }
268 return formattedNumber;
269 }
270
getLocationName(const std::string & number,const std::string & locale)271 std::string PhoneNumberFormat::getLocationName(
272 const std::string &number, const std::string &locale)
273 {
274 if (util == nullptr) {
275 return "";
276 }
277 // Combine country and locale parameters
278 UErrorCode status = U_ZERO_ERROR;
279 icu::Locale displayLocale = icu::Locale::createFromName(locale.c_str());
280 displayLocale.addLikelySubtags(status);
281 if (U_FAILURE(status)) {
282 HILOG_ERROR_I18N("PhoneNumberFormat::getLocationName: Add likely subtag failed.");
283 return "";
284 }
285 icu::LocaleBuilder builder = icu::LocaleBuilder().setRegion(country).setLanguage(displayLocale.getLanguage()).
286 setScript(displayLocale.getScript());
287 icu::Locale phoneLocale = builder.build(status);
288 if (U_FAILURE(status)) {
289 HILOG_ERROR_I18N("PhoneNumberFormat::getLocationName: Build phone locale failed.");
290 return "";
291 }
292 i18n::phonenumbers::PhoneNumber phoneNumber;
293 PhoneNumberUtil::ErrorType type = util->Parse(number, phoneLocale.getCountry(), &phoneNumber);
294 if (type != PhoneNumberUtil::ErrorType::NO_PARSING_ERROR) {
295 return "";
296 }
297 std::string regionCode;
298 util->GetRegionCodeForNumber(phoneNumber, ®ionCode);
299
300 std::string locName = getPhoneLocationName(number, phoneLocale.getName(), locale);
301
302 icu::LocaleBuilder regionbuilder = icu::LocaleBuilder().setRegion(regionCode);
303 icu::Locale regionLocale = regionbuilder.build(status);
304 icu::UnicodeString displayRegion;
305 regionLocale.getDisplayCountry(displayLocale, displayRegion);
306 std::string displayCountryName;
307 displayRegion.toUTF8String(displayCountryName);
308
309 // Check if region name is a country name
310 if (locName.compare(displayCountryName) == 0) {
311 if (getBlockedRegionName(regionCode, displayLocale.getLanguage())) {
312 return "";
313 }
314 TabooUtils* tabooUtils = TabooUtils::GetInstance();
315 if (tabooUtils == nullptr) {
316 HILOG_ERROR_I18N("PhoneNumberFormat::getLocationName: tabooUtils is nullptr.");
317 return displayCountryName;
318 }
319 return tabooUtils->ReplaceCountryName(regionCode, locale, displayCountryName);
320 }
321
322 // Process the city name
323 std::string formattedNumber;
324 util->Format(phoneNumber, PhoneNumberUtil::PhoneNumberFormat::E164, &formattedNumber);
325 return getCityName(locale, formattedNumber.substr(1), locName);
326 }
327
getPhoneLocationName(const std::string & number,const std::string & phoneLocale,const std::string & displayLocale)328 std::string PhoneNumberFormat::getPhoneLocationName(
329 const std::string& number, const std::string& phoneLocale,
330 const std::string& displayLocale)
331 {
332 OpenHandler();
333 std::lock_guard<std::mutex> phoneLock(phoneMutex);
334 std::string locName;
335 if (dynamicHandler) {
336 HILOG_ERROR_I18N("LocationNameFunc Init");
337 ExposeLocationName locationNameFunc = reinterpret_cast<ExposeLocationName>(
338 dlsym(dynamicHandler, "exposeLocationName"));
339 if (locationNameFunc) {
340 // The function uses the same locale for phone and display.
341 char recvArr[RECV_CHAR_LEN];
342 const char* numberArr = number.c_str();
343 const char* phoneLocaleArr = phoneLocale.c_str();
344 locationNameFunc(numberArr, phoneLocaleArr, recvArr, RECV_CHAR_LEN);
345 locName = recvArr;
346 }
347 }
348 return locName;
349 }
350
OpenHandler()351 void PhoneNumberFormat::OpenHandler()
352 {
353 if (dynamicHandler == nullptr) {
354 HILOG_INFO_I18N("DynamicHandler init.");
355 std::lock_guard<std::mutex> phoneLock(phoneMutex);
356 if (dynamicHandler == nullptr) {
357 HILOG_INFO_I18N("DynamicHandler lock init.");
358 #ifndef SUPPORT_ASAN
359 const char* geocodingSO = "libgeocoding.z.so";
360 #else
361 const char* geocodingSO = "system/asan/lib64/platformsdk/libgeocoding.z.so";
362 #endif
363 HILOG_INFO_I18N("DynamicHandler lock init.");
364 dynamicHandler = dlopen(geocodingSO, RTLD_NOW);
365 }
366 }
367 }
368
getBlockedRegionName(const std::string & regionCode,const std::string & language)369 bool PhoneNumberFormat::getBlockedRegionName(
370 const std::string& regionCode, const std::string& language)
371 {
372 TabooUtils* tabooUtils = TabooUtils::GetInstance();
373 if (tabooUtils == nullptr) {
374 HILOG_ERROR_I18N("PhoneNumberFormat::getBlockedRegionName: tabooUtils is nullptr.");
375 return false;
376 }
377 std::unordered_set<std::string> blockedRegions = tabooUtils->GetBlockedRegions(language);
378 return blockedRegions.find(regionCode) != blockedRegions.end();
379 }
380
getCityName(const std::string & language,const std::string & phonenumber,const std::string & locationName)381 std::string PhoneNumberFormat::getCityName(const std::string& language, const std::string& phonenumber,
382 const std::string& locationName)
383 {
384 TabooUtils* tabooUtils = TabooUtils::GetInstance();
385 if (tabooUtils == nullptr) {
386 HILOG_ERROR_I18N("PhoneNumberFormat::getCityName: tabooUtils is nullptr.");
387 return locationName;
388 }
389 std::unordered_set<std::string> blockedPhoneNumbers = tabooUtils->GetBlockedPhoneNumbers();
390 for (const auto& blockedPhoneNumber : blockedPhoneNumbers) {
391 if (phonenumber.compare(0, blockedPhoneNumber.length(), blockedPhoneNumber) == 0) {
392 return "";
393 }
394 }
395 return tabooUtils->ReplacePhoneLocationName(phonenumber, language, locationName);
396 }
397 } // namespace I18n
398 } // namespace Global
399 } // namespace OHOS
400