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 restree = NULL;
59 : volatile xsltSecurityPrefsPtr xslt_sec_prefs = NULL;
60 : volatile xsltTransformContextPtr xslt_ctxt = NULL;
61 : volatile int resstat = -1;
62 : xmlChar *volatile resstr = NULL;
63 :
64 : if (fcinfo->nargs == 3)
65 : {
66 : paramstr = PG_GETARG_TEXT_PP(2);
67 : params = parse_params(paramstr);
68 : }
69 : else
70 : {
71 : /* No parameters */
72 : params = palloc_object(const char *);
73 : params[0] = NULL;
74 : }
75 :
76 : /* Setup parser */
77 : xmlerrcxt = pgxml_parser_init(PG_XML_STRICTNESS_LEGACY);
78 :
79 : PG_TRY();
80 : {
81 : xmlDocPtr ssdoc;
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 : /* After this call we need not free ssdoc separately */
104 : stylesheet = xsltParseStylesheetDoc(ssdoc);
105 :
106 : if (stylesheet == NULL || pg_xml_error_occurred(xmlerrcxt))
107 : xml_ereport(xmlerrcxt, ERROR, ERRCODE_INVALID_ARGUMENT_FOR_XQUERY,
108 : "failed to parse stylesheet");
109 :
110 : xslt_ctxt = xsltNewTransformContext(stylesheet, doctree);
111 :
112 : xslt_sec_prefs_error = false;
113 : if ((xslt_sec_prefs = xsltNewSecurityPrefs()) == NULL)
114 : xslt_sec_prefs_error = true;
115 :
116 : if (xsltSetSecurityPrefs(xslt_sec_prefs, XSLT_SECPREF_READ_FILE,
117 : xsltSecurityForbid) != 0)
118 : xslt_sec_prefs_error = true;
119 : if (xsltSetSecurityPrefs(xslt_sec_prefs, XSLT_SECPREF_WRITE_FILE,
120 : xsltSecurityForbid) != 0)
121 : xslt_sec_prefs_error = true;
122 : if (xsltSetSecurityPrefs(xslt_sec_prefs, XSLT_SECPREF_CREATE_DIRECTORY,
123 : xsltSecurityForbid) != 0)
124 : xslt_sec_prefs_error = true;
125 : if (xsltSetSecurityPrefs(xslt_sec_prefs, XSLT_SECPREF_READ_NETWORK,
126 : xsltSecurityForbid) != 0)
127 : xslt_sec_prefs_error = true;
128 : if (xsltSetSecurityPrefs(xslt_sec_prefs, XSLT_SECPREF_WRITE_NETWORK,
129 : xsltSecurityForbid) != 0)
130 : xslt_sec_prefs_error = true;
131 : if (xsltSetCtxtSecurityPrefs(xslt_sec_prefs, xslt_ctxt) != 0)
132 : xslt_sec_prefs_error = true;
133 :
134 : if (xslt_sec_prefs_error)
135 : ereport(ERROR,
136 : (errmsg("could not set libxslt security preferences")));
137 :
138 : restree = xsltApplyStylesheetUser(stylesheet, doctree, params,
139 : NULL, NULL, xslt_ctxt);
140 :
141 : if (restree == NULL || pg_xml_error_occurred(xmlerrcxt))
142 : xml_ereport(xmlerrcxt, ERROR, ERRCODE_INVALID_ARGUMENT_FOR_XQUERY,
143 : "failed to apply stylesheet");
144 :
145 : resstat = xsltSaveResultToString((xmlChar **) &resstr, &reslen,
146 : restree, stylesheet);
147 :
148 : if (resstat >= 0)
149 : {
150 : /*
151 : * If an empty string has been returned, resstr would be NULL. In
152 : * this case, assume that the result is an empty string.
153 : */
154 : if (reslen == 0)
155 : result = cstring_to_text("");
156 : else
157 : result = cstring_to_text_with_len((char *) resstr, reslen);
158 : }
159 : }
160 : PG_CATCH();
161 : {
162 : if (restree != NULL)
163 : xmlFreeDoc(restree);
164 : if (xslt_ctxt != NULL)
165 : xsltFreeTransformContext(xslt_ctxt);
166 : if (xslt_sec_prefs != NULL)
167 : xsltFreeSecurityPrefs(xslt_sec_prefs);
168 : if (stylesheet != NULL)
169 : xsltFreeStylesheet(stylesheet);
170 : if (doctree != NULL)
171 : xmlFreeDoc(doctree);
172 : if (resstr != NULL)
173 : xmlFree(resstr);
174 : xsltCleanupGlobals();
175 :
176 : pg_xml_done(xmlerrcxt, true);
177 :
178 : PG_RE_THROW();
179 : }
180 : PG_END_TRY();
181 :
182 : xmlFreeDoc(restree);
183 : xsltFreeTransformContext(xslt_ctxt);
184 : xsltFreeSecurityPrefs(xslt_sec_prefs);
185 : xsltFreeStylesheet(stylesheet);
186 : xmlFreeDoc(doctree);
187 : xsltCleanupGlobals();
188 :
189 : if (resstr)
190 : xmlFree(resstr);
191 :
192 : pg_xml_done(xmlerrcxt, false);
193 :
194 : /* XXX this is pretty dubious, really ought to throw error instead */
195 : if (resstat < 0)
196 : PG_RETURN_NULL();
197 :
198 : PG_RETURN_TEXT_P(result);
199 : #else /* !USE_LIBXSLT */
200 :
201 6 : ereport(ERROR,
202 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
203 : errmsg("xslt_process() is not available without libxslt")));
204 : PG_RETURN_NULL();
205 : #endif /* USE_LIBXSLT */
206 : }
207 :
208 : #ifdef USE_LIBXSLT
209 :
210 : static const char **
211 : parse_params(text *paramstr)
212 : {
213 : char *pos;
214 : char *pstr;
215 : char *nvsep = "=";
216 : char *itsep = ",";
217 : const char **params;
218 : int max_params;
219 : int nparams;
220 :
221 : pstr = text_to_cstring(paramstr);
222 :
223 : max_params = 20; /* must be even! */
224 : params = (const char **) palloc((max_params + 1) * sizeof(char *));
225 : nparams = 0;
226 :
227 : pos = pstr;
228 :
229 : while (*pos != '\0')
230 : {
231 : if (nparams >= max_params)
232 : {
233 : max_params *= 2;
234 : params = (const char **) repalloc(params,
235 : (max_params + 1) * sizeof(char *));
236 : }
237 : params[nparams++] = pos;
238 : pos = strstr(pos, nvsep);
239 : if (pos != NULL)
240 : {
241 : *pos = '\0';
242 : pos++;
243 : }
244 : else
245 : {
246 : /* No equal sign, so ignore this "parameter" */
247 : nparams--;
248 : break;
249 : }
250 :
251 : /* since max_params is even, we still have nparams < max_params */
252 : params[nparams++] = pos;
253 : pos = strstr(pos, itsep);
254 : if (pos != NULL)
255 : {
256 : *pos = '\0';
257 : pos++;
258 : }
259 : else
260 : break;
261 : }
262 :
263 : /* Add the terminator marker; we left room for it in the palloc's */
264 : params[nparams] = NULL;
265 :
266 : return params;
267 : }
268 :
269 : #endif /* USE_LIBXSLT */
|