1 /*
2 * Copyright (C) 2021 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
16 #include "preferences_xml_utils.h"
17
18 #include <sys/stat.h>
19
20 #include <cerrno>
21 #include <cstring>
22
23 #include "libxml/parser.h"
24 #include "logger.h"
25
26 #include "adaptor.h"
27
28 namespace OHOS {
29 namespace NativePreferences {
30 static bool ParseNodeElement(const xmlNode *node, Element &element);
31 static bool ParsePrimitiveNodeElement(const xmlNode *node, Element &element);
32 static bool ParseStringNodeElement(const xmlNode *node, Element &element);
33 static bool ParseArrayNodeElement(const xmlNode *node, Element &element);
34 static xmlNode *CreateElementNode(Element &element);
35 static xmlNode *CreatePrimitiveNode(Element &element);
36 static xmlNode *CreateStringNode(Element &element);
37 static xmlNode *CreateArrayNode(Element &element);
38 /* static */
ReadSettingXml(const std::string & fileName,std::vector<Element> & settings)39 bool PreferencesXmlUtils::ReadSettingXml(const std::string &fileName, std::vector<Element> &settings)
40 {
41 LOG_DEBUG("Read setting xml %{private}s start.", fileName.c_str());
42 if (fileName.size() == 0) {
43 LOG_ERROR("The length of the file name is 0.");
44 return false;
45 }
46 char path[PATH_MAX + 1] = { 0x00 };
47 if (strlen(fileName.c_str()) > PATH_MAX || REALPATH(fileName.c_str(), path, PATH_MAX) == nullptr) {
48 LOG_ERROR("The file name is incorrect.");
49 return false;
50 }
51 const char *pathString = path;
52 xmlDoc *doc = xmlReadFile(pathString, "UTF-8", XML_PARSE_NOBLANKS);
53 if (doc == nullptr) {
54 LOG_ERROR("The file name is incorrect.");
55 return false;
56 }
57
58 xmlNode *root = xmlDocGetRootElement(doc);
59 if (!root || xmlStrcmp(root->name, reinterpret_cast<const xmlChar *>("preferences"))) {
60 xmlFreeDoc(doc);
61 LOG_ERROR("Failed to obtain the XML root element.");
62 return false;
63 }
64
65 bool success = true;
66 const xmlNode *cur = nullptr;
67 for (cur = root->children; cur != nullptr; cur = cur->next) {
68 Element element;
69
70 if (ParseNodeElement(cur, element)) {
71 settings.push_back(element);
72 } else {
73 success = false;
74 LOG_ERROR("The error occurred during getting xml child elements.");
75 break;
76 }
77 }
78
79 /* free the document */
80 xmlFreeDoc(doc);
81 LOG_DEBUG("Read setting xml %{private}s end.", fileName.c_str());
82 return success;
83 }
84
85 /* static */
ParseNodeElement(const xmlNode * node,Element & element)86 bool ParseNodeElement(const xmlNode *node, Element &element)
87 {
88 if (!xmlStrcmp(node->name, reinterpret_cast<const xmlChar *>("int"))
89 || !xmlStrcmp(node->name, reinterpret_cast<const xmlChar *>("long"))
90 || !xmlStrcmp(node->name, reinterpret_cast<const xmlChar *>("bool"))
91 || !xmlStrcmp(node->name, reinterpret_cast<const xmlChar *>("float"))
92 || !xmlStrcmp(node->name, reinterpret_cast<const xmlChar *>("double"))) {
93 return ParsePrimitiveNodeElement(node, element);
94 }
95
96 if (!xmlStrcmp(node->name, reinterpret_cast<const xmlChar *>("string"))) {
97 return ParseStringNodeElement(node, element);
98 }
99
100 if (!xmlStrcmp(node->name, reinterpret_cast<const xmlChar *>("boolArray"))
101 || !xmlStrcmp(node->name, reinterpret_cast<const xmlChar *>("stringArray"))
102 || !xmlStrcmp(node->name, reinterpret_cast<const xmlChar *>("doubleArray"))
103 || !xmlStrcmp(node->name, reinterpret_cast<const xmlChar *>("set"))) {
104 return ParseArrayNodeElement(node, element);
105 }
106
107 LOG_ERROR("An unsupported element type was encountered in parsing = %{public}s.", node->name);
108 return false;
109 }
110
111 /* static */
ParsePrimitiveNodeElement(const xmlNode * node,Element & element)112 bool ParsePrimitiveNodeElement(const xmlNode *node, Element &element)
113 {
114 if (node == nullptr) {
115 return false;
116 }
117
118 xmlChar *key = xmlGetProp(node, reinterpret_cast<const xmlChar *>("key"));
119 xmlChar *value = xmlGetProp(node, reinterpret_cast<const xmlChar *>("value"));
120
121 bool success = false;
122 if (value != nullptr) {
123 element.tag_ = std::string(reinterpret_cast<const char *>(node->name));
124 if (key != nullptr) {
125 element.key_ = std::string(reinterpret_cast<char *>(key));
126 }
127 element.value_ = std::string(reinterpret_cast<char *>(value));
128 success = true;
129 } else {
130 LOG_ERROR("Failed to obtain a valid key or value when parsing %{public}s.", node->name);
131 }
132
133 if (key != nullptr) {
134 xmlFree(key);
135 }
136 if (value != nullptr) {
137 xmlFree(value);
138 }
139 return success;
140 }
141
142 /* static */
ParseStringNodeElement(const xmlNode * node,Element & element)143 bool ParseStringNodeElement(const xmlNode *node, Element &element)
144 {
145 if (node == nullptr) {
146 return false;
147 }
148
149 xmlChar *key = xmlGetProp(node, (const xmlChar *)"key");
150 xmlChar *text = xmlNodeGetContent(node);
151
152 bool success = false;
153 if (text != nullptr) {
154 element.tag_ = std::string(reinterpret_cast<const char *>(node->name));
155 if (key != nullptr) {
156 element.key_ = std::string(reinterpret_cast<char *>(key));
157 }
158 element.value_ = std::string(reinterpret_cast<char *>(text));
159 success = true;
160 } else {
161 LOG_ERROR("Failed to obtain a valid key or value when parsing a String element.");
162 }
163
164 if (key != nullptr) {
165 xmlFree(key);
166 }
167 if (text != nullptr) {
168 xmlFree(text);
169 }
170 return success;
171 }
172
173 /* static */
ParseArrayNodeElement(const xmlNode * node,Element & element)174 bool ParseArrayNodeElement(const xmlNode *node, Element &element)
175 {
176 if (node == nullptr) {
177 return false;
178 }
179
180 xmlChar *key = xmlGetProp(node, (const xmlChar *)"key");
181 const xmlNode *children = node->children;
182
183 bool success = false;
184 if (key != nullptr) {
185 element.tag_ = std::string(reinterpret_cast<const char *>(node->name));
186 element.key_ = std::string(reinterpret_cast<char *>(key));
187
188 const xmlNode *cur = nullptr;
189 bool finishTravelChild = true;
190 for (cur = children; cur != nullptr; cur = cur->next) {
191 Element child;
192 if (ParseNodeElement(cur, child)) {
193 element.children_.push_back(child);
194 } else {
195 finishTravelChild = false;
196 LOG_ERROR("Failed to parse the Array element and could not be completed successfully.");
197 break;
198 }
199 }
200 success = finishTravelChild;
201 } else {
202 LOG_ERROR("Failed to obtain a valid key or value when parsing a Array element.");
203 }
204
205 if (key != nullptr) {
206 xmlFree(key);
207 }
208 return success;
209 }
210
211 /* static */
WriteSettingXml(const std::string & fileName,std::vector<Element> & settings)212 bool PreferencesXmlUtils::WriteSettingXml(const std::string &fileName, std::vector<Element> &settings)
213 {
214 LOG_DEBUG("Write setting xml %{private}s start.", fileName.c_str());
215 if (fileName.size() == 0) {
216 LOG_ERROR("The length of the file name is 0.");
217 return false;
218 }
219
220 // define doc and root Node
221 xmlDoc *doc = xmlNewDoc(BAD_CAST "1.0");
222 if (doc == nullptr) {
223 LOG_ERROR("Failed to initialize the xmlDoc.");
224 return false;
225 }
226 xmlNode *root_node = xmlNewNode(NULL, BAD_CAST "preferences");
227 if (root_node == nullptr) {
228 LOG_ERROR("The xmlDoc failed to initialize the root node.");
229 xmlFreeDoc(doc);
230 return false;
231 }
232 xmlNewProp(root_node, BAD_CAST "version", BAD_CAST "1.0");
233
234 // set root node
235 xmlDocSetRootElement(doc, root_node);
236
237 // set children node
238 for (Element element : settings) {
239 xmlNode *node = CreateElementNode(element);
240 if (node == nullptr) {
241 LOG_ERROR("The xmlDoc failed to initialize the element node.");
242 xmlFreeDoc(doc);
243 return false;
244 }
245 if (root_node == nullptr || xmlAddChild(root_node, node) == nullptr) {
246 /* free node in case of error */
247 LOG_ERROR("The xmlDoc failed to add the child node.");
248 xmlFreeNode(node);
249 xmlFreeDoc(doc);
250 return false;
251 }
252 }
253
254 /* 1: formatting spaces are added. */
255 int result = xmlSaveFormatFileEnc(fileName.c_str(), doc, "UTF-8", 1);
256
257 xmlFreeDoc(doc);
258
259 if (result > 0) {
260 LimitXmlPermission(fileName);
261 }
262
263 LOG_DEBUG("Write setting xml %{private}s end.", fileName.c_str());
264 return (result > 0) ? true : false;
265 }
266
267 /* static */
CreateElementNode(Element & element)268 xmlNode *CreateElementNode(Element &element)
269 {
270 if ((element.tag_.compare("int") == 0) || (element.tag_.compare("long") == 0)
271 || (element.tag_.compare("float") == 0) || (element.tag_.compare("bool") == 0)
272 || (element.tag_.compare("double") == 0)) {
273 return CreatePrimitiveNode(element);
274 }
275
276 if (element.tag_.compare("string") == 0) {
277 return CreateStringNode(element);
278 }
279
280 if ((element.tag_.compare("doubleArray") == 0) || (element.tag_.compare("stringArray") == 0)
281 || (element.tag_.compare("boolArray") == 0)) {
282 return CreateArrayNode(element);
283 }
284
285 LOG_ERROR("An unsupported element type was encountered in parsing = %{public}s.", element.tag_.c_str());
286 return nullptr;
287 }
288
289 /* static */
CreatePrimitiveNode(Element & element)290 xmlNode *CreatePrimitiveNode(Element &element)
291 {
292 xmlNode *node = xmlNewNode(NULL, BAD_CAST element.tag_.c_str());
293 if (node == nullptr) {
294 LOG_ERROR("The xmlDoc failed to initialize the primitive element node.");
295 return nullptr;
296 }
297 if (!element.key_.empty()) {
298 const char *key = element.key_.c_str();
299 xmlNewProp(node, BAD_CAST "key", BAD_CAST key);
300 }
301
302 const char *value = element.value_.c_str();
303 xmlNewProp(node, BAD_CAST "value", BAD_CAST value);
304 return node;
305 }
306
CreateStringNode(Element & element)307 xmlNode *CreateStringNode(Element &element)
308 {
309 xmlNode *node = xmlNewNode(NULL, BAD_CAST element.tag_.c_str());
310 if (node == nullptr) {
311 LOG_ERROR("The xmlDoc failed to initialize the string element node.");
312 return nullptr;
313 }
314
315 if (!element.key_.empty()) {
316 const char *key = element.key_.c_str();
317 xmlNewProp(node, BAD_CAST "key", BAD_CAST key);
318 }
319
320 const char *value = element.value_.c_str();
321 xmlNodePtr text = xmlNewText(BAD_CAST value);
322 if (xmlAddChild(node, text) == nullptr) {
323 xmlFreeNode(text);
324 }
325 return node;
326 }
327
CreateArrayNode(Element & element)328 xmlNode *CreateArrayNode(Element &element)
329 {
330 xmlNode *node = xmlNewNode(NULL, BAD_CAST element.tag_.c_str());
331 if (node == nullptr) {
332 LOG_ERROR("The xmlDoc failed to initialize the array element node.");
333 return nullptr;
334 }
335
336 const char *key = element.key_.c_str();
337 xmlNewProp(node, BAD_CAST "key", BAD_CAST key);
338
339 Element flag = element.children_[0];
340 if ((flag.tag_.compare("bool") == 0) || (flag.tag_.compare("double") == 0)) {
341 for (Element &child : element.children_) {
342 xmlNode *childNode = CreatePrimitiveNode(child);
343 if (childNode == nullptr) {
344 continue;
345 }
346 if (xmlAddChild(node, childNode) == nullptr) {
347 xmlFreeNode(childNode);
348 }
349 }
350 } else if (flag.tag_.compare("string") == 0) {
351 for (Element child : element.children_) {
352 xmlNode *childNode = CreateStringNode(child);
353 if (childNode == nullptr) {
354 continue;
355 }
356 if (xmlAddChild(node, childNode) == nullptr) {
357 xmlFreeNode(childNode);
358 }
359 }
360 }
361 return node;
362 }
363
LimitXmlPermission(const std::string & fileName)364 void PreferencesXmlUtils::LimitXmlPermission(const std::string &fileName)
365 {
366 /* clear execute permission of owner, clear execute permission of group, clear all permission of group. */
367 struct stat fileStat = { 0 };
368 if (stat(fileName.c_str(), &fileStat) != 0) {
369 LOG_ERROR("Failed to obtain stat of file, errno:%{public}d.", errno);
370 return;
371 }
372 if ((fileStat.st_mode & (S_IXUSR | S_IXGRP | S_IRWXO)) != 0) {
373 int result = chmod(fileName.c_str(), fileStat.st_mode & (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP));
374 if (result != 0) {
375 LOG_ERROR("Failed to chmod file, errno:%{public}d.", errno);
376 }
377 }
378 }
379
XmlInitParser()380 void PreferencesXmlUtils::XmlInitParser()
381 {
382 xmlInitParser();
383 LOG_DEBUG("Xml parser get ready.");
384 }
385
XmlCleanupParser()386 void PreferencesXmlUtils::XmlCleanupParser()
387 {
388 xmlCleanupParser();
389 LOG_DEBUG("Xml parser clean up.");
390 }
391 } // End of namespace NativePreferences
392 } // End of namespace OHOS