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