Reactos
at master 2492 lines 66 kB view raw
1/* 2 * xinclude.c : Code to implement XInclude processing 3 * 4 * World Wide Web Consortium W3C Last Call Working Draft 10 November 2003 5 * http://www.w3.org/TR/2003/WD-xinclude-20031110 6 * 7 * See Copyright for the status of this software. 8 * 9 * daniel@veillard.com 10 */ 11 12#define IN_LIBXML 13#include "libxml.h" 14 15#include <string.h> 16#include <libxml/xmlmemory.h> 17#include <libxml/tree.h> 18#include <libxml/parser.h> 19#include <libxml/uri.h> 20#include <libxml/xpath.h> 21#include <libxml/xpointer.h> 22#include <libxml/parserInternals.h> 23#include <libxml/xmlerror.h> 24#include <libxml/encoding.h> 25 26#ifdef LIBXML_XINCLUDE_ENABLED 27#include <libxml/xinclude.h> 28 29#include "private/buf.h" 30#include "private/error.h" 31#include "private/tree.h" 32#include "private/xinclude.h" 33 34#define XINCLUDE_MAX_DEPTH 40 35 36/************************************************************************ 37 * * 38 * XInclude context handling * 39 * * 40 ************************************************************************/ 41 42/* 43 * An XInclude context 44 */ 45typedef xmlChar *xmlURL; 46 47typedef struct _xmlXIncludeRef xmlXIncludeRef; 48typedef xmlXIncludeRef *xmlXIncludeRefPtr; 49struct _xmlXIncludeRef { 50 xmlChar *URI; /* the fully resolved resource URL */ 51 xmlChar *fragment; /* the fragment in the URI */ 52 xmlNodePtr elem; /* the xi:include element */ 53 xmlNodePtr inc; /* the included copy */ 54 int xml; /* xml or txt */ 55 int fallback; /* fallback was loaded */ 56 int emptyFb; /* flag to show fallback empty */ 57 int expanding; /* flag to detect inclusion loops */ 58 int replace; /* should the node be replaced? */ 59}; 60 61typedef struct _xmlXIncludeDoc xmlXIncludeDoc; 62typedef xmlXIncludeDoc *xmlXIncludeDocPtr; 63struct _xmlXIncludeDoc { 64 xmlDocPtr doc; /* the parsed document */ 65 xmlChar *url; /* the URL */ 66 int expanding; /* flag to detect inclusion loops */ 67}; 68 69typedef struct _xmlXIncludeTxt xmlXIncludeTxt; 70typedef xmlXIncludeTxt *xmlXIncludeTxtPtr; 71struct _xmlXIncludeTxt { 72 xmlChar *text; /* text string */ 73 xmlChar *url; /* the URL */ 74}; 75 76struct _xmlXIncludeCtxt { 77 xmlDocPtr doc; /* the source document */ 78 int incNr; /* number of includes */ 79 int incMax; /* size of includes tab */ 80 xmlXIncludeRefPtr *incTab; /* array of included references */ 81 82 int txtNr; /* number of unparsed documents */ 83 int txtMax; /* size of unparsed documents tab */ 84 xmlXIncludeTxt *txtTab; /* array of unparsed documents */ 85 86 int urlNr; /* number of documents stacked */ 87 int urlMax; /* size of document stack */ 88 xmlXIncludeDoc *urlTab; /* document stack */ 89 90 int nbErrors; /* the number of errors detected */ 91 int fatalErr; /* abort processing */ 92 int legacy; /* using XINCLUDE_OLD_NS */ 93 int parseFlags; /* the flags used for parsing XML documents */ 94 xmlChar * base; /* the current xml:base */ 95 96 void *_private; /* application data */ 97 98#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION 99 unsigned long incTotal; /* total number of processed inclusions */ 100#endif 101 int depth; /* recursion depth */ 102 int isStream; /* streaming mode */ 103}; 104 105static xmlXIncludeRefPtr 106xmlXIncludeExpandNode(xmlXIncludeCtxtPtr ctxt, xmlNodePtr node); 107 108static int 109xmlXIncludeLoadNode(xmlXIncludeCtxtPtr ctxt, xmlXIncludeRefPtr ref); 110 111static int 112xmlXIncludeDoProcess(xmlXIncludeCtxtPtr ctxt, xmlNodePtr tree); 113 114 115/************************************************************************ 116 * * 117 * XInclude error handler * 118 * * 119 ************************************************************************/ 120 121/** 122 * xmlXIncludeErrMemory: 123 * @extra: extra information 124 * 125 * Handle an out of memory condition 126 */ 127static void 128xmlXIncludeErrMemory(xmlXIncludeCtxtPtr ctxt, xmlNodePtr node, 129 const char *extra) 130{ 131 if (ctxt != NULL) 132 ctxt->nbErrors++; 133 __xmlRaiseError(NULL, NULL, NULL, ctxt, node, XML_FROM_XINCLUDE, 134 XML_ERR_NO_MEMORY, XML_ERR_ERROR, NULL, 0, 135 extra, NULL, NULL, 0, 0, 136 "Memory allocation failed : %s\n", extra); 137} 138 139/** 140 * xmlXIncludeErr: 141 * @ctxt: the XInclude context 142 * @node: the context node 143 * @msg: the error message 144 * @extra: extra information 145 * 146 * Handle an XInclude error 147 */ 148static void LIBXML_ATTR_FORMAT(4,0) 149xmlXIncludeErr(xmlXIncludeCtxtPtr ctxt, xmlNodePtr node, int error, 150 const char *msg, const xmlChar *extra) 151{ 152 if (ctxt != NULL) 153 ctxt->nbErrors++; 154 __xmlRaiseError(NULL, NULL, NULL, ctxt, node, XML_FROM_XINCLUDE, 155 error, XML_ERR_ERROR, NULL, 0, 156 (const char *) extra, NULL, NULL, 0, 0, 157 msg, (const char *) extra); 158} 159 160#if 0 161/** 162 * xmlXIncludeWarn: 163 * @ctxt: the XInclude context 164 * @node: the context node 165 * @msg: the error message 166 * @extra: extra information 167 * 168 * Emit an XInclude warning. 169 */ 170static void LIBXML_ATTR_FORMAT(4,0) 171xmlXIncludeWarn(xmlXIncludeCtxtPtr ctxt, xmlNodePtr node, int error, 172 const char *msg, const xmlChar *extra) 173{ 174 __xmlRaiseError(NULL, NULL, NULL, ctxt, node, XML_FROM_XINCLUDE, 175 error, XML_ERR_WARNING, NULL, 0, 176 (const char *) extra, NULL, NULL, 0, 0, 177 msg, (const char *) extra); 178} 179#endif 180 181/** 182 * xmlXIncludeGetProp: 183 * @ctxt: the XInclude context 184 * @cur: the node 185 * @name: the attribute name 186 * 187 * Get an XInclude attribute 188 * 189 * Returns the value (to be freed) or NULL if not found 190 */ 191static xmlChar * 192xmlXIncludeGetProp(xmlXIncludeCtxtPtr ctxt, xmlNodePtr cur, 193 const xmlChar *name) { 194 xmlChar *ret; 195 196 ret = xmlGetNsProp(cur, XINCLUDE_NS, name); 197 if (ret != NULL) 198 return(ret); 199 if (ctxt->legacy != 0) { 200 ret = xmlGetNsProp(cur, XINCLUDE_OLD_NS, name); 201 if (ret != NULL) 202 return(ret); 203 } 204 ret = xmlGetProp(cur, name); 205 return(ret); 206} 207/** 208 * xmlXIncludeFreeRef: 209 * @ref: the XInclude reference 210 * 211 * Free an XInclude reference 212 */ 213static void 214xmlXIncludeFreeRef(xmlXIncludeRefPtr ref) { 215 if (ref == NULL) 216 return; 217 if (ref->URI != NULL) 218 xmlFree(ref->URI); 219 if (ref->fragment != NULL) 220 xmlFree(ref->fragment); 221 xmlFree(ref); 222} 223 224/** 225 * xmlXIncludeNewRef: 226 * @ctxt: the XInclude context 227 * @URI: the resource URI 228 * @elem: the xi:include element 229 * 230 * Creates a new reference within an XInclude context 231 * 232 * Returns the new set 233 */ 234static xmlXIncludeRefPtr 235xmlXIncludeNewRef(xmlXIncludeCtxtPtr ctxt, const xmlChar *URI, 236 xmlNodePtr elem) { 237 xmlXIncludeRefPtr ret; 238 239 ret = (xmlXIncludeRefPtr) xmlMalloc(sizeof(xmlXIncludeRef)); 240 if (ret == NULL) { 241 xmlXIncludeErrMemory(ctxt, elem, "growing XInclude context"); 242 return(NULL); 243 } 244 memset(ret, 0, sizeof(xmlXIncludeRef)); 245 if (URI == NULL) 246 ret->URI = NULL; 247 else 248 ret->URI = xmlStrdup(URI); 249 ret->fragment = NULL; 250 ret->elem = elem; 251 ret->xml = 0; 252 ret->inc = NULL; 253 if (ctxt->incNr >= ctxt->incMax) { 254 xmlXIncludeRefPtr *tmp; 255#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION 256 size_t newSize = ctxt->incMax ? ctxt->incMax * 2 : 1; 257#else 258 size_t newSize = ctxt->incMax ? ctxt->incMax * 2 : 4; 259#endif 260 261 tmp = (xmlXIncludeRefPtr *) xmlRealloc(ctxt->incTab, 262 newSize * sizeof(ctxt->incTab[0])); 263 if (tmp == NULL) { 264 xmlXIncludeErrMemory(ctxt, elem, "growing XInclude context"); 265 xmlXIncludeFreeRef(ret); 266 return(NULL); 267 } 268 ctxt->incTab = tmp; 269 ctxt->incMax = newSize; 270 } 271 ctxt->incTab[ctxt->incNr++] = ret; 272 return(ret); 273} 274 275/** 276 * xmlXIncludeNewContext: 277 * @doc: an XML Document 278 * 279 * Creates a new XInclude context 280 * 281 * Returns the new set 282 */ 283xmlXIncludeCtxtPtr 284xmlXIncludeNewContext(xmlDocPtr doc) { 285 xmlXIncludeCtxtPtr ret; 286 287 if (doc == NULL) 288 return(NULL); 289 ret = (xmlXIncludeCtxtPtr) xmlMalloc(sizeof(xmlXIncludeCtxt)); 290 if (ret == NULL) { 291 xmlXIncludeErrMemory(NULL, (xmlNodePtr) doc, 292 "creating XInclude context"); 293 return(NULL); 294 } 295 memset(ret, 0, sizeof(xmlXIncludeCtxt)); 296 ret->doc = doc; 297 ret->incNr = 0; 298 ret->incMax = 0; 299 ret->incTab = NULL; 300 ret->nbErrors = 0; 301 return(ret); 302} 303 304/** 305 * xmlXIncludeFreeContext: 306 * @ctxt: the XInclude context 307 * 308 * Free an XInclude context 309 */ 310void 311xmlXIncludeFreeContext(xmlXIncludeCtxtPtr ctxt) { 312 int i; 313 314 if (ctxt == NULL) 315 return; 316 if (ctxt->urlTab != NULL) { 317 for (i = 0; i < ctxt->urlNr; i++) { 318 xmlFreeDoc(ctxt->urlTab[i].doc); 319 xmlFree(ctxt->urlTab[i].url); 320 } 321 xmlFree(ctxt->urlTab); 322 } 323 for (i = 0;i < ctxt->incNr;i++) { 324 if (ctxt->incTab[i] != NULL) 325 xmlXIncludeFreeRef(ctxt->incTab[i]); 326 } 327 if (ctxt->incTab != NULL) 328 xmlFree(ctxt->incTab); 329 if (ctxt->txtTab != NULL) { 330 for (i = 0;i < ctxt->txtNr;i++) { 331 xmlFree(ctxt->txtTab[i].text); 332 xmlFree(ctxt->txtTab[i].url); 333 } 334 xmlFree(ctxt->txtTab); 335 } 336 if (ctxt->base != NULL) { 337 xmlFree(ctxt->base); 338 } 339 xmlFree(ctxt); 340} 341 342/** 343 * xmlXIncludeParseFile: 344 * @ctxt: the XInclude context 345 * @URL: the URL or file path 346 * 347 * parse a document for XInclude 348 */ 349static xmlDocPtr 350xmlXIncludeParseFile(xmlXIncludeCtxtPtr ctxt, const char *URL) { 351 xmlDocPtr ret; 352 xmlParserCtxtPtr pctxt; 353 xmlParserInputPtr inputStream; 354 355 xmlInitParser(); 356 357 pctxt = xmlNewParserCtxt(); 358 if (pctxt == NULL) { 359 xmlXIncludeErrMemory(ctxt, NULL, "cannot allocate parser context"); 360 return(NULL); 361 } 362 363 /* 364 * pass in the application data to the parser context. 365 */ 366 pctxt->_private = ctxt->_private; 367 368 /* 369 * try to ensure that new documents included are actually 370 * built with the same dictionary as the including document. 371 */ 372 if ((ctxt->doc != NULL) && (ctxt->doc->dict != NULL)) { 373 if (pctxt->dict != NULL) 374 xmlDictFree(pctxt->dict); 375 pctxt->dict = ctxt->doc->dict; 376 xmlDictReference(pctxt->dict); 377 } 378 379 xmlCtxtUseOptions(pctxt, ctxt->parseFlags | XML_PARSE_DTDLOAD); 380 381 /* Don't read from stdin. */ 382 if ((URL != NULL) && (strcmp(URL, "-") == 0)) 383 URL = "./-"; 384 385 inputStream = xmlLoadExternalEntity(URL, NULL, pctxt); 386 if (inputStream == NULL) { 387 xmlFreeParserCtxt(pctxt); 388 return(NULL); 389 } 390 391 inputPush(pctxt, inputStream); 392 393 if (pctxt->directory == NULL) 394 pctxt->directory = xmlParserGetDirectory(URL); 395 396 pctxt->loadsubset |= XML_DETECT_IDS; 397 398 xmlParseDocument(pctxt); 399 400 if (pctxt->wellFormed) { 401 ret = pctxt->myDoc; 402 } 403 else { 404 ret = NULL; 405 if (pctxt->myDoc != NULL) 406 xmlFreeDoc(pctxt->myDoc); 407 pctxt->myDoc = NULL; 408 } 409 xmlFreeParserCtxt(pctxt); 410 411 return(ret); 412} 413 414/** 415 * xmlXIncludeAddNode: 416 * @ctxt: the XInclude context 417 * @cur: the new node 418 * 419 * Add a new node to process to an XInclude context 420 */ 421static xmlXIncludeRefPtr 422xmlXIncludeAddNode(xmlXIncludeCtxtPtr ctxt, xmlNodePtr cur) { 423 xmlXIncludeRefPtr ref; 424 xmlURIPtr uri; 425 xmlChar *URL; 426 xmlChar *fragment = NULL; 427 xmlChar *href; 428 xmlChar *parse; 429 xmlChar *base; 430 xmlChar *URI; 431 int xml = 1; 432 int local = 0; 433 434 435 if (ctxt == NULL) 436 return(NULL); 437 if (cur == NULL) 438 return(NULL); 439 440 /* 441 * read the attributes 442 */ 443 href = xmlXIncludeGetProp(ctxt, cur, XINCLUDE_HREF); 444 if (href == NULL) { 445 href = xmlStrdup(BAD_CAST ""); /* @@@@ href is now optional */ 446 if (href == NULL) 447 return(NULL); 448 } 449 parse = xmlXIncludeGetProp(ctxt, cur, XINCLUDE_PARSE); 450 if (parse != NULL) { 451 if (xmlStrEqual(parse, XINCLUDE_PARSE_XML)) 452 xml = 1; 453 else if (xmlStrEqual(parse, XINCLUDE_PARSE_TEXT)) 454 xml = 0; 455 else { 456 xmlXIncludeErr(ctxt, cur, XML_XINCLUDE_PARSE_VALUE, 457 "invalid value %s for 'parse'\n", parse); 458 if (href != NULL) 459 xmlFree(href); 460 if (parse != NULL) 461 xmlFree(parse); 462 return(NULL); 463 } 464 } 465 466 /* 467 * compute the URI 468 */ 469 base = xmlNodeGetBase(ctxt->doc, cur); 470 if (base == NULL) { 471 URI = xmlBuildURI(href, ctxt->doc->URL); 472 } else { 473 URI = xmlBuildURI(href, base); 474 } 475 if (URI == NULL) { 476 xmlChar *escbase; 477 xmlChar *eschref; 478 /* 479 * Some escaping may be needed 480 */ 481 escbase = xmlURIEscape(base); 482 eschref = xmlURIEscape(href); 483 URI = xmlBuildURI(eschref, escbase); 484 if (escbase != NULL) 485 xmlFree(escbase); 486 if (eschref != NULL) 487 xmlFree(eschref); 488 } 489 if (parse != NULL) 490 xmlFree(parse); 491 if (href != NULL) 492 xmlFree(href); 493 if (base != NULL) 494 xmlFree(base); 495 if (URI == NULL) { 496 xmlXIncludeErr(ctxt, cur, XML_XINCLUDE_HREF_URI, 497 "failed build URL\n", NULL); 498 return(NULL); 499 } 500 fragment = xmlXIncludeGetProp(ctxt, cur, XINCLUDE_PARSE_XPOINTER); 501 502 /* 503 * Check the URL and remove any fragment identifier 504 */ 505 uri = xmlParseURI((const char *)URI); 506 if (uri == NULL) { 507 xmlXIncludeErr(ctxt, cur, XML_XINCLUDE_HREF_URI, 508 "invalid value URI %s\n", URI); 509 if (fragment != NULL) 510 xmlFree(fragment); 511 xmlFree(URI); 512 return(NULL); 513 } 514 515 if (uri->fragment != NULL) { 516 if (ctxt->legacy != 0) { 517 if (fragment == NULL) { 518 fragment = (xmlChar *) uri->fragment; 519 } else { 520 xmlFree(uri->fragment); 521 } 522 } else { 523 xmlXIncludeErr(ctxt, cur, XML_XINCLUDE_FRAGMENT_ID, 524 "Invalid fragment identifier in URI %s use the xpointer attribute\n", 525 URI); 526 if (fragment != NULL) 527 xmlFree(fragment); 528 xmlFreeURI(uri); 529 xmlFree(URI); 530 return(NULL); 531 } 532 uri->fragment = NULL; 533 } 534 URL = xmlSaveUri(uri); 535 xmlFreeURI(uri); 536 if (URL == NULL) { 537 xmlXIncludeErr(ctxt, cur, XML_XINCLUDE_HREF_URI, 538 "invalid value URI %s\n", URI); 539 if (fragment != NULL) 540 xmlFree(fragment); 541 xmlFree(URI); 542 return(NULL); 543 } 544 xmlFree(URI); 545 546 if (xmlStrEqual(URL, ctxt->doc->URL)) 547 local = 1; 548 549 /* 550 * If local and xml then we need a fragment 551 */ 552 if ((local == 1) && (xml == 1) && 553 ((fragment == NULL) || (fragment[0] == 0))) { 554 xmlXIncludeErr(ctxt, cur, XML_XINCLUDE_RECURSION, 555 "detected a local recursion with no xpointer in %s\n", 556 URL); 557 xmlFree(URL); 558 xmlFree(fragment); 559 return(NULL); 560 } 561 562 ref = xmlXIncludeNewRef(ctxt, URL, cur); 563 xmlFree(URL); 564 if (ref == NULL) { 565 xmlFree(fragment); 566 return(NULL); 567 } 568 ref->fragment = fragment; 569 ref->xml = xml; 570 return(ref); 571} 572 573/** 574 * xmlXIncludeRecurseDoc: 575 * @ctxt: the XInclude context 576 * @doc: the new document 577 * @url: the associated URL 578 * 579 * The XInclude recursive nature is handled at this point. 580 */ 581static void 582xmlXIncludeRecurseDoc(xmlXIncludeCtxtPtr ctxt, xmlDocPtr doc, 583 const xmlURL url ATTRIBUTE_UNUSED) { 584 xmlDocPtr oldDoc; 585 xmlXIncludeRefPtr *oldIncTab; 586 int oldIncMax, oldIncNr, oldIsStream; 587 int i; 588 589 oldDoc = ctxt->doc; 590 oldIncMax = ctxt->incMax; 591 oldIncNr = ctxt->incNr; 592 oldIncTab = ctxt->incTab; 593 oldIsStream = ctxt->isStream; 594 ctxt->doc = doc; 595 ctxt->incMax = 0; 596 ctxt->incNr = 0; 597 ctxt->incTab = NULL; 598 ctxt->isStream = 0; 599 600 xmlXIncludeDoProcess(ctxt, xmlDocGetRootElement(doc)); 601 602 if (ctxt->incTab != NULL) { 603 for (i = 0; i < ctxt->incNr; i++) 604 xmlXIncludeFreeRef(ctxt->incTab[i]); 605 xmlFree(ctxt->incTab); 606 } 607 608 ctxt->doc = oldDoc; 609 ctxt->incMax = oldIncMax; 610 ctxt->incNr = oldIncNr; 611 ctxt->incTab = oldIncTab; 612 ctxt->isStream = oldIsStream; 613} 614 615/************************************************************************ 616 * * 617 * Node copy with specific semantic * 618 * * 619 ************************************************************************/ 620 621/** 622 * xmlXIncludeCopyNode: 623 * @ctxt: the XInclude context 624 * @elem: the element 625 * @copyChildren: copy children instead of node if true 626 * 627 * Make a copy of the node while expanding nested XIncludes. 628 * 629 * Returns a node list, not a single node. 630 */ 631static xmlNodePtr 632xmlXIncludeCopyNode(xmlXIncludeCtxtPtr ctxt, xmlNodePtr elem, 633 int copyChildren) { 634 xmlNodePtr result = NULL; 635 xmlNodePtr insertParent = NULL; 636 xmlNodePtr insertLast = NULL; 637 xmlNodePtr cur; 638 639 if (copyChildren) { 640 cur = elem->children; 641 if (cur == NULL) 642 return(NULL); 643 } else { 644 cur = elem; 645 } 646 647 while (1) { 648 xmlNodePtr copy = NULL; 649 int recurse = 0; 650 651 if ((cur->type == XML_DOCUMENT_NODE) || 652 (cur->type == XML_DTD_NODE)) { 653 ; 654 } else if ((cur->type == XML_ELEMENT_NODE) && 655 (cur->ns != NULL) && 656 (xmlStrEqual(cur->name, XINCLUDE_NODE)) && 657 ((xmlStrEqual(cur->ns->href, XINCLUDE_NS)) || 658 (xmlStrEqual(cur->ns->href, XINCLUDE_OLD_NS)))) { 659 xmlXIncludeRefPtr ref = xmlXIncludeExpandNode(ctxt, cur); 660 661 if (ref == NULL) 662 goto error; 663 /* 664 * TODO: Insert XML_XINCLUDE_START and XML_XINCLUDE_END nodes 665 */ 666 if (ref->inc != NULL) { 667 copy = xmlStaticCopyNodeList(ref->inc, ctxt->doc, 668 insertParent); 669 if (copy == NULL) 670 goto error; 671 } 672 } else { 673 copy = xmlStaticCopyNode(cur, ctxt->doc, insertParent, 2); 674 if (copy == NULL) 675 goto error; 676 677 recurse = (cur->type != XML_ENTITY_REF_NODE) && 678 (cur->children != NULL); 679 } 680 681 if (copy != NULL) { 682 if (result == NULL) 683 result = copy; 684 if (insertLast != NULL) { 685 insertLast->next = copy; 686 copy->prev = insertLast; 687 } else if (insertParent != NULL) { 688 insertParent->children = copy; 689 } 690 insertLast = copy; 691 while (insertLast->next != NULL) { 692 insertLast = insertLast->next; 693 } 694 } 695 696 if (recurse) { 697 cur = cur->children; 698 insertParent = insertLast; 699 insertLast = NULL; 700 continue; 701 } 702 703 if (cur == elem) 704 return(result); 705 706 while (cur->next == NULL) { 707 if (insertParent != NULL) 708 insertParent->last = insertLast; 709 cur = cur->parent; 710 if (cur == elem) 711 return(result); 712 insertLast = insertParent; 713 insertParent = insertParent->parent; 714 } 715 716 cur = cur->next; 717 } 718 719error: 720 xmlFreeNodeList(result); 721 return(NULL); 722} 723 724#ifdef LIBXML_XPTR_LOCS_ENABLED 725/** 726 * xmlXIncludeGetNthChild: 727 * @cur: the node 728 * @no: the child number 729 * 730 * Returns the @n'th element child of @cur or NULL 731 */ 732static xmlNodePtr 733xmlXIncludeGetNthChild(xmlNodePtr cur, int no) { 734 int i; 735 if ((cur == NULL) || (cur->type == XML_NAMESPACE_DECL)) 736 return(NULL); 737 cur = cur->children; 738 for (i = 0;i <= no;cur = cur->next) { 739 if (cur == NULL) 740 return(cur); 741 if ((cur->type == XML_ELEMENT_NODE) || 742 (cur->type == XML_DOCUMENT_NODE) || 743 (cur->type == XML_HTML_DOCUMENT_NODE)) { 744 i++; 745 if (i == no) 746 break; 747 } 748 } 749 return(cur); 750} 751 752xmlNodePtr xmlXPtrAdvanceNode(xmlNodePtr cur, int *level); /* in xpointer.c */ 753/** 754 * xmlXIncludeCopyRange: 755 * @ctxt: the XInclude context 756 * @obj: the XPointer result from the evaluation. 757 * 758 * Build a node list tree copy of the XPointer result. 759 * 760 * Returns an xmlNodePtr list or NULL. 761 * The caller has to free the node tree. 762 */ 763static xmlNodePtr 764xmlXIncludeCopyRange(xmlXIncludeCtxtPtr ctxt, xmlXPathObjectPtr range) { 765 /* pointers to generated nodes */ 766 xmlNodePtr list = NULL, last = NULL, listParent = NULL; 767 xmlNodePtr tmp, tmp2; 768 /* pointers to traversal nodes */ 769 xmlNodePtr start, cur, end; 770 int index1, index2; 771 int level = 0, lastLevel = 0, endLevel = 0, endFlag = 0; 772 773 if ((ctxt == NULL) || (range == NULL)) 774 return(NULL); 775 if (range->type != XPATH_RANGE) 776 return(NULL); 777 start = (xmlNodePtr) range->user; 778 779 if ((start == NULL) || (start->type == XML_NAMESPACE_DECL)) 780 return(NULL); 781 end = range->user2; 782 if (end == NULL) 783 return(xmlDocCopyNode(start, ctxt->doc, 1)); 784 if (end->type == XML_NAMESPACE_DECL) 785 return(NULL); 786 787 cur = start; 788 index1 = range->index; 789 index2 = range->index2; 790 /* 791 * level is depth of the current node under consideration 792 * list is the pointer to the root of the output tree 793 * listParent is a pointer to the parent of output tree (within 794 the included file) in case we need to add another level 795 * last is a pointer to the last node added to the output tree 796 * lastLevel is the depth of last (relative to the root) 797 */ 798 while (cur != NULL) { 799 /* 800 * Check if our output tree needs a parent 801 */ 802 if (level < 0) { 803 while (level < 0) { 804 /* copy must include namespaces and properties */ 805 tmp2 = xmlDocCopyNode(listParent, ctxt->doc, 2); 806 xmlAddChild(tmp2, list); 807 list = tmp2; 808 listParent = listParent->parent; 809 level++; 810 } 811 last = list; 812 lastLevel = 0; 813 } 814 /* 815 * Check whether we need to change our insertion point 816 */ 817 while (level < lastLevel) { 818 last = last->parent; 819 lastLevel --; 820 } 821 if (cur == end) { /* Are we at the end of the range? */ 822 if (cur->type == XML_TEXT_NODE) { 823 const xmlChar *content = cur->content; 824 int len; 825 826 if (content == NULL) { 827 tmp = xmlNewDocTextLen(ctxt->doc, NULL, 0); 828 } else { 829 len = index2; 830 if ((cur == start) && (index1 > 1)) { 831 content += (index1 - 1); 832 len -= (index1 - 1); 833 } else { 834 len = index2; 835 } 836 tmp = xmlNewDocTextLen(ctxt->doc, content, len); 837 } 838 /* single sub text node selection */ 839 if (list == NULL) 840 return(tmp); 841 /* prune and return full set */ 842 if (level == lastLevel) 843 xmlAddNextSibling(last, tmp); 844 else 845 xmlAddChild(last, tmp); 846 return(list); 847 } else { /* ending node not a text node */ 848 endLevel = level; /* remember the level of the end node */ 849 endFlag = 1; 850 /* last node - need to take care of properties + namespaces */ 851 tmp = xmlDocCopyNode(cur, ctxt->doc, 2); 852 if (list == NULL) { 853 list = tmp; 854 listParent = cur->parent; 855 last = tmp; 856 } else { 857 if (level == lastLevel) 858 last = xmlAddNextSibling(last, tmp); 859 else { 860 last = xmlAddChild(last, tmp); 861 lastLevel = level; 862 } 863 } 864 865 if (index2 > 1) { 866 end = xmlXIncludeGetNthChild(cur, index2 - 1); 867 index2 = 0; 868 } 869 if ((cur == start) && (index1 > 1)) { 870 cur = xmlXIncludeGetNthChild(cur, index1 - 1); 871 index1 = 0; 872 } else { 873 cur = cur->children; 874 } 875 level++; /* increment level to show change */ 876 /* 877 * Now gather the remaining nodes from cur to end 878 */ 879 continue; /* while */ 880 } 881 } else if (cur == start) { /* Not at the end, are we at start? */ 882 if ((cur->type == XML_TEXT_NODE) || 883 (cur->type == XML_CDATA_SECTION_NODE)) { 884 const xmlChar *content = cur->content; 885 886 if (content == NULL) { 887 tmp = xmlNewDocTextLen(ctxt->doc, NULL, 0); 888 } else { 889 if (index1 > 1) { 890 content += (index1 - 1); 891 index1 = 0; 892 } 893 tmp = xmlNewDocText(ctxt->doc, content); 894 } 895 last = list = tmp; 896 listParent = cur->parent; 897 } else { /* Not text node */ 898 /* 899 * start of the range - need to take care of 900 * properties and namespaces 901 */ 902 tmp = xmlDocCopyNode(cur, ctxt->doc, 2); 903 list = last = tmp; 904 listParent = cur->parent; 905 if (index1 > 1) { /* Do we need to position? */ 906 cur = xmlXIncludeGetNthChild(cur, index1 - 1); 907 level = lastLevel = 1; 908 index1 = 0; 909 /* 910 * Now gather the remaining nodes from cur to end 911 */ 912 continue; /* while */ 913 } 914 } 915 } else { 916 tmp = NULL; 917 switch (cur->type) { 918 case XML_DTD_NODE: 919 case XML_ELEMENT_DECL: 920 case XML_ATTRIBUTE_DECL: 921 case XML_ENTITY_NODE: 922 /* Do not copy DTD information */ 923 break; 924 case XML_ENTITY_DECL: 925 /* handle crossing entities -> stack needed */ 926 break; 927 case XML_XINCLUDE_START: 928 case XML_XINCLUDE_END: 929 /* don't consider it part of the tree content */ 930 break; 931 case XML_ATTRIBUTE_NODE: 932 /* Humm, should not happen ! */ 933 break; 934 default: 935 /* 936 * Middle of the range - need to take care of 937 * properties and namespaces 938 */ 939 tmp = xmlDocCopyNode(cur, ctxt->doc, 2); 940 break; 941 } 942 if (tmp != NULL) { 943 if (level == lastLevel) 944 last = xmlAddNextSibling(last, tmp); 945 else { 946 last = xmlAddChild(last, tmp); 947 lastLevel = level; 948 } 949 } 950 } 951 /* 952 * Skip to next node in document order 953 */ 954 cur = xmlXPtrAdvanceNode(cur, &level); 955 if (endFlag && (level >= endLevel)) 956 break; 957 } 958 return(list); 959} 960#endif /* LIBXML_XPTR_LOCS_ENABLED */ 961 962/** 963 * xmlXIncludeCopyXPointer: 964 * @ctxt: the XInclude context 965 * @obj: the XPointer result from the evaluation. 966 * 967 * Build a node list tree copy of the XPointer result. 968 * This will drop Attributes and Namespace declarations. 969 * 970 * Returns an xmlNodePtr list or NULL. 971 * the caller has to free the node tree. 972 */ 973static xmlNodePtr 974xmlXIncludeCopyXPointer(xmlXIncludeCtxtPtr ctxt, xmlXPathObjectPtr obj) { 975 xmlNodePtr list = NULL, last = NULL, copy; 976 int i; 977 978 if ((ctxt == NULL) || (obj == NULL)) 979 return(NULL); 980 switch (obj->type) { 981 case XPATH_NODESET: { 982 xmlNodeSetPtr set = obj->nodesetval; 983 if (set == NULL) 984 return(NULL); 985 for (i = 0;i < set->nodeNr;i++) { 986 xmlNodePtr node; 987 988 if (set->nodeTab[i] == NULL) 989 continue; 990 switch (set->nodeTab[i]->type) { 991 case XML_DOCUMENT_NODE: 992 case XML_HTML_DOCUMENT_NODE: 993 node = xmlDocGetRootElement( 994 (xmlDocPtr) set->nodeTab[i]); 995 if (node == NULL) { 996 xmlXIncludeErr(ctxt, set->nodeTab[i], 997 XML_ERR_INTERNAL_ERROR, 998 "document without root\n", NULL); 999 continue; 1000 } 1001 break; 1002 case XML_TEXT_NODE: 1003 case XML_CDATA_SECTION_NODE: 1004 case XML_ELEMENT_NODE: 1005 case XML_PI_NODE: 1006 case XML_COMMENT_NODE: 1007 node = set->nodeTab[i]; 1008 break; 1009 default: 1010 xmlXIncludeErr(ctxt, set->nodeTab[i], 1011 XML_XINCLUDE_XPTR_RESULT, 1012 "invalid node type in XPtr result\n", 1013 NULL); 1014 continue; /* for */ 1015 } 1016 /* 1017 * OPTIMIZE TODO: External documents should already be 1018 * expanded, so xmlDocCopyNode should work as well. 1019 * xmlXIncludeCopyNode is only required for the initial 1020 * document. 1021 */ 1022 copy = xmlXIncludeCopyNode(ctxt, node, 0); 1023 if (copy == NULL) { 1024 xmlFreeNodeList(list); 1025 return(NULL); 1026 } 1027 if (last == NULL) { 1028 list = copy; 1029 } else { 1030 while (last->next != NULL) 1031 last = last->next; 1032 copy->prev = last; 1033 last->next = copy; 1034 } 1035 last = copy; 1036 } 1037 break; 1038 } 1039#ifdef LIBXML_XPTR_LOCS_ENABLED 1040 case XPATH_LOCATIONSET: { 1041 xmlLocationSetPtr set = (xmlLocationSetPtr) obj->user; 1042 if (set == NULL) 1043 return(NULL); 1044 for (i = 0;i < set->locNr;i++) { 1045 if (last == NULL) 1046 list = last = xmlXIncludeCopyXPointer(ctxt, 1047 set->locTab[i]); 1048 else 1049 xmlAddNextSibling(last, 1050 xmlXIncludeCopyXPointer(ctxt, set->locTab[i])); 1051 if (last != NULL) { 1052 while (last->next != NULL) 1053 last = last->next; 1054 } 1055 } 1056 break; 1057 } 1058 case XPATH_RANGE: 1059 return(xmlXIncludeCopyRange(ctxt, obj)); 1060 case XPATH_POINT: 1061 /* points are ignored in XInclude */ 1062 break; 1063#endif 1064 default: 1065 break; 1066 } 1067 return(list); 1068} 1069/************************************************************************ 1070 * * 1071 * XInclude I/O handling * 1072 * * 1073 ************************************************************************/ 1074 1075typedef struct _xmlXIncludeMergeData xmlXIncludeMergeData; 1076typedef xmlXIncludeMergeData *xmlXIncludeMergeDataPtr; 1077struct _xmlXIncludeMergeData { 1078 xmlDocPtr doc; 1079 xmlXIncludeCtxtPtr ctxt; 1080}; 1081 1082/** 1083 * xmlXIncludeMergeOneEntity: 1084 * @ent: the entity 1085 * @doc: the including doc 1086 * @name: the entity name 1087 * 1088 * Implements the merge of one entity 1089 */ 1090static void 1091xmlXIncludeMergeEntity(void *payload, void *vdata, 1092 const xmlChar *name ATTRIBUTE_UNUSED) { 1093 xmlEntityPtr ent = (xmlEntityPtr) payload; 1094 xmlXIncludeMergeDataPtr data = (xmlXIncludeMergeDataPtr) vdata; 1095 xmlEntityPtr ret, prev; 1096 xmlDocPtr doc; 1097 xmlXIncludeCtxtPtr ctxt; 1098 1099 if ((ent == NULL) || (data == NULL)) 1100 return; 1101 ctxt = data->ctxt; 1102 doc = data->doc; 1103 if ((ctxt == NULL) || (doc == NULL)) 1104 return; 1105 switch (ent->etype) { 1106 case XML_INTERNAL_PARAMETER_ENTITY: 1107 case XML_EXTERNAL_PARAMETER_ENTITY: 1108 case XML_INTERNAL_PREDEFINED_ENTITY: 1109 return; 1110 case XML_INTERNAL_GENERAL_ENTITY: 1111 case XML_EXTERNAL_GENERAL_PARSED_ENTITY: 1112 case XML_EXTERNAL_GENERAL_UNPARSED_ENTITY: 1113 break; 1114 } 1115 ret = xmlAddDocEntity(doc, ent->name, ent->etype, ent->ExternalID, 1116 ent->SystemID, ent->content); 1117 if (ret != NULL) { 1118 if (ent->URI != NULL) 1119 ret->URI = xmlStrdup(ent->URI); 1120 } else { 1121 prev = xmlGetDocEntity(doc, ent->name); 1122 if (prev != NULL) { 1123 if (ent->etype != prev->etype) 1124 goto error; 1125 1126 if ((ent->SystemID != NULL) && (prev->SystemID != NULL)) { 1127 if (!xmlStrEqual(ent->SystemID, prev->SystemID)) 1128 goto error; 1129 } else if ((ent->ExternalID != NULL) && 1130 (prev->ExternalID != NULL)) { 1131 if (!xmlStrEqual(ent->ExternalID, prev->ExternalID)) 1132 goto error; 1133 } else if ((ent->content != NULL) && (prev->content != NULL)) { 1134 if (!xmlStrEqual(ent->content, prev->content)) 1135 goto error; 1136 } else { 1137 goto error; 1138 } 1139 1140 } 1141 } 1142 return; 1143error: 1144 switch (ent->etype) { 1145 case XML_INTERNAL_PARAMETER_ENTITY: 1146 case XML_EXTERNAL_PARAMETER_ENTITY: 1147 case XML_INTERNAL_PREDEFINED_ENTITY: 1148 case XML_INTERNAL_GENERAL_ENTITY: 1149 case XML_EXTERNAL_GENERAL_PARSED_ENTITY: 1150 return; 1151 case XML_EXTERNAL_GENERAL_UNPARSED_ENTITY: 1152 break; 1153 } 1154 xmlXIncludeErr(ctxt, (xmlNodePtr) ent, XML_XINCLUDE_ENTITY_DEF_MISMATCH, 1155 "mismatch in redefinition of entity %s\n", 1156 ent->name); 1157} 1158 1159/** 1160 * xmlXIncludeMergeEntities: 1161 * @ctxt: an XInclude context 1162 * @doc: the including doc 1163 * @from: the included doc 1164 * 1165 * Implements the entity merge 1166 * 1167 * Returns 0 if merge succeeded, -1 if some processing failed 1168 */ 1169static int 1170xmlXIncludeMergeEntities(xmlXIncludeCtxtPtr ctxt, xmlDocPtr doc, 1171 xmlDocPtr from) { 1172 xmlNodePtr cur; 1173 xmlDtdPtr target, source; 1174 1175 if (ctxt == NULL) 1176 return(-1); 1177 1178 if ((from == NULL) || (from->intSubset == NULL)) 1179 return(0); 1180 1181 target = doc->intSubset; 1182 if (target == NULL) { 1183 cur = xmlDocGetRootElement(doc); 1184 if (cur == NULL) 1185 return(-1); 1186 target = xmlCreateIntSubset(doc, cur->name, NULL, NULL); 1187 if (target == NULL) 1188 return(-1); 1189 } 1190 1191 source = from->intSubset; 1192 if ((source != NULL) && (source->entities != NULL)) { 1193 xmlXIncludeMergeData data; 1194 1195 data.ctxt = ctxt; 1196 data.doc = doc; 1197 1198 xmlHashScan((xmlHashTablePtr) source->entities, 1199 xmlXIncludeMergeEntity, &data); 1200 } 1201 source = from->extSubset; 1202 if ((source != NULL) && (source->entities != NULL)) { 1203 xmlXIncludeMergeData data; 1204 1205 data.ctxt = ctxt; 1206 data.doc = doc; 1207 1208 /* 1209 * don't duplicate existing stuff when external subsets are the same 1210 */ 1211 if ((!xmlStrEqual(target->ExternalID, source->ExternalID)) && 1212 (!xmlStrEqual(target->SystemID, source->SystemID))) { 1213 xmlHashScan((xmlHashTablePtr) source->entities, 1214 xmlXIncludeMergeEntity, &data); 1215 } 1216 } 1217 return(0); 1218} 1219 1220/** 1221 * xmlXIncludeLoadDoc: 1222 * @ctxt: the XInclude context 1223 * @url: the associated URL 1224 * @ref: an XMLXincludeRefPtr 1225 * 1226 * Load the document, and store the result in the XInclude context 1227 * 1228 * Returns 0 in case of success, -1 in case of failure 1229 */ 1230static int 1231xmlXIncludeLoadDoc(xmlXIncludeCtxtPtr ctxt, const xmlChar *url, 1232 xmlXIncludeRefPtr ref) { 1233 xmlXIncludeDocPtr cache; 1234 xmlDocPtr doc; 1235 xmlURIPtr uri; 1236 xmlChar *URL = NULL; 1237 xmlChar *fragment = NULL; 1238 int i = 0; 1239 int ret = -1; 1240 int cacheNr; 1241#ifdef LIBXML_XPTR_ENABLED 1242 int saveFlags; 1243#endif 1244 1245 /* 1246 * Check the URL and remove any fragment identifier 1247 */ 1248 uri = xmlParseURI((const char *)url); 1249 if (uri == NULL) { 1250 xmlXIncludeErr(ctxt, ref->elem, XML_XINCLUDE_HREF_URI, 1251 "invalid value URI %s\n", url); 1252 goto error; 1253 } 1254 if (uri->fragment != NULL) { 1255 fragment = (xmlChar *) uri->fragment; 1256 uri->fragment = NULL; 1257 } 1258 if (ref->fragment != NULL) { 1259 if (fragment != NULL) xmlFree(fragment); 1260 fragment = xmlStrdup(ref->fragment); 1261 } 1262 URL = xmlSaveUri(uri); 1263 xmlFreeURI(uri); 1264 if (URL == NULL) { 1265 xmlXIncludeErr(ctxt, ref->elem, XML_XINCLUDE_HREF_URI, 1266 "invalid value URI %s\n", url); 1267 goto error; 1268 } 1269 1270 /* 1271 * Handling of references to the local document are done 1272 * directly through ctxt->doc. 1273 */ 1274 if ((URL[0] == 0) || (URL[0] == '#') || 1275 ((ctxt->doc != NULL) && (xmlStrEqual(URL, ctxt->doc->URL)))) { 1276 doc = ctxt->doc; 1277 goto loaded; 1278 } 1279 1280 /* 1281 * Prevent reloading the document twice. 1282 */ 1283 for (i = 0; i < ctxt->urlNr; i++) { 1284 if (xmlStrEqual(URL, ctxt->urlTab[i].url)) { 1285 if (ctxt->urlTab[i].expanding) { 1286 xmlXIncludeErr(ctxt, ref->elem, XML_XINCLUDE_RECURSION, 1287 "inclusion loop detected\n", NULL); 1288 goto error; 1289 } 1290 doc = ctxt->urlTab[i].doc; 1291 if (doc == NULL) 1292 goto error; 1293 goto loaded; 1294 } 1295 } 1296 1297 /* 1298 * Load it. 1299 */ 1300#ifdef LIBXML_XPTR_ENABLED 1301 /* 1302 * If this is an XPointer evaluation, we want to assure that 1303 * all entities have been resolved prior to processing the 1304 * referenced document 1305 */ 1306 saveFlags = ctxt->parseFlags; 1307 if (fragment != NULL) { /* if this is an XPointer eval */ 1308 ctxt->parseFlags |= XML_PARSE_NOENT; 1309 } 1310#endif 1311 1312 doc = xmlXIncludeParseFile(ctxt, (const char *)URL); 1313#ifdef LIBXML_XPTR_ENABLED 1314 ctxt->parseFlags = saveFlags; 1315#endif 1316 1317 /* Also cache NULL docs */ 1318 if (ctxt->urlNr >= ctxt->urlMax) { 1319 xmlXIncludeDoc *tmp; 1320#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION 1321 size_t newSize = ctxt->urlMax ? ctxt->urlMax * 2 : 1; 1322#else 1323 size_t newSize = ctxt->urlMax ? ctxt->urlMax * 2 : 8; 1324#endif 1325 1326 tmp = xmlRealloc(ctxt->urlTab, sizeof(xmlXIncludeDoc) * newSize); 1327 if (tmp == NULL) { 1328 xmlXIncludeErrMemory(ctxt, ref->elem, 1329 "growing XInclude URL table"); 1330 xmlFreeDoc(doc); 1331 goto error; 1332 } 1333 ctxt->urlMax = newSize; 1334 ctxt->urlTab = tmp; 1335 } 1336 cacheNr = ctxt->urlNr++; 1337 cache = &ctxt->urlTab[cacheNr]; 1338 cache->doc = doc; 1339 cache->url = xmlStrdup(URL); 1340 cache->expanding = 0; 1341 1342 if (doc == NULL) 1343 goto error; 1344 /* 1345 * It's possible that the requested URL has been mapped to a 1346 * completely different location (e.g. through a catalog entry). 1347 * To check for this, we compare the URL with that of the doc 1348 * and change it if they disagree (bug 146988). 1349 */ 1350 if (!xmlStrEqual(URL, doc->URL)) { 1351 xmlFree(URL); 1352 URL = xmlStrdup(doc->URL); 1353 } 1354 1355 /* 1356 * Make sure we have all entities fixed up 1357 */ 1358 xmlXIncludeMergeEntities(ctxt, ctxt->doc, doc); 1359 1360 /* 1361 * We don't need the DTD anymore, free up space 1362 if (doc->intSubset != NULL) { 1363 xmlUnlinkNode((xmlNodePtr) doc->intSubset); 1364 xmlFreeNode((xmlNodePtr) doc->intSubset); 1365 doc->intSubset = NULL; 1366 } 1367 if (doc->extSubset != NULL) { 1368 xmlUnlinkNode((xmlNodePtr) doc->extSubset); 1369 xmlFreeNode((xmlNodePtr) doc->extSubset); 1370 doc->extSubset = NULL; 1371 } 1372 */ 1373 cache->expanding = 1; 1374 xmlXIncludeRecurseDoc(ctxt, doc, URL); 1375 /* urlTab might be reallocated. */ 1376 cache = &ctxt->urlTab[cacheNr]; 1377 cache->expanding = 0; 1378 1379loaded: 1380 if (fragment == NULL) { 1381 /* 1382 * Add the top children list as the replacement copy. 1383 */ 1384 ref->inc = xmlDocCopyNode(xmlDocGetRootElement(doc), ctxt->doc, 1); 1385 } 1386#ifdef LIBXML_XPTR_ENABLED 1387 else { 1388 /* 1389 * Computes the XPointer expression and make a copy used 1390 * as the replacement copy. 1391 */ 1392 xmlXPathObjectPtr xptr; 1393 xmlXPathContextPtr xptrctxt; 1394 xmlNodeSetPtr set; 1395 1396 if (ctxt->isStream && doc == ctxt->doc) { 1397 xmlXIncludeErr(ctxt, ref->elem, XML_XINCLUDE_XPTR_FAILED, 1398 "XPointer expressions not allowed in streaming" 1399 " mode\n", NULL); 1400 goto error; 1401 } 1402 1403 xptrctxt = xmlXPtrNewContext(doc, NULL, NULL); 1404 if (xptrctxt == NULL) { 1405 xmlXIncludeErr(ctxt, ref->elem, XML_XINCLUDE_XPTR_FAILED, 1406 "could not create XPointer context\n", NULL); 1407 goto error; 1408 } 1409 xptr = xmlXPtrEval(fragment, xptrctxt); 1410 if (xptr == NULL) { 1411 xmlXIncludeErr(ctxt, ref->elem, XML_XINCLUDE_XPTR_FAILED, 1412 "XPointer evaluation failed: #%s\n", 1413 fragment); 1414 xmlXPathFreeContext(xptrctxt); 1415 goto error; 1416 } 1417 switch (xptr->type) { 1418 case XPATH_UNDEFINED: 1419 case XPATH_BOOLEAN: 1420 case XPATH_NUMBER: 1421 case XPATH_STRING: 1422#ifdef LIBXML_XPTR_LOCS_ENABLED 1423 case XPATH_POINT: 1424#endif 1425 case XPATH_USERS: 1426 case XPATH_XSLT_TREE: 1427 xmlXIncludeErr(ctxt, ref->elem, XML_XINCLUDE_XPTR_RESULT, 1428 "XPointer is not a range: #%s\n", 1429 fragment); 1430 xmlXPathFreeObject(xptr); 1431 xmlXPathFreeContext(xptrctxt); 1432 goto error; 1433 case XPATH_NODESET: 1434 if ((xptr->nodesetval == NULL) || 1435 (xptr->nodesetval->nodeNr <= 0)) { 1436 xmlXPathFreeObject(xptr); 1437 xmlXPathFreeContext(xptrctxt); 1438 goto error; 1439 } 1440 1441#ifdef LIBXML_XPTR_LOCS_ENABLED 1442 case XPATH_RANGE: 1443 case XPATH_LOCATIONSET: 1444 break; 1445#endif 1446 } 1447 set = xptr->nodesetval; 1448 if (set != NULL) { 1449 for (i = 0;i < set->nodeNr;i++) { 1450 if (set->nodeTab[i] == NULL) 1451 continue; 1452 switch (set->nodeTab[i]->type) { 1453 case XML_ELEMENT_NODE: 1454 case XML_TEXT_NODE: 1455 case XML_CDATA_SECTION_NODE: 1456 case XML_ENTITY_REF_NODE: 1457 case XML_ENTITY_NODE: 1458 case XML_PI_NODE: 1459 case XML_COMMENT_NODE: 1460 case XML_DOCUMENT_NODE: 1461 case XML_HTML_DOCUMENT_NODE: 1462 continue; 1463 1464 case XML_ATTRIBUTE_NODE: 1465 xmlXIncludeErr(ctxt, ref->elem, 1466 XML_XINCLUDE_XPTR_RESULT, 1467 "XPointer selects an attribute: #%s\n", 1468 fragment); 1469 set->nodeTab[i] = NULL; 1470 continue; 1471 case XML_NAMESPACE_DECL: 1472 xmlXIncludeErr(ctxt, ref->elem, 1473 XML_XINCLUDE_XPTR_RESULT, 1474 "XPointer selects a namespace: #%s\n", 1475 fragment); 1476 set->nodeTab[i] = NULL; 1477 continue; 1478 case XML_DOCUMENT_TYPE_NODE: 1479 case XML_DOCUMENT_FRAG_NODE: 1480 case XML_NOTATION_NODE: 1481 case XML_DTD_NODE: 1482 case XML_ELEMENT_DECL: 1483 case XML_ATTRIBUTE_DECL: 1484 case XML_ENTITY_DECL: 1485 case XML_XINCLUDE_START: 1486 case XML_XINCLUDE_END: 1487 xmlXIncludeErr(ctxt, ref->elem, 1488 XML_XINCLUDE_XPTR_RESULT, 1489 "XPointer selects unexpected nodes: #%s\n", 1490 fragment); 1491 set->nodeTab[i] = NULL; 1492 set->nodeTab[i] = NULL; 1493 continue; /* for */ 1494 } 1495 } 1496 } 1497 ref->inc = xmlXIncludeCopyXPointer(ctxt, xptr); 1498 xmlXPathFreeObject(xptr); 1499 xmlXPathFreeContext(xptrctxt); 1500 } 1501#endif 1502 1503 /* 1504 * Do the xml:base fixup if needed 1505 */ 1506 if ((doc != NULL) && (URL != NULL) && 1507 (!(ctxt->parseFlags & XML_PARSE_NOBASEFIX)) && 1508 (!(doc->parseFlags & XML_PARSE_NOBASEFIX))) { 1509 xmlNodePtr node; 1510 xmlChar *base; 1511 xmlChar *curBase; 1512 1513 /* 1514 * The base is only adjusted if "necessary", i.e. if the xinclude node 1515 * has a base specified, or the URL is relative 1516 */ 1517 base = xmlGetNsProp(ref->elem, BAD_CAST "base", XML_XML_NAMESPACE); 1518 if (base == NULL) { 1519 /* 1520 * No xml:base on the xinclude node, so we check whether the 1521 * URI base is different than (relative to) the context base 1522 */ 1523 curBase = xmlBuildRelativeURI(URL, ctxt->base); 1524 if (curBase == NULL) { /* Error return */ 1525 xmlXIncludeErr(ctxt, ref->elem, XML_XINCLUDE_HREF_URI, 1526 "trying to build relative URI from %s\n", URL); 1527 } else { 1528 /* If the URI doesn't contain a slash, it's not relative */ 1529 if (!xmlStrchr(curBase, '/')) 1530 xmlFree(curBase); 1531 else 1532 base = curBase; 1533 } 1534 } 1535 if (base != NULL) { /* Adjustment may be needed */ 1536 node = ref->inc; 1537 while (node != NULL) { 1538 /* Only work on element nodes */ 1539 if (node->type == XML_ELEMENT_NODE) { 1540 curBase = xmlNodeGetBase(node->doc, node); 1541 /* If no current base, set it */ 1542 if (curBase == NULL) { 1543 xmlNodeSetBase(node, base); 1544 } else { 1545 /* 1546 * If the current base is the same as the 1547 * URL of the document, then reset it to be 1548 * the specified xml:base or the relative URI 1549 */ 1550 if (xmlStrEqual(curBase, node->doc->URL)) { 1551 xmlNodeSetBase(node, base); 1552 } else { 1553 /* 1554 * If the element already has an xml:base 1555 * set, then relativise it if necessary 1556 */ 1557 xmlChar *xmlBase; 1558 xmlBase = xmlGetNsProp(node, 1559 BAD_CAST "base", 1560 XML_XML_NAMESPACE); 1561 if (xmlBase != NULL) { 1562 xmlChar *relBase; 1563 relBase = xmlBuildURI(xmlBase, base); 1564 if (relBase == NULL) { /* error */ 1565 xmlXIncludeErr(ctxt, 1566 ref->elem, 1567 XML_XINCLUDE_HREF_URI, 1568 "trying to rebuild base from %s\n", 1569 xmlBase); 1570 } else { 1571 xmlNodeSetBase(node, relBase); 1572 xmlFree(relBase); 1573 } 1574 xmlFree(xmlBase); 1575 } 1576 } 1577 xmlFree(curBase); 1578 } 1579 } 1580 node = node->next; 1581 } 1582 xmlFree(base); 1583 } 1584 } 1585 ret = 0; 1586 1587error: 1588 xmlFree(URL); 1589 xmlFree(fragment); 1590 return(ret); 1591} 1592 1593/** 1594 * xmlXIncludeLoadTxt: 1595 * @ctxt: the XInclude context 1596 * @url: the associated URL 1597 * @ref: an XMLXincludeRefPtr 1598 * 1599 * Load the content, and store the result in the XInclude context 1600 * 1601 * Returns 0 in case of success, -1 in case of failure 1602 */ 1603static int 1604xmlXIncludeLoadTxt(xmlXIncludeCtxtPtr ctxt, const xmlChar *url, 1605 xmlXIncludeRefPtr ref) { 1606 xmlParserInputBufferPtr buf; 1607 xmlNodePtr node = NULL; 1608 xmlURIPtr uri = NULL; 1609 xmlChar *URL = NULL; 1610 int i; 1611 int ret = -1; 1612 xmlChar *encoding = NULL; 1613 xmlCharEncoding enc = (xmlCharEncoding) 0; 1614 xmlParserCtxtPtr pctxt = NULL; 1615 xmlParserInputPtr inputStream = NULL; 1616 int len; 1617 const xmlChar *content; 1618 1619 1620 /* Don't read from stdin. */ 1621 if (xmlStrcmp(url, BAD_CAST "-") == 0) 1622 url = BAD_CAST "./-"; 1623 1624 /* 1625 * Check the URL and remove any fragment identifier 1626 */ 1627 uri = xmlParseURI((const char *)url); 1628 if (uri == NULL) { 1629 xmlXIncludeErr(ctxt, ref->elem, XML_XINCLUDE_HREF_URI, 1630 "invalid value URI %s\n", url); 1631 goto error; 1632 } 1633 if (uri->fragment != NULL) { 1634 xmlXIncludeErr(ctxt, ref->elem, XML_XINCLUDE_TEXT_FRAGMENT, 1635 "fragment identifier forbidden for text: %s\n", 1636 (const xmlChar *) uri->fragment); 1637 goto error; 1638 } 1639 URL = xmlSaveUri(uri); 1640 if (URL == NULL) { 1641 xmlXIncludeErr(ctxt, ref->elem, XML_XINCLUDE_HREF_URI, 1642 "invalid value URI %s\n", url); 1643 goto error; 1644 } 1645 1646 /* 1647 * Handling of references to the local document are done 1648 * directly through ctxt->doc. 1649 */ 1650 if (URL[0] == 0) { 1651 xmlXIncludeErr(ctxt, ref->elem, XML_XINCLUDE_TEXT_DOCUMENT, 1652 "text serialization of document not available\n", NULL); 1653 goto error; 1654 } 1655 1656 /* 1657 * Prevent reloading the document twice. 1658 */ 1659 for (i = 0; i < ctxt->txtNr; i++) { 1660 if (xmlStrEqual(URL, ctxt->txtTab[i].url)) { 1661 node = xmlNewDocText(ctxt->doc, ctxt->txtTab[i].text); 1662 goto loaded; 1663 } 1664 } 1665 1666 /* 1667 * Try to get the encoding if available 1668 */ 1669 if (ref->elem != NULL) { 1670 encoding = xmlGetProp(ref->elem, XINCLUDE_PARSE_ENCODING); 1671 } 1672 if (encoding != NULL) { 1673 /* 1674 * TODO: we should not have to remap to the xmlCharEncoding 1675 * predefined set, a better interface than 1676 * xmlParserInputBufferCreateFilename should allow any 1677 * encoding supported by iconv 1678 */ 1679 enc = xmlParseCharEncoding((const char *) encoding); 1680 if (enc == XML_CHAR_ENCODING_ERROR) { 1681 xmlXIncludeErr(ctxt, ref->elem, XML_XINCLUDE_UNKNOWN_ENCODING, 1682 "encoding %s not supported\n", encoding); 1683 goto error; 1684 } 1685 } 1686 1687 /* 1688 * Load it. 1689 */ 1690 pctxt = xmlNewParserCtxt(); 1691 inputStream = xmlLoadExternalEntity((const char*)URL, NULL, pctxt); 1692 if(inputStream == NULL) 1693 goto error; 1694 buf = inputStream->buf; 1695 if (buf == NULL) 1696 goto error; 1697 if (buf->encoder) 1698 xmlCharEncCloseFunc(buf->encoder); 1699 buf->encoder = xmlGetCharEncodingHandler(enc); 1700 node = xmlNewDocText(ctxt->doc, NULL); 1701 if (node == NULL) { 1702 xmlXIncludeErrMemory(ctxt, ref->elem, NULL); 1703 goto error; 1704 } 1705 1706 /* 1707 * Scan all chars from the resource and add the to the node 1708 */ 1709 while (xmlParserInputBufferRead(buf, 4096) > 0) 1710 ; 1711 1712 content = xmlBufContent(buf->buffer); 1713 len = xmlBufLength(buf->buffer); 1714 for (i = 0; i < len;) { 1715 int cur; 1716 int l; 1717 1718 l = len - i; 1719 cur = xmlGetUTF8Char(&content[i], &l); 1720 if ((cur < 0) || (!IS_CHAR(cur))) { 1721 xmlXIncludeErr(ctxt, ref->elem, XML_XINCLUDE_INVALID_CHAR, 1722 "%s contains invalid char\n", URL); 1723 goto error; 1724 } 1725 1726 i += l; 1727 } 1728 1729 xmlNodeAddContentLen(node, content, len); 1730 1731 if (ctxt->txtNr >= ctxt->txtMax) { 1732 xmlXIncludeTxt *tmp; 1733#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION 1734 size_t newSize = ctxt->txtMax ? ctxt->txtMax * 2 : 1; 1735#else 1736 size_t newSize = ctxt->txtMax ? ctxt->txtMax * 2 : 8; 1737#endif 1738 1739 tmp = xmlRealloc(ctxt->txtTab, sizeof(xmlXIncludeTxt) * newSize); 1740 if (tmp == NULL) { 1741 xmlXIncludeErrMemory(ctxt, ref->elem, 1742 "growing XInclude text table"); 1743 goto error; 1744 } 1745 ctxt->txtMax = newSize; 1746 ctxt->txtTab = tmp; 1747 } 1748 ctxt->txtTab[ctxt->txtNr].text = xmlStrdup(node->content); 1749 ctxt->txtTab[ctxt->txtNr].url = xmlStrdup(URL); 1750 ctxt->txtNr++; 1751 1752loaded: 1753 /* 1754 * Add the element as the replacement copy. 1755 */ 1756 ref->inc = node; 1757 node = NULL; 1758 ret = 0; 1759 1760error: 1761 xmlFreeNode(node); 1762 xmlFreeInputStream(inputStream); 1763 xmlFreeParserCtxt(pctxt); 1764 xmlFree(encoding); 1765 xmlFreeURI(uri); 1766 xmlFree(URL); 1767 return(ret); 1768} 1769 1770/** 1771 * xmlXIncludeLoadFallback: 1772 * @ctxt: the XInclude context 1773 * @fallback: the fallback node 1774 * @ref: an XMLXincludeRefPtr 1775 * 1776 * Load the content of the fallback node, and store the result 1777 * in the XInclude context 1778 * 1779 * Returns 0 in case of success, -1 in case of failure 1780 */ 1781static int 1782xmlXIncludeLoadFallback(xmlXIncludeCtxtPtr ctxt, xmlNodePtr fallback, 1783 xmlXIncludeRefPtr ref) { 1784 int ret = 0; 1785 int oldNbErrors; 1786 1787 if ((fallback == NULL) || (fallback->type == XML_NAMESPACE_DECL) || 1788 (ctxt == NULL)) 1789 return(-1); 1790 if (fallback->children != NULL) { 1791 /* 1792 * It's possible that the fallback also has 'includes' 1793 * (Bug 129969), so we re-process the fallback just in case 1794 */ 1795 oldNbErrors = ctxt->nbErrors; 1796 ref->inc = xmlXIncludeCopyNode(ctxt, fallback, 1); 1797 if (ctxt->nbErrors > oldNbErrors) 1798 ret = -1; 1799 else if (ref->inc == NULL) 1800 ref->emptyFb = 1; 1801 } else { 1802 ref->inc = NULL; 1803 ref->emptyFb = 1; /* flag empty callback */ 1804 } 1805 ref->fallback = 1; 1806 return(ret); 1807} 1808 1809/************************************************************************ 1810 * * 1811 * XInclude Processing * 1812 * * 1813 ************************************************************************/ 1814 1815/** 1816 * xmlXIncludeExpandNode: 1817 * @ctxt: an XInclude context 1818 * @node: an XInclude node 1819 * 1820 * If the XInclude node wasn't processed yet, create a new RefPtr, 1821 * add it to ctxt->incTab and load the included items. 1822 * 1823 * Returns the new or existing xmlXIncludeRefPtr, or NULL in case of error. 1824 */ 1825static xmlXIncludeRefPtr 1826xmlXIncludeExpandNode(xmlXIncludeCtxtPtr ctxt, xmlNodePtr node) { 1827 xmlXIncludeRefPtr ref; 1828 int i; 1829 1830 if (ctxt->fatalErr) 1831 return(NULL); 1832 if (ctxt->depth >= XINCLUDE_MAX_DEPTH) { 1833 xmlXIncludeErr(ctxt, node, XML_XINCLUDE_RECURSION, 1834 "maximum recursion depth exceeded\n", NULL); 1835 ctxt->fatalErr = 1; 1836 return(NULL); 1837 } 1838 1839#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION 1840 /* 1841 * The XInclude engine offers no protection against exponential 1842 * expansion attacks similar to "billion laughs". Avoid timeouts by 1843 * limiting the total number of replacements when fuzzing. 1844 * 1845 * Unfortuately, a single XInclude can already result in quadratic 1846 * behavior: 1847 * 1848 * <doc xmlns:xi="http://www.w3.org/2001/XInclude"> 1849 * <xi:include xpointer="xpointer(//e)"/> 1850 * <e> 1851 * <e> 1852 * <e> 1853 * <!-- more nested elements --> 1854 * </e> 1855 * </e> 1856 * </e> 1857 * </doc> 1858 */ 1859 if (ctxt->incTotal >= 20) 1860 return(NULL); 1861 ctxt->incTotal++; 1862#endif 1863 1864 for (i = 0; i < ctxt->incNr; i++) { 1865 if (ctxt->incTab[i]->elem == node) { 1866 if (ctxt->incTab[i]->expanding) { 1867 xmlXIncludeErr(ctxt, node, XML_XINCLUDE_RECURSION, 1868 "inclusion loop detected\n", NULL); 1869 return(NULL); 1870 } 1871 return(ctxt->incTab[i]); 1872 } 1873 } 1874 1875 ref = xmlXIncludeAddNode(ctxt, node); 1876 if (ref == NULL) 1877 return(NULL); 1878 ref->expanding = 1; 1879 ctxt->depth++; 1880 xmlXIncludeLoadNode(ctxt, ref); 1881 ctxt->depth--; 1882 ref->expanding = 0; 1883 1884 return(ref); 1885} 1886 1887/** 1888 * xmlXIncludeLoadNode: 1889 * @ctxt: an XInclude context 1890 * @ref: an xmlXIncludeRefPtr 1891 * 1892 * Find and load the infoset replacement for the given node. 1893 * 1894 * Returns 0 if substitution succeeded, -1 if some processing failed 1895 */ 1896static int 1897xmlXIncludeLoadNode(xmlXIncludeCtxtPtr ctxt, xmlXIncludeRefPtr ref) { 1898 xmlNodePtr cur; 1899 xmlChar *href; 1900 xmlChar *parse; 1901 xmlChar *base; 1902 xmlChar *oldBase; 1903 xmlChar *URI; 1904 int xml = 1; /* default Issue 64 */ 1905 int ret; 1906 1907 if ((ctxt == NULL) || (ref == NULL)) 1908 return(-1); 1909 cur = ref->elem; 1910 if (cur == NULL) 1911 return(-1); 1912 1913 /* 1914 * read the attributes 1915 */ 1916 href = xmlXIncludeGetProp(ctxt, cur, XINCLUDE_HREF); 1917 if (href == NULL) { 1918 href = xmlStrdup(BAD_CAST ""); /* @@@@ href is now optional */ 1919 if (href == NULL) 1920 return(-1); 1921 } 1922 parse = xmlXIncludeGetProp(ctxt, cur, XINCLUDE_PARSE); 1923 if (parse != NULL) { 1924 if (xmlStrEqual(parse, XINCLUDE_PARSE_XML)) 1925 xml = 1; 1926 else if (xmlStrEqual(parse, XINCLUDE_PARSE_TEXT)) 1927 xml = 0; 1928 else { 1929 xmlXIncludeErr(ctxt, cur, XML_XINCLUDE_PARSE_VALUE, 1930 "invalid value %s for 'parse'\n", parse); 1931 if (href != NULL) 1932 xmlFree(href); 1933 if (parse != NULL) 1934 xmlFree(parse); 1935 return(-1); 1936 } 1937 } 1938 1939 /* 1940 * compute the URI 1941 */ 1942 base = xmlNodeGetBase(ctxt->doc, cur); 1943 if (base == NULL) { 1944 URI = xmlBuildURI(href, ctxt->doc->URL); 1945 } else { 1946 URI = xmlBuildURI(href, base); 1947 } 1948 if (URI == NULL) { 1949 xmlChar *escbase; 1950 xmlChar *eschref; 1951 /* 1952 * Some escaping may be needed 1953 */ 1954 escbase = xmlURIEscape(base); 1955 eschref = xmlURIEscape(href); 1956 URI = xmlBuildURI(eschref, escbase); 1957 if (escbase != NULL) 1958 xmlFree(escbase); 1959 if (eschref != NULL) 1960 xmlFree(eschref); 1961 } 1962 if (URI == NULL) { 1963 xmlXIncludeErr(ctxt, cur, XML_XINCLUDE_HREF_URI, 1964 "failed build URL\n", NULL); 1965 if (parse != NULL) 1966 xmlFree(parse); 1967 if (href != NULL) 1968 xmlFree(href); 1969 if (base != NULL) 1970 xmlFree(base); 1971 return(-1); 1972 } 1973 1974 /* 1975 * Save the base for this include (saving the current one) 1976 */ 1977 oldBase = ctxt->base; 1978 ctxt->base = base; 1979 1980 if (xml) { 1981 ret = xmlXIncludeLoadDoc(ctxt, URI, ref); 1982 /* xmlXIncludeGetFragment(ctxt, cur, URI); */ 1983 } else { 1984 ret = xmlXIncludeLoadTxt(ctxt, URI, ref); 1985 } 1986 1987 /* 1988 * Restore the original base before checking for fallback 1989 */ 1990 ctxt->base = oldBase; 1991 1992 if (ret < 0) { 1993 xmlNodePtr children; 1994 1995 /* 1996 * Time to try a fallback if available 1997 */ 1998 children = cur->children; 1999 while (children != NULL) { 2000 if ((children->type == XML_ELEMENT_NODE) && 2001 (children->ns != NULL) && 2002 (xmlStrEqual(children->name, XINCLUDE_FALLBACK)) && 2003 ((xmlStrEqual(children->ns->href, XINCLUDE_NS)) || 2004 (xmlStrEqual(children->ns->href, XINCLUDE_OLD_NS)))) { 2005 ret = xmlXIncludeLoadFallback(ctxt, children, ref); 2006 break; 2007 } 2008 children = children->next; 2009 } 2010 } 2011 if (ret < 0) { 2012 xmlXIncludeErr(ctxt, cur, XML_XINCLUDE_NO_FALLBACK, 2013 "could not load %s, and no fallback was found\n", 2014 URI); 2015 } 2016 2017 /* 2018 * Cleanup 2019 */ 2020 if (URI != NULL) 2021 xmlFree(URI); 2022 if (parse != NULL) 2023 xmlFree(parse); 2024 if (href != NULL) 2025 xmlFree(href); 2026 if (base != NULL) 2027 xmlFree(base); 2028 return(0); 2029} 2030 2031/** 2032 * xmlXIncludeIncludeNode: 2033 * @ctxt: an XInclude context 2034 * @ref: an xmlXIncludeRefPtr 2035 * 2036 * Implement the infoset replacement for the given node 2037 * 2038 * Returns 0 if substitution succeeded, -1 if some processing failed 2039 */ 2040static int 2041xmlXIncludeIncludeNode(xmlXIncludeCtxtPtr ctxt, xmlXIncludeRefPtr ref) { 2042 xmlNodePtr cur, end, list, tmp; 2043 2044 if ((ctxt == NULL) || (ref == NULL)) 2045 return(-1); 2046 cur = ref->elem; 2047 if ((cur == NULL) || (cur->type == XML_NAMESPACE_DECL)) 2048 return(-1); 2049 2050 list = ref->inc; 2051 ref->inc = NULL; 2052 ref->emptyFb = 0; 2053 2054 /* 2055 * Check against the risk of generating a multi-rooted document 2056 */ 2057 if ((cur->parent != NULL) && 2058 (cur->parent->type != XML_ELEMENT_NODE)) { 2059 int nb_elem = 0; 2060 2061 tmp = list; 2062 while (tmp != NULL) { 2063 if (tmp->type == XML_ELEMENT_NODE) 2064 nb_elem++; 2065 tmp = tmp->next; 2066 } 2067 if (nb_elem > 1) { 2068 xmlXIncludeErr(ctxt, ref->elem, XML_XINCLUDE_MULTIPLE_ROOT, 2069 "XInclude error: would result in multiple root nodes\n", 2070 NULL); 2071 xmlFreeNodeList(list); 2072 return(-1); 2073 } 2074 } 2075 2076 if (ctxt->parseFlags & XML_PARSE_NOXINCNODE) { 2077 /* 2078 * Add the list of nodes 2079 */ 2080 while (list != NULL) { 2081 end = list; 2082 list = list->next; 2083 2084 xmlAddPrevSibling(cur, end); 2085 } 2086 /* 2087 * FIXME: xmlUnlinkNode doesn't coalesce text nodes. 2088 */ 2089 xmlUnlinkNode(cur); 2090 xmlFreeNode(cur); 2091 } else { 2092 xmlNodePtr child, next; 2093 2094 /* 2095 * Change the current node as an XInclude start one, and add an 2096 * XInclude end one 2097 */ 2098 if (ref->fallback) 2099 xmlUnsetProp(cur, BAD_CAST "href"); 2100 cur->type = XML_XINCLUDE_START; 2101 /* Remove fallback children */ 2102 for (child = cur->children; child != NULL; child = next) { 2103 next = child->next; 2104 xmlUnlinkNode(child); 2105 xmlFreeNode(child); 2106 } 2107 end = xmlNewDocNode(cur->doc, cur->ns, cur->name, NULL); 2108 if (end == NULL) { 2109 xmlXIncludeErr(ctxt, ref->elem, XML_XINCLUDE_BUILD_FAILED, 2110 "failed to build node\n", NULL); 2111 xmlFreeNodeList(list); 2112 return(-1); 2113 } 2114 end->type = XML_XINCLUDE_END; 2115 xmlAddNextSibling(cur, end); 2116 2117 /* 2118 * Add the list of nodes 2119 */ 2120 while (list != NULL) { 2121 cur = list; 2122 list = list->next; 2123 2124 xmlAddPrevSibling(end, cur); 2125 } 2126 } 2127 2128 2129 return(0); 2130} 2131 2132/** 2133 * xmlXIncludeTestNode: 2134 * @ctxt: the XInclude processing context 2135 * @node: an XInclude node 2136 * 2137 * test if the node is an XInclude node 2138 * 2139 * Returns 1 true, 0 otherwise 2140 */ 2141static int 2142xmlXIncludeTestNode(xmlXIncludeCtxtPtr ctxt, xmlNodePtr node) { 2143 if (node == NULL) 2144 return(0); 2145 if (node->type != XML_ELEMENT_NODE) 2146 return(0); 2147 if (node->ns == NULL) 2148 return(0); 2149 if ((xmlStrEqual(node->ns->href, XINCLUDE_NS)) || 2150 (xmlStrEqual(node->ns->href, XINCLUDE_OLD_NS))) { 2151 if (xmlStrEqual(node->ns->href, XINCLUDE_OLD_NS)) { 2152 if (ctxt->legacy == 0) { 2153#if 0 /* wait for the XML Core Working Group to get something stable ! */ 2154 xmlXIncludeWarn(ctxt, node, XML_XINCLUDE_DEPRECATED_NS, 2155 "Deprecated XInclude namespace found, use %s", 2156 XINCLUDE_NS); 2157#endif 2158 ctxt->legacy = 1; 2159 } 2160 } 2161 if (xmlStrEqual(node->name, XINCLUDE_NODE)) { 2162 xmlNodePtr child = node->children; 2163 int nb_fallback = 0; 2164 2165 while (child != NULL) { 2166 if ((child->type == XML_ELEMENT_NODE) && 2167 (child->ns != NULL) && 2168 ((xmlStrEqual(child->ns->href, XINCLUDE_NS)) || 2169 (xmlStrEqual(child->ns->href, XINCLUDE_OLD_NS)))) { 2170 if (xmlStrEqual(child->name, XINCLUDE_NODE)) { 2171 xmlXIncludeErr(ctxt, node, 2172 XML_XINCLUDE_INCLUDE_IN_INCLUDE, 2173 "%s has an 'include' child\n", 2174 XINCLUDE_NODE); 2175 return(0); 2176 } 2177 if (xmlStrEqual(child->name, XINCLUDE_FALLBACK)) { 2178 nb_fallback++; 2179 } 2180 } 2181 child = child->next; 2182 } 2183 if (nb_fallback > 1) { 2184 xmlXIncludeErr(ctxt, node, XML_XINCLUDE_FALLBACKS_IN_INCLUDE, 2185 "%s has multiple fallback children\n", 2186 XINCLUDE_NODE); 2187 return(0); 2188 } 2189 return(1); 2190 } 2191 if (xmlStrEqual(node->name, XINCLUDE_FALLBACK)) { 2192 if ((node->parent == NULL) || 2193 (node->parent->type != XML_ELEMENT_NODE) || 2194 (node->parent->ns == NULL) || 2195 ((!xmlStrEqual(node->parent->ns->href, XINCLUDE_NS)) && 2196 (!xmlStrEqual(node->parent->ns->href, XINCLUDE_OLD_NS))) || 2197 (!xmlStrEqual(node->parent->name, XINCLUDE_NODE))) { 2198 xmlXIncludeErr(ctxt, node, 2199 XML_XINCLUDE_FALLBACK_NOT_IN_INCLUDE, 2200 "%s is not the child of an 'include'\n", 2201 XINCLUDE_FALLBACK); 2202 } 2203 } 2204 } 2205 return(0); 2206} 2207 2208/** 2209 * xmlXIncludeDoProcess: 2210 * @ctxt: the XInclude processing context 2211 * @tree: the top of the tree to process 2212 * 2213 * Implement the XInclude substitution on the XML document @doc 2214 * 2215 * Returns 0 if no substitution were done, -1 if some processing failed 2216 * or the number of substitutions done. 2217 */ 2218static int 2219xmlXIncludeDoProcess(xmlXIncludeCtxtPtr ctxt, xmlNodePtr tree) { 2220 xmlXIncludeRefPtr ref; 2221 xmlNodePtr cur; 2222 int ret = 0; 2223 int i, start; 2224 2225 if ((tree == NULL) || (tree->type == XML_NAMESPACE_DECL)) 2226 return(-1); 2227 if (ctxt == NULL) 2228 return(-1); 2229 2230 /* 2231 * First phase: lookup the elements in the document 2232 */ 2233 start = ctxt->incNr; 2234 cur = tree; 2235 do { 2236 /* TODO: need to work on entities -> stack */ 2237 if (xmlXIncludeTestNode(ctxt, cur) == 1) { 2238 ref = xmlXIncludeExpandNode(ctxt, cur); 2239 /* 2240 * Mark direct includes. 2241 */ 2242 if (ref != NULL) 2243 ref->replace = 1; 2244 } else if ((cur->children != NULL) && 2245 ((cur->type == XML_DOCUMENT_NODE) || 2246 (cur->type == XML_ELEMENT_NODE))) { 2247 cur = cur->children; 2248 continue; 2249 } 2250 do { 2251 if (cur == tree) 2252 break; 2253 if (cur->next != NULL) { 2254 cur = cur->next; 2255 break; 2256 } 2257 cur = cur->parent; 2258 } while (cur != NULL); 2259 } while ((cur != NULL) && (cur != tree)); 2260 2261 /* 2262 * Second phase: extend the original document infoset. 2263 */ 2264 for (i = start; i < ctxt->incNr; i++) { 2265 if (ctxt->incTab[i]->replace != 0) { 2266 if ((ctxt->incTab[i]->inc != NULL) || 2267 (ctxt->incTab[i]->emptyFb != 0)) { /* (empty fallback) */ 2268 xmlXIncludeIncludeNode(ctxt, ctxt->incTab[i]); 2269 } 2270 ctxt->incTab[i]->replace = 0; 2271 } else { 2272 /* 2273 * Ignore includes which were added indirectly, for example 2274 * inside xi:fallback elements. 2275 */ 2276 if (ctxt->incTab[i]->inc != NULL) { 2277 xmlFreeNodeList(ctxt->incTab[i]->inc); 2278 ctxt->incTab[i]->inc = NULL; 2279 } 2280 } 2281 ret++; 2282 } 2283 2284 if (ctxt->isStream) { 2285 /* 2286 * incTab references nodes which will eventually be deleted in 2287 * streaming mode. The table is only required for XPointer 2288 * expressions which aren't allowed in streaming mode. 2289 */ 2290 for (i = 0;i < ctxt->incNr;i++) { 2291 xmlXIncludeFreeRef(ctxt->incTab[i]); 2292 } 2293 ctxt->incNr = 0; 2294 } 2295 2296 return(ret); 2297} 2298 2299/** 2300 * xmlXIncludeSetFlags: 2301 * @ctxt: an XInclude processing context 2302 * @flags: a set of xmlParserOption used for parsing XML includes 2303 * 2304 * Set the flags used for further processing of XML resources. 2305 * 2306 * Returns 0 in case of success and -1 in case of error. 2307 */ 2308int 2309xmlXIncludeSetFlags(xmlXIncludeCtxtPtr ctxt, int flags) { 2310 if (ctxt == NULL) 2311 return(-1); 2312 ctxt->parseFlags = flags; 2313 return(0); 2314} 2315 2316/** 2317 * xmlXIncludeSetStreamingMode: 2318 * @ctxt: an XInclude processing context 2319 * @mode: whether streaming mode should be enabled 2320 * 2321 * In streaming mode, XPointer expressions aren't allowed. 2322 * 2323 * Returns 0 in case of success and -1 in case of error. 2324 */ 2325int 2326xmlXIncludeSetStreamingMode(xmlXIncludeCtxtPtr ctxt, int mode) { 2327 if (ctxt == NULL) 2328 return(-1); 2329 ctxt->isStream = !!mode; 2330 return(0); 2331} 2332 2333/** 2334 * xmlXIncludeProcessTreeFlagsData: 2335 * @tree: an XML node 2336 * @flags: a set of xmlParserOption used for parsing XML includes 2337 * @data: application data that will be passed to the parser context 2338 * in the _private field of the parser context(s) 2339 * 2340 * Implement the XInclude substitution on the XML node @tree 2341 * 2342 * Returns 0 if no substitution were done, -1 if some processing failed 2343 * or the number of substitutions done. 2344 */ 2345 2346int 2347xmlXIncludeProcessTreeFlagsData(xmlNodePtr tree, int flags, void *data) { 2348 xmlXIncludeCtxtPtr ctxt; 2349 int ret = 0; 2350 2351 if ((tree == NULL) || (tree->type == XML_NAMESPACE_DECL) || 2352 (tree->doc == NULL)) 2353 return(-1); 2354 2355 ctxt = xmlXIncludeNewContext(tree->doc); 2356 if (ctxt == NULL) 2357 return(-1); 2358 ctxt->_private = data; 2359 ctxt->base = xmlStrdup((xmlChar *)tree->doc->URL); 2360 xmlXIncludeSetFlags(ctxt, flags); 2361 ret = xmlXIncludeDoProcess(ctxt, tree); 2362 if ((ret >= 0) && (ctxt->nbErrors > 0)) 2363 ret = -1; 2364 2365 xmlXIncludeFreeContext(ctxt); 2366 return(ret); 2367} 2368 2369/** 2370 * xmlXIncludeProcessFlagsData: 2371 * @doc: an XML document 2372 * @flags: a set of xmlParserOption used for parsing XML includes 2373 * @data: application data that will be passed to the parser context 2374 * in the _private field of the parser context(s) 2375 * 2376 * Implement the XInclude substitution on the XML document @doc 2377 * 2378 * Returns 0 if no substitution were done, -1 if some processing failed 2379 * or the number of substitutions done. 2380 */ 2381int 2382xmlXIncludeProcessFlagsData(xmlDocPtr doc, int flags, void *data) { 2383 xmlNodePtr tree; 2384 2385 if (doc == NULL) 2386 return(-1); 2387 tree = xmlDocGetRootElement(doc); 2388 if (tree == NULL) 2389 return(-1); 2390 return(xmlXIncludeProcessTreeFlagsData(tree, flags, data)); 2391} 2392 2393/** 2394 * xmlXIncludeProcessFlags: 2395 * @doc: an XML document 2396 * @flags: a set of xmlParserOption used for parsing XML includes 2397 * 2398 * Implement the XInclude substitution on the XML document @doc 2399 * 2400 * Returns 0 if no substitution were done, -1 if some processing failed 2401 * or the number of substitutions done. 2402 */ 2403int 2404xmlXIncludeProcessFlags(xmlDocPtr doc, int flags) { 2405 return xmlXIncludeProcessFlagsData(doc, flags, NULL); 2406} 2407 2408/** 2409 * xmlXIncludeProcess: 2410 * @doc: an XML document 2411 * 2412 * Implement the XInclude substitution on the XML document @doc 2413 * 2414 * Returns 0 if no substitution were done, -1 if some processing failed 2415 * or the number of substitutions done. 2416 */ 2417int 2418xmlXIncludeProcess(xmlDocPtr doc) { 2419 return(xmlXIncludeProcessFlags(doc, 0)); 2420} 2421 2422/** 2423 * xmlXIncludeProcessTreeFlags: 2424 * @tree: a node in an XML document 2425 * @flags: a set of xmlParserOption used for parsing XML includes 2426 * 2427 * Implement the XInclude substitution for the given subtree 2428 * 2429 * Returns 0 if no substitution were done, -1 if some processing failed 2430 * or the number of substitutions done. 2431 */ 2432int 2433xmlXIncludeProcessTreeFlags(xmlNodePtr tree, int flags) { 2434 xmlXIncludeCtxtPtr ctxt; 2435 int ret = 0; 2436 2437 if ((tree == NULL) || (tree->type == XML_NAMESPACE_DECL) || 2438 (tree->doc == NULL)) 2439 return(-1); 2440 ctxt = xmlXIncludeNewContext(tree->doc); 2441 if (ctxt == NULL) 2442 return(-1); 2443 ctxt->base = xmlNodeGetBase(tree->doc, tree); 2444 xmlXIncludeSetFlags(ctxt, flags); 2445 ret = xmlXIncludeDoProcess(ctxt, tree); 2446 if ((ret >= 0) && (ctxt->nbErrors > 0)) 2447 ret = -1; 2448 2449 xmlXIncludeFreeContext(ctxt); 2450 return(ret); 2451} 2452 2453/** 2454 * xmlXIncludeProcessTree: 2455 * @tree: a node in an XML document 2456 * 2457 * Implement the XInclude substitution for the given subtree 2458 * 2459 * Returns 0 if no substitution were done, -1 if some processing failed 2460 * or the number of substitutions done. 2461 */ 2462int 2463xmlXIncludeProcessTree(xmlNodePtr tree) { 2464 return(xmlXIncludeProcessTreeFlags(tree, 0)); 2465} 2466 2467/** 2468 * xmlXIncludeProcessNode: 2469 * @ctxt: an existing XInclude context 2470 * @node: a node in an XML document 2471 * 2472 * Implement the XInclude substitution for the given subtree reusing 2473 * the information and data coming from the given context. 2474 * 2475 * Returns 0 if no substitution were done, -1 if some processing failed 2476 * or the number of substitutions done. 2477 */ 2478int 2479xmlXIncludeProcessNode(xmlXIncludeCtxtPtr ctxt, xmlNodePtr node) { 2480 int ret = 0; 2481 2482 if ((node == NULL) || (node->type == XML_NAMESPACE_DECL) || 2483 (node->doc == NULL) || (ctxt == NULL)) 2484 return(-1); 2485 ret = xmlXIncludeDoProcess(ctxt, node); 2486 if ((ret >= 0) && (ctxt->nbErrors > 0)) 2487 ret = -1; 2488 return(ret); 2489} 2490 2491#else /* !LIBXML_XINCLUDE_ENABLED */ 2492#endif