1 /*
2  * Copyright (c) 2023 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 <cctype>
16 #include <filesystem>
17 #include "phonenumbers/phonenumber.h"
18 #include "phonenumbers/shortnumberinfo.h"
19 #include "phonenumbers/phonenumberutil.h"
20 #include "i18n_hilog.h"
21 #include "libxml/globals.h"
22 #include "libxml/tree.h"
23 #include "libxml/xmlstring.h"
24 #include "matched_number_info.h"
25 #include "phone_number_rule.h"
26 #include "utils.h"
27 
28 namespace OHOS {
29 namespace Global {
30 namespace I18n {
31 using i18n::phonenumbers::PhoneNumber;
32 using i18n::phonenumbers::PhoneNumberUtil;
33 using i18n::phonenumbers::ShortNumberInfo;
34 
35 std::string PhoneNumberRule::xmlCommonPath = "/system/usr/ohos_locale_config/phonenumber/common.xml";
36 
PhoneNumberRule(std::string & country)37 PhoneNumberRule::PhoneNumberRule(std::string& country)
38 {
39     xmlPath = "/system/usr/ohos_locale_config/phonenumber/" + country + ".xml";
40     isFixed = true;
41     try {
42         isFixed = std::filesystem::exists(xmlPath);
43     } catch (const std::filesystem::filesystem_error &except) {
44         HILOG_ERROR_I18N("FileExist failed because filesystem_error, error message: %{public}s.",
45             except.code().message().c_str());
46         isFixed = false;
47     } catch (const std::__h::__fs::filesystem::filesystem_error &except) {
48         HILOG_ERROR_I18N("FileExist failed because filesystem_error, error message: %{public}s.",
49             except.code().message().c_str());
50         isFixed = false;
51     } catch (const std::bad_alloc &except) {
52         HILOG_ERROR_I18N("FileExist failed because bad_alloc, error message: %{public}s.", except.what());
53         isFixed = false;
54     }
55     this->commonExit = false;
56     this->country = country;
57 }
58 
~PhoneNumberRule()59 PhoneNumberRule::~PhoneNumberRule()
60 {
61     for (auto it = negativeRules.begin(); it != negativeRules.end(); ++it) {
62         if (*it != nullptr) {
63             delete *it;
64         }
65     }
66     for (auto it = positiveRules.begin(); it != positiveRules.end(); ++it) {
67         if (*it != nullptr) {
68             delete *it;
69         }
70     }
71     for (auto it = borderRules.begin(); it != borderRules.end(); ++it) {
72         if (*it != nullptr) {
73             delete *it;
74         }
75     }
76     for (auto it = codesRules.begin(); it != codesRules.end(); ++it) {
77         if (*it != nullptr) {
78             delete *it;
79         }
80     }
81     for (auto it = findRules.begin(); it != findRules.end(); ++it) {
82         if (*it != nullptr) {
83             delete *it;
84         }
85     }
86 }
87 
Init()88 void PhoneNumberRule::Init()
89 {
90     if (isFixed) {
91         InitRule(xmlPath);
92     }
93     InitRule(xmlCommonPath);
94 }
95 
96 // Load the rules of the corresponding country
InitRule(std::string & xmlPath)97 void PhoneNumberRule::InitRule(std::string& xmlPath)
98 {
99     if (!CheckTzDataFilePath(xmlPath)) {
100         return;
101     }
102     xmlKeepBlanksDefault(0);
103     xmlDocPtr doc = xmlParseFile(xmlPath.c_str());
104     if (doc == nullptr) return;
105     xmlNodePtr root = xmlDocGetRootElement(doc);
106     xmlNodePtr cur = root->xmlChildrenNode;
107     while (cur != nullptr) {
108         std::string category = reinterpret_cast<const char*>(cur->name);
109         ParseXmlNode(cur, category);
110         cur = cur->next;
111     }
112     xmlFreeDoc(doc);
113 }
114 
ParseXmlNode(xmlNodePtr cur,std::string & category)115 void PhoneNumberRule::ParseXmlNode(xmlNodePtr cur, std::string& category)
116 {
117     xmlNodePtr rule = cur->xmlChildrenNode;
118     while (rule != nullptr && !xmlStrcmp(rule->name, reinterpret_cast<const xmlChar*>("rule"))) {
119         xmlNodePtr value = rule->xmlChildrenNode;
120         std::string insensitive = XmlNodePtrToString(value);
121         value = value->next;
122         if (category == "common_exit") {
123             commonExit = (insensitive == "True");
124             break;
125         }
126         std::string type = XmlNodePtrToString(value);
127         value = value->next;
128         std::string valid = XmlNodePtrToString(value);
129         value = value->next;
130         std::string handle = XmlNodePtrToString(value);
131         if (IsXmlNodeValueEmpty(insensitive, type, valid, handle)) {
132             break;
133         }
134         icu::UnicodeString content = "";
135         while (value->next != nullptr && !xmlStrcmp(value->next->name,
136             reinterpret_cast<const xmlChar*>("content"))) {
137             value = value->next;
138             xmlChar* contentPtr = xmlNodeGetContent(value);
139             if (contentPtr == nullptr) continue;
140             icu::UnicodeString tempContent = reinterpret_cast<char*>(contentPtr);
141             content += tempContent;
142             xmlFree(contentPtr);
143         }
144         SetRules(category, content, valid, handle, insensitive, type);
145         rule = rule->next;
146     }
147 }
148 
IsXmlNodeValueEmpty(const std::string & insensitive,const std::string & type,const std::string & valid,const std::string & handle)149 bool PhoneNumberRule::IsXmlNodeValueEmpty(const std::string& insensitive, const std::string& type,
150     const std::string& valid, const std::string& handle)
151 {
152     if (insensitive.empty() || type.empty() ||
153         valid.empty() || handle.empty()) {
154         return true;
155     }
156     return false;
157 }
158 
XmlNodePtrToString(xmlNodePtr valuePtr)159 std::string PhoneNumberRule::XmlNodePtrToString(xmlNodePtr valuePtr)
160 {
161     if (valuePtr == nullptr) {
162         return "";
163     }
164     xmlChar* charPtr = xmlNodeGetContent(valuePtr);
165     if (charPtr == nullptr) {
166         return "";
167     }
168     std::string result = reinterpret_cast<char*>(charPtr);
169     xmlFree(charPtr);
170     return result;
171 }
172 
SetRules(std::string & category,icu::UnicodeString & content,std::string & valid,std::string & handle,std::string & insensitive,std::string & type)173 void PhoneNumberRule::SetRules(std::string& category, icu::UnicodeString& content, std::string& valid,
174     std::string& handle, std::string& insensitive, std::string& type)
175 {
176     if (category == "negative" || (category == "common" && commonExit)) {
177         negativeRules.push_back(new NegativeRule(content, insensitive));
178     } else if (category == "positive") {
179         positiveRules.push_back(new PositiveRule(content, handle, insensitive));
180     } else if (category == "border") {
181         borderRules.push_back(new BorderRule(content, insensitive, type));
182     } else if (category == "codes") {
183         codesRules.push_back(new CodeRule(valid));
184     } else if (category == "find_number") {
185         findRules.push_back(new FindRule(content, insensitive));
186     }
187 }
188 
GetBorderRules()189 std::vector<BorderRule*> PhoneNumberRule::GetBorderRules()
190 {
191     return borderRules;
192 }
193 
GetCodesRules()194 std::vector<CodeRule*> PhoneNumberRule::GetCodesRules()
195 {
196     return codesRules;
197 }
198 
GetPositiveRules()199 std::vector<PositiveRule*> PhoneNumberRule::GetPositiveRules()
200 {
201     return positiveRules;
202 }
203 
GetNegativeRules()204 std::vector<NegativeRule*> PhoneNumberRule::GetNegativeRules()
205 {
206     return negativeRules;
207 }
208 
GetFindRules()209 std::vector<FindRule*> PhoneNumberRule::GetFindRules()
210 {
211     return findRules;
212 }
213 } // namespace I18n
214 } // namespace Global
215 } // namespace OHOS