1/*
2 * documents.c: Implementation of the documents handling
3 *
4 * See Copyright for the status of this software.
5 *
6 * daniel@veillard.com
7 */
8
9#define IN_LIBXSLT
10#include "libxslt.h"
11
12#include <string.h>
13
14#include <libxml/xmlmemory.h>
15#include <libxml/tree.h>
16#include <libxml/hash.h>
17#include <libxml/parser.h>
18#include <libxml/parserInternals.h>
19#include "xslt.h"
20#include "xsltInternals.h"
21#include "xsltutils.h"
22#include "documents.h"
23#include "transform.h"
24#include "imports.h"
25#include "keys.h"
26#include "security.h"
27
28#ifdef LIBXML_XINCLUDE_ENABLED
29#include <libxml/xinclude.h>
30#endif
31
32#define WITH_XSLT_DEBUG_DOCUMENTS
33
34#ifdef WITH_XSLT_DEBUG
35#define WITH_XSLT_DEBUG_DOCUMENTS
36#endif
37
38/************************************************************************
39 * 									*
40 * 		Hooks for the document loader				*
41 * 									*
42 ************************************************************************/
43
44/**
45 * xsltDocDefaultLoaderFunc:
46 * @URI: the URI of the document to load
47 * @dict: the dictionnary to use when parsing that document
48 * @options: parsing options, a set of xmlParserOption
49 * @ctxt: the context, either a stylesheet or a transformation context
50 * @type: the xsltLoadType indicating the kind of loading required
51 *
52 * Default function to load document not provided by the compilation or
53 * transformation API themselve, for example when an xsl:import,
54 * xsl:include is found at compilation time or when a document()
55 * call is made at runtime.
56 *
57 * Returns the pointer to the document (which will be modified and
58 * freed by the engine later), or NULL in case of error.
59 */
60static xmlDocPtr
61xsltDocDefaultLoaderFunc(const xmlChar * URI, xmlDictPtr dict, int options,
62                         void *ctxt ATTRIBUTE_UNUSED,
63			 xsltLoadType type ATTRIBUTE_UNUSED)
64{
65    xmlParserCtxtPtr pctxt;
66    xmlParserInputPtr inputStream;
67    xmlDocPtr doc;
68
69    pctxt = xmlNewParserCtxt();
70    if (pctxt == NULL)
71        return(NULL);
72    if ((dict != NULL) && (pctxt->dict != NULL)) {
73        xmlDictFree(pctxt->dict);
74	pctxt->dict = NULL;
75    }
76    if (dict != NULL) {
77	pctxt->dict = dict;
78	xmlDictReference(pctxt->dict);
79#ifdef WITH_XSLT_DEBUG
80	xsltGenericDebug(xsltGenericDebugContext,
81                     "Reusing dictionary for document\n");
82#endif
83    }
84    xmlCtxtUseOptions(pctxt, options);
85    inputStream = xmlLoadExternalEntity((const char *) URI, NULL, pctxt);
86    if (inputStream == NULL) {
87        xmlFreeParserCtxt(pctxt);
88	return(NULL);
89    }
90    inputPush(pctxt, inputStream);
91    if (pctxt->directory == NULL)
92        pctxt->directory = xmlParserGetDirectory((const char *) URI);
93
94    xmlParseDocument(pctxt);
95
96    if (pctxt->wellFormed) {
97        doc = pctxt->myDoc;
98    }
99    else {
100        doc = NULL;
101        xmlFreeDoc(pctxt->myDoc);
102        pctxt->myDoc = NULL;
103    }
104    xmlFreeParserCtxt(pctxt);
105
106    return(doc);
107}
108
109
110xsltDocLoaderFunc xsltDocDefaultLoader = xsltDocDefaultLoaderFunc;
111
112/**
113 * xsltSetLoaderFunc:
114 * @f: the new function to handle document loading.
115 *
116 * Set the new function to load document, if NULL it resets it to the
117 * default function.
118 */
119
120void
121xsltSetLoaderFunc(xsltDocLoaderFunc f) {
122    if (f == NULL)
123        xsltDocDefaultLoader = xsltDocDefaultLoaderFunc;
124    else
125        xsltDocDefaultLoader = f;
126}
127
128/************************************************************************
129 *									*
130 *			Module interfaces				*
131 *									*
132 ************************************************************************/
133
134/**
135 * xsltNewDocument:
136 * @ctxt: an XSLT transformation context (or NULL)
137 * @doc:  a parsed XML document
138 *
139 * Register a new document, apply key computations
140 *
141 * Returns a handler to the document
142 */
143xsltDocumentPtr
144xsltNewDocument(xsltTransformContextPtr ctxt, xmlDocPtr doc) {
145    xsltDocumentPtr cur;
146
147    cur = (xsltDocumentPtr) xmlMalloc(sizeof(xsltDocument));
148    if (cur == NULL) {
149	xsltTransformError(ctxt, NULL, (xmlNodePtr) doc,
150		"xsltNewDocument : malloc failed\n");
151	return(NULL);
152    }
153    memset(cur, 0, sizeof(xsltDocument));
154    cur->doc = doc;
155    if (ctxt != NULL) {
156        if (! XSLT_IS_RES_TREE_FRAG(doc)) {
157	    cur->next = ctxt->docList;
158	    ctxt->docList = cur;
159	}
160#ifdef XSLT_REFACTORED_KEYCOMP
161	/*
162	* A key with a specific name for a specific document
163	* will only be computed if there's a call to the key()
164	* function using that specific name for that specific
165	* document. I.e. computation of keys will be done in
166	* xsltGetKey() (keys.c) on an on-demand basis.
167	*/
168#else
169	/*
170	* Old behaviour.
171	*/
172	xsltInitCtxtKeys(ctxt, cur);
173#endif
174    }
175    return(cur);
176}
177
178/**
179 * xsltNewStyleDocument:
180 * @style: an XSLT style sheet
181 * @doc:  a parsed XML document
182 *
183 * Register a new document, apply key computations
184 *
185 * Returns a handler to the document
186 */
187xsltDocumentPtr
188xsltNewStyleDocument(xsltStylesheetPtr style, xmlDocPtr doc) {
189    xsltDocumentPtr cur;
190
191    cur = (xsltDocumentPtr) xmlMalloc(sizeof(xsltDocument));
192    if (cur == NULL) {
193	xsltTransformError(NULL, style, (xmlNodePtr) doc,
194		"xsltNewStyleDocument : malloc failed\n");
195	return(NULL);
196    }
197    memset(cur, 0, sizeof(xsltDocument));
198    cur->doc = doc;
199    if (style != NULL) {
200	cur->next = style->docList;
201	style->docList = cur;
202    }
203    return(cur);
204}
205
206/**
207 * xsltFreeStyleDocuments:
208 * @style: an XSLT stylesheet (representing a stylesheet-level)
209 *
210 * Frees the node-trees (and xsltDocument structures) of all
211 * stylesheet-modules of the stylesheet-level represented by
212 * the given @style.
213 */
214void
215xsltFreeStyleDocuments(xsltStylesheetPtr style) {
216    xsltDocumentPtr doc, cur;
217#ifdef XSLT_REFACTORED_XSLT_NSCOMP
218    xsltNsMapPtr nsMap;
219#endif
220
221    if (style == NULL)
222	return;
223
224#ifdef XSLT_REFACTORED_XSLT_NSCOMP
225    if (XSLT_HAS_INTERNAL_NSMAP(style))
226	nsMap = XSLT_GET_INTERNAL_NSMAP(style);
227    else
228	nsMap = NULL;
229#endif
230
231    cur = style->docList;
232    while (cur != NULL) {
233	doc = cur;
234	cur = cur->next;
235#ifdef XSLT_REFACTORED_XSLT_NSCOMP
236	/*
237	* Restore all changed namespace URIs of ns-decls.
238	*/
239	if (nsMap)
240	    xsltRestoreDocumentNamespaces(nsMap, doc->doc);
241#endif
242	xsltFreeDocumentKeys(doc);
243	if (!doc->main)
244	    xmlFreeDoc(doc->doc);
245        xmlFree(doc);
246    }
247}
248
249/**
250 * xsltFreeDocuments:
251 * @ctxt: an XSLT transformation context
252 *
253 * Free up all the space used by the loaded documents
254 */
255void
256xsltFreeDocuments(xsltTransformContextPtr ctxt) {
257    xsltDocumentPtr doc, cur;
258
259    cur = ctxt->docList;
260    while (cur != NULL) {
261	doc = cur;
262	cur = cur->next;
263	xsltFreeDocumentKeys(doc);
264	if (!doc->main)
265	    xmlFreeDoc(doc->doc);
266        xmlFree(doc);
267    }
268    cur = ctxt->styleList;
269    while (cur != NULL) {
270	doc = cur;
271	cur = cur->next;
272	xsltFreeDocumentKeys(doc);
273	if (!doc->main)
274	    xmlFreeDoc(doc->doc);
275        xmlFree(doc);
276    }
277}
278
279/**
280 * xsltLoadDocument:
281 * @ctxt: an XSLT transformation context
282 * @URI:  the computed URI of the document
283 *
284 * Try to load a document (not a stylesheet)
285 * within the XSLT transformation context
286 *
287 * Returns the new xsltDocumentPtr or NULL in case of error
288 */
289xsltDocumentPtr
290xsltLoadDocument(xsltTransformContextPtr ctxt, const xmlChar *URI) {
291    xsltDocumentPtr ret;
292    xmlDocPtr doc;
293
294    if ((ctxt == NULL) || (URI == NULL))
295	return(NULL);
296
297    /*
298     * Security framework check
299     */
300    if (ctxt->sec != NULL) {
301	int res;
302
303	res = xsltCheckRead(ctxt->sec, ctxt, URI);
304	if (res == 0) {
305	    xsltTransformError(ctxt, NULL, NULL,
306		 "xsltLoadDocument: read rights for %s denied\n",
307			     URI);
308	    return(NULL);
309	}
310    }
311
312    /*
313     * Walk the context list to find the document if preparsed
314     */
315    ret = ctxt->docList;
316    while (ret != NULL) {
317	if ((ret->doc != NULL) && (ret->doc->URL != NULL) &&
318	    (xmlStrEqual(ret->doc->URL, URI)))
319	    return(ret);
320	ret = ret->next;
321    }
322
323    doc = xsltDocDefaultLoader(URI, ctxt->dict, ctxt->parserOptions,
324                               (void *) ctxt, XSLT_LOAD_DOCUMENT);
325
326    if (doc == NULL)
327	return(NULL);
328
329    if (ctxt->xinclude != 0) {
330#ifdef LIBXML_XINCLUDE_ENABLED
331#if LIBXML_VERSION >= 20603
332	xmlXIncludeProcessFlags(doc, ctxt->parserOptions);
333#else
334	xmlXIncludeProcess(doc);
335#endif
336#else
337	xsltTransformError(ctxt, NULL, NULL,
338	    "xsltLoadDocument(%s) : XInclude processing not compiled in\n",
339	                 URI);
340#endif
341    }
342    /*
343     * Apply white-space stripping if asked for
344     */
345    if (xsltNeedElemSpaceHandling(ctxt))
346	xsltApplyStripSpaces(ctxt, xmlDocGetRootElement(doc));
347    if (ctxt->debugStatus == XSLT_DEBUG_NONE)
348	xmlXPathOrderDocElems(doc);
349
350    ret = xsltNewDocument(ctxt, doc);
351    return(ret);
352}
353
354/**
355 * xsltLoadStyleDocument:
356 * @style: an XSLT style sheet
357 * @URI:  the computed URI of the document
358 *
359 * Try to load a stylesheet document within the XSLT transformation context
360 *
361 * Returns the new xsltDocumentPtr or NULL in case of error
362 */
363xsltDocumentPtr
364xsltLoadStyleDocument(xsltStylesheetPtr style, const xmlChar *URI) {
365    xsltDocumentPtr ret;
366    xmlDocPtr doc;
367    xsltSecurityPrefsPtr sec;
368
369    if ((style == NULL) || (URI == NULL))
370	return(NULL);
371
372    /*
373     * Security framework check
374     */
375    sec = xsltGetDefaultSecurityPrefs();
376    if (sec != NULL) {
377	int res;
378
379	res = xsltCheckRead(sec, NULL, URI);
380	if (res == 0) {
381	    xsltTransformError(NULL, NULL, NULL,
382		 "xsltLoadStyleDocument: read rights for %s denied\n",
383			     URI);
384	    return(NULL);
385	}
386    }
387
388    /*
389     * Walk the context list to find the document if preparsed
390     */
391    ret = style->docList;
392    while (ret != NULL) {
393	if ((ret->doc != NULL) && (ret->doc->URL != NULL) &&
394	    (xmlStrEqual(ret->doc->URL, URI)))
395	    return(ret);
396	ret = ret->next;
397    }
398
399    doc = xsltDocDefaultLoader(URI, style->dict, XSLT_PARSE_OPTIONS,
400                               (void *) style, XSLT_LOAD_STYLESHEET);
401    if (doc == NULL)
402	return(NULL);
403
404    ret = xsltNewStyleDocument(style, doc);
405    return(ret);
406}
407
408/**
409 * xsltFindDocument:
410 * @ctxt: an XSLT transformation context
411 * @doc: a parsed XML document
412 *
413 * Try to find a document within the XSLT transformation context
414 *
415 * Returns the desired xsltDocumentPtr or NULL in case of error
416 */
417xsltDocumentPtr
418xsltFindDocument (xsltTransformContextPtr ctxt, xmlDocPtr doc) {
419    xsltDocumentPtr ret;
420
421    if ((ctxt == NULL) || (doc == NULL))
422	return(NULL);
423
424    /*
425     * Walk the context list to find the document
426     */
427    ret = ctxt->docList;
428    while (ret != NULL) {
429	if (ret->doc == doc)
430	    return(ret);
431	ret = ret->next;
432    }
433    if (doc == ctxt->style->doc)
434	return(ctxt->document);
435    return(NULL);
436}
437
438