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