/src/libxslt/libxslt/documents.c
Line | Count | Source (jump to first uncovered line) |
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 | | */ |
60 | | static xmlDocPtr |
61 | | xsltDocDefaultLoaderFunc(const xmlChar * URI, xmlDictPtr dict, int options, |
62 | | void *ctxt ATTRIBUTE_UNUSED, |
63 | | xsltLoadType type ATTRIBUTE_UNUSED) |
64 | 302k | { |
65 | 302k | xmlParserCtxtPtr pctxt; |
66 | 302k | xmlParserInputPtr inputStream; |
67 | 302k | xmlDocPtr doc; |
68 | | |
69 | 302k | pctxt = xmlNewParserCtxt(); |
70 | 302k | if (pctxt == NULL) |
71 | 148 | return(NULL); |
72 | 302k | if ((dict != NULL) && (pctxt->dict != NULL)) { |
73 | 302k | xmlDictFree(pctxt->dict); |
74 | 302k | pctxt->dict = NULL; |
75 | 302k | } |
76 | 302k | if (dict != NULL) { |
77 | 302k | pctxt->dict = dict; |
78 | 302k | xmlDictReference(pctxt->dict); |
79 | | #ifdef WITH_XSLT_DEBUG |
80 | | xsltGenericDebug(xsltGenericDebugContext, |
81 | | "Reusing dictionary for document\n"); |
82 | | #endif |
83 | 302k | } |
84 | 302k | xmlCtxtUseOptions(pctxt, options); |
85 | 302k | inputStream = xmlLoadExternalEntity((const char *) URI, NULL, pctxt); |
86 | 302k | if (inputStream == NULL) { |
87 | 168k | xmlFreeParserCtxt(pctxt); |
88 | 168k | return(NULL); |
89 | 168k | } |
90 | 133k | inputPush(pctxt, inputStream); |
91 | 133k | if (pctxt->directory == NULL) |
92 | 133k | pctxt->directory = xmlParserGetDirectory((const char *) URI); |
93 | | |
94 | 133k | xmlParseDocument(pctxt); |
95 | | |
96 | 133k | if (pctxt->wellFormed) { |
97 | 38.1k | doc = pctxt->myDoc; |
98 | 38.1k | } |
99 | 95.1k | else { |
100 | 95.1k | doc = NULL; |
101 | 95.1k | xmlFreeDoc(pctxt->myDoc); |
102 | 95.1k | pctxt->myDoc = NULL; |
103 | 95.1k | } |
104 | 133k | xmlFreeParserCtxt(pctxt); |
105 | | |
106 | 133k | return(doc); |
107 | 302k | } |
108 | | |
109 | | |
110 | | xsltDocLoaderFunc 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 | | |
120 | | void |
121 | 0 | xsltSetLoaderFunc(xsltDocLoaderFunc f) { |
122 | 0 | if (f == NULL) |
123 | 0 | xsltDocDefaultLoader = xsltDocDefaultLoaderFunc; |
124 | 0 | else |
125 | 0 | xsltDocDefaultLoader = f; |
126 | 0 | } |
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 | | */ |
143 | | xsltDocumentPtr |
144 | 17.4k | xsltNewDocument(xsltTransformContextPtr ctxt, xmlDocPtr doc) { |
145 | 17.4k | xsltDocumentPtr cur; |
146 | | |
147 | 17.4k | cur = (xsltDocumentPtr) xmlMalloc(sizeof(xsltDocument)); |
148 | 17.4k | if (cur == NULL) { |
149 | 62 | xsltTransformError(ctxt, NULL, (xmlNodePtr) doc, |
150 | 62 | "xsltNewDocument : malloc failed\n"); |
151 | 62 | return(NULL); |
152 | 62 | } |
153 | 17.3k | memset(cur, 0, sizeof(xsltDocument)); |
154 | 17.3k | cur->doc = doc; |
155 | 17.3k | if (ctxt != NULL) { |
156 | 17.3k | if (! XSLT_IS_RES_TREE_FRAG(doc)) { |
157 | 12.2k | cur->next = ctxt->docList; |
158 | 12.2k | ctxt->docList = cur; |
159 | 12.2k | } |
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 | 17.3k | } |
170 | 17.3k | return(cur); |
171 | 17.4k | } |
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 | | */ |
182 | | xsltDocumentPtr |
183 | 2.61k | xsltNewStyleDocument(xsltStylesheetPtr style, xmlDocPtr doc) { |
184 | 2.61k | xsltDocumentPtr cur; |
185 | | |
186 | 2.61k | cur = (xsltDocumentPtr) xmlMalloc(sizeof(xsltDocument)); |
187 | 2.61k | if (cur == NULL) { |
188 | 17 | xsltTransformError(NULL, style, (xmlNodePtr) doc, |
189 | 17 | "xsltNewStyleDocument : malloc failed\n"); |
190 | 17 | return(NULL); |
191 | 17 | } |
192 | 2.60k | memset(cur, 0, sizeof(xsltDocument)); |
193 | 2.60k | cur->doc = doc; |
194 | 2.60k | if (style != NULL) { |
195 | 2.60k | cur->next = style->docList; |
196 | 2.60k | style->docList = cur; |
197 | 2.60k | } |
198 | 2.60k | return(cur); |
199 | 2.61k | } |
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 | | */ |
209 | | void |
210 | 56.3k | xsltFreeStyleDocuments(xsltStylesheetPtr style) { |
211 | 56.3k | xsltDocumentPtr doc, cur; |
212 | | #ifdef XSLT_REFACTORED_XSLT_NSCOMP |
213 | | xsltNsMapPtr nsMap; |
214 | | #endif |
215 | | |
216 | 56.3k | if (style == NULL) |
217 | 0 | 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 | 56.3k | cur = style->docList; |
227 | 58.9k | while (cur != NULL) { |
228 | 2.60k | doc = cur; |
229 | 2.60k | 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 | 2.60k | xsltFreeDocumentKeys(doc); |
238 | 2.60k | if (!doc->main) |
239 | 2.60k | xmlFreeDoc(doc->doc); |
240 | 2.60k | xmlFree(doc); |
241 | 2.60k | } |
242 | 56.3k | } |
243 | | |
244 | | /** |
245 | | * xsltFreeDocuments: |
246 | | * @ctxt: an XSLT transformation context |
247 | | * |
248 | | * Free up all the space used by the loaded documents |
249 | | */ |
250 | | void |
251 | 12.3k | xsltFreeDocuments(xsltTransformContextPtr ctxt) { |
252 | 12.3k | xsltDocumentPtr doc, cur; |
253 | | |
254 | 12.3k | cur = ctxt->docList; |
255 | 24.6k | while (cur != NULL) { |
256 | 12.2k | doc = cur; |
257 | 12.2k | cur = cur->next; |
258 | 12.2k | xsltFreeDocumentKeys(doc); |
259 | 12.2k | if (!doc->main) |
260 | 0 | xmlFreeDoc(doc->doc); |
261 | 12.2k | xmlFree(doc); |
262 | 12.2k | } |
263 | 12.3k | cur = ctxt->styleList; |
264 | 12.3k | while (cur != NULL) { |
265 | 0 | doc = cur; |
266 | 0 | cur = cur->next; |
267 | 0 | xsltFreeDocumentKeys(doc); |
268 | 0 | if (!doc->main) |
269 | 0 | xmlFreeDoc(doc->doc); |
270 | 0 | xmlFree(doc); |
271 | 0 | } |
272 | 12.3k | } |
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 | | */ |
284 | | xsltDocumentPtr |
285 | 15.7k | xsltLoadDocument(xsltTransformContextPtr ctxt, const xmlChar *URI) { |
286 | 15.7k | xsltDocumentPtr ret; |
287 | 15.7k | xmlDocPtr doc; |
288 | | |
289 | 15.7k | if ((ctxt == NULL) || (URI == NULL)) |
290 | 0 | return(NULL); |
291 | | |
292 | | /* |
293 | | * Security framework check |
294 | | */ |
295 | 15.7k | if (ctxt->sec != NULL) { |
296 | 15.7k | int res; |
297 | | |
298 | 15.7k | res = xsltCheckRead(ctxt->sec, ctxt, URI); |
299 | 15.7k | if (res <= 0) { |
300 | 15.7k | if (res == 0) |
301 | 15.7k | xsltTransformError(ctxt, NULL, NULL, |
302 | 15.7k | "xsltLoadDocument: read rights for %s denied\n", |
303 | 15.7k | URI); |
304 | 15.7k | return(NULL); |
305 | 15.7k | } |
306 | 15.7k | } |
307 | | |
308 | | /* |
309 | | * Walk the context list to find the document if preparsed |
310 | | */ |
311 | 0 | ret = ctxt->docList; |
312 | 0 | while (ret != NULL) { |
313 | 0 | if ((ret->doc != NULL) && (ret->doc->URL != NULL) && |
314 | 0 | (xmlStrEqual(ret->doc->URL, URI))) |
315 | 0 | return(ret); |
316 | 0 | ret = ret->next; |
317 | 0 | } |
318 | | |
319 | 0 | doc = xsltDocDefaultLoader(URI, ctxt->dict, ctxt->parserOptions, |
320 | 0 | (void *) ctxt, XSLT_LOAD_DOCUMENT); |
321 | |
|
322 | 0 | if (doc == NULL) |
323 | 0 | return(NULL); |
324 | | |
325 | 0 | if (ctxt->xinclude != 0) { |
326 | 0 | #ifdef LIBXML_XINCLUDE_ENABLED |
327 | 0 | #if LIBXML_VERSION >= 20603 |
328 | 0 | xmlXIncludeProcessFlags(doc, ctxt->parserOptions); |
329 | | #else |
330 | | xmlXIncludeProcess(doc); |
331 | | #endif |
332 | | #else |
333 | | xsltTransformError(ctxt, NULL, NULL, |
334 | | "xsltLoadDocument(%s) : XInclude processing not compiled in\n", |
335 | | URI); |
336 | | #endif |
337 | 0 | } |
338 | | /* |
339 | | * Apply white-space stripping if asked for |
340 | | */ |
341 | 0 | if (xsltNeedElemSpaceHandling(ctxt)) |
342 | 0 | xsltApplyStripSpaces(ctxt, xmlDocGetRootElement(doc)); |
343 | 0 | if (ctxt->debugStatus == XSLT_DEBUG_NONE) |
344 | 0 | xmlXPathOrderDocElems(doc); |
345 | |
|
346 | 0 | ret = xsltNewDocument(ctxt, doc); |
347 | 0 | return(ret); |
348 | 0 | } |
349 | | |
350 | | /** |
351 | | * xsltLoadStyleDocument: |
352 | | * @style: an XSLT style sheet |
353 | | * @URI: the computed URI of the document |
354 | | * |
355 | | * Try to load a stylesheet document within the XSLT transformation context |
356 | | * |
357 | | * Returns the new xsltDocumentPtr or NULL in case of error |
358 | | */ |
359 | | xsltDocumentPtr |
360 | 198k | xsltLoadStyleDocument(xsltStylesheetPtr style, const xmlChar *URI) { |
361 | 198k | xsltDocumentPtr ret; |
362 | 198k | xmlDocPtr doc; |
363 | 198k | xsltSecurityPrefsPtr sec; |
364 | | |
365 | 198k | if ((style == NULL) || (URI == NULL)) |
366 | 0 | return(NULL); |
367 | | |
368 | | /* |
369 | | * Security framework check |
370 | | */ |
371 | 198k | sec = xsltGetDefaultSecurityPrefs(); |
372 | 198k | if (sec != NULL) { |
373 | 0 | int res; |
374 | |
|
375 | 0 | res = xsltCheckRead(sec, NULL, URI); |
376 | 0 | if (res <= 0) { |
377 | 0 | if (res == 0) |
378 | 0 | xsltTransformError(NULL, NULL, NULL, |
379 | 0 | "xsltLoadStyleDocument: read rights for %s denied\n", |
380 | 0 | URI); |
381 | 0 | return(NULL); |
382 | 0 | } |
383 | 0 | } |
384 | | |
385 | | /* |
386 | | * Walk the context list to find the document if preparsed |
387 | | */ |
388 | 198k | ret = style->docList; |
389 | 227k | while (ret != NULL) { |
390 | 44.9k | if ((ret->doc != NULL) && (ret->doc->URL != NULL) && |
391 | 44.9k | (xmlStrEqual(ret->doc->URL, URI))) |
392 | 16.1k | return(ret); |
393 | 28.7k | ret = ret->next; |
394 | 28.7k | } |
395 | | |
396 | 182k | doc = xsltDocDefaultLoader(URI, style->dict, XSLT_PARSE_OPTIONS, |
397 | 182k | (void *) style, XSLT_LOAD_STYLESHEET); |
398 | 182k | if (doc == NULL) |
399 | 179k | return(NULL); |
400 | | |
401 | 2.61k | ret = xsltNewStyleDocument(style, doc); |
402 | 2.61k | if (ret == NULL) |
403 | 17 | xmlFreeDoc(doc); |
404 | 2.61k | return(ret); |
405 | 182k | } |
406 | | |
407 | | /** |
408 | | * xsltFindDocument: |
409 | | * @ctxt: an XSLT transformation context |
410 | | * @doc: a parsed XML document |
411 | | * |
412 | | * Try to find a document within the XSLT transformation context. |
413 | | * This will not find document infos for temporary |
414 | | * Result Tree Fragments. |
415 | | * |
416 | | * Returns the desired xsltDocumentPtr or NULL in case of error |
417 | | */ |
418 | | xsltDocumentPtr |
419 | 0 | xsltFindDocument (xsltTransformContextPtr ctxt, xmlDocPtr doc) { |
420 | 0 | xsltDocumentPtr ret; |
421 | |
|
422 | 0 | if ((ctxt == NULL) || (doc == NULL)) |
423 | 0 | return(NULL); |
424 | | |
425 | | /* |
426 | | * Walk the context list to find the document |
427 | | */ |
428 | 0 | ret = ctxt->docList; |
429 | 0 | while (ret != NULL) { |
430 | 0 | if (ret->doc == doc) |
431 | 0 | return(ret); |
432 | 0 | ret = ret->next; |
433 | 0 | } |
434 | 0 | if (doc == ctxt->style->doc) |
435 | 0 | return(ctxt->document); |
436 | 0 | return(NULL); |
437 | 0 | } |
438 | | |