xml.c 21.9 KB
Newer Older
1 2 3
/*
 * xml.c: XML based interfaces for the libvir library
 *
4
 * Copyright (C) 2005, 2007-2011 Red Hat, Inc.
5 6 7 8 9 10
 *
 * See COPYING.LIB for the License of this software
 *
 * Daniel Veillard <veillard@redhat.com>
 */

11
#include <config.h>
12

13 14 15 16
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
17
#include <limits.h>
18
#include <math.h>               /* for isnan() */
19
#include <sys/stat.h>
20 21

#include "virterror_internal.h"
22
#include "xml.h"
23
#include "buf.h"
24
#include "util.h"
25
#include "memory.h"
26
#include "virfile.h"
27

28 29
#define VIR_FROM_THIS VIR_FROM_XML

30
#define virGenericReportError(from, code, ...)                          \
31
        virReportErrorHelper(from, code, __FILE__,                      \
32
                             __FUNCTION__, __LINE__, __VA_ARGS__)
33

34 35 36 37 38 39 40 41 42
#define virXMLError(code, ...)                                          \
        virGenericReportError(VIR_FROM_XML, code, __VA_ARGS__)


/* Internal data to be passed to SAX parser and used by error handler. */
struct virParserData {
    int domcode;
};

43 44 45 46 47 48 49

/************************************************************************
 *									*
 * Wrappers around libxml2 XPath specific functions			*
 *									*
 ************************************************************************/

50 51 52 53 54 55 56 57 58 59 60
/**
 * virXPathString:
 * @xpath: the XPath string to evaluate
 * @ctxt: an XPath context
 *
 * Convenience function to evaluate an XPath string
 *
 * Returns a new string which must be deallocated by the caller or NULL
 *         if the evaluation failed.
 */
char *
61
virXPathString(const char *xpath,
62
               xmlXPathContextPtr ctxt)
63
{
64
    xmlXPathObjectPtr obj;
65
    xmlNodePtr relnode;
66 67 68
    char *ret;

    if ((ctxt == NULL) || (xpath == NULL)) {
69
        virXMLError(VIR_ERR_INTERNAL_ERROR,
J
Jim Meyering 已提交
70
                    "%s", _("Invalid parameter to virXPathString()"));
71
        return (NULL);
72
    }
73
    relnode = ctxt->node;
74
    obj = xmlXPathEval(BAD_CAST xpath, ctxt);
75
    ctxt->node = relnode;
76
    if ((obj == NULL) || (obj->type != XPATH_STRING) ||
D
Daniel P. Berrange 已提交
77
        (obj->stringval == NULL) || (obj->stringval[0] == 0)) {
78
        xmlXPathFreeObject(obj);
79
        return (NULL);
D
Daniel P. Berrange 已提交
80
    }
81 82 83
    ret = strdup((char *) obj->stringval);
    xmlXPathFreeObject(obj);
    if (ret == NULL) {
84
        virReportOOMError();
85
    }
86
    return (ret);
87 88
}

89 90 91 92 93 94 95 96 97 98 99 100 101
/**
 * virXPathStringLimit:
 * @xpath: the XPath string to evaluate
 * @maxlen: maximum length permittred string
 * @ctxt: an XPath context
 *
 * Wrapper for virXPathString, which validates the length of the returned
 * string.
 *
 * Returns a new string which must be deallocated by the caller or NULL if
 * the evaluation failed.
 */
char *
102
virXPathStringLimit(const char *xpath,
103 104 105
                    size_t maxlen,
                    xmlXPathContextPtr ctxt)
{
106
    char *tmp = virXPathString(xpath, ctxt);
107 108

    if (tmp != NULL && strlen(tmp) >= maxlen) {
109
        virXMLError(VIR_ERR_INTERNAL_ERROR,
P
Phil Petty 已提交
110
                    _("\'%s\' value longer than %zd bytes"),
111
                    xpath, maxlen);
P
Phil Petty 已提交
112 113
        VIR_FREE(tmp);
        return NULL;
114 115 116 117 118
    }

    return tmp;
}

