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