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