119 120 121 122 123 124 125 126 127 128 129 130
/**
 * virXPathNumber:
 * @xpath: the XPath string to evaluate
 * @ctxt: an XPath context
 * @value: the returned double value
 *
 * Convenience function to evaluate an XPath number
 *
 * Returns 0 in case of success in which case @value is set,
 *         or -1 if the evaluation failed.
 */
int
131
virXPathNumber(const char *xpath,
132 133
               xmlXPathContextPtr ctxt,
               double *value)
134
{
135
    xmlXPathObjectPtr obj;
136
    xmlNodePtr relnode;
137 138

    if ((ctxt == NULL) || (xpath == NULL) || (value == NULL)) {
139
        virXMLError(VIR_ERR_INTERNAL_ERROR,
J
Jim Meyering 已提交
140
                    "%s", _("Invalid parameter to virXPathNumber()"));
141
        return (-1);
142
    }
143
    relnode = ctxt->node;
144
    obj = xmlXPathEval(BAD_CAST xpath, ctxt);
145
    ctxt->node = relnode;
146 147
    if ((obj == NULL) || (obj->type != XPATH_NUMBER) ||
        (isnan(obj->floatval))) {
148 149
        xmlXPathFreeObject(obj);
        return (-1);
150
    }
151

152 153
    *value = obj->floatval;
    xmlXPathFreeObject(obj);
154
    return (0);
155 156
}

157
static int
158
virXPathLongBase(const char *xpath,
159 160 161
                 xmlXPathContextPtr ctxt,
                 int base,
                 long *value)
162
{
163
    xmlXPathObjectPtr obj;
164
    xmlNodePtr relnode;
165 166 167
    int ret = 0;

    if ((ctxt == NULL) || (xpath == NULL) || (value == NULL)) {
168
        virXMLError(VIR_ERR_INTERNAL_ERROR,
169
                    "%s", _("Invalid parameter to virXPathLong()"));
170
        return (-1);
171
    }
172
    relnode = ctxt->node;
173
    obj = xmlXPathEval(BAD_CAST xpath, ctxt);
174
    ctxt->node = relnode;
175 176 177
    if ((obj != NULL) && (obj->type == XPATH_STRING) &&
        (obj->stringval != NULL) && (obj->stringval[0] != 0)) {
        char *conv = NULL;
178
        long val;
179

180
        val = strtol((const char *) obj->stringval, &conv, base);
181
        if (conv == (const char *) obj->stringval) {
182 183
            ret = -2;
        } else {
184 185
            *value = val;
        }
186 187
    } else if ((obj != NULL) && (obj->type == XPATH_NUMBER) &&
               (!(isnan(obj->floatval)))) {
188 189 190 191
        *value = (long) obj->floatval;
        if (*value != obj->floatval) {
            ret = -2;
        }
192
    } else {
193
        ret = -1;
194
    }
195

196
    xmlXPathFreeObject(obj);
197
    return (ret);
198 199
}

200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228
/**
 * virXPathInt:
 * @xpath: the XPath string to evaluate
 * @ctxt: an XPath context
 * @value: the returned int value
 *
 * Convenience function to evaluate an XPath number
 *
 * Returns 0 in case of success in which case @value is set,
 *         or -1 if the XPath evaluation failed or -2 if the
 *         value doesn't have an int format.
 */
int
virXPathInt(const char *xpath,
            xmlXPathContextPtr ctxt,
            int *value)
{
    long tmp;
    int ret;

    ret = virXPathLongBase(xpath, ctxt, 10, &tmp);
    if (ret < 0)
        return ret;
    if ((int) tmp != tmp)
        return -2;
    *value = tmp;
    return 0;
}

229
/**
230
 * virXPathLong:
231 232 233 234 235 236 237 238 239 240 241
 * @xpath: the XPath string to evaluate
 * @ctxt: an XPath context
 * @value: the returned long value
 *
 * Convenience function to evaluate an XPath number
 *
 * Returns 0 in case of success in which case @value is set,
 *         or -1 if the XPath evaluation failed or -2 if the
 *         value doesn't have a long format.
 */
