Line data Source code
1 : /*
2 : * contrib/xml2/xslt_proc.c
3 : *
4 : * XSLT processing functions (requiring libxslt)
5 : *
6 : * John Gray, for Torchbox 2003-04-01
7 : */
8 : #include "postgres.h"
9 :
10 : #include "fmgr.h"
11 : #include "utils/builtins.h"
12 : #include "utils/xml.h"
13 : #include "varatt.h"
14 :
15 : #ifdef USE_LIBXSLT
16 :
17 : /* libxml includes */
18 :
19 : #include <libxml/xpath.h>
20 : #include <libxml/tree.h>
21 : #include <libxml/xmlmemory.h>
22 :
23 : /* libxslt includes */
24 :
25 : #include <libxslt/xslt.h>
26 : #include <libxslt/xsltInternals.h>
27 : #include <libxslt/security.h>
28 : #include <libxslt/transform.h>
29 : #include <libxslt/xsltutils.h>
30 : #endif /* USE_LIBXSLT */
31 :
32 :
33 : #ifdef USE_LIBXSLT
34 :
35 : /* declarations to come from xpath.c */
36 : extern PgXmlErrorContext *pgxml_parser_init(PgXmlStrictness strictness);
37 :
38 : /* local defs */
39 : static const char **parse_params(text *paramstr);
40 : #endif /* USE_LIBXSLT */
41 :
42 :
43 4 : PG_FUNCTION_INFO_V1(xslt_process);
44 :
45 : Datum
46 6 : xslt_process(PG_FUNCTION_ARGS)
47 : {
48 : #ifdef USE_LIBXSLT
49 :
50 : text *doct = PG_GETARG_TEXT_PP(0);
51 : text *ssheet = PG_GETARG_TEXT_PP(1);
52 : text *volatile result = NULL;
53 : text *paramstr;
54 : const char **params;
55 : PgXmlErrorContext *xmlerrcxt;
56 : volatile xsltStylesheetPtr stylesheet = NULL;
57 : volatile xmlDocPtr doctree = NULL;
58 : volatile xmlDocPtr ssdoc = NULL;
59 : volatile xmlDocPtr restree = NULL;
60 : volatile xsltSecurityPrefsPtr xslt_sec_prefs = NULL;
61 : volatile xsltTransformContextPtr xslt_ctxt = NULL;
62 : volatile int resstat = -1;
63 : xmlChar *volatile resstr = NULL;
64 :
65 : if (fcinfo->nargs == 3)
66 : {
67 : paramstr = PG_GETARG_TEXT_PP(2);
68 : params = parse_params(paramstr);
69 : }
70 : else
71 : {
72 : /* No parameters */
73 : params = palloc_object(const char *);
74 : params[0] = NULL;
75 : }
76 :
77 : /* Setup parser */
78 : xmlerrcxt = pgxml_parser_init(PG_XML_STRICTNESS_LEGACY);
79 :
80 : PG_TRY();
81 : {
82 : bool xslt_sec_prefs_error;
83 : int reslen = 0;
84 :
85 : /* Parse document */
86 : doctree = xmlReadMemory((char *) VARDATA_ANY(doct),
87 : VARSIZE_ANY_EXHDR(doct), NULL, NULL,
88 : XML_PARSE_NOENT);
89 :
90 : if (doctree == NULL || pg_xml_error_occurred(xmlerrcxt))
91 : xml_ereport(xmlerrcxt, ERROR, ERRCODE_INVALID_XML_DOCUMENT,
92 : "error parsing XML document");
93 :
94 : /* Same for stylesheet */
95 : ssdoc = xmlReadMemory((char *) VARDATA_ANY(ssheet),
96 : VARSIZE_ANY_EXHDR(ssheet), NULL, NULL,
97 : XML_PARSE_NOENT);
98 :
99 : if (ssdoc == NULL || pg_xml_error_occurred(xmlerrcxt))
100 : xml_ereport(xmlerrcxt, ERROR, ERRCODE_INVALID_XML_DOCUMENT,
101 : "error parsing stylesheet as XML document");
102 :
103 : /*
104 : * On success, the stylesheet owns ssdoc, with xsltFreeStylesheet()
105 : * calling xmlFreeDoc() on its associated doc.
106 : */
107 : stylesheet = xsltParseStylesheetDoc(ssdoc);
108 : if (stylesheet != NULL)
109 : ssdoc = NULL;
110 :
111 : if (stylesheet == NULL || pg_xml_error_occurred(xmlerrcxt))
112 : xml_ereport(xmlerrcxt, ERROR, ERRCODE_INVALID_ARGUMENT_FOR_XQUERY,
113 : "failed to parse stylesheet");
114 :
115 : xslt_ctxt = xsltNewTransformContext(stylesheet, doctree);
116 :
117 : xslt_sec_prefs_error = false;
118 : if ((xslt_sec_prefs = xsltNewSecurityPrefs()) == NULL)
119 : xslt_sec_prefs_error = true;
120 :
121 : if (xsltSetSecurityPrefs(xslt_sec_prefs, XSLT_SECPREF_READ_FILE,
122 : xsltSecurityForbid) != 0)
123 : xslt_sec_prefs_error = true;
124 : if (xsltSetSecurityPrefs(xslt_sec_prefs, XSLT_SECPREF_WRITE_FILE,
125 : xsltSecurityForbid) != 0)
126 : xslt_sec_prefs_error = true;
127 : if (xsltSetSecurityPrefs(xslt_sec_prefs, XSLT_SECPREF_CREATE_DIRECTORY,
128 : xsltSecurityForbid) != 0)
129 : xslt_sec_prefs_error = true;
130 : if (xsltSetSecurityPrefs(xslt_sec_prefs, XSLT_SECPREF_READ_NETWORK,
131 : xsltSecurityForbid) != 0)
132 : xslt_sec_prefs_error = true;
133 : if (xsltSetSecurityPrefs(xslt_sec_prefs, XSLT_SECPREF_WRITE_NETWORK,
134 : xsltSecurityForbid) != 0)
135 : xslt_sec_prefs_error = true;
136 : if (xsltSetCtxtSecurityPrefs(xslt_sec_prefs, xslt_ctxt) != 0)
137 : xslt_sec_prefs_error = true;
138 :
139 : if (xslt_sec_prefs_error)
140 : ereport(ERROR,
141 : (errmsg("could not set libxslt security preferences")));
142 :
143 : restree = xsltApplyStylesheetUser(stylesheet, doctree, params,
144 : NULL, NULL, xslt_ctxt);
145 :
146 : if (restree == NULL || pg_xml_error_occurred(xmlerrcxt))
147 : xml_ereport(xmlerrcxt, ERROR, ERRCODE_INVALID_ARGUMENT_FOR_XQUERY,
148 : "failed to apply stylesheet");
149 :
150 : resstat = xsltSaveResultToString((xmlChar **) &resstr, &reslen,
151 : restree, stylesheet);
152 :
153 : if (resstat >= 0)
154 : {
155 : /*
156 : * If an empty string has been returned, resstr would be NULL. In
157 : * this case, assume that the result is an empty string.
158 : */
159 : if (reslen == 0)
160 : result = cstring_to_text("");
161 : else
162 : result = cstring_to_text_with_len((char *) resstr, reslen);
163 : }
164 : }
165 : PG_CATCH();
166 : {
167 : if (restree != NULL)
168 : xmlFreeDoc(restree);
169 : if (xslt_ctxt != NULL)
170 : xsltFreeTransformContext(xslt_ctxt);
171 : if (xslt_sec_prefs != NULL)
172 : xsltFreeSecurityPrefs(xslt_sec_prefs);
173 : if (stylesheet != NULL)
174 : xsltFreeStylesheet(stylesheet);
175 : if (ssdoc != NULL)
176 : xmlFreeDoc(ssdoc);
177 : if (doctree != NULL)
178 : xmlFreeDoc(doctree);
179 : if (resstr != NULL)
180 : xmlFree(resstr);
181 : xsltCleanupGlobals();
182 :
183 : pg_xml_done(xmlerrcxt, true);
184 :
185 : PG_RE_THROW();
186 : }
187 : PG_END_TRY();
188 :
189 : xmlFreeDoc(restree);
190 : xsltFreeTransformContext(xslt_ctxt);
191 : xsltFreeSecurityPrefs(xslt_sec_prefs);
192 : xsltFreeStylesheet(stylesheet);
193 : xmlFreeDoc(doctree);
194 : xsltCleanupGlobals();
195 :
196 : if (resstr)
197 : xmlFree(resstr);
198 :
199 : pg_xml_done(xmlerrcxt, false);
200 :
201 : /* XXX this is pretty dubious, really ought to throw error instead */
202 : if (resstat < 0)
203 : PG_RETURN_NULL();
204 :
205 : PG_RETURN_TEXT_P(result);
206 : #else /* !USE_LIBXSLT */
207 :
208 6 : ereport(ERROR,
209 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
210 : errmsg("xslt_process() is not available without libxslt")));
211 : PG_RETURN_NULL();
212 : #endif /* USE_LIBXSLT */
213 : }
214 :
215 : #ifdef USE_LIBXSLT
216 :
217 : static const char **
218 : parse_params(text *paramstr)
219 : {
220 : char *pos;
221 : char *pstr;
222 : char *nvsep = "=";
223 : char *itsep = ",";
224 : const char **params;
225 : int max_params;
226 : int nparams;
227 :
228 : pstr = text_to_cstring(paramstr);
229 :
230 : max_params = 20; /* must be even! */
231 : params = (const char **) palloc((max_params + 1) * sizeof(char *));
232 : nparams = 0;
233 :
234 : pos = pstr;
235 :
236 : while (*pos != '\0')
237 : {
238 : if (nparams >= max_params)
239 : {
240 : max_params *= 2;
241 : params = (const char **) repalloc(params,
242 : (max_params + 1) * sizeof(char *));
243 : }
244 : params[nparams++] = pos;
245 : pos = strstr(pos, nvsep);
246 : if (pos != NULL)
247 : {
248 : *pos = '\0';
249 : pos++;
250 : }
251 : else
252 : {
253 : /* No equal sign, so ignore this "parameter" */
254 : nparams--;
255 : break;
256 : }
257 :
258 : /* since max_params is even, we still have nparams < max_params */
259 : params[nparams++] = pos;
260 : pos = strstr(pos, itsep);
261 : if (pos != NULL)
262 : {
263 : *pos = '\0';
264 : pos++;
265 : }
266 : else
267 : break;
268 : }
269 :
270 : /* Add the terminator marker; we left room for it in the palloc's */
271 : params[nparams] = NULL;
272 :
273 : return params;
274 : }
275 :
276 : #endif /* USE_LIBXSLT */
|