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