int
242
virXPathLong(const char *xpath,
243 244 245
             xmlXPathContextPtr ctxt,
             long *value)
{
246
    return virXPathLongBase(xpath, ctxt, 10, value);
247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262
}

/**
 * virXPathLongHex:
 * @xpath: the XPath string to evaluate
 * @ctxt: an XPath context
 * @value: the returned long value
 *
 * Convenience function to evaluate an XPath number
 * according to a base of 16
 *
 * Returns 0 in case of success in which case @value is set,
 *         or -1 if the XPath evaluation failed or -2 if the
 *         value doesn't have a long format.
 */
int
263
virXPathLongHex(const char *xpath,
264 265 266
                xmlXPathContextPtr ctxt,
                long *value)
{
267
    return virXPathLongBase(xpath, ctxt, 16, value);
268 269 270
}

static int
271
virXPathULongBase(const char *xpath,
272 273 274
                  xmlXPathContextPtr ctxt,
                  int base,
                  unsigned long *value)
275 276 277 278 279 280
{
    xmlXPathObjectPtr obj;
    xmlNodePtr relnode;
    int ret = 0;

    if ((ctxt == NULL) || (xpath == NULL) || (value == NULL)) {
281
        virXMLError(VIR_ERR_INTERNAL_ERROR,
282
                    "%s", _("Invalid parameter to virXPathULong()"));
283 284 285 286
        return (-1);
    }
    relnode = ctxt->node;
    obj = xmlXPathEval(BAD_CAST xpath, ctxt);
287
    ctxt->node = relnode;
288 289 290 291 292
    if ((obj != NULL) && (obj->type == XPATH_STRING) &&
        (obj->stringval != NULL) && (obj->stringval[0] != 0)) {
        char *conv = NULL;
        long val;

293
        val = strtoul((const char *) obj->stringval, &conv, base);
294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312
        if (conv == (const char *) obj->stringval) {
            ret = -2;
        } else {
            *value = val;
        }
    } else if ((obj != NULL) && (obj->type == XPATH_NUMBER) &&
               (!(isnan(obj->floatval)))) {
        *value = (unsigned long) obj->floatval;
        if (*value != obj->floatval) {
            ret = -2;
        }
    } else {
        ret = -1;
    }

    xmlXPathFreeObject(obj);
    return (ret);
}

313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341
/**
 * virXPathUInt:
 * @xpath: the XPath string to evaluate
 * @ctxt: an XPath context
 * @value: the returned int value
 *
 * Convenience function to evaluate an XPath number
 *
 * Returns 0 in case of success in which case @value is set,
 *         or -1 if the XPath evaluation failed or -2 if the
 *         value doesn't have an int format.
 */
int
virXPathUInt(const char *xpath,
             xmlXPathContextPtr ctxt,
             unsigned int *value)
{
    unsigned long tmp;
    int ret;

    ret = virXPathULongBase(xpath, ctxt, 10, &tmp);
    if (ret < 0)
        return ret;
    if ((unsigned int) tmp != tmp)
        return -2;
    *value = tmp;
    return 0;
}

342 343 344 345 346 347 348 349 350 351 352 353 354
/**
 * virXPathULong:
 * @xpath: the XPath string to evaluate
 * @ctxt: an XPath context
 * @value: the returned long value
 *
 * Convenience function to evaluate an XPath number
 *
 * Returns 0 in case of success in which case @value is set,
 *         or -1 if the XPath evaluation failed or -2 if the
 *         value doesn't have a long format.
 */
int
355
virXPathULong(const char *xpath,
356 357 358
              xmlXPathContextPtr ctxt,
              unsigned long *value)
{
359
    return virXPathULongBase(xpath, ctxt, 10, value);
360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375
}

/**
 * virXPathUHex:
 * @xpath: the XPath string to evaluate
 * @ctxt: an XPath context
 * @value: the returned long value
 *
 * Convenience function to evaluate an XPath number
 * according to base of 16
 *
 * Returns 0 in case of success in which case @value is set,
 *         or -1 if the XPath evaluation failed or -2 if the
 *         value doesn't have a long format.
 */
