Coverage Report

Created: 2026-09-14 06:09

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