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