int
376
virXPathULongHex(const char *xpath,
377 378 379
                 xmlXPathContextPtr ctxt,
                 unsigned long *value)
{
380
    return virXPathULongBase(xpath, ctxt, 16, value);
381 382
}

M
Mark McLoughlin 已提交
383 384 385 386 387 388 389 390 391 392 393 394 395
/**
 * virXPathULongLong:
 * @xpath: the XPath string to evaluate
 * @ctxt: an XPath context
 * @value: the returned long long value
 *
 * Convenience function to evaluate an XPath number
 *
 * Returns 0 in case of success in which case @value is set,
 *         or -1 if the XPath evaluation failed or -2 if the
 *         value doesn't have a long format.
 */
int
396
virXPathULongLong(const char *xpath,
M
Mark McLoughlin 已提交
397 398 399 400 401 402 403 404
                  xmlXPathContextPtr ctxt,
                  unsigned long long *value)
{
    xmlXPathObjectPtr obj;
    xmlNodePtr relnode;
    int ret = 0;

    if ((ctxt == NULL) || (xpath == NULL) || (value == NULL)) {
405
        virXMLError(VIR_ERR_INTERNAL_ERROR,
M
Mark McLoughlin 已提交
406 407 408 409 410
                    "%s", _("Invalid parameter to virXPathULong()"));
        return (-1);
    }
    relnode = ctxt->node;
    obj = xmlXPathEval(BAD_CAST xpath, ctxt);
411
    ctxt->node = relnode;
M
Mark McLoughlin 已提交
412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436
    if ((obj != NULL) && (obj->type == XPATH_STRING) &&
        (obj->stringval != NULL) && (obj->stringval[0] != 0)) {
        char *conv = NULL;
        unsigned long long val;

        val = strtoull((const char *) obj->stringval, &conv, 10);
        if (conv == (const char *) obj->stringval) {
            ret = -2;
        } else {
            *value = val;
        }
    } else if ((obj != NULL) && (obj->type == XPATH_NUMBER) &&
               (!(isnan(obj->floatval)))) {
        *value = (unsigned long long) obj->floatval;
        if (*value != obj->floatval) {
            ret = -2;
        }
    } else {
        ret = -1;
    }

    xmlXPathFreeObject(obj);
    return (ret);
}

437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490
/**
 * virXPathULongLong:
 * @xpath: the XPath string to evaluate
 * @ctxt: an XPath context
 * @value: the returned long long value
 *
 * Convenience function to evaluate an XPath number
 *
 * Returns 0 in case of success in which case @value is set,
 *         or -1 if the XPath evaluation failed or -2 if the
 *         value doesn't have a long format.
 */
int
virXPathLongLong(const char *xpath,
                 xmlXPathContextPtr ctxt,
                 long long *value)
{
    xmlXPathObjectPtr obj;
    xmlNodePtr relnode;
    int ret = 0;

    if ((ctxt == NULL) || (xpath == NULL) || (value == NULL)) {
        virXMLError(VIR_ERR_INTERNAL_ERROR,
                    "%s", _("Invalid parameter to virXPathLongLong()"));
        return (-1);
    }
    relnode = ctxt->node;
    obj = xmlXPathEval(BAD_CAST xpath, ctxt);
    ctxt->node = relnode;
    if ((obj != NULL) && (obj->type == XPATH_STRING) &&
        (obj->stringval != NULL) && (obj->stringval[0] != 0)) {
        char *conv = NULL;
        unsigned long long val;

        val = strtoll((const char *) obj->stringval, &conv, 10);
        if (conv == (const char *) obj->stringval) {
            ret = -2;
        } else {
            *value = val;
        }
    } else if ((obj != NULL) && (obj->type == XPATH_NUMBER) &&
               (!(isnan(obj->floatval)))) {
        *value = (long long) obj->floatval;
        if (*value != obj->floatval) {
            ret = -2;
        }
    } else {
        ret = -1;
    }

    xmlXPathFreeObject(obj);
    return (ret);
}

