• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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