Coverage Report

Created: 2025-07-12 06:30

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