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