1 /*
2  * virxml.c: helper APIs for dealing with XML documents
3  *
4  * Copyright (C) 2005, 2007-2012 Red Hat, Inc.
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library.  If not, see
18  * <http://www.gnu.org/licenses/>.
19  */
20 
21 #include <config.h>
22 
23 #include <stdarg.h>
24 #include <math.h>               /* for isnan() */
25 #include <sys/stat.h>
26 
27 #include <libxml/xpathInternals.h>
28 
29 #include "virerror.h"
30 #include "virxml.h"
31 #include "virbuffer.h"
32 #include "viralloc.h"
33 #include "virfile.h"
34 #include "virstring.h"
35 #include "virutil.h"
36 #include "configmake.h"
37 
38 #define VIR_FROM_THIS VIR_FROM_XML
39 
40 #define virGenericReportError(from, code, ...) \
41         virReportErrorHelper(from, code, __FILE__, \
42                              __FUNCTION__, __LINE__, __VA_ARGS__)
43 
44 /* Internal data to be passed to SAX parser and used by error handler. */
45 struct virParserData {
46     int domcode;
47 };
48 
49 
50 xmlXPathContextPtr
virXMLXPathContextNew(xmlDocPtr xml)51 virXMLXPathContextNew(xmlDocPtr xml)
52 {
53     xmlXPathContextPtr ctxt;
54 
55     if (!(ctxt = xmlXPathNewContext(xml)))
56         abort();
57 
58     return ctxt;
59 }
60 
61 
62 /**
63  * virXPathString:
64  * @xpath: the XPath string to evaluate
65  * @ctxt: an XPath context
66  *
67  * Convenience function to evaluate an XPath string
68  *
69  * Returns a new string which must be deallocated by the caller or NULL
70  *         if the evaluation failed.
71  */
72 char *
virXPathString(const char * xpath,xmlXPathContextPtr ctxt)73 virXPathString(const char *xpath,
74                xmlXPathContextPtr ctxt)
75 {
76     g_autoptr(xmlXPathObject) obj = NULL;
77 
78     if ((ctxt == NULL) || (xpath == NULL)) {
79         virReportError(VIR_ERR_INTERNAL_ERROR,
80                        "%s", _("Invalid parameter to virXPathString()"));
81         return NULL;
82     }
83     obj = xmlXPathEval(BAD_CAST xpath, ctxt);
84     if ((obj == NULL) || (obj->type != XPATH_STRING) ||
85         (obj->stringval == NULL) || (obj->stringval[0] == 0)) {
86         return NULL;
87     }
88     return g_strdup((char *)obj->stringval);
89 }
90 
91 
92 /**
93  * virXPathNumber:
94  * @xpath: the XPath string to evaluate
95  * @ctxt: an XPath context
96  * @value: the returned double value
97  *
98  * Convenience function to evaluate an XPath number
99  *
100  * Returns 0 in case of success in which case @value is set,
101  *         or -1 if the evaluation failed.
102  */
103 int
virXPathNumber(const char * xpath,xmlXPathContextPtr ctxt,double * value)104 virXPathNumber(const char *xpath,
105                xmlXPathContextPtr ctxt,
106                double *value)
107 {
108     g_autoptr(xmlXPathObject) obj = NULL;
109 
110     if ((ctxt == NULL) || (xpath == NULL) || (value == NULL)) {
111         virReportError(VIR_ERR_INTERNAL_ERROR,
112                        "%s", _("Invalid parameter to virXPathNumber()"));
113         return -1;
114     }
115     obj = xmlXPathEval(BAD_CAST xpath, ctxt);
116     if ((obj == NULL) || (obj->type != XPATH_NUMBER) ||
117         (isnan(obj->floatval))) {
118         return -1;
119     }
120 
121     *value = obj->floatval;
122     return 0;
123 }
124 
125 static int
virXPathLongBase(const char * xpath,xmlXPathContextPtr ctxt,int base,long * value)126 virXPathLongBase(const char *xpath,
127                  xmlXPathContextPtr ctxt,
128                  int base,
129                  long *value)
130 {
131     g_autoptr(xmlXPathObject) obj = NULL;
132     int ret = 0;
133 
134     if ((ctxt == NULL) || (xpath == NULL) || (value == NULL)) {
135         virReportError(VIR_ERR_INTERNAL_ERROR,
136                        "%s", _("Invalid parameter to virXPathLong()"));
137         return -1;
138     }
139     obj = xmlXPathEval(BAD_CAST xpath, ctxt);
140     if ((obj != NULL) && (obj->type == XPATH_STRING) &&
141         (obj->stringval != NULL) && (obj->stringval[0] != 0)) {
142         if (virStrToLong_l((char *) obj->stringval, NULL, base, value) < 0)
143             ret = -2;
144     } else if ((obj != NULL) && (obj->type == XPATH_NUMBER) &&
145                (!(isnan(obj->floatval)))) {
146         *value = (long) obj->floatval;
147         if (*value != obj->floatval)
148             ret = -2;
149     } else {
150         ret = -1;
151     }
152 
153     return ret;
154 }
155 
156 /**
157  * virXPathInt:
158  * @xpath: the XPath string to evaluate
159  * @ctxt: an XPath context
160  * @value: the returned int value
161  *
162  * Convenience function to evaluate an XPath number
163  *
164  * Returns 0 in case of success in which case @value is set,
165  *         or -1 if the XPath evaluation failed or -2 if the
166  *         value doesn't have an int format.
167  */
168 int
virXPathInt(const char * xpath,xmlXPathContextPtr ctxt,int * value)169 virXPathInt(const char *xpath,
170             xmlXPathContextPtr ctxt,
171             int *value)
172 {
173     long tmp;
174     int ret;
175 
176     ret = virXPathLongBase(xpath, ctxt, 10, &tmp);
177     if (ret < 0)
178         return ret;
179     if ((int) tmp != tmp)
180         return -2;
181     *value = tmp;
182     return 0;
183 }
184 
185 /**
186  * virXPathLong:
187  * @xpath: the XPath string to evaluate
188  * @ctxt: an XPath context
189  * @value: the returned long value
190  *
191  * Convenience function to evaluate an XPath number
192  *
193  * Returns 0 in case of success in which case @value is set,
194  *         or -1 if the XPath evaluation failed or -2 if the
195  *         value doesn't have a long format.
196  */
197 int
virXPathLong(const char * xpath,xmlXPathContextPtr ctxt,long * value)198 virXPathLong(const char *xpath,
199              xmlXPathContextPtr ctxt,
200              long *value)
201 {
202     return virXPathLongBase(xpath, ctxt, 10, value);
203 }
204 
205 /**
206  * virXPathLongHex:
207  * @xpath: the XPath string to evaluate
208  * @ctxt: an XPath context
209  * @value: the returned long value
210  *
211  * Convenience function to evaluate an XPath number
212  * according to a base of 16
213  *
214  * Returns 0 in case of success in which case @value is set,
215  *         or -1 if the XPath evaluation failed or -2 if the
216  *         value doesn't have a long format.
217  */
218 int
virXPathLongHex(const char * xpath,xmlXPathContextPtr ctxt,long * value)219 virXPathLongHex(const char *xpath,
220                 xmlXPathContextPtr ctxt,
221                 long *value)
222 {
223     return virXPathLongBase(xpath, ctxt, 16, value);
224 }
225 
226 static int
virXPathULongBase(const char * xpath,xmlXPathContextPtr ctxt,int base,unsigned long * value)227 virXPathULongBase(const char *xpath,
228                   xmlXPathContextPtr ctxt,
229                   int base,
230                   unsigned long *value)
231 {
232     g_autoptr(xmlXPathObject) obj = NULL;
233     int ret = 0;
234 
235     if ((ctxt == NULL) || (xpath == NULL) || (value == NULL)) {
236         virReportError(VIR_ERR_INTERNAL_ERROR,
237                        "%s", _("Invalid parameter to virXPathULong()"));
238         return -1;
239     }
240     obj = xmlXPathEval(BAD_CAST xpath, ctxt);
241     if ((obj != NULL) && (obj->type == XPATH_STRING) &&
242         (obj->stringval != NULL) && (obj->stringval[0] != 0)) {
243         if (virStrToLong_ul((char *) obj->stringval, NULL, base, value) < 0)
244             ret = -2;
245     } else if ((obj != NULL) && (obj->type == XPATH_NUMBER) &&
246                (!(isnan(obj->floatval)))) {
247         *value = (unsigned long) obj->floatval;
248         if (*value != obj->floatval)
249             ret = -2;
250     } else {
251         ret = -1;
252     }
253 
254     return ret;
255 }
256 
257 /**
258  * virXPathUInt:
259  * @xpath: the XPath string to evaluate
260  * @ctxt: an XPath context
261  * @value: the returned int value
262  *
263  * Convenience function to evaluate an XPath number
264  *
265  * Returns 0 in case of success in which case @value is set,
266  *         or -1 if the XPath evaluation failed or -2 if the
267  *         value doesn't have an int format.
268  */
269 int
virXPathUInt(const char * xpath,xmlXPathContextPtr ctxt,unsigned int * value)270 virXPathUInt(const char *xpath,
271              xmlXPathContextPtr ctxt,
272              unsigned int *value)
273 {
274     unsigned long tmp;
275     int ret;
276 
277     ret = virXPathULongBase(xpath, ctxt, 10, &tmp);
278     if (ret < 0)
279         return ret;
280     if ((unsigned int) tmp != tmp)
281         return -2;
282     *value = tmp;
283     return 0;
284 }
285 
286 /**
287  * virXPathULong:
288  * @xpath: the XPath string to evaluate
289  * @ctxt: an XPath context
290  * @value: the returned long value
291  *
292  * Convenience function to evaluate an XPath number
293  *
294  * Returns 0 in case of success in which case @value is set,
295  *         or -1 if the XPath evaluation failed or -2 if the
296  *         value doesn't have a long format.
297  */
298 int
virXPathULong(const char * xpath,xmlXPathContextPtr ctxt,unsigned long * value)299 virXPathULong(const char *xpath,
300               xmlXPathContextPtr ctxt,
301               unsigned long *value)
302 {
303     return virXPathULongBase(xpath, ctxt, 10, value);
304 }
305 
306 /**
307  * virXPathUHex:
308  * @xpath: the XPath string to evaluate
309  * @ctxt: an XPath context
310  * @value: the returned long value
311  *
312  * Convenience function to evaluate an XPath number
313  * according to base of 16
314  *
315  * Returns 0 in case of success in which case @value is set,
316  *         or -1 if the XPath evaluation failed or -2 if the
317  *         value doesn't have a long format.
318  */
319 int
virXPathULongHex(const char * xpath,xmlXPathContextPtr ctxt,unsigned long * value)320 virXPathULongHex(const char *xpath,
321                  xmlXPathContextPtr ctxt,
322                  unsigned long *value)
323 {
324     return virXPathULongBase(xpath, ctxt, 16, value);
325 }
326 
327 /**
328  * virXPathULongLong:
329  * @xpath: the XPath string to evaluate
330  * @ctxt: an XPath context
331  * @value: the returned long long value
332  *
333  * Convenience function to evaluate an XPath number
334  *
335  * Returns 0 in case of success in which case @value is set,
336  *         or -1 if the XPath evaluation failed or -2 if the
337  *         value doesn't have a long format.
338  */
339 int
virXPathULongLong(const char * xpath,xmlXPathContextPtr ctxt,unsigned long long * value)340 virXPathULongLong(const char *xpath,
341                   xmlXPathContextPtr ctxt,
342                   unsigned long long *value)
343 {
344     g_autoptr(xmlXPathObject) obj = NULL;
345     int ret = 0;
346 
347     if ((ctxt == NULL) || (xpath == NULL) || (value == NULL)) {
348         virReportError(VIR_ERR_INTERNAL_ERROR,
349                        "%s", _("Invalid parameter to virXPathULong()"));
350         return -1;
351     }
352     obj = xmlXPathEval(BAD_CAST xpath, ctxt);
353     if ((obj != NULL) && (obj->type == XPATH_STRING) &&
354         (obj->stringval != NULL) && (obj->stringval[0] != 0)) {
355         if (virStrToLong_ull((char *) obj->stringval, NULL, 10, value) < 0)
356             ret = -2;
357     } else if ((obj != NULL) && (obj->type == XPATH_NUMBER) &&
358                (!(isnan(obj->floatval)))) {
359         *value = (unsigned long long) obj->floatval;
360         if (*value != obj->floatval)
361             ret = -2;
362     } else {
363         ret = -1;
364     }
365 
366     return ret;
367 }
368 
369 /**
370  * virXPathLongLong:
371  * @xpath: the XPath string to evaluate
372  * @ctxt: an XPath context
373  * @value: the returned long long value
374  *
375  * Convenience function to evaluate an XPath number
376  *
377  * Returns 0 in case of success in which case @value is set,
378  *         or -1 if the XPath evaluation failed or -2 if the
379  *         value doesn't have a long format.
380  */
381 int
virXPathLongLong(const char * xpath,xmlXPathContextPtr ctxt,long long * value)382 virXPathLongLong(const char *xpath,
383                  xmlXPathContextPtr ctxt,
384                  long long *value)
385 {
386     g_autoptr(xmlXPathObject) obj = NULL;
387     int ret = 0;
388 
389     if ((ctxt == NULL) || (xpath == NULL) || (value == NULL)) {
390         virReportError(VIR_ERR_INTERNAL_ERROR,
391                        "%s", _("Invalid parameter to virXPathLongLong()"));
392         return -1;
393     }
394     obj = xmlXPathEval(BAD_CAST xpath, ctxt);
395     if ((obj != NULL) && (obj->type == XPATH_STRING) &&
396         (obj->stringval != NULL) && (obj->stringval[0] != 0)) {
397         if (virStrToLong_ll((char *) obj->stringval, NULL, 10, value) < 0)
398             ret = -2;
399     } else if ((obj != NULL) && (obj->type == XPATH_NUMBER) &&
400                (!(isnan(obj->floatval)))) {
401         *value = (long long) obj->floatval;
402         if (*value != obj->floatval)
403             ret = -2;
404     } else {
405         ret = -1;
406     }
407 
408     return ret;
409 }
410 
411 
412 /**
413  * virXMLCheckIllegalChars:
414  * @nodeName: Name of checked node
415  * @str: string to check
416  * @illegal: illegal chars to check
417  *
418  * If string contains any of illegal chars VIR_ERR_XML_DETAIL error will be
419  * reported.
420  *
421  * Returns: 0 if string don't contains any of given characters, -1 otherwise
422  */
423 int
virXMLCheckIllegalChars(const char * nodeName,const char * str,const char * illegal)424 virXMLCheckIllegalChars(const char *nodeName,
425                         const char *str,
426                         const char *illegal)
427 {
428     char *c;
429     if ((c = strpbrk(str, illegal))) {
430         virReportError(VIR_ERR_XML_DETAIL,
431                        _("invalid char in %s: %c"), nodeName, *c);
432         return -1;
433     }
434     return 0;
435 }
436 
437 
438 /**
439  * virXMLPropString:
440  * @node: XML dom node pointer
441  * @name: Name of the property (attribute) to get
442  *
443  * Convenience function to return copy of an attribute value of a XML node.
444  *
445  * Returns the property (attribute) value as string or NULL in case of failure.
446  * The caller is responsible for freeing the returned buffer.
447  */
448 char *
virXMLPropString(xmlNodePtr node,const char * name)449 virXMLPropString(xmlNodePtr node,
450                  const char *name)
451 {
452     return (char *)xmlGetProp(node, BAD_CAST name);
453 }
454 
455 
456 /**
457  * virXMLNodeContentString:
458  * @node: XML dom node pointer
459  *
460  * Convenience function to return copy of content of an XML node.
461  *
462  * Returns the content value as string or NULL in case of failure.
463  * The caller is responsible for freeing the returned buffer.
464  */
465 char *
virXMLNodeContentString(xmlNodePtr node)466 virXMLNodeContentString(xmlNodePtr node)
467 {
468     char *ret = NULL;
469 
470     if (node->type !=  XML_ELEMENT_NODE) {
471         virReportError(VIR_ERR_INTERNAL_ERROR,
472                        _("node '%s' has unexpected type %d"),
473                        node->name, node->type);
474         return NULL;
475     }
476 
477     ret = (char *)xmlNodeGetContent(node);
478 
479     if (!ret) {
480         virReportError(VIR_ERR_INTERNAL_ERROR,
481                        _("node '%s' has unexpected NULL content. This could be caused by malformed input, or a memory allocation failure"),
482                        node->name);
483         return NULL;
484     }
485 
486     return ret;
487 }
488 
489 static int
virXMLPropEnumInternal(xmlNodePtr node,const char * name,int (* strToInt)(const char *),virXMLPropFlags flags,unsigned int * result,unsigned int defaultResult)490 virXMLPropEnumInternal(xmlNodePtr node,
491                        const char* name,
492                        int (*strToInt)(const char*),
493                        virXMLPropFlags flags,
494                        unsigned int *result,
495                        unsigned int defaultResult)
496 
497 {
498     g_autofree char *tmp = NULL;
499     int ret;
500 
501     *result = defaultResult;
502 
503     if (!(tmp = virXMLPropString(node, name))) {
504         if (!(flags & VIR_XML_PROP_REQUIRED))
505             return 0;
506 
507         virReportError(VIR_ERR_XML_ERROR,
508                        _("Missing required attribute '%s' in element '%s'"),
509                        name, node->name);
510         return -1;
511     }
512 
513     ret = strToInt(tmp);
514     if (ret < 0 ||
515         ((flags & VIR_XML_PROP_NONZERO) && (ret == 0))) {
516         virReportError(VIR_ERR_XML_ERROR,
517                        _("Invalid value for attribute '%s' in element '%s': '%s'."),
518                        name, node->name, NULLSTR(tmp));
519         return -1;
520     }
521 
522     *result = ret;
523     return 1;
524 }
525 
526 
527 /**
528  * virXMLPropTristateBool:
529  * @node: XML dom node pointer
530  * @name: Name of the property (attribute) to get
531  * @flags: Bitwise or of virXMLPropFlags
532  * @result: The returned value
533  *
534  * Convenience function to return value of a yes / no attribute.
535  * In case when the property is missing @result is initialized to
536  * VIR_TRISTATE_BOOL_ABSENT.
537  *
538  * Returns 1 in case of success in which case @result is set,
539  *         or 0 if the attribute is not present,
540  *         or -1 and reports an error on failure.
541  */
542 int
virXMLPropTristateBool(xmlNodePtr node,const char * name,virXMLPropFlags flags,virTristateBool * result)543 virXMLPropTristateBool(xmlNodePtr node,
544                        const char* name,
545                        virXMLPropFlags flags,
546                        virTristateBool *result)
547 {
548     flags |= VIR_XML_PROP_NONZERO;
549 
550     return virXMLPropEnumInternal(node, name, virTristateBoolTypeFromString,
551                                   flags, result, VIR_TRISTATE_BOOL_ABSENT);
552 }
553 
554 
555 /**
556  * virXMLPropTristateSwitch:
557  * @node: XML dom node pointer
558  * @name: Name of the property (attribute) to get
559  * @flags: Bitwise or of virXMLPropFlags
560  * @result: The returned value
561  *
562  * Convenience function to return value of an on / off attribute.
563  * In case when the property is missing @result is initialized to
564  * VIR_TRISTATE_SWITCH_ABSENT.
565  *
566  * Returns 1 in case of success in which case @result is set,
567  *         or 0 if the attribute is not present,
568  *         or -1 and reports an error on failure.
569  */
570 int
virXMLPropTristateSwitch(xmlNodePtr node,const char * name,virXMLPropFlags flags,virTristateSwitch * result)571 virXMLPropTristateSwitch(xmlNodePtr node,
572                          const char* name,
573                          virXMLPropFlags flags,
574                          virTristateSwitch *result)
575 {
576     flags |= VIR_XML_PROP_NONZERO;
577 
578     return virXMLPropEnumInternal(node, name, virTristateSwitchTypeFromString,
579                                   flags, result, VIR_TRISTATE_SWITCH_ABSENT);
580 }
581 
582 
583 /**
584  * virXMLPropInt:
585  * @node: XML dom node pointer
586  * @name: Name of the property (attribute) to get
587  * @base: Number base, see strtol
588  * @flags: Bitwise or of virXMLPropFlags
589  * @result: The returned value
590  * @defaultResult: default value of @result in case the property is not found
591  *
592  * Convenience function to return value of an integer attribute.
593  *
594  * Returns 1 in case of success in which case @result is set,
595  *         or 0 if the attribute is not present,
596  *         or -1 and reports an error on failure.
597  */
598 int
virXMLPropInt(xmlNodePtr node,const char * name,int base,virXMLPropFlags flags,int * result,int defaultResult)599 virXMLPropInt(xmlNodePtr node,
600               const char *name,
601               int base,
602               virXMLPropFlags flags,
603               int *result,
604               int defaultResult)
605 {
606     g_autofree char *tmp = NULL;
607     int val;
608 
609     *result = defaultResult;
610 
611     if (!(tmp = virXMLPropString(node, name))) {
612         if (!(flags & VIR_XML_PROP_REQUIRED))
613             return 0;
614 
615         virReportError(VIR_ERR_XML_ERROR,
616                        _("Missing required attribute '%s' in element '%s'"),
617                        name, node->name);
618         return -1;
619     }
620 
621     if (virStrToLong_i(tmp, NULL, base, &val) < 0) {
622         virReportError(VIR_ERR_XML_ERROR,
623                        _("Invalid value for attribute '%s' in element '%s': '%s'. Expected integer value"),
624                        name, node->name, tmp);
625         return -1;
626     }
627 
628     if ((flags & VIR_XML_PROP_NONZERO) && (val == 0)) {
629         virReportError(VIR_ERR_XML_ERROR,
630                        _("Invalid value for attribute '%s' in element '%s': Zero is not permitted"),
631                        name, node->name);
632         return -1;
633     }
634 
635     *result = val;
636     return 1;
637 }
638 
639 
640 /**
641  * virXMLPropUInt:
642  * @node: XML dom node pointer
643  * @name: Name of the property (attribute) to get
644  * @base: Number base, see strtol
645  * @flags: Bitwise or of virXMLPropFlags
646  * @result: The returned value
647  *
648  * Convenience function to return value of an unsigned integer attribute.
649  * @result is initialized to 0 on error or if the element is not found.
650  *
651  * Returns 1 in case of success in which case @result is set,
652  *         or 0 if the attribute is not present,
653  *         or -1 and reports an error on failure.
654  */
655 int
virXMLPropUInt(xmlNodePtr node,const char * name,int base,virXMLPropFlags flags,unsigned int * result)656 virXMLPropUInt(xmlNodePtr node,
657                const char* name,
658                int base,
659                virXMLPropFlags flags,
660                unsigned int *result)
661 {
662     g_autofree char *tmp = NULL;
663     int ret;
664     unsigned int val;
665 
666     *result = 0;
667 
668     if (!(tmp = virXMLPropString(node, name))) {
669         if (!(flags & VIR_XML_PROP_REQUIRED))
670             return 0;
671 
672         virReportError(VIR_ERR_XML_ERROR,
673                        _("Missing required attribute '%s' in element '%s'"),
674                        name, node->name);
675         return -1;
676     }
677 
678     ret = virStrToLong_uip(tmp, NULL, base, &val);
679 
680     if (ret < 0) {
681         virReportError(VIR_ERR_XML_ERROR,
682                        _("Invalid value for attribute '%s' in element '%s': '%s'. Expected integer value"),
683                        name, node->name, tmp);
684         return -1;
685     }
686 
687     if ((flags & VIR_XML_PROP_NONZERO) && (val == 0)) {
688         virReportError(VIR_ERR_XML_ERROR,
689                        _("Invalid value for attribute '%s' in element '%s': Zero is not permitted"),
690                        name, node->name);
691         return -1;
692     }
693 
694     *result = val;
695     return 1;
696 }
697 
698 
699 /**
700  * virXMLPropULongLong:
701  * @node: XML dom node pointer
702  * @name: Name of the property (attribute) to get
703  * @base: Number base, see strtol
704  * @flags: Bitwise or of virXMLPropFlags
705  * @result: The returned value
706  *
707  * Convenience function to return value of an unsigned long long attribute.
708  * @result is initialized to 0 on error or if the element is not found.
709  *
710  * Returns 1 in case of success in which case @result is set,
711  *         or 0 if the attribute is not present,
712  *         or -1 and reports an error on failure.
713  */
714 int
virXMLPropULongLong(xmlNodePtr node,const char * name,int base,virXMLPropFlags flags,unsigned long long * result)715 virXMLPropULongLong(xmlNodePtr node,
716                     const char* name,
717                     int base,
718                     virXMLPropFlags flags,
719                     unsigned long long *result)
720 {
721     g_autofree char *tmp = NULL;
722     int ret;
723     unsigned long long val;
724 
725     *result = 0;
726 
727     if (!(tmp = virXMLPropString(node, name))) {
728         if (!(flags & VIR_XML_PROP_REQUIRED))
729             return 0;
730 
731         virReportError(VIR_ERR_XML_ERROR,
732                        _("Missing required attribute '%s' in element '%s'"),
733                        name, node->name);
734         return -1;
735     }
736 
737     ret = virStrToLong_ullp(tmp, NULL, base, &val);
738 
739     if (ret < 0) {
740         virReportError(VIR_ERR_XML_ERROR,
741                        _("Invalid value for attribute '%s' in element '%s': '%s'. Expected integer value"),
742                        name, node->name, tmp);
743         return -1;
744     }
745 
746     if ((flags & VIR_XML_PROP_NONZERO) && (val == 0)) {
747         virReportError(VIR_ERR_XML_ERROR,
748                        _("Invalid value for attribute '%s' in element '%s': Zero is not permitted"),
749                        name, node->name);
750         return -1;
751     }
752 
753     *result = val;
754     return 1;
755 }
756 
757 
758 /**
759  * virXMLPropEnumDefault:
760  * @node: XML dom node pointer
761  * @name: Name of the property (attribute) to get
762  * @strToInt: Conversion function to turn enum name to value. Expected to
763  *            return negative value on failure.
764  * @flags: Bitwise or of virXMLPropFlags
765  * @result: The returned value
766  * @defaultResult: default value set to @result in case the property is missing
767  *
768  * Convenience function to return value of an enum attribute.
769  *
770  * Returns 1 in case of success in which case @result is set,
771  *         or 0 if the attribute is not present,
772  *         or -1 and reports an error on failure.
773  */
774 int
virXMLPropEnumDefault(xmlNodePtr node,const char * name,int (* strToInt)(const char *),virXMLPropFlags flags,unsigned int * result,unsigned int defaultResult)775 virXMLPropEnumDefault(xmlNodePtr node,
776                       const char* name,
777                       int (*strToInt)(const char*),
778                       virXMLPropFlags flags,
779                       unsigned int *result,
780                       unsigned int defaultResult)
781 {
782     return virXMLPropEnumInternal(node, name, strToInt, flags, result, defaultResult);
783 }
784 
785 
786 /**
787  * virXMLPropEnum:
788  * @node: XML dom node pointer
789  * @name: Name of the property (attribute) to get
790  * @strToInt: Conversion function to turn enum name to value. Expected to
791  *            return negative value on failure.
792  * @flags: Bitwise or of virXMLPropFlags
793  * @result: The returned value
794  *
795  * Convenience function to return value of an enum attribute.
796  * @result is initialized to 0 on error or if the element is not found.
797  *
798  * Returns 1 in case of success in which case @result is set,
799  *         or 0 if the attribute is not present,
800  *         or -1 and reports an error on failure.
801  */
802 int
virXMLPropEnum(xmlNodePtr node,const char * name,int (* strToInt)(const char *),virXMLPropFlags flags,unsigned int * result)803 virXMLPropEnum(xmlNodePtr node,
804                const char* name,
805                int (*strToInt)(const char*),
806                virXMLPropFlags flags,
807                unsigned int *result)
808 {
809     return virXMLPropEnumInternal(node, name, strToInt, flags, result, 0);
810 }
811 
812 
813 /**
814  * virXPathBoolean:
815  * @xpath: the XPath string to evaluate
816  * @ctxt: an XPath context
817  *
818  * Convenience function to evaluate an XPath boolean
819  *
820  * Returns 0 if false, 1 if true, or -1 if the evaluation failed.
821  */
822 int
virXPathBoolean(const char * xpath,xmlXPathContextPtr ctxt)823 virXPathBoolean(const char *xpath,
824                 xmlXPathContextPtr ctxt)
825 {
826     g_autoptr(xmlXPathObject) obj = NULL;
827 
828     if ((ctxt == NULL) || (xpath == NULL)) {
829         virReportError(VIR_ERR_INTERNAL_ERROR,
830                        "%s", _("Invalid parameter to virXPathBoolean()"));
831         return -1;
832     }
833     obj = xmlXPathEval(BAD_CAST xpath, ctxt);
834     if ((obj == NULL) || (obj->type != XPATH_BOOLEAN) ||
835         (obj->boolval < 0) || (obj->boolval > 1)) {
836         return -1;
837     }
838     return obj->boolval;
839 }
840 
841 /**
842  * virXPathNode:
843  * @xpath: the XPath string to evaluate
844  * @ctxt: an XPath context
845  *
846  * Convenience function to evaluate an XPath node set and returning
847  * only one node, the first one in the set if any
848  *
849  * Returns a pointer to the node or NULL if the evaluation failed.
850  */
851 xmlNodePtr
virXPathNode(const char * xpath,xmlXPathContextPtr ctxt)852 virXPathNode(const char *xpath,
853              xmlXPathContextPtr ctxt)
854 {
855     g_autoptr(xmlXPathObject) obj = NULL;
856 
857     if ((ctxt == NULL) || (xpath == NULL)) {
858         virReportError(VIR_ERR_INTERNAL_ERROR,
859                        "%s", _("Invalid parameter to virXPathNode()"));
860         return NULL;
861     }
862     obj = xmlXPathEval(BAD_CAST xpath, ctxt);
863     if ((obj == NULL) || (obj->type != XPATH_NODESET) ||
864         (obj->nodesetval == NULL) || (obj->nodesetval->nodeNr <= 0) ||
865         (obj->nodesetval->nodeTab == NULL)) {
866         return NULL;
867     }
868 
869     return obj->nodesetval->nodeTab[0];
870 }
871 
872 /**
873  * virXPathNodeSet:
874  * @xpath: the XPath string to evaluate
875  * @ctxt: an XPath context
876  * @list: the returned list of nodes (or NULL if only count matters)
877  *
878  * Convenience function to evaluate an XPath node set
879  *
880  * Returns the number of nodes found in which case @list is set (and
881  *         must be freed) or -1 if the evaluation failed.
882  */
883 int
virXPathNodeSet(const char * xpath,xmlXPathContextPtr ctxt,xmlNodePtr ** list)884 virXPathNodeSet(const char *xpath,
885                 xmlXPathContextPtr ctxt,
886                 xmlNodePtr **list)
887 {
888     g_autoptr(xmlXPathObject) obj = NULL;
889     int ret;
890 
891     if ((ctxt == NULL) || (xpath == NULL)) {
892         virReportError(VIR_ERR_INTERNAL_ERROR,
893                        "%s", _("Invalid parameter to virXPathNodeSet()"));
894         return -1;
895     }
896 
897     if (list != NULL)
898         *list = NULL;
899 
900     obj = xmlXPathEval(BAD_CAST xpath, ctxt);
901     if (obj == NULL)
902         return 0;
903 
904     if (obj->type != XPATH_NODESET) {
905         virReportError(VIR_ERR_INTERNAL_ERROR,
906                        _("Incorrect xpath '%s'"), xpath);
907         return -1;
908     }
909 
910     if ((obj->nodesetval == NULL)  || (obj->nodesetval->nodeNr < 0))
911         return 0;
912 
913     ret = obj->nodesetval->nodeNr;
914     if (list != NULL && ret) {
915         *list = g_new0(xmlNodePtr, ret);
916         memcpy(*list, obj->nodesetval->nodeTab, ret * sizeof(xmlNodePtr));
917     }
918     return ret;
919 }
920 
921 
922 /**
923  * catchXMLError:
924  *
925  * Called from SAX on parsing errors in the XML.
926  *
927  * This version is heavily based on xmlParserPrintFileContextInternal from libxml2.
928  */
929 static void
catchXMLError(void * ctx,const char * msg G_GNUC_UNUSED,...)930 catchXMLError(void *ctx, const char *msg G_GNUC_UNUSED, ...)
931 {
932     xmlParserCtxtPtr ctxt = (xmlParserCtxtPtr) ctx;
933 
934     const xmlChar *cur, *base;
935     unsigned int n, col;        /* GCC warns if signed, because compared with sizeof() */
936     int domcode = VIR_FROM_XML;
937     g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
938     g_autofree char *contextstr = NULL;
939     g_autofree char *pointerstr = NULL;
940 
941 
942     /* conditions for error printing */
943     if (!ctxt ||
944         (virGetLastErrorCode()) ||
945         ctxt->input == NULL ||
946         ctxt->lastError.level != XML_ERR_FATAL ||
947         ctxt->lastError.message == NULL)
948         return;
949 
950     if (ctxt->_private)
951         domcode = ((struct virParserData *) ctxt->_private)->domcode;
952 
953 
954     cur = ctxt->input->cur;
955     base = ctxt->input->base;
956 
957     /* skip backwards over any end-of-lines */
958     while ((cur > base) && ((*(cur) == '\n') || (*(cur) == '\r')))
959         cur--;
960 
961     /* search backwards for beginning-of-line (to max buff size) */
962     while ((cur > base) && (*(cur) != '\n') && (*(cur) != '\r'))
963         cur--;
964     if ((*(cur) == '\n') || (*(cur) == '\r')) cur++;
965 
966     /* calculate the error position in terms of the current position */
967     col = ctxt->input->cur - cur;
968 
969     /* search forward for end-of-line (to max buff size) */
970     /* copy selected text to our buffer */
971     while ((*cur != 0) && (*(cur) != '\n') && (*(cur) != '\r'))
972         virBufferAddChar(&buf, *cur++);
973 
974     /* create blank line with problem pointer */
975     contextstr = virBufferContentAndReset(&buf);
976 
977     /* (leave buffer space for pointer + line terminator) */
978     for  (n = 0; (n<col) && (contextstr[n] != 0); n++) {
979         if (contextstr[n] == '\t')
980             virBufferAddChar(&buf, '\t');
981         else
982             virBufferAddChar(&buf, '-');
983     }
984 
985     virBufferAddChar(&buf, '^');
986 
987     pointerstr = virBufferContentAndReset(&buf);
988 
989     if (ctxt->lastError.file) {
990         virGenericReportError(domcode, VIR_ERR_XML_DETAIL,
991                               _("%s:%d: %s%s\n%s"),
992                               ctxt->lastError.file,
993                               ctxt->lastError.line,
994                               ctxt->lastError.message,
995                               contextstr,
996                               pointerstr);
997     } else {
998         virGenericReportError(domcode, VIR_ERR_XML_DETAIL,
999                               _("at line %d: %s%s\n%s"),
1000                               ctxt->lastError.line,
1001                               ctxt->lastError.message,
1002                               contextstr,
1003                               pointerstr);
1004     }
1005 }
1006 
1007 /**
1008  * virXMLParseHelper:
1009  * @domcode: error domain of the caller, usually VIR_FROM_THIS
1010  * @filename: file to be parsed or NULL if string parsing is requested
1011  * @xmlStr: XML string to be parsed in case filename is NULL
1012  * @url: URL of XML document for string parser
1013  * @rootelement: Optional name of the expected root element
1014  * @ctxt: optional pointer to populate with new context pointer
1015  * @schemafile: optional name of the file the parsed XML will be validated against
1016  * @validate: if true, the XML will be validated against schema in @schemafile
1017  *
1018  * Parse XML document provided either as a file or a string. The function
1019  * guarantees that the XML document contains a root element.
1020  *
1021  * If @rootelement is not NULL, the name of the root element of the parsed XML
1022  * is vaidated against
1023  *
1024  * Returns parsed XML document.
1025  */
1026 xmlDocPtr
virXMLParseHelper(int domcode,const char * filename,const char * xmlStr,const char * url,const char * rootelement,xmlXPathContextPtr * ctxt,const char * schemafile,bool validate)1027 virXMLParseHelper(int domcode,
1028                   const char *filename,
1029                   const char *xmlStr,
1030                   const char *url,
1031                   const char *rootelement,
1032                   xmlXPathContextPtr *ctxt,
1033                   const char *schemafile,
1034                   bool validate)
1035 {
1036     struct virParserData private;
1037     g_autoptr(xmlParserCtxt) pctxt = NULL;
1038     g_autoptr(xmlDoc) xml = NULL;
1039     xmlNodePtr rootnode;
1040     const char *docname;
1041 
1042     if (filename)
1043         docname = filename;
1044     else if (url)
1045         docname = url;
1046     else
1047         docname = "[inline data]";
1048 
1049     /* Set up a parser context so we can catch the details of XML errors. */
1050     pctxt = xmlNewParserCtxt();
1051     if (!pctxt || !pctxt->sax)
1052         abort();
1053 
1054     private.domcode = domcode;
1055     pctxt->_private = &private;
1056     pctxt->sax->error = catchXMLError;
1057 
1058     if (filename) {
1059         xml = xmlCtxtReadFile(pctxt, filename, NULL,
1060                               XML_PARSE_NONET |
1061                               XML_PARSE_NOWARNING);
1062     } else {
1063         xml = xmlCtxtReadDoc(pctxt, BAD_CAST xmlStr, url, NULL,
1064                              XML_PARSE_NONET |
1065                              XML_PARSE_NOWARNING);
1066     }
1067 
1068     if (!xml) {
1069         if (virGetLastErrorCode() == VIR_ERR_OK) {
1070             virGenericReportError(domcode, VIR_ERR_XML_ERROR,
1071                                   _("failed to parse xml document '%s'"),
1072                                   docname);
1073         }
1074 
1075         return NULL;
1076     }
1077 
1078     if (!(rootnode = xmlDocGetRootElement(xml))) {
1079         virGenericReportError(domcode, VIR_ERR_INTERNAL_ERROR,
1080                               "%s", _("missing root element"));
1081 
1082         return NULL;
1083     }
1084 
1085     if (rootelement &&
1086         !virXMLNodeNameEqual(rootnode, rootelement)) {
1087         virReportError(VIR_ERR_XML_ERROR,
1088                        _("expecting root element of '%s', not '%s'"),
1089                        rootelement, rootnode->name);
1090         return NULL;
1091     }
1092 
1093     if (ctxt) {
1094         if (!(*ctxt = virXMLXPathContextNew(xml)))
1095             return NULL;
1096 
1097         (*ctxt)->node = rootnode;
1098     }
1099 
1100     if (validate && schemafile != NULL) {
1101         g_autofree char *schema = virFileFindResource(schemafile,
1102                                                       abs_top_srcdir "/docs/schemas",
1103                                                       PKGDATADIR "/schemas");
1104         if (!schema ||
1105             (virXMLValidateAgainstSchema(schema, xml) < 0))
1106             return NULL;
1107     }
1108 
1109     return g_steal_pointer(&xml);
1110 }
1111 
virXMLPickShellSafeComment(const char * str1,const char * str2)1112 const char *virXMLPickShellSafeComment(const char *str1, const char *str2)
1113 {
1114     if (str1 && !strpbrk(str1, "\r\t\n !\"#$&'()*;<>?[\\]^`{|}~") &&
1115         !strstr(str1, "--"))
1116         return str1;
1117     if (str2 && !strpbrk(str2, "\r\t\n !\"#$&'()*;<>?[\\]^`{|}~") &&
1118         !strstr(str2, "--"))
1119         return str2;
1120     return NULL;
1121 }
1122 
virXMLEmitWarning(int fd,const char * name,const char * cmd)1123 static int virXMLEmitWarning(int fd,
1124                              const char *name,
1125                              const char *cmd)
1126 {
1127     size_t len;
1128     const char *prologue =
1129         "<!--\n"
1130         "WARNING: THIS IS AN AUTO-GENERATED FILE. CHANGES TO IT ARE LIKELY TO BE\n"
1131         "OVERWRITTEN AND LOST. Changes to this xml configuration should be made using:\n"
1132         "  virsh ";
1133     const char *epilogue =
1134         "\n"
1135         "or other application using the libvirt API.\n"
1136         "-->\n\n";
1137 
1138     if (fd < 0 || !cmd) {
1139         errno = EINVAL;
1140         return -1;
1141     }
1142 
1143     len = strlen(prologue);
1144     if (safewrite(fd, prologue, len) != len)
1145         return -1;
1146 
1147     len = strlen(cmd);
1148     if (safewrite(fd, cmd, len) != len)
1149         return -1;
1150 
1151     if (name) {
1152         if (safewrite(fd, " ", 1) != 1)
1153             return -1;
1154 
1155         len = strlen(name);
1156         if (safewrite(fd, name, len) != len)
1157             return -1;
1158     }
1159 
1160     len = strlen(epilogue);
1161     if (safewrite(fd, epilogue, len) != len)
1162         return -1;
1163 
1164     return 0;
1165 }
1166 
1167 
1168 struct virXMLRewriteFileData {
1169     const char *warnName;
1170     const char *warnCommand;
1171     const char *xml;
1172 };
1173 
1174 static int
virXMLRewriteFile(int fd,const void * opaque)1175 virXMLRewriteFile(int fd, const void *opaque)
1176 {
1177     const struct virXMLRewriteFileData *data = opaque;
1178 
1179     if (data->warnCommand) {
1180         if (virXMLEmitWarning(fd, data->warnName, data->warnCommand) < 0)
1181             return -1;
1182     }
1183 
1184     if (safewrite(fd, data->xml, strlen(data->xml)) < 0)
1185         return -1;
1186 
1187     return 0;
1188 }
1189 
1190 int
virXMLSaveFile(const char * path,const char * warnName,const char * warnCommand,const char * xml)1191 virXMLSaveFile(const char *path,
1192                const char *warnName,
1193                const char *warnCommand,
1194                const char *xml)
1195 {
1196     struct virXMLRewriteFileData data = { warnName, warnCommand, xml };
1197 
1198     return virFileRewrite(path, S_IRUSR | S_IWUSR, virXMLRewriteFile, &data);
1199 }
1200 
1201 /**
1202  * virXMLNodeToString: convert an XML node ptr to an XML string
1203  *
1204  * Returns the XML string of the document or NULL on error.
1205  * The caller has to free the string.
1206  */
1207 char *
virXMLNodeToString(xmlDocPtr doc,xmlNodePtr node)1208 virXMLNodeToString(xmlDocPtr doc,
1209                    xmlNodePtr node)
1210 {
1211     g_autoptr(xmlBuffer) xmlbuf = virXMLBufferCreate();
1212 
1213     if (xmlNodeDump(xmlbuf, doc, node, 0, 1) == 0) {
1214         virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
1215                        _("failed to convert the XML node tree"));
1216         return NULL;
1217     }
1218 
1219     return g_strdup((const char *)xmlBufferContent(xmlbuf));
1220 }
1221 
1222 
1223 /**
1224  * virXMLNodeNameEqual:
1225  * @node: xml Node pointer to check
1226  * @name: name of the @node
1227  *
1228  * Compares the @node name with @name.
1229  */
1230 bool
virXMLNodeNameEqual(xmlNodePtr node,const char * name)1231 virXMLNodeNameEqual(xmlNodePtr node,
1232                     const char *name)
1233 {
1234     return xmlStrEqual(node->name, BAD_CAST name);
1235 }
1236 
1237 
1238 typedef int (*virXMLForeachCallback)(xmlNodePtr node,
1239                                      void *opaque);
1240 
1241 static int
virXMLForeachNode(xmlNodePtr root,virXMLForeachCallback cb,void * opaque)1242 virXMLForeachNode(xmlNodePtr root,
1243                   virXMLForeachCallback cb,
1244                   void *opaque)
1245 {
1246     xmlNodePtr next;
1247     int ret;
1248 
1249     for (next = root; next; next = next->next) {
1250         if ((ret = cb(next, opaque)) != 0)
1251             return ret;
1252 
1253         /* recurse into children */
1254         if (next->children) {
1255             if ((ret = virXMLForeachNode(next->children, cb, opaque)) != 0)
1256                 return ret;
1257         }
1258     }
1259 
1260     return 0;
1261 }
1262 
1263 
1264 static int
virXMLRemoveElementNamespace(xmlNodePtr node,void * opaque)1265 virXMLRemoveElementNamespace(xmlNodePtr node,
1266                              void *opaque)
1267 {
1268     const char *uri = opaque;
1269 
1270     if (node->ns &&
1271         STREQ_NULLABLE((const char *)node->ns->href, uri))
1272         xmlSetNs(node, NULL);
1273     return 0;
1274 }
1275 
1276 
1277 xmlNodePtr
virXMLFindChildNodeByNs(xmlNodePtr root,const char * uri)1278 virXMLFindChildNodeByNs(xmlNodePtr root,
1279                         const char *uri)
1280 {
1281     xmlNodePtr next;
1282 
1283     if (!root)
1284         return NULL;
1285 
1286     for (next = root->children; next; next = next->next) {
1287         if (next->ns &&
1288             STREQ_NULLABLE((const char *) next->ns->href, uri))
1289             return next;
1290     }
1291 
1292     return NULL;
1293 }
1294 
1295 
1296 /**
1297  * virXMLExtractNamespaceXML: extract a sub-namespace of XML as string
1298  */
1299 int
virXMLExtractNamespaceXML(xmlNodePtr root,const char * uri,char ** doc)1300 virXMLExtractNamespaceXML(xmlNodePtr root,
1301                           const char *uri,
1302                           char **doc)
1303 {
1304     xmlNodePtr node;
1305     xmlNodePtr nodeCopy = NULL;
1306     xmlNsPtr actualNs;
1307     xmlNsPtr prevNs = NULL;
1308     char *xmlstr = NULL;
1309     int ret = -1;
1310 
1311     if (!(node = virXMLFindChildNodeByNs(root, uri))) {
1312         /* node not found */
1313         ret = 1;
1314         goto cleanup;
1315     }
1316 
1317     /* copy the node so that we can modify the namespace */
1318     if (!(nodeCopy = xmlCopyNode(node, 1))) {
1319         virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
1320                        _("Failed to copy XML node"));
1321         goto cleanup;
1322     }
1323 
1324     virXMLForeachNode(nodeCopy, virXMLRemoveElementNamespace,
1325                       (void *)uri);
1326 
1327     /* remove the namespace declaration
1328      *  - it's only a single linked list ... doh */
1329     for (actualNs = nodeCopy->nsDef; actualNs; actualNs = actualNs->next) {
1330         if (STREQ_NULLABLE((const char *)actualNs->href, uri)) {
1331 
1332             /* unlink */
1333             if (prevNs)
1334                 prevNs->next = actualNs->next;
1335             else
1336                 nodeCopy->nsDef = actualNs->next;
1337 
1338             /* discard */
1339             xmlFreeNs(actualNs);
1340             break;
1341         }
1342 
1343         prevNs = actualNs;
1344     }
1345 
1346     if (!(xmlstr = virXMLNodeToString(nodeCopy->doc, nodeCopy)))
1347         goto cleanup;
1348 
1349     ret = 0;
1350 
1351  cleanup:
1352     if (doc)
1353         *doc = xmlstr;
1354     else
1355         VIR_FREE(xmlstr);
1356     xmlFreeNode(nodeCopy);
1357     return ret;
1358 }
1359 
1360 
1361 static int
virXMLAddElementNamespace(xmlNodePtr node,void * opaque)1362 virXMLAddElementNamespace(xmlNodePtr node,
1363                           void *opaque)
1364 {
1365     xmlNsPtr ns = opaque;
1366 
1367     if (!node->ns)
1368         xmlSetNs(node, ns);
1369 
1370     return 0;
1371 }
1372 
1373 
1374 int
virXMLInjectNamespace(xmlNodePtr node,const char * uri,const char * key)1375 virXMLInjectNamespace(xmlNodePtr node,
1376                       const char *uri,
1377                       const char *key)
1378 {
1379     xmlNsPtr ns;
1380 
1381     if (xmlValidateNCName((const unsigned char *)key, 1) != 0) {
1382         virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
1383                        _("failed to validate prefix for a new XML namespace"));
1384         return -1;
1385     }
1386 
1387     if (!(ns = xmlNewNs(node, (const unsigned char *)uri, (const unsigned char *)key))) {
1388         virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
1389                        _("failed to create a new XML namespace"));
1390         return -1;
1391     }
1392 
1393     virXMLForeachNode(node, virXMLAddElementNamespace, ns);
1394 
1395     return 0;
1396 }
1397 
1398 /**
1399  * virXMLNodeSanitizeNamespaces()
1400  * @node: Sanitize the namespaces for this node
1401  *
1402  * This function removes subnodes in node that share the namespace.
1403  * The first instance of every duplicate namespace is kept.
1404  * Additionally nodes with no namespace are deleted.
1405  */
1406 void
virXMLNodeSanitizeNamespaces(xmlNodePtr node)1407 virXMLNodeSanitizeNamespaces(xmlNodePtr node)
1408 {
1409     xmlNodePtr child;
1410     xmlNodePtr next;
1411     xmlNodePtr dupl;
1412 
1413     if (!node)
1414         return;
1415 
1416     child = node->children;
1417     while (child) {
1418         /* remove subelements that don't have any namespace at all */
1419         if (!child->ns || !child->ns->href) {
1420             dupl = child;
1421             child = child->next;
1422 
1423             xmlUnlinkNode(dupl);
1424             xmlFreeNode(dupl);
1425             continue;
1426         }
1427 
1428         /* check that every other child of @root doesn't share the namespace of
1429          * the current one and delete them possibly */
1430         next = child->next;
1431         while (next) {
1432             dupl = NULL;
1433 
1434             if (child->ns && next->ns &&
1435                 STREQ_NULLABLE((const char *) child->ns->href,
1436                                (const char *) next->ns->href))
1437                 dupl = next;
1438 
1439             next = next->next;
1440             if (dupl) {
1441                 xmlUnlinkNode(dupl);
1442                 xmlFreeNode(dupl);
1443             }
1444         }
1445         child = child->next;
1446     }
1447 }
1448 
1449 
catchRNGError(void * ctx,const char * msg,...)1450 static void catchRNGError(void *ctx,
1451                           const char *msg,
1452                           ...)
1453 {
1454     virBuffer *buf = ctx;
1455     va_list args;
1456 
1457     va_start(args, msg);
1458     VIR_WARNINGS_NO_PRINTF;
1459     virBufferVasprintf(buf, msg, args);
1460     VIR_WARNINGS_RESET;
1461     va_end(args);
1462 }
1463 
1464 
ignoreRNGError(void * ctx G_GNUC_UNUSED,const char * msg G_GNUC_UNUSED,...)1465 static void ignoreRNGError(void *ctx G_GNUC_UNUSED,
1466                            const char *msg G_GNUC_UNUSED,
1467                            ...)
1468 {}
1469 
1470 
1471 virXMLValidator *
virXMLValidatorInit(const char * schemafile)1472 virXMLValidatorInit(const char *schemafile)
1473 {
1474     virXMLValidator *validator = NULL;
1475 
1476     validator = g_new0(virXMLValidator, 1);
1477 
1478     validator->schemafile = g_strdup(schemafile);
1479 
1480     if (!(validator->rngParser =
1481           xmlRelaxNGNewParserCtxt(validator->schemafile))) {
1482         virReportError(VIR_ERR_INTERNAL_ERROR,
1483                        _("Unable to create RNG parser for %s"),
1484                        validator->schemafile);
1485         goto error;
1486     }
1487 
1488     xmlRelaxNGSetParserErrors(validator->rngParser,
1489                               catchRNGError,
1490                               ignoreRNGError,
1491                               &validator->buf);
1492 
1493     if (!(validator->rng = xmlRelaxNGParse(validator->rngParser))) {
1494         virReportError(VIR_ERR_INTERNAL_ERROR,
1495                        _("Unable to parse RNG %s: %s"),
1496                        validator->schemafile,
1497                        virBufferCurrentContent(&validator->buf));
1498         goto error;
1499     }
1500 
1501     if (!(validator->rngValid = xmlRelaxNGNewValidCtxt(validator->rng))) {
1502         virReportError(VIR_ERR_INTERNAL_ERROR,
1503                        _("Unable to create RNG validation context %s"),
1504                        validator->schemafile);
1505         goto error;
1506     }
1507 
1508     xmlRelaxNGSetValidErrors(validator->rngValid,
1509                              catchRNGError,
1510                              ignoreRNGError,
1511                              &validator->buf);
1512     return validator;
1513 
1514  error:
1515     virXMLValidatorFree(validator);
1516     return NULL;
1517 }
1518 
1519 
1520 int
virXMLValidatorValidate(virXMLValidator * validator,xmlDocPtr doc)1521 virXMLValidatorValidate(virXMLValidator *validator,
1522                         xmlDocPtr doc)
1523 {
1524     if (xmlRelaxNGValidateDoc(validator->rngValid, doc) != 0) {
1525         virReportError(VIR_ERR_XML_INVALID_SCHEMA,
1526                        _("Unable to validate doc against %s\n%s"),
1527                        validator->schemafile,
1528                        virBufferCurrentContent(&validator->buf));
1529         return -1;
1530     }
1531 
1532     return 0;
1533 }
1534 
1535 
1536 int
virXMLValidateAgainstSchema(const char * schemafile,xmlDocPtr doc)1537 virXMLValidateAgainstSchema(const char *schemafile,
1538                             xmlDocPtr doc)
1539 {
1540     virXMLValidator *validator = NULL;
1541     int ret = -1;
1542 
1543     if (!(validator = virXMLValidatorInit(schemafile)))
1544         return -1;
1545 
1546     if (virXMLValidatorValidate(validator, doc) < 0)
1547         goto cleanup;
1548 
1549     ret = 0;
1550  cleanup:
1551     virXMLValidatorFree(validator);
1552     return ret;
1553 }
1554 
1555 
1556 int
virXMLValidateNodeAgainstSchema(const char * schemafile,xmlNodePtr node)1557 virXMLValidateNodeAgainstSchema(const char *schemafile, xmlNodePtr node)
1558 {
1559     int ret;
1560     g_autoptr(xmlDoc) copy = xmlNewDoc(NULL);
1561 
1562     xmlDocSetRootElement(copy, xmlCopyNode(node, true));
1563     ret = virXMLValidateAgainstSchema(schemafile, copy);
1564 
1565     return ret;
1566 }
1567 
1568 
1569 void
virXMLValidatorFree(virXMLValidator * validator)1570 virXMLValidatorFree(virXMLValidator *validator)
1571 {
1572     if (!validator)
1573         return;
1574 
1575     g_free(validator->schemafile);
1576     virBufferFreeAndReset(&validator->buf);
1577     xmlRelaxNGFreeParserCtxt(validator->rngParser);
1578     xmlRelaxNGFreeValidCtxt(validator->rngValid);
1579     xmlRelaxNGFree(validator->rng);
1580     g_free(validator);
1581 }
1582 
1583 
1584 /* same as virXMLFormatElement but outputs an empty element if @attrBuf and
1585  * @childBuf are both empty */
1586 void
virXMLFormatElementEmpty(virBuffer * buf,const char * name,virBuffer * attrBuf,virBuffer * childBuf)1587 virXMLFormatElementEmpty(virBuffer *buf,
1588                          const char *name,
1589                          virBuffer *attrBuf,
1590                          virBuffer *childBuf)
1591 {
1592     virBufferAsprintf(buf, "<%s", name);
1593 
1594     if (attrBuf && virBufferUse(attrBuf) > 0)
1595         virBufferAddBuffer(buf, attrBuf);
1596 
1597     if (childBuf && virBufferUse(childBuf) > 0) {
1598         virBufferAddLit(buf, ">\n");
1599         virBufferAddBuffer(buf, childBuf);
1600         virBufferAsprintf(buf, "</%s>\n", name);
1601     } else {
1602         virBufferAddLit(buf, "/>\n");
1603     }
1604 
1605     virBufferFreeAndReset(attrBuf);
1606     virBufferFreeAndReset(childBuf);
1607 }
1608 
1609 
1610 /**
1611  * virXMLFormatElement
1612  * @buf: the parent buffer where the element will be placed
1613  * @name: the name of the element
1614  * @attrBuf: buffer with attributes for element, may be NULL
1615  * @childBuf: buffer with child elements, may be NULL
1616  *
1617  * Helper to format element where attributes or child elements
1618  * are optional and may not be formatted.  If both @attrBuf and
1619  * @childBuf are NULL or are empty buffers the element is not
1620  * formatted.
1621  *
1622  * Both passed buffers are always consumed and freed.
1623  */
1624 void
virXMLFormatElement(virBuffer * buf,const char * name,virBuffer * attrBuf,virBuffer * childBuf)1625 virXMLFormatElement(virBuffer *buf,
1626                     const char *name,
1627                     virBuffer *attrBuf,
1628                     virBuffer *childBuf)
1629 {
1630     if ((!attrBuf || virBufferUse(attrBuf) == 0) &&
1631         (!childBuf || virBufferUse(childBuf) == 0))
1632         return;
1633 
1634     virXMLFormatElementEmpty(buf, name, attrBuf, childBuf);
1635 }
1636 
1637 
1638 /**
1639  * virXMLFormatMetadata:
1640  * @buf: the parent buffer where the element will be placed
1641  * @metadata: pointer to metadata node
1642  *
1643  * Helper to format metadata element. If @metadata is NULL then
1644  * this function is a NOP.
1645  *
1646  * Returns: 0 on success,
1647  *         -1 otherwise.
1648  */
1649 int
virXMLFormatMetadata(virBuffer * buf,xmlNodePtr metadata)1650 virXMLFormatMetadata(virBuffer *buf,
1651                      xmlNodePtr metadata)
1652 {
1653     g_autoptr(xmlBuffer) xmlbuf = NULL;
1654     const char *xmlbufContent = NULL;
1655     int oldIndentTreeOutput = xmlIndentTreeOutput;
1656 
1657     if (!metadata)
1658         return 0;
1659 
1660     /* Indentation on output requires that we previously set
1661      * xmlKeepBlanksDefault to 0 when parsing; also, libxml does 2
1662      * spaces per level of indentation of intermediate elements,
1663      * but no leading indentation before the starting element.
1664      * Thankfully, libxml maps what looks like globals into
1665      * thread-local uses, so we are thread-safe.  */
1666     xmlIndentTreeOutput = 1;
1667     xmlbuf = virXMLBufferCreate();
1668 
1669     if (xmlNodeDump(xmlbuf, metadata->doc, metadata,
1670                     virBufferGetIndent(buf) / 2, 1) < 0) {
1671         xmlIndentTreeOutput = oldIndentTreeOutput;
1672         virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
1673                        _("Unable to format metadata element"));
1674         return -1;
1675     }
1676 
1677     /* After libxml2-v2.9.12-2-g85b1792e even the first line is indented.
1678      * But virBufferAsprintf() also adds indentation. Skip one of them. */
1679     xmlbufContent = (const char *) xmlBufferContent(xmlbuf);
1680     virSkipSpaces(&xmlbufContent);
1681 
1682     virBufferAsprintf(buf, "%s\n", xmlbufContent);
1683     xmlIndentTreeOutput = oldIndentTreeOutput;
1684 
1685     return 0;
1686 }
1687 
1688 
1689 void
virXPathContextNodeRestore(virXPathContextNodeSave * save)1690 virXPathContextNodeRestore(virXPathContextNodeSave *save)
1691 {
1692     if (!save->ctxt)
1693         return;
1694 
1695     save->ctxt->node = save->node;
1696 }
1697 
1698 
1699 void
virXMLNamespaceFormatNS(virBuffer * buf,virXMLNamespace const * ns)1700 virXMLNamespaceFormatNS(virBuffer *buf,
1701                         virXMLNamespace const *ns)
1702 {
1703     virBufferAsprintf(buf, " xmlns:%s='%s'", ns->prefix, ns->uri);
1704 }
1705 
1706 
1707 int
virXMLNamespaceRegister(xmlXPathContextPtr ctxt,virXMLNamespace const * ns)1708 virXMLNamespaceRegister(xmlXPathContextPtr ctxt,
1709                         virXMLNamespace const *ns)
1710 {
1711     if (xmlXPathRegisterNs(ctxt,
1712                            BAD_CAST ns->prefix,
1713                            BAD_CAST ns->uri) < 0) {
1714         virReportError(VIR_ERR_INTERNAL_ERROR,
1715                        _("Failed to register xml namespace '%s'"),
1716                        ns->uri);
1717         return -1;
1718     }
1719 
1720     return 0;
1721 }
1722 
1723 
1724 /**
1725  * virParseScaledValue:
1726  * @xpath: XPath to memory amount
1727  * @units_xpath: XPath to units attribute
1728  * @ctxt: XPath context
1729  * @val: scaled value is stored here
1730  * @scale: default scale for @val
1731  * @max: maximal @val allowed
1732  * @required: is the value required?
1733  *
1734  * Parse a value located at @xpath within @ctxt, and store the
1735  * result into @val. The value is scaled by units located at
1736  * @units_xpath (or the 'unit' attribute under @xpath if
1737  * @units_xpath is NULL). If units are not present, the default
1738  * @scale is used. If @required is set, then the value must
1739  * exist; otherwise, the value is optional. The resulting value
1740  * is in bytes.
1741  *
1742  * Returns 1 on success,
1743  *         0 if the value was not present and !@required,
1744  *         -1 on failure after issuing error.
1745  */
1746 int
virParseScaledValue(const char * xpath,const char * units_xpath,xmlXPathContextPtr ctxt,unsigned long long * val,unsigned long long scale,unsigned long long max,bool required)1747 virParseScaledValue(const char *xpath,
1748                     const char *units_xpath,
1749                     xmlXPathContextPtr ctxt,
1750                     unsigned long long *val,
1751                     unsigned long long scale,
1752                     unsigned long long max,
1753                     bool required)
1754 {
1755     unsigned long long bytes;
1756     g_autofree char *xpath_full = NULL;
1757     g_autofree char *unit = NULL;
1758     g_autofree char *bytes_str = NULL;
1759 
1760     *val = 0;
1761     xpath_full = g_strdup_printf("string(%s)", xpath);
1762 
1763     bytes_str = virXPathString(xpath_full, ctxt);
1764     if (!bytes_str) {
1765         if (!required)
1766             return 0;
1767         virReportError(VIR_ERR_XML_ERROR,
1768                        _("missing element or attribute '%s'"),
1769                        xpath);
1770         return -1;
1771     }
1772     VIR_FREE(xpath_full);
1773 
1774     if (virStrToLong_ullp(bytes_str, NULL, 10, &bytes) < 0) {
1775         virReportError(VIR_ERR_XML_ERROR,
1776                        _("Invalid value '%s' for element or attribute '%s'"),
1777                        bytes_str, xpath);
1778         return -1;
1779     }
1780 
1781     if (units_xpath)
1782          xpath_full = g_strdup_printf("string(%s)", units_xpath);
1783     else
1784          xpath_full = g_strdup_printf("string(%s/@unit)", xpath);
1785     unit = virXPathString(xpath_full, ctxt);
1786 
1787     if (virScaleInteger(&bytes, unit, scale, max) < 0)
1788         return -1;
1789 
1790     *val = bytes;
1791     return 1;
1792 }
1793 
1794 
1795 xmlBufferPtr
virXMLBufferCreate(void)1796 virXMLBufferCreate(void)
1797 {
1798     xmlBufferPtr ret;
1799 
1800     if (!(ret = xmlBufferCreate()))
1801         abort();
1802 
1803     return ret;
1804 }
1805 
1806 
1807 xmlNodePtr
virXMLNewNode(xmlNsPtr ns,const char * name)1808 virXMLNewNode(xmlNsPtr ns,
1809               const char *name)
1810 {
1811     xmlNodePtr ret;
1812 
1813     if (!(ret = xmlNewNode(ns, BAD_CAST name)))
1814         abort();
1815 
1816     return ret;
1817 }
1818