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