Coverage Report

Created: 2025-07-07 10:01

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