• 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 "log_print.h"
25 #include "preferences_impl.h"
26 
27 namespace OHOS {
28 namespace NativePreferences {
29 static bool ParseNodeElement(const xmlNode *node, Element &element);
30 static bool ParsePrimitiveNodeElement(const xmlNode *node, Element &element);
31 static bool ParseStringNodeElement(const xmlNode *node, Element &element);
32 static bool ParseArrayNodeElement(const xmlNode *node, Element &element);
33 static xmlNode *CreateElementNode(Element &element);
34 static xmlNode *CreatePrimitiveNode(Element &element);
35 static xmlNode *CreateStringNode(Element &element);
36 static xmlNode *CreateArrayNode(Element &element);
37 
IsFileExist(const std::string & inputPath)38 static bool IsFileExist(const std::string &inputPath)
39 {
40     if (inputPath.length() > PATH_MAX) {
41         return false;
42     }
43     struct stat buffer;
44     return (stat(inputPath.c_str(), &buffer) == 0);
45 }
46 
RemoveBackupFile(const std::string & fileName)47 static void RemoveBackupFile(const std::string &fileName)
48 {
49     std::string backupFileName = PreferencesImpl::MakeFilePath(fileName, STR_BACKUP);
50     if (IsFileExist(backupFileName) && std::remove(backupFileName.c_str())) {
51         LOG_WARN("failed to delete backup file %{public}d.", errno);
52     }
53 }
54 
RenameFromBackupFile(const std::string & fileName)55 static bool RenameFromBackupFile(const std::string &fileName)
56 {
57     std::string backupFileName = PreferencesImpl::MakeFilePath(fileName, STR_BACKUP);
58     if (!IsFileExist(backupFileName)) {
59         LOG_INFO("the backup file does not exist.");
60         return false;
61     }
62     if (std::rename(backupFileName.c_str(), fileName.c_str())) {
63         LOG_ERROR("failed to restore backup errno %{public}d.", errno);
64         return false;
65     }
66     return true;
67 }
68 
RenameFile(const std::string & fileName,const std::string & fileType)69 static bool RenameFile(const std::string &fileName, const std::string &fileType)
70 {
71     std::string name = PreferencesImpl::MakeFilePath(fileName, fileType);
72     if (std::rename(fileName.c_str(), name.c_str())) {
73         LOG_ERROR("failed to rename file to %{public}s file %{public}d.", fileType.c_str(), errno);
74         return false;
75     }
76     return true;
77 }
78 
RenameToBackupFile(const std::string & fileName)79 static bool RenameToBackupFile(const std::string &fileName)
80 {
81     return RenameFile(fileName, STR_BACKUP);
82 }
83 
RenameToBrokenFile(const std::string & fileName)84 static bool RenameToBrokenFile(const std::string &fileName)
85 {
86     return RenameFile(fileName, STR_BROKEN);
87 }
88 
ReadFile(const std::string & fileName)89 static xmlDoc *ReadFile(const std::string &fileName)
90 {
91     return xmlReadFile(fileName.c_str(), "UTF-8", XML_PARSE_NOBLANKS);
92 }
93 
XmlReadFile(const std::string & fileName)94 static xmlDoc *XmlReadFile(const std::string &fileName)
95 {
96     xmlDoc *doc = nullptr;
97     if (IsFileExist(fileName)) {
98         doc = ReadFile(fileName);
99         if (doc != nullptr) {
100             return doc;
101         }
102         if (RenameToBrokenFile(fileName)) {
103             return doc;
104         }
105     }
106     if (RenameFromBackupFile(fileName)) {
107         return ReadFile(fileName);
108     }
109     return nullptr;
110 }
111 
112 /* static */
ReadSettingXml(const std::string & fileName,std::vector<Element> & settings)113 bool PreferencesXmlUtils::ReadSettingXml(const std::string &fileName, std::vector<Element> &settings)
114 {
115     LOG_RECORD_FILE_NAME("Read setting xml start.");
116     if (fileName.size() == 0) {
117         LOG_ERROR("The length of the file name is 0.");
118         return false;
119     }
120     auto doc = std::shared_ptr<xmlDoc>(XmlReadFile(fileName), [](xmlDoc *doc) { xmlFreeDoc(doc); });
121     if (doc == nullptr) {
122         return false;
123     }
124 
125     xmlNode *root = xmlDocGetRootElement(doc.get());
126     if (!root || xmlStrcmp(root->name, reinterpret_cast<const xmlChar *>("preferences"))) {
127         LOG_ERROR("Failed to obtain the XML root element.");
128         return false;
129     }
130 
131     bool success = true;
132     const xmlNode *cur = nullptr;
133     for (cur = root->children; cur != nullptr; cur = cur->next) {
134         Element element;
135 
136         if (ParseNodeElement(cur, element)) {
137             settings.push_back(element);
138         } else {
139             success = false;
140             LOG_ERROR("The error occurred during getting xml child elements.");
141             break;
142         }
143     }
144 
145     /* free the document */
146     LOG_RECORD_FILE_NAME("Read setting xml end.");
147     return success;
148 }
149 
150 /* static */
ParseNodeElement(const xmlNode * node,Element & element)151 bool ParseNodeElement(const xmlNode *node, Element &element)
152 {
153     if (!xmlStrcmp(node->name, reinterpret_cast<const xmlChar *>("int"))
154         || !xmlStrcmp(node->name, reinterpret_cast<const xmlChar *>("long"))
155         || !xmlStrcmp(node->name, reinterpret_cast<const xmlChar *>("bool"))
156         || !xmlStrcmp(node->name, reinterpret_cast<const xmlChar *>("float"))
157         || !xmlStrcmp(node->name, reinterpret_cast<const xmlChar *>("double"))) {
158         return ParsePrimitiveNodeElement(node, element);
159     }
160 
161     if (!xmlStrcmp(node->name, reinterpret_cast<const xmlChar *>("string"))) {
162         return ParseStringNodeElement(node, element);
163     }
164 
165     if (!xmlStrcmp(node->name, reinterpret_cast<const xmlChar *>("boolArray"))
166         || !xmlStrcmp(node->name, reinterpret_cast<const xmlChar *>("stringArray"))
167         || !xmlStrcmp(node->name, reinterpret_cast<const xmlChar *>("doubleArray"))
168         || !xmlStrcmp(node->name, reinterpret_cast<const xmlChar *>("set"))) {
169         return ParseArrayNodeElement(node, element);
170     }
171 
172     LOG_ERROR("An unsupported element type was encountered in parsing = %{public}s.", node->name);
173     return false;
174 }
175 
176 /* static */
ParsePrimitiveNodeElement(const xmlNode * node,Element & element)177 bool ParsePrimitiveNodeElement(const xmlNode *node, Element &element)
178 {
179     if (node == nullptr) {
180         return false;
181     }
182 
183     xmlChar *key = xmlGetProp(node, reinterpret_cast<const xmlChar *>("key"));
184     xmlChar *value = xmlGetProp(node, reinterpret_cast<const xmlChar *>("value"));
185 
186     bool success = false;
187     if (value != nullptr) {
188         element.tag_ = std::string(reinterpret_cast<const char *>(node->name));
189         if (key != nullptr) {
190             element.key_ = std::string(reinterpret_cast<char *>(key));
191         }
192         element.value_ = std::string(reinterpret_cast<char *>(value));
193         success = true;
194     } else {
195         LOG_ERROR("Failed to obtain a valid key or value when parsing %{public}s.", node->name);
196     }
197 
198     if (key != nullptr) {
199         xmlFree(key);
200     }
201     if (value != nullptr) {
202         xmlFree(value);
203     }
204     return success;
205 }
206 
207 /* static */
ParseStringNodeElement(const xmlNode * node,Element & element)208 bool ParseStringNodeElement(const xmlNode *node, Element &element)
209 {
210     if (node == nullptr) {
211         return false;
212     }
213 
214     xmlChar *key = xmlGetProp(node, (const xmlChar *)"key");
215     xmlChar *text = xmlNodeGetContent(node);
216 
217     bool success = false;
218     if (text != nullptr) {
219         element.tag_ = std::string(reinterpret_cast<const char *>(node->name));
220         if (key != nullptr) {
221             element.key_ = std::string(reinterpret_cast<char *>(key));
222         }
223         element.value_ = std::string(reinterpret_cast<char *>(text));
224         success = true;
225     } else {
226         LOG_ERROR("Failed to obtain a valid key or value when parsing a String element.");
227     }
228 
229     if (key != nullptr) {
230         xmlFree(key);
231     }
232     if (text != nullptr) {
233         xmlFree(text);
234     }
235     return success;
236 }
237 
238 /* static */
ParseArrayNodeElement(const xmlNode * node,Element & element)239 bool ParseArrayNodeElement(const xmlNode *node, Element &element)
240 {
241     if (node == nullptr) {
242         return false;
243     }
244 
245     xmlChar *key = xmlGetProp(node, (const xmlChar *)"key");
246     const xmlNode *children = node->children;
247 
248     bool success = false;
249     if (key != nullptr) {
250         element.tag_ = std::string(reinterpret_cast<const char *>(node->name));
251         element.key_ = std::string(reinterpret_cast<char *>(key));
252 
253         const xmlNode *cur = nullptr;
254         bool finishTravelChild = true;
255         for (cur = children; cur != nullptr; cur = cur->next) {
256             Element child;
257             if (ParseNodeElement(cur, child)) {
258                 element.children_.push_back(child);
259             } else {
260                 finishTravelChild = false;
261                 LOG_ERROR("Failed to parse the Array element and could not be completed successfully.");
262                 break;
263             }
264         }
265         success = finishTravelChild;
266     } else {
267         LOG_ERROR("Failed to obtain a valid key or value when parsing a Array element.");
268     }
269 
270     if (key != nullptr) {
271         xmlFree(key);
272     }
273     return success;
274 }
275 
SaveFormatFileEnc(const std::string & fileName,xmlDoc * doc)276 static bool SaveFormatFileEnc(const std::string &fileName, xmlDoc *doc)
277 {
278     return xmlSaveFormatFileEnc(fileName.c_str(), doc, "UTF-8", 1) > 0;
279 }
280 
XmlSaveFormatFileEnc(const std::string & fileName,xmlDoc * doc)281 bool XmlSaveFormatFileEnc(const std::string &fileName, xmlDoc *doc)
282 {
283     if (IsFileExist(fileName) && !RenameToBackupFile(fileName)) {
284         return false;
285     }
286 
287     if (!SaveFormatFileEnc(fileName, doc)) {
288         LOG_ERROR("Failed to save XML format file, attempting to restore backup");
289         if (IsFileExist(fileName)) {
290             RenameToBrokenFile(fileName);
291         }
292         RenameFromBackupFile(fileName);
293         return false;
294     }
295 
296     RemoveBackupFile(fileName);
297     PreferencesXmlUtils::LimitXmlPermission(fileName);
298     LOG_DEBUG("successfully saved the XML format file");
299     return true;
300 }
301 
302 /* static */
WriteSettingXml(const std::string & fileName,std::vector<Element> & settings)303 bool PreferencesXmlUtils::WriteSettingXml(const std::string &fileName, std::vector<Element> &settings)
304 {
305     LOG_RECORD_FILE_NAME("Write setting xml start.");
306     if (fileName.size() == 0) {
307         LOG_ERROR("The length of the file name is 0.");
308         return false;
309     }
310 
311     // define doc and root Node
312     auto doc = std::shared_ptr<xmlDoc>(xmlNewDoc(BAD_CAST "1.0"), [](xmlDoc *doc) { xmlFreeDoc(doc); });
313     if (doc == nullptr) {
314         LOG_ERROR("Failed to initialize the xmlDoc.");
315         return false;
316     }
317     xmlNode *rootNode = xmlNewNode(NULL, BAD_CAST "preferences");
318     if (rootNode == nullptr) {
319         LOG_ERROR("The xmlDoc failed to initialize the root node.");
320         return false;
321     }
322     xmlNewProp(rootNode, BAD_CAST "version", BAD_CAST "1.0");
323 
324     // set root node
325     xmlDocSetRootElement(doc.get(), rootNode);
326 
327     // set children node
328     for (Element element : settings) {
329         xmlNode *node = CreateElementNode(element);
330         if (node == nullptr) {
331             LOG_ERROR("The xmlDoc failed to initialize the element node.");
332             return false;
333         }
334         if (xmlAddChild(rootNode, node) == nullptr) {
335             /* free node in case of error */
336             LOG_ERROR("The xmlDoc failed to add the child node.");
337             xmlFreeNode(node);
338             return false;
339         }
340     }
341 
342     /* 1: formatting spaces are added. */
343     bool result = XmlSaveFormatFileEnc(fileName.c_str(), doc.get());
344     LOG_RECORD_FILE_NAME("Write setting xml end.");
345     return result;
346 }
347 
348 /* static */
CreateElementNode(Element & element)349 xmlNode *CreateElementNode(Element &element)
350 {
351     if ((element.tag_.compare("int") == 0) || (element.tag_.compare("long") == 0)
352         || (element.tag_.compare("float") == 0) || (element.tag_.compare("bool") == 0)
353         || (element.tag_.compare("double") == 0)) {
354         return CreatePrimitiveNode(element);
355     }
356 
357     if (element.tag_.compare("string") == 0) {
358         return CreateStringNode(element);
359     }
360 
361     if ((element.tag_.compare("doubleArray") == 0) || (element.tag_.compare("stringArray") == 0)
362         || (element.tag_.compare("boolArray") == 0)) {
363         return CreateArrayNode(element);
364     }
365 
366     LOG_ERROR("An unsupported element type was encountered in parsing = %{public}s.", element.tag_.c_str());
367     return nullptr;
368 }
369 
370 /* static */
CreatePrimitiveNode(Element & element)371 xmlNode *CreatePrimitiveNode(Element &element)
372 {
373     xmlNode *node = xmlNewNode(NULL, BAD_CAST element.tag_.c_str());
374     if (node == nullptr) {
375         LOG_ERROR("The xmlDoc failed to initialize the primitive element node.");
376         return nullptr;
377     }
378     if (!element.key_.empty()) {
379         const char *key = element.key_.c_str();
380         xmlNewProp(node, BAD_CAST "key", BAD_CAST key);
381     }
382 
383     const char *value = element.value_.c_str();
384     xmlNewProp(node, BAD_CAST "value", BAD_CAST value);
385     return node;
386 }
387 
CreateStringNode(Element & element)388 xmlNode *CreateStringNode(Element &element)
389 {
390     xmlNode *node = xmlNewNode(NULL, BAD_CAST element.tag_.c_str());
391     if (node == nullptr) {
392         LOG_ERROR("The xmlDoc failed to initialize the string element node.");
393         return nullptr;
394     }
395 
396     if (!element.key_.empty()) {
397         const char *key = element.key_.c_str();
398         xmlNewProp(node, BAD_CAST "key", BAD_CAST key);
399     }
400 
401     const char *value = element.value_.c_str();
402     xmlNodePtr text = xmlNewText(BAD_CAST value);
403     if (xmlAddChild(node, text) == nullptr) {
404         xmlFreeNode(text);
405     }
406     return node;
407 }
408 
CreateArrayNode(Element & element)409 xmlNode *CreateArrayNode(Element &element)
410 {
411     xmlNode *node = xmlNewNode(NULL, BAD_CAST element.tag_.c_str());
412     if (node == nullptr) {
413         LOG_ERROR("The xmlDoc failed to initialize the array element node.");
414         return nullptr;
415     }
416 
417     const char *key = element.key_.c_str();
418     xmlNewProp(node, BAD_CAST "key", BAD_CAST key);
419 
420     if (element.children_.empty()) {
421         return node;
422     }
423     Element flag = element.children_[0];
424     if ((flag.tag_.compare("bool") == 0) || (flag.tag_.compare("double") == 0)) {
425         for (Element &child : element.children_) {
426             xmlNode *childNode = CreatePrimitiveNode(child);
427             if (childNode == nullptr) {
428                 continue;
429             }
430             if (xmlAddChild(node, childNode) == nullptr) {
431                 xmlFreeNode(childNode);
432             }
433         }
434     } else if (flag.tag_.compare("string") == 0) {
435         for (Element child : element.children_) {
436             xmlNode *childNode = CreateStringNode(child);
437             if (childNode == nullptr) {
438                 continue;
439             }
440             if (xmlAddChild(node, childNode) == nullptr) {
441                 xmlFreeNode(childNode);
442             }
443         }
444     }
445     return node;
446 }
447 
LimitXmlPermission(const std::string & fileName)448 void PreferencesXmlUtils::LimitXmlPermission(const std::string &fileName)
449 {
450     /* clear execute permission of owner, clear execute permission of group, clear all permission of group. */
451     struct stat fileStat = { 0 };
452     if (stat(fileName.c_str(), &fileStat) != 0) {
453         LOG_ERROR("Failed to obtain stat of file, errno:%{public}d.", errno);
454         return;
455     }
456     if ((fileStat.st_mode & (S_IXUSR | S_IXGRP | S_IRWXO)) != 0) {
457         int result = chmod(fileName.c_str(), fileStat.st_mode & (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP));
458         if (result != 0) {
459             LOG_ERROR("Failed to chmod file, errno:%{public}d.", errno);
460         }
461     }
462 }
463 
XmlInitParser()464 void PreferencesXmlUtils::XmlInitParser()
465 {
466     xmlInitParser();
467     LOG_DEBUG("Xml parser get ready.");
468 }
469 
XmlCleanupParser()470 void PreferencesXmlUtils::XmlCleanupParser()
471 {
472     xmlCleanupParser();
473     LOG_DEBUG("Xml parser clean up.");
474 }
475 } // End of namespace NativePreferences
476 } // End of namespace OHOS