Coverage Report

Created: 2024-02-04 06:19

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