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