491 492 493 494 495 496 497
char *
virXMLPropString(xmlNodePtr node,
                 const char *name)
{
    return (char *)xmlGetProp(node, BAD_CAST name);
}

498 499 500 501 502 503 504 505 506 507
/**
 * virXPathBoolean:
 * @xpath: the XPath string to evaluate
 * @ctxt: an XPath context
 *
 * Convenience function to evaluate an XPath boolean
 *
 * Returns 0 if false, 1 if true, or -1 if the evaluation failed.
 */
int
508
virXPathBoolean(const char *xpath,
509
                xmlXPathContextPtr ctxt)
510
{
511
    xmlXPathObjectPtr obj;
512
    xmlNodePtr relnode;
513 514 515
    int ret;

    if ((ctxt == NULL) || (xpath == NULL)) {
516
        virXMLError(VIR_ERR_INTERNAL_ERROR,
J
Jim Meyering 已提交
517
                    "%s", _("Invalid parameter to virXPathBoolean()"));
518
        return (-1);
519
    }
520
    relnode = ctxt->node;
521
    obj = xmlXPathEval(BAD_CAST xpath, ctxt);
522
    ctxt->node = relnode;
523 524
    if ((obj == NULL) || (obj->type != XPATH_BOOLEAN) ||
        (obj->boolval < 0) || (obj->boolval > 1)) {
525 526
        xmlXPathFreeObject(obj);
        return (-1);
527 528
    }
    ret = obj->boolval;
529

530
    xmlXPathFreeObject(obj);
531
    return (ret);
532 533 534 535 536 537 538 539 540 541 542 543 544
}

/**
 * virXPathNode:
 * @xpath: the XPath string to evaluate
 * @ctxt: an XPath context
 *
 * Convenience function to evaluate an XPath node set and returning
 * only one node, the first one in the set if any
 *
 * Returns a pointer to the node or NULL if the evaluation failed.
 */
xmlNodePtr
545
virXPathNode(const char *xpath,
546
             xmlXPathContextPtr ctxt)
547
{
548
    xmlXPathObjectPtr obj;
549
    xmlNodePtr relnode;
550 551 552
    xmlNodePtr ret;

    if ((ctxt == NULL) || (xpath == NULL)) {
553
        virXMLError(VIR_ERR_INTERNAL_ERROR,
J
Jim Meyering 已提交
554
                    "%s", _("Invalid parameter to virXPathNode()"));
555
        return (NULL);
556
    }
557
    relnode = ctxt->node;
558
    obj = xmlXPathEval(BAD_CAST xpath, ctxt);
559
    ctxt->node = relnode;
560 561
    if ((obj == NULL) || (obj->type != XPATH_NODESET) ||
        (obj->nodesetval == NULL) || (obj->nodesetval->nodeNr <= 0) ||
562 563 564
        (obj->nodesetval->nodeTab == NULL)) {
        xmlXPathFreeObject(obj);
        return (NULL);
565
    }
566

567 568
    ret = obj->nodesetval->nodeTab[0];
    xmlXPathFreeObject(obj);
569
    return (ret);
570
}
571

572 573 574 575 576 577 578 579 580 581 582 583
/**
 * virXPathNodeSet:
 * @xpath: the XPath string to evaluate
 * @ctxt: an XPath context
 * @list: the returned list of nodes (or NULL if only count matters)
 *
 * Convenience function to evaluate an XPath node set
 *
 * Returns the number of nodes found in which case @list is set (and
 *         must be freed) or -1 if the evaluation failed.
 */
int
584
virXPathNodeSet(const char *xpath,
585 586
                xmlXPathContextPtr ctxt,
                xmlNodePtr **list)
