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