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