587
{
588
    xmlXPathObjectPtr obj;
589
    xmlNodePtr relnode;
590 591 592
    int ret;

    if ((ctxt == NULL) || (xpath == NULL)) {
593
        virXMLError(VIR_ERR_INTERNAL_ERROR,
J
Jim Meyering 已提交
594
                    "%s", _("Invalid parameter to virXPathNodeSet()"));
595
        return (-1);
596
    }
597 598 599 600

    if (list != NULL)
        *list = NULL;

601
    relnode = ctxt->node;
602
    obj = xmlXPathEval(BAD_CAST xpath, ctxt);
603
    ctxt->node = relnode;
D
Daniel Veillard 已提交
604 605
    if (obj == NULL)
        return(0);
606

D
Daniel Veillard 已提交
607
    if (obj->type != XPATH_NODESET) {
608 609
        virXMLError(VIR_ERR_INTERNAL_ERROR,
                    _("Incorrect xpath '%s'"), xpath);
610 611
        xmlXPathFreeObject(obj);
        return (-1);
612
    }
613

D
Daniel Veillard 已提交
614 615 616 617
    if ((obj->nodesetval == NULL)  || (obj->nodesetval->nodeNr < 0)) {
        xmlXPathFreeObject(obj);
        return (0);
    }
618

619
    ret = obj->nodesetval->nodeNr;
620
    if (list != NULL && ret) {
621
        if (VIR_ALLOC_N(*list, ret) < 0) {
622
            virReportOOMError();
623
            ret = -1;
624 625 626 627
        } else {
            memcpy(*list, obj->nodesetval->nodeTab,
                   ret * sizeof(xmlNodePtr));
        }
628 629
    }
    xmlXPathFreeObject(obj);
630
    return (ret);
631
}
632 633 634 635 636 637


/**
 * catchXMLError:
 *
 * Called from SAX on parsing errors in the XML.
638 639
 *
 * This version is heavily based on xmlParserPrintFileContextInternal from libxml2.
640 641 642 643 644 645
 */
static void
catchXMLError(void *ctx, const char *msg ATTRIBUTE_UNUSED, ...)
{
    xmlParserCtxtPtr ctxt = (xmlParserCtxtPtr) ctx;

646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663
    const xmlChar *cur, *base;
    unsigned int n, col;	/* GCC warns if signed, because compared with sizeof() */
    int domcode = VIR_FROM_XML;

    virBuffer buf = VIR_BUFFER_INITIALIZER;
    char *contextstr = NULL;
    char *pointerstr = NULL;


    /* conditions for error printing */
    if (!ctxt ||
        (virGetLastError() != NULL) ||
        ctxt->input == NULL ||
        ctxt->lastError.level != XML_ERR_FATAL ||
        ctxt->lastError.message == NULL)
        return;

    if (ctxt->_private)
664 665
            domcode = ((struct virParserData *) ctxt->_private)->domcode;

666 667 668 669 670 671 672

    cur = ctxt->input->cur;
    base = ctxt->input->base;

    /* skip backwards over any end-of-lines */
    while ((cur > base) && ((*(cur) == '\n') || (*(cur) == '\r'))) {
        cur--;
673 674
    }

675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723
    /* search backwards for beginning-of-line (to max buff size) */
    while ((cur > base) && (*(cur) != '\n') && (*(cur) != '\r'))
        cur--;
    if ((*(cur) == '\n') || (*(cur) == '\r')) cur++;

    /* calculate the error position in terms of the current position */
    col = ctxt->input->cur - cur;

    /* search forward for end-of-line (to max buff size) */
    /* copy selected text to our buffer */
    while ((*cur != 0) && (*(cur) != '\n') && (*(cur) != '\r')) {
        virBufferAddChar(&buf, *cur++);
    }

    /* create blank line with problem pointer */
    contextstr = virBufferContentAndReset(&buf);

    /* (leave buffer space for pointer + line terminator) */
    for  (n = 0; (n<col) && (contextstr[n] != 0); n++) {
        if (contextstr[n] == '\t')
            virBufferAddChar(&buf, '\t');
        else
            virBufferAddChar(&buf, '-');
    }

    virBufferAddChar(&buf, '^');

    pointerstr = virBufferContentAndReset(&buf);

    if (ctxt->lastError.file) {
        virGenericReportError(domcode, VIR_ERR_XML_DETAIL,
                              _("%s:%d: %s%s\n%s"),
                              ctxt->lastError.file,
                              ctxt->lastError.line,
                              ctxt->lastError.message,
                              contextstr,
                              pointerstr);
    } else {
         virGenericReportError(domcode, VIR_ERR_XML_DETAIL,
                              _("at line %d: %s%s\n%s"),
                              ctxt->lastError.line,
                              ctxt->lastError.message,
                              contextstr,
                              pointerstr);
    }

    VIR_FREE(contextstr);
    VIR_FREE(pointerstr);
}
724 725 726 727 728 729 730

