/* * Copyright (c) 2021-2022 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "phone_number_format.h" #include <cctype> #include <dlfcn.h> #include <regex> #include "unicode/localebuilder.h" #include "locale_config.h" #include "unicode/locid.h" #include "i18n_hilog.h" #include "map" #include "new" #include "set" #include "securec.h" #include "string" #include "taboo_utils.h" #include "utility" #include "utils.h" namespace OHOS { namespace Global { namespace I18n { const int RECV_CHAR_LEN = 128; using i18n::phonenumbers::PhoneNumberUtil; void* PhoneNumberFormat::dynamicHandler = nullptr; std::mutex PhoneNumberFormat::phoneMutex; std::mutex PhoneNumberFormat::AS_YOU_TYPE_FORMAT_MUTEX; size_t PhoneNumberFormat::MAX_NUMBER_LENGTH = 30; std::map<char, char> PhoneNumberFormat::VALID_PHONE_NUMBER_CHARS { { '+', '+' }, { ' ', ' ' }, { '*', '*' }, { '-', '-' }, { '#', '#' }, { '(', '(' }, { ')', ')' }, { ';', ';' }, { ',', ',' }, { 'A', '2' }, { 'B', '2' }, { 'C', '2' }, { 'D', '3' }, { 'E', '3' }, { 'F', '3' }, { 'G', '4' }, { 'H', '4' }, { 'I', '4' }, { 'J', '5' }, { 'K', '5' }, { 'L', '5' }, { 'M', '6' }, { 'N', '6' }, { 'O', '6' }, { 'P', '7' }, { 'Q', '7' }, { 'R', '7' }, { 'S', '7' }, { 'T', '8' }, { 'U', '8' }, { 'V', '8' }, { 'W', '9' }, { 'X', '9' }, { 'Y', '9' }, { 'Z', '9' }, { 'a', '2' }, { 'b', '2' }, { 'c', '2' }, { 'd', '3' }, { 'e', '3' }, { 'f', '3' }, { 'g', '4' }, { 'h', '4' }, { 'i', '4' }, { 'j', '5' }, { 'k', '5' }, { 'l', '5' }, { 'm', '6' }, { 'n', '6' }, { 'o', '6' }, { 'p', '7' }, { 'q', '7' }, { 'r', '7' }, { 's', '7' }, { 't', '8' }, { 'u', '8' }, { 'v', '8' }, { 'w', '9' }, { 'x', '9' }, { 'y', '9' }, { 'z', '9' }, }; PhoneNumberFormat::PhoneNumberFormat(const std::string &countryTag, const std::map<std::string, std::string> &options) { util = PhoneNumberUtil::GetInstance(); if (LocaleConfig::IsValidRegion(countryTag)) { country = countryTag; } else { icu::Locale locale = icu::Locale::createFromName(countryTag.c_str()); country = locale.getCountry(); } std::string type = ""; auto search = options.find("type"); if (search != options.end()) { type = search->second; } std::map<std::string, PhoneNumberUtil::PhoneNumberFormat> type2PhoneNumberFormat = { {"E164", PhoneNumberUtil::PhoneNumberFormat::E164}, {"RFC3966", PhoneNumberUtil::PhoneNumberFormat::RFC3966}, {"INTERNATIONAL", PhoneNumberUtil::PhoneNumberFormat::INTERNATIONAL}, {"NATIONAL", PhoneNumberUtil::PhoneNumberFormat::NATIONAL} }; std::set<std::string> validType = {"E164", "RFC3966", "INTERNATIONAL", "NATIONAL"}; if (validType.find(type) != validType.end()) { withOptions = true; phoneNumberFormat = type2PhoneNumberFormat[type]; } else { phoneNumberFormat = PhoneNumberUtil::PhoneNumberFormat::NATIONAL; } if (type.compare("TYPING") == 0) { formatter = std::unique_ptr<AsYouTypeFormatter>(util->GetAsYouTypeFormatter(country)); } } PhoneNumberFormat::~PhoneNumberFormat() { } void PhoneNumberFormat::CloseDynamicHandler() { std::lock_guard<std::mutex> phoneLock(phoneMutex); if (dynamicHandler != nullptr) { dlclose(dynamicHandler); } dynamicHandler = nullptr; } std::unique_ptr<PhoneNumberFormat> PhoneNumberFormat::CreateInstance(const std::string &countryTag, const std::map<std::string, std::string> &options) { std::unique_ptr<PhoneNumberFormat> phoneNumberFormat = std::make_unique<PhoneNumberFormat>(countryTag, options); if (phoneNumberFormat->GetPhoneNumberUtil() == nullptr) { return nullptr; } return phoneNumberFormat; } PhoneNumberUtil* PhoneNumberFormat::GetPhoneNumberUtil() { return util; } bool PhoneNumberFormat::isValidPhoneNumber(const std::string &number) const { i18n::phonenumbers::PhoneNumber phoneNumber; PhoneNumberUtil::ErrorType type = util->Parse(number, country, &phoneNumber); if (type != PhoneNumberUtil::ErrorType::NO_PARSING_ERROR) { return false; } return util->IsValidNumber(phoneNumber); } std::string PhoneNumberFormat::format(const std::string &number) { std::string formatted_number; if (formatter != nullptr) { formatted_number = GetAsYouTypeFormatResult(number); return PseudoLocalizationProcessor(formatted_number); } i18n::phonenumbers::PhoneNumber phoneNumber; PhoneNumberUtil::ErrorType type = util->ParseAndKeepRawInput(number, country, &phoneNumber); if (type != PhoneNumberUtil::ErrorType::NO_PARSING_ERROR) { return PseudoLocalizationProcessor(""); } const std::string prefix = "106"; if (number.compare(0, prefix.length(), prefix) == 0) { util->FormatInOriginalFormat(phoneNumber, country, &formatted_number); } else { util->Format(phoneNumber, phoneNumberFormat, &formatted_number); } return PseudoLocalizationProcessor(formatted_number); } std::string PhoneNumberFormat::GetAsYouTypeFormatResult(const std::string &number) { std::lock_guard<std::mutex> formatLock(AS_YOU_TYPE_FORMAT_MUTEX); if (formatter == nullptr || number.length() > MAX_NUMBER_LENGTH) { return number; } std::regex pattern("[^\\d]"); std::string phoneNumber = std::regex_replace(number, pattern, ""); if (lastFormatNumber.length() > 0 && phoneNumber.length() == lastFormatNumber.length() + 1) { if (phoneNumber.compare(0, lastFormatNumber.length(), lastFormatNumber) != 0) { return FormatAllInputNumber(number, phoneNumber); } char lastChar = *(phoneNumber.rbegin()); std::string result; this->lastFormatNumber = phoneNumber; return formatter->InputDigit(lastChar, &result); } return FormatAllInputNumber(number, phoneNumber); } std::string PhoneNumberFormat::FormatAllInputNumber(const std::string &originalNumber, std::string &replacedNumber) { formatter->Clear(); this->lastFormatNumber = replacedNumber; std::string result; std::string formattedNumber; for (size_t i = 0; i < originalNumber.length(); i++) { char c = originalNumber.at(i); auto iter = VALID_PHONE_NUMBER_CHARS.find(c); if (isdigit(c)) { formattedNumber = formatter->InputDigit(c, &result); } else if (iter != VALID_PHONE_NUMBER_CHARS.end()) { char replacedChar = VALID_PHONE_NUMBER_CHARS[c]; formattedNumber = formatter->InputDigit(replacedChar, &result); } } return formattedNumber; } std::string PhoneNumberFormat::getLocationName( const std::string &number, const std::string &locale) { // Combine country and locale parameters UErrorCode status = U_ZERO_ERROR; icu::Locale displayLocale = icu::Locale::createFromName(locale.c_str()); displayLocale.addLikelySubtags(status); icu::LocaleBuilder builder = icu::LocaleBuilder().setRegion(country); builder.setLanguage(displayLocale.getLanguage()); builder.setScript(displayLocale.getScript()); icu::Locale phoneLocale = builder.build(status); i18n::phonenumbers::PhoneNumber phoneNumber; PhoneNumberUtil::ErrorType type = util->Parse(number, phoneLocale.getCountry(), &phoneNumber); if (type != PhoneNumberUtil::ErrorType::NO_PARSING_ERROR) { return ""; } std::string regionCode; util->GetRegionCodeForNumber(phoneNumber, ®ionCode); std::string locName = getPhoneLocationName(number, phoneLocale.getName(), locale, regionCode); icu::LocaleBuilder regionbuilder = icu::LocaleBuilder().setRegion(regionCode); icu::Locale regionLocale = builder.build(status); icu::UnicodeString displayRegion; regionLocale.getDisplayCountry(displayLocale, displayRegion); std::string displayCountryName; displayRegion.toUTF8String(displayCountryName); // Check if region name is a country name if (locName.compare(displayCountryName) == 0) { if (getBlockedRegionName(regionCode, displayLocale.getLanguage())) { return ""; } TabooUtils taboo; return taboo.ReplaceCountryName(regionCode, locale, displayCountryName); } // Process the city name std::string formatted_number; util->Format(phoneNumber, PhoneNumberUtil::PhoneNumberFormat::E164, &formatted_number); return getCityName(locale, formatted_number.substr(1), locName); } std::string PhoneNumberFormat::getPhoneLocationName( const std::string& number, const std::string& phoneLocale, const std::string& displayLocale, const std::string& regionCode) { OpenHandler(); std::lock_guard<std::mutex> phoneLock(phoneMutex); std::string locName; if (dynamicHandler) { HILOG_ERROR_I18N("LocationNameFunc Init"); ExposeLocationName locationNameFunc = reinterpret_cast<ExposeLocationName>( dlsym(dynamicHandler, "exposeLocationName")); if (locationNameFunc) { // The function uses the same locale for phone and display. char recvArr[RECV_CHAR_LEN]; const char* numberArr = number.c_str(); const char* phoneLocaleArr = phoneLocale.c_str(); locationNameFunc(numberArr, phoneLocaleArr, recvArr, RECV_CHAR_LEN); locName = recvArr; } } return locName; } void PhoneNumberFormat::OpenHandler() { if (dynamicHandler == nullptr) { HILOG_INFO_I18N("DynamicHandler init."); std::lock_guard<std::mutex> phoneLock(phoneMutex); if (dynamicHandler == nullptr) { HILOG_INFO_I18N("DynamicHandler lock init."); #ifndef SUPPORT_ASAN const char* geocodingSO = "libgeocoding.z.so"; #else const char* geocodingSO = "system/asan/lib64/platformsdk/libgeocoding.z.so"; #endif HILOG_INFO_I18N("DynamicHandler lock init."); dynamicHandler = dlopen(geocodingSO, RTLD_NOW); } } } bool PhoneNumberFormat::getBlockedRegionName( const std::string& regionCode, const std::string& language) { void *i18nUtilHandle = dlopen("libi18n_util.z.so", RTLD_NOW); if (i18nUtilHandle == nullptr) { return false; } GetBlockedRegionName getBlockedRegionName = (GetBlockedRegionName)dlsym(i18nUtilHandle, "GetBlockedRegionName"); bool ret = false; if (getBlockedRegionName) { ret = getBlockedRegionName(language.c_str(), regionCode.c_str()); } dlclose(i18nUtilHandle); i18nUtilHandle = nullptr; return ret; } std::string PhoneNumberFormat::getCityName( const std::string& language, const std::string& phonenumber, const std::string& locationName) { char recvArr[RECV_CHAR_LEN]; int err = strcpy_s(recvArr, RECV_CHAR_LEN, locationName.c_str()); if (err != 0) { return locationName; } void *i18nUtilHandle = dlopen("libi18n_util.z.so", RTLD_NOW); if (i18nUtilHandle == nullptr) { return locationName; } GetBlockedPhoneLocationName getBlockedPhoneLocationName = (GetBlockedPhoneLocationName)dlsym(i18nUtilHandle, "GetBlockedPhoneLocationName"); if (getBlockedPhoneLocationName) { getBlockedPhoneLocationName(language.c_str(), phonenumber.c_str(), recvArr); } if (locationName.compare(recvArr) != 0) { dlclose(i18nUtilHandle); i18nUtilHandle = nullptr; return ""; } GetReplacedPhoneLocationName getReplacedPhoneLocationName = (GetReplacedPhoneLocationName)dlsym(i18nUtilHandle, "GetReplacedPhoneLocationName"); if (getReplacedPhoneLocationName) { getReplacedPhoneLocationName(language.c_str(), phonenumber.c_str(), recvArr); } dlclose(i18nUtilHandle); i18nUtilHandle = nullptr; std::string ret(recvArr); return ret; } } // namespace I18n } // namespace Global } // namespace OHOS