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 dictionary 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	/*
161	* A key with a specific name for a specific document
162	* will only be computed if there's a call to the key()
163	* function using that specific name for that specific
164	* document. I.e. computation of keys will be done in
165	* xsltGetKey() (keys.c) on an on-demand basis.
166	*
167	* xsltInitCtxtKeys(ctxt, cur); not called here anymore
168	*/
169    }
170    return(cur);
171}
172
173/**
174 * xsltNewStyleDocument:
175 * @style: an XSLT style sheet
176 * @doc:  a parsed XML document
177 *
178 * Register a new document, apply key computations
179 *
180 * Returns a handler to the document
181 */
182xsltDocumentPtr
183xsltNewStyleDocument(xsltStylesheetPtr style, xmlDocPtr doc) {
184    xsltDocumentPtr cur;
185
186    cur = (xsltDocumentPtr) xmlMalloc(sizeof(xsltDocument));
187    if (cur == NULL) {
188	xsltTransformError(NULL, style, (xmlNodePtr) doc,
189		"xsltNewStyleDocument : malloc failed\n");
190	return(NULL);
191    }
192    memset(cur, 0, sizeof(xsltDocument));
193    cur->doc = doc;
194    if (style != NULL) {
195	cur->next = style->docList;
196	style->docList = cur;
197    }
198    return(cur);
199}
200
201/**
202 * xsltFreeStyleDocuments:
203 * @style: an XSLT stylesheet (representing a stylesheet-level)
204 *
205 * Frees the node-trees (and xsltDocument structures) of all
206 * stylesheet-modules of the stylesheet-level represented by
207 * the given @style.
208 */
209void
210xsltFreeStyleDocuments(xsltStylesheetPtr style) {
211    xsltDocumentPtr doc, cur;
212#ifdef XSLT_REFACTORED_XSLT_NSCOMP
213    xsltNsMapPtr nsMap;
214#endif
215
216    if (style == NULL)
217	return;
218
219#ifdef XSLT_REFACTORED_XSLT_NSCOMP
220    if (XSLT_HAS_INTERNAL_NSMAP(style))
221	nsMap = XSLT_GET_INTERNAL_NSMAP(style);
222    else
223	nsMap = NULL;
224#endif
225
226    cur = style->docList;
227    while (cur != NULL) {
228	doc = cur;
229	cur = cur->next;
230#ifdef XSLT_REFACTORED_XSLT_NSCOMP
231	/*
232	* Restore all changed namespace URIs of ns-decls.
233	*/
234	if (nsMap)
235	    xsltRestoreDocumentNamespaces(nsMap, doc->doc);
236#endif
237	xsltFreeDocumentKeys(doc);
238	if (!doc->main)
239	    xmlFreeDoc(doc->doc);
240        xmlFree(doc);
241    }
242}
243
244/**
245 * xsltFreeDocuments:
246 * @ctxt: an XSLT transformation context
247 *
248 * Free up all the space used by the loaded documents
249 */
250void
251xsltFreeDocuments(xsltTransformContextPtr ctxt) {
252    xsltDocumentPtr doc, cur;
253
254    cur = ctxt->docList;
255    while (cur != NULL) {
256	doc = cur;
257	cur = cur->next;
258	xsltFreeDocumentKeys(doc);
259	if (!doc->main)
260	    xmlFreeDoc(doc->doc);
261        xmlFree(doc);
262    }
263    cur = ctxt->styleList;
264    while (cur != NULL) {
265	doc = cur;
266	cur = cur->next;
267	xsltFreeDocumentKeys(doc);
268	if (!doc->main)
269	    xmlFreeDoc(doc->doc);
270        xmlFree(doc);
271    }
272}
273
274/**
275 * xsltLoadDocument:
276 * @ctxt: an XSLT transformation context
277 * @URI:  the computed URI of the document
278 *
279 * Try to load a document (not a stylesheet)
280 * within the XSLT transformation context
281 *
282 * Returns the new xsltDocumentPtr or NULL in case of error
283 */
284xsltDocumentPtr
285xsltLoadDocument(xsltTransformContextPtr ctxt, const xmlChar *URI) {
286    xsltDocumentPtr ret;
287    xmlDocPtr doc;
288
289    if ((ctxt == NULL) || (URI == NULL))
290	return(NULL);
291
292    /*
293     * Security framework check
294     */
295    if (ctxt->sec != NULL) {
296	int res;
297
298	res = xsltCheckRead(ctxt->sec, ctxt, URI);
299	if (res == 0) {
300	    xsltTransformError(ctxt, NULL, NULL,
301		 "xsltLoadDocument: read rights for %s denied\n",
302			     URI);
303	    return(NULL);
304	}
305    }
306
307    /*
308     * Walk the context list to find the document if preparsed
309     */
310    ret = ctxt->docList;
311    while (ret != NULL) {
312	if ((ret->doc != NULL) && (ret->doc->URL != NULL) &&
313	    (xmlStrEqual(ret->doc->URL, URI)))
314	    return(ret);
315	ret = ret->next;
316    }
317
318    doc = xsltDocDefaultLoader(URI, ctxt->dict, ctxt->parserOptions,
319                               (void *) ctxt, XSLT_LOAD_DOCUMENT);
320
321    if (doc == NULL)
322	return(NULL);
323
324    if (ctxt->xinclude != 0) {
325#ifdef LIBXML_XINCLUDE_ENABLED
326#if LIBXML_VERSION >= 20603
327	xmlXIncludeProcessFlags(doc, ctxt->parserOptions);
328#else
329	xmlXIncludeProcess(doc);
330#endif
331#else
332	xsltTransformError(ctxt, NULL, NULL,
333	    "xsltLoadDocument(%s) : XInclude processing not compiled in\n",
334	                 URI);
335#endif
336    }
337    /*
338     * Apply white-space stripping if asked for
339     */
340    if (xsltNeedElemSpaceHandling(ctxt))
341	xsltApplyStripSpaces(ctxt, xmlDocGetRootElement(doc));
342    if (ctxt->debugStatus == XSLT_DEBUG_NONE)
343	xmlXPathOrderDocElems(doc);
344
345    ret = xsltNewDocument(ctxt, doc);
346    return(ret);
347}
348
349/**
350 * xsltLoadStyleDocument:
351 * @style: an XSLT style sheet
352 * @URI:  the computed URI of the document
353 *
354 * Try to load a stylesheet document within the XSLT transformation context
355 *
356 * Returns the new xsltDocumentPtr or NULL in case of error
357 */
358xsltDocumentPtr
359xsltLoadStyleDocument(xsltStylesheetPtr style, const xmlChar *URI) {
360    xsltDocumentPtr ret;
361    xmlDocPtr doc;
362    xsltSecurityPrefsPtr sec;
363
364    if ((style == NULL) || (URI == NULL))
365	return(NULL);
366
367    /*
368     * Security framework check
369     */
370    sec = xsltGetDefaultSecurityPrefs();
371    if (sec != NULL) {
372	int res;
373
374	res = xsltCheckRead(sec, NULL, URI);
375	if (res == 0) {
376	    xsltTransformError(NULL, NULL, NULL,
377		 "xsltLoadStyleDocument: read rights for %s denied\n",
378			     URI);
379	    return(NULL);
380	}
381    }
382
383    /*
384     * Walk the context list to find the document if preparsed
385     */
386    ret = style->docList;
387    while (ret != NULL) {
388	if ((ret->doc != NULL) && (ret->doc->URL != NULL) &&
389	    (xmlStrEqual(ret->doc->URL, URI)))
390	    return(ret);
391	ret = ret->next;
392    }
393
394    doc = xsltDocDefaultLoader(URI, style->dict, XSLT_PARSE_OPTIONS,
395                               (void *) style, XSLT_LOAD_STYLESHEET);
396    if (doc == NULL)
397	return(NULL);
398
399    ret = xsltNewStyleDocument(style, doc);
400    return(ret);
401}
402
403/**
404 * xsltFindDocument:
405 * @ctxt: an XSLT transformation context
406 * @doc: a parsed XML document
407 *
408 * Try to find a document within the XSLT transformation context.
409 * This will not find document infos for temporary
410 * Result Tree Fragments.
411 *
412 * Returns the desired xsltDocumentPtr or NULL in case of error
413 */
414xsltDocumentPtr
415xsltFindDocument (xsltTransformContextPtr ctxt, xmlDocPtr doc) {
416    xsltDocumentPtr ret;
417
418    if ((ctxt == NULL) || (doc == NULL))
419	return(NULL);
420
421    /*
422     * Walk the context list to find the document
423     */
424    ret = ctxt->docList;
425    while (ret != NULL) {
426	if (ret->doc == doc)
427	    return(ret);
428	ret = ret->next;
429    }
430    if (doc == ctxt->style->doc)
431	return(ctxt->document);
432    return(NULL);
433}
434
435