/**
 * virXMLParseHelper:
 * @domcode: error domain of the caller, usually VIR_FROM_THIS
 * @filename: file to be parsed or NULL if string parsing is requested
 * @xmlStr: XML string to be parsed in case filename is NULL
 * @url: URL of XML document for string parser
731
 * @ctxt: optional pointer to populate with new context pointer
732 733 734 735 736 737 738 739 740 741
 *
 * Parse XML document provided either as a file or a string. The function
 * guarantees that the XML document contains a root element.
 *
 * Returns parsed XML document.
 */
xmlDocPtr
virXMLParseHelper(int domcode,
                  const char *filename,
                  const char *xmlStr,
742 743
                  const char *url,
                  xmlXPathContextPtr *ctxt)
744 745 746 747 748 749 750
{
    struct virParserData private;
    xmlParserCtxtPtr pctxt;
    xmlDocPtr xml = NULL;

    /* Set up a parser context so we can catch the details of XML errors. */
    pctxt = xmlNewParserCtxt();
751 752
    if (!pctxt || !pctxt->sax) {
        virReportOOMError();
753
        goto error;
754
    }
755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777

    private.domcode = domcode;
    pctxt->_private = &private;
    pctxt->sax->error = catchXMLError;

    if (filename) {
        xml = xmlCtxtReadFile(pctxt, filename, NULL,
                              XML_PARSE_NOENT | XML_PARSE_NONET |
                              XML_PARSE_NOWARNING);
    } else {
        xml = xmlCtxtReadDoc(pctxt, BAD_CAST xmlStr, url, NULL,
                             XML_PARSE_NOENT | XML_PARSE_NONET |
                             XML_PARSE_NOWARNING);
    }
    if (!xml)
        goto error;

    if (xmlDocGetRootElement(xml) == NULL) {
        virGenericReportError(domcode, VIR_ERR_INTERNAL_ERROR,
                              "%s", _("missing root element"));
        goto error;
    }

778 779 780 781 782 783 784 785 786
    if (ctxt) {
        *ctxt = xmlXPathNewContext(xml);
        if (!*ctxt) {
            virReportOOMError();
            goto error;
        }
        (*ctxt)->node = xmlDocGetRootElement(xml);
    }

787 788 789 790 791 792 793 794 795 796 797 798 799 800 801
cleanup:
    xmlFreeParserCtxt(pctxt);

    return xml;

error:
    xmlFreeDoc(xml);
    xml = NULL;

    if (virGetLastError() == NULL) {
        virGenericReportError(domcode, VIR_ERR_XML_ERROR,
                              "%s", _("failed to parse xml document"));
    }
    goto cleanup;
}
802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835


struct virXMLRewritFileData {
    const char *warnName;
    const char *warnCommand;
    const char *xml;
};

static int
virXMLRewriteFile(int fd, void *opaque)
{
    struct virXMLRewritFileData *data = opaque;

    if (data->warnName && data->warnCommand) {
        if (virEmitXMLWarning(fd, data->warnName, data->warnCommand) < 0)
            return -1;
    }

    if (safewrite(fd, data->xml, strlen(data->xml)) < 0)
        return -1;

    return 0;
}

int
virXMLSaveFile(const char *path,
               const char *warnName,
               const char *warnCommand,
               const char *xml)
{
    struct virXMLRewritFileData data = { warnName, warnCommand, xml };

    return virFileRewrite(path, S_IRUSR | S_IWUSR, virXMLRewriteFile, &data);
}