VirtualBox

source: vbox/trunk/src/libs/libxml2-2.13.2/xinclude.c

Last change on this file was 105420, checked in by vboxsync, 4 months ago

libxml2-2.12.6: Applied and adjusted our libxml2 changes to 2.12.6. bugref:10730

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette