1/*
2 * imports.c: Implementation of the XSLT imports
3 *
4 * Reference:
5 *   http://www.w3.org/TR/1999/REC-xslt-19991116
6 *
7 * See Copyright for the status of this software.
8 *
9 * daniel@veillard.com
10 */
11
12#define IN_LIBXSLT
13#include "libxslt.h"
14
15#include <string.h>
16
17#ifdef HAVE_SYS_TYPES_H
18#include <sys/types.h>
19#endif
20#ifdef HAVE_MATH_H
21#include <math.h>
22#endif
23#ifdef HAVE_FLOAT_H
24#include <float.h>
25#endif
26#ifdef HAVE_IEEEFP_H
27#include <ieeefp.h>
28#endif
29#ifdef HAVE_NAN_H
30#include <nan.h>
31#endif
32#ifdef HAVE_CTYPE_H
33#include <ctype.h>
34#endif
35
36#include <libxml/xmlmemory.h>
37#include <libxml/tree.h>
38#include <libxml/hash.h>
39#include <libxml/xmlerror.h>
40#include <libxml/uri.h>
41#include "xslt.h"
42#include "xsltInternals.h"
43#include "xsltutils.h"
44#include "preproc.h"
45#include "imports.h"
46#include "documents.h"
47#include "security.h"
48#include "pattern.h"
49
50
51/************************************************************************
52 *									*
53 *			Module interfaces				*
54 *									*
55 ************************************************************************/
56/**
57 * xsltFixImportedCompSteps:
58 * @master: the "master" stylesheet
59 * @style: the stylesheet being imported by the master
60 *
61 * normalize the comp steps for the stylesheet being imported
62 * by the master, together with any imports within that.
63 *
64 */
65static void xsltFixImportedCompSteps(xsltStylesheetPtr master,
66			xsltStylesheetPtr style) {
67    xsltStylesheetPtr res;
68    xmlHashScan(style->templatesHash,
69	            (xmlHashScanner) xsltNormalizeCompSteps, master);
70    master->extrasNr += style->extrasNr;
71    for (res = style->imports; res != NULL; res = res->next) {
72        xsltFixImportedCompSteps(master, res);
73    }
74}
75
76/**
77 * xsltParseStylesheetImport:
78 * @style:  the XSLT stylesheet
79 * @cur:  the import element
80 *
81 * parse an XSLT stylesheet import element
82 *
83 * Returns 0 in case of success -1 in case of failure.
84 */
85
86int
87xsltParseStylesheetImport(xsltStylesheetPtr style, xmlNodePtr cur) {
88    int ret = -1;
89    xmlDocPtr import = NULL;
90    xmlChar *base = NULL;
91    xmlChar *uriRef = NULL;
92    xmlChar *URI = NULL;
93    xsltStylesheetPtr res;
94    xsltSecurityPrefsPtr sec;
95
96    if ((cur == NULL) || (style == NULL))
97	return (ret);
98
99    uriRef = xmlGetNsProp(cur, (const xmlChar *)"href", NULL);
100    if (uriRef == NULL) {
101	xsltTransformError(NULL, style, cur,
102	    "xsl:import : missing href attribute\n");
103	goto error;
104    }
105
106    base = xmlNodeGetBase(style->doc, cur);
107    URI = xmlBuildURI(uriRef, base);
108    if (URI == NULL) {
109	xsltTransformError(NULL, style, cur,
110	    "xsl:import : invalid URI reference %s\n", uriRef);
111	goto error;
112    }
113
114    res = style;
115    while (res != NULL) {
116        if (res->doc == NULL)
117	    break;
118	if (xmlStrEqual(res->doc->URL, URI)) {
119	    xsltTransformError(NULL, style, cur,
120	       "xsl:import : recursion detected on imported URL %s\n", URI);
121	    goto error;
122	}
123	res = res->parent;
124    }
125
126    /*
127     * Security framework check
128     */
129    sec = xsltGetDefaultSecurityPrefs();
130    if (sec != NULL) {
131	int secres;
132
133	secres = xsltCheckRead(sec, NULL, URI);
134	if (secres == 0) {
135	    xsltTransformError(NULL, NULL, NULL,
136		 "xsl:import: read rights for %s denied\n",
137			     URI);
138	    goto error;
139	}
140    }
141
142    import = xsltDocDefaultLoader(URI, style->dict, XSLT_PARSE_OPTIONS,
143                                  (void *) style, XSLT_LOAD_STYLESHEET);
144    if (import == NULL) {
145	xsltTransformError(NULL, style, cur,
146	    "xsl:import : unable to load %s\n", URI);
147	goto error;
148    }
149
150    res = xsltParseStylesheetImportedDoc(import, style);
151    if (res != NULL) {
152	res->next = style->imports;
153	style->imports = res;
154	if (style->parent == NULL) {
155	    xsltFixImportedCompSteps(style, res);
156	}
157	ret = 0;
158    } else {
159	xmlFreeDoc(import);
160	}
161
162error:
163    if (uriRef != NULL)
164	xmlFree(uriRef);
165    if (base != NULL)
166	xmlFree(base);
167    if (URI != NULL)
168	xmlFree(URI);
169
170    return (ret);
171}
172
173/**
174 * xsltParseStylesheetInclude:
175 * @style:  the XSLT stylesheet
176 * @cur:  the include node
177 *
178 * parse an XSLT stylesheet include element
179 *
180 * Returns 0 in case of success -1 in case of failure
181 */
182
183int
184xsltParseStylesheetInclude(xsltStylesheetPtr style, xmlNodePtr cur) {
185    int ret = -1;
186    xmlDocPtr oldDoc;
187    xmlChar *base = NULL;
188    xmlChar *uriRef = NULL;
189    xmlChar *URI = NULL;
190    xsltStylesheetPtr result;
191    xsltDocumentPtr include;
192    xsltDocumentPtr docptr;
193    int oldNopreproc;
194
195    if ((cur == NULL) || (style == NULL))
196	return (ret);
197
198    uriRef = xmlGetNsProp(cur, (const xmlChar *)"href", NULL);
199    if (uriRef == NULL) {
200	xsltTransformError(NULL, style, cur,
201	    "xsl:include : missing href attribute\n");
202	goto error;
203    }
204
205    base = xmlNodeGetBase(style->doc, cur);
206    URI = xmlBuildURI(uriRef, base);
207    if (URI == NULL) {
208	xsltTransformError(NULL, style, cur,
209	    "xsl:include : invalid URI reference %s\n", uriRef);
210	goto error;
211    }
212
213    /*
214     * in order to detect recursion, we check all previously included
215     * stylesheets.
216     */
217    docptr = style->includes;
218    while (docptr != NULL) {
219        if (xmlStrEqual(docptr->doc->URL, URI)) {
220	    xsltTransformError(NULL, style, cur,
221	        "xsl:include : recursion detected on included URL %s\n", URI);
222	    goto error;
223	}
224	docptr = docptr->includes;
225    }
226
227    include = xsltLoadStyleDocument(style, URI);
228    if (include == NULL) {
229	xsltTransformError(NULL, style, cur,
230	    "xsl:include : unable to load %s\n", URI);
231	goto error;
232    }
233#ifdef XSLT_REFACTORED
234    if (IS_XSLT_ELEM_FAST(cur) && (cur->psvi != NULL)) {
235	((xsltStyleItemIncludePtr) cur->psvi)->include = include;
236    } else {
237	xsltTransformError(NULL, style, cur,
238	    "Internal error: (xsltParseStylesheetInclude) "
239	    "The xsl:include element was not compiled.\n", URI);
240	style->errors++;
241    }
242#endif
243    oldDoc = style->doc;
244    style->doc = include->doc;
245    /* chain to stylesheet for recursion checking */
246    include->includes = style->includes;
247    style->includes = include;
248    oldNopreproc = style->nopreproc;
249    style->nopreproc = include->preproc;
250    /*
251    * TODO: This will change some values of the
252    *  including stylesheet with every included module
253    *  (e.g. excluded-result-prefixes)
254    *  We need to strictly seperate such stylesheet-owned values.
255    */
256    result = xsltParseStylesheetProcess(style, include->doc);
257    style->nopreproc = oldNopreproc;
258    include->preproc = 1;
259    style->includes = include->includes;
260    style->doc = oldDoc;
261    if (result == NULL) {
262	ret = -1;
263	goto error;
264    }
265    ret = 0;
266
267error:
268    if (uriRef != NULL)
269	xmlFree(uriRef);
270    if (base != NULL)
271	xmlFree(base);
272    if (URI != NULL)
273	xmlFree(URI);
274
275    return (ret);
276}
277
278/**
279 * xsltNextImport:
280 * @cur:  the current XSLT stylesheet
281 *
282 * Find the next stylesheet in import precedence.
283 *
284 * Returns the next stylesheet or NULL if it was the last one
285 */
286
287xsltStylesheetPtr
288xsltNextImport(xsltStylesheetPtr cur) {
289    if (cur == NULL)
290	return(NULL);
291    if (cur->imports != NULL)
292	return(cur->imports);
293    if (cur->next != NULL)
294	return(cur->next) ;
295    do {
296	cur = cur->parent;
297	if (cur == NULL) break;
298	if (cur->next != NULL) return(cur->next);
299    } while (cur != NULL);
300    return(cur);
301}
302
303/**
304 * xsltNeedElemSpaceHandling:
305 * @ctxt:  an XSLT transformation context
306 *
307 * Checks whether that stylesheet requires white-space stripping
308 *
309 * Returns 1 if space should be stripped, 0 if not
310 */
311
312int
313xsltNeedElemSpaceHandling(xsltTransformContextPtr ctxt) {
314    xsltStylesheetPtr style;
315
316    if (ctxt == NULL)
317	return(0);
318    style = ctxt->style;
319    while (style != NULL) {
320	if (style->stripSpaces != NULL)
321	    return(1);
322	style = xsltNextImport(style);
323    }
324    return(0);
325}
326
327/**
328 * xsltFindElemSpaceHandling:
329 * @ctxt:  an XSLT transformation context
330 * @node:  an XML node
331 *
332 * Find strip-space or preserve-space informations for an element
333 * respect the import precedence or the wildcards
334 *
335 * Returns 1 if space should be stripped, 0 if not, and 2 if everything
336 *         should be CDTATA wrapped.
337 */
338
339int
340xsltFindElemSpaceHandling(xsltTransformContextPtr ctxt, xmlNodePtr node) {
341    xsltStylesheetPtr style;
342    const xmlChar *val;
343
344    if ((ctxt == NULL) || (node == NULL))
345	return(0);
346    style = ctxt->style;
347    while (style != NULL) {
348	if (node->ns != NULL) {
349	    val = (const xmlChar *)
350	      xmlHashLookup2(style->stripSpaces, node->name, node->ns->href);
351            if (val == NULL) {
352                val = (const xmlChar *)
353                    xmlHashLookup2(style->stripSpaces, BAD_CAST "*",
354                                   node->ns->href);
355            }
356	} else {
357	    val = (const xmlChar *)
358		  xmlHashLookup2(style->stripSpaces, node->name, NULL);
359	}
360	if (val != NULL) {
361	    if (xmlStrEqual(val, (xmlChar *) "strip"))
362		return(1);
363	    if (xmlStrEqual(val, (xmlChar *) "preserve"))
364		return(0);
365	}
366	if (style->stripAll == 1)
367	    return(1);
368	if (style->stripAll == -1)
369	    return(0);
370
371	style = xsltNextImport(style);
372    }
373    return(0);
374}
375
376/**
377 * xsltFindTemplate:
378 * @ctxt:  an XSLT transformation context
379 * @name: the template name
380 * @nameURI: the template name URI
381 *
382 * Finds the named template, apply import precedence rule.
383 * REVISIT TODO: We'll change the nameURI fields of
384 *  templates to be in the string dict, so if the
385 *  specified @nameURI is in the same dict, then use pointer
386 *  comparison. Check if this can be done in a sane way.
387 *  Maybe this function is not needed internally at
388 *  transformation-time if we hard-wire the called templates
389 *  to the caller.
390 *
391 * Returns the xsltTemplatePtr or NULL if not found
392 */
393xsltTemplatePtr
394xsltFindTemplate(xsltTransformContextPtr ctxt, const xmlChar *name,
395	         const xmlChar *nameURI) {
396    xsltTemplatePtr cur;
397    xsltStylesheetPtr style;
398
399    if ((ctxt == NULL) || (name == NULL))
400	return(NULL);
401    style = ctxt->style;
402    while (style != NULL) {
403	cur = style->templates;
404	while (cur != NULL) {
405	    if (xmlStrEqual(name, cur->name)) {
406		if (((nameURI == NULL) && (cur->nameURI == NULL)) ||
407		    ((nameURI != NULL) && (cur->nameURI != NULL) &&
408		     (xmlStrEqual(nameURI, cur->nameURI)))) {
409		    return(cur);
410		}
411	    }
412	    cur = cur->next;
413	}
414
415	style = xsltNextImport(style);
416    }
417    return(NULL);
418}
419
420