Age Owner Branch data TLA 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 : :
8045 bruce@momjian.us 43 :CBC 4 : PG_FUNCTION_INFO_V1(xslt_process);
44 : :
45 : : Datum
7868 46 : 6 : xslt_process(PG_FUNCTION_ARGS)
47 : : {
48 : : #ifdef USE_LIBXSLT
49 : :
3290 noah@leadboat.com 50 : 6 : text *doct = PG_GETARG_TEXT_PP(0);
51 : 6 : text *ssheet = PG_GETARG_TEXT_PP(1);
250 tgl@sss.pgh.pa.us 52 :GNC 6 : text *volatile result = NULL;
53 : : text *paramstr;
54 : : const char **params;
55 : : PgXmlErrorContext *xmlerrcxt;
5352 tgl@sss.pgh.pa.us 56 :CBC 6 : volatile xsltStylesheetPtr stylesheet = NULL;
57 : 6 : volatile xmlDocPtr doctree = NULL;
58 : 6 : volatile xmlDocPtr restree = NULL;
4961 59 : 6 : volatile xsltSecurityPrefsPtr xslt_sec_prefs = NULL;
60 : 6 : volatile xsltTransformContextPtr xslt_ctxt = NULL;
5352 61 : 6 : volatile int resstat = -1;
250 tgl@sss.pgh.pa.us 62 :GNC 6 : xmlChar *volatile resstr = NULL;
63 : :
7868 bruce@momjian.us 64 [ + + ]:CBC 6 : if (fcinfo->nargs == 3)
65 : : {
3290 noah@leadboat.com 66 : 1 : paramstr = PG_GETARG_TEXT_PP(2);
5696 tgl@sss.pgh.pa.us 67 : 1 : params = parse_params(paramstr);
68 : : }
69 : : else
70 : : {
71 : : /* No parameters */
100 michael@paquier.xyz 72 :GNC 5 : params = palloc_object(const char *);
7868 bruce@momjian.us 73 :CBC 5 : params[0] = NULL;
74 : : }
75 : :
76 : : /* Setup parser */
5352 tgl@sss.pgh.pa.us 77 : 6 : xmlerrcxt = pgxml_parser_init(PG_XML_STRICTNESS_LEGACY);
78 : :
79 [ + + ]: 6 : PG_TRY();
80 : : {
81 : : xmlDocPtr ssdoc;
82 : : bool xslt_sec_prefs_error;
250 tgl@sss.pgh.pa.us 83 :GNC 6 : int reslen = 0;
84 : :
85 : : /* Parse document */
788 michael@paquier.xyz 86 [ - + ]:CBC 6 : doctree = xmlReadMemory((char *) VARDATA_ANY(doct),
87 [ - + - - : 6 : VARSIZE_ANY_EXHDR(doct), NULL, NULL,
- - - - -
+ ]
88 : : XML_PARSE_NOENT);
89 : :
257 michael@paquier.xyz 90 [ + - - + ]:GNC 6 : if (doctree == NULL || pg_xml_error_occurred(xmlerrcxt))
537 tgl@sss.pgh.pa.us 91 :UBC 0 : xml_ereport(xmlerrcxt, ERROR, ERRCODE_INVALID_XML_DOCUMENT,
92 : : "error parsing XML document");
93 : :
94 : : /* Same for stylesheet */
788 michael@paquier.xyz 95 [ - + ]:CBC 6 : ssdoc = xmlReadMemory((char *) VARDATA_ANY(ssheet),
96 [ - + - - : 6 : VARSIZE_ANY_EXHDR(ssheet), NULL, NULL,
- - - - -
+ ]
97 : : XML_PARSE_NOENT);
98 : :
257 michael@paquier.xyz 99 [ + - - + ]:GNC 6 : if (ssdoc == NULL || pg_xml_error_occurred(xmlerrcxt))
537 tgl@sss.pgh.pa.us 100 :UBC 0 : 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 */
4961 tgl@sss.pgh.pa.us 104 :CBC 6 : stylesheet = xsltParseStylesheetDoc(ssdoc);
105 : :
257 michael@paquier.xyz 106 [ + - - + ]:GNC 6 : if (stylesheet == NULL || pg_xml_error_occurred(xmlerrcxt))
537 tgl@sss.pgh.pa.us 107 :UBC 0 : xml_ereport(xmlerrcxt, ERROR, ERRCODE_INVALID_ARGUMENT_FOR_XQUERY,
108 : : "failed to parse stylesheet");
109 : :
4961 tgl@sss.pgh.pa.us 110 :CBC 6 : xslt_ctxt = xsltNewTransformContext(stylesheet, doctree);
111 : :
112 : 6 : xslt_sec_prefs_error = false;
113 [ - + ]: 6 : if ((xslt_sec_prefs = xsltNewSecurityPrefs()) == NULL)
4961 tgl@sss.pgh.pa.us 114 :UBC 0 : xslt_sec_prefs_error = true;
115 : :
4961 tgl@sss.pgh.pa.us 116 [ - + ]:CBC 6 : if (xsltSetSecurityPrefs(xslt_sec_prefs, XSLT_SECPREF_READ_FILE,
117 : : xsltSecurityForbid) != 0)
4961 tgl@sss.pgh.pa.us 118 :UBC 0 : xslt_sec_prefs_error = true;
4961 tgl@sss.pgh.pa.us 119 [ - + ]:CBC 6 : if (xsltSetSecurityPrefs(xslt_sec_prefs, XSLT_SECPREF_WRITE_FILE,
120 : : xsltSecurityForbid) != 0)
4961 tgl@sss.pgh.pa.us 121 :UBC 0 : xslt_sec_prefs_error = true;
4961 tgl@sss.pgh.pa.us 122 [ - + ]:CBC 6 : if (xsltSetSecurityPrefs(xslt_sec_prefs, XSLT_SECPREF_CREATE_DIRECTORY,
123 : : xsltSecurityForbid) != 0)
4961 tgl@sss.pgh.pa.us 124 :UBC 0 : xslt_sec_prefs_error = true;
4961 tgl@sss.pgh.pa.us 125 [ - + ]:CBC 6 : if (xsltSetSecurityPrefs(xslt_sec_prefs, XSLT_SECPREF_READ_NETWORK,
126 : : xsltSecurityForbid) != 0)
4961 tgl@sss.pgh.pa.us 127 :UBC 0 : xslt_sec_prefs_error = true;
4961 tgl@sss.pgh.pa.us 128 [ - + ]:CBC 6 : if (xsltSetSecurityPrefs(xslt_sec_prefs, XSLT_SECPREF_WRITE_NETWORK,
129 : : xsltSecurityForbid) != 0)
4961 tgl@sss.pgh.pa.us 130 :UBC 0 : xslt_sec_prefs_error = true;
4961 tgl@sss.pgh.pa.us 131 [ - + ]:CBC 6 : if (xsltSetCtxtSecurityPrefs(xslt_sec_prefs, xslt_ctxt) != 0)
4961 tgl@sss.pgh.pa.us 132 :UBC 0 : xslt_sec_prefs_error = true;
133 : :
4961 tgl@sss.pgh.pa.us 134 [ - + ]:CBC 6 : if (xslt_sec_prefs_error)
4961 tgl@sss.pgh.pa.us 135 [ # # ]:UBC 0 : ereport(ERROR,
136 : : (errmsg("could not set libxslt security preferences")));
137 : :
4961 tgl@sss.pgh.pa.us 138 :CBC 6 : restree = xsltApplyStylesheetUser(stylesheet, doctree, params,
139 : : NULL, NULL, xslt_ctxt);
140 : :
257 michael@paquier.xyz 141 [ + + - + ]:GNC 6 : if (restree == NULL || pg_xml_error_occurred(xmlerrcxt))
537 tgl@sss.pgh.pa.us 142 :CBC 1 : xml_ereport(xmlerrcxt, ERROR, ERRCODE_INVALID_ARGUMENT_FOR_XQUERY,
143 : : "failed to apply stylesheet");
144 : :
257 michael@paquier.xyz 145 :GNC 5 : resstat = xsltSaveResultToString((xmlChar **) &resstr, &reslen,
146 : : restree, stylesheet);
147 : :
148 [ + - ]: 5 : 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 : : */
2 154 [ + + ]: 5 : if (reslen == 0)
155 : 1 : result = cstring_to_text("");
156 : : else
157 : 4 : result = cstring_to_text_with_len((char *) resstr, reslen);
158 : : }
159 : : }
5352 tgl@sss.pgh.pa.us 160 :CBC 1 : PG_CATCH();
161 : : {
162 [ - + ]: 1 : if (restree != NULL)
5352 tgl@sss.pgh.pa.us 163 :UBC 0 : xmlFreeDoc(restree);
4961 tgl@sss.pgh.pa.us 164 [ + - ]:CBC 1 : if (xslt_ctxt != NULL)
165 : 1 : xsltFreeTransformContext(xslt_ctxt);
4126 166 [ + - ]: 1 : if (xslt_sec_prefs != NULL)
167 : 1 : xsltFreeSecurityPrefs(xslt_sec_prefs);
168 [ + - ]: 1 : if (stylesheet != NULL)
169 : 1 : xsltFreeStylesheet(stylesheet);
170 [ + - ]: 1 : if (doctree != NULL)
171 : 1 : xmlFreeDoc(doctree);
257 michael@paquier.xyz 172 [ - + ]:GNC 1 : if (resstr != NULL)
250 tgl@sss.pgh.pa.us 173 :UNC 0 : xmlFree(resstr);
5352 tgl@sss.pgh.pa.us 174 :CBC 1 : xsltCleanupGlobals();
175 : :
176 : 1 : pg_xml_done(xmlerrcxt, true);
177 : :
178 : 1 : PG_RE_THROW();
179 : : }
180 [ - + ]: 5 : PG_END_TRY();
181 : :
7868 bruce@momjian.us 182 : 5 : xmlFreeDoc(restree);
4961 tgl@sss.pgh.pa.us 183 : 5 : xsltFreeTransformContext(xslt_ctxt);
4126 184 : 5 : xsltFreeSecurityPrefs(xslt_sec_prefs);
185 : 5 : xsltFreeStylesheet(stylesheet);
186 : 5 : xmlFreeDoc(doctree);
7868 bruce@momjian.us 187 : 5 : xsltCleanupGlobals();
188 : :
257 michael@paquier.xyz 189 [ + + ]:GNC 5 : if (resstr)
250 tgl@sss.pgh.pa.us 190 : 4 : xmlFree(resstr);
191 : :
5352 tgl@sss.pgh.pa.us 192 :CBC 5 : pg_xml_done(xmlerrcxt, false);
193 : :
194 : : /* XXX this is pretty dubious, really ought to throw error instead */
7868 bruce@momjian.us 195 [ - + ]: 5 : if (resstat < 0)
7868 bruce@momjian.us 196 :UBC 0 : PG_RETURN_NULL();
197 : :
5032 tgl@sss.pgh.pa.us 198 :CBC 5 : PG_RETURN_TEXT_P(result);
199 : : #else /* !USE_LIBXSLT */
200 : :
201 : : 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 **
5696 211 : 1 : parse_params(text *paramstr)
212 : : {
213 : : char *pos;
214 : : char *pstr;
7868 bruce@momjian.us 215 : 1 : char *nvsep = "=";
216 : 1 : char *itsep = ",";
217 : : const char **params;
218 : : int max_params;
219 : : int nparams;
220 : :
6564 tgl@sss.pgh.pa.us 221 : 1 : pstr = text_to_cstring(paramstr);
222 : :
5696 223 : 1 : max_params = 20; /* must be even! */
224 : 1 : params = (const char **) palloc((max_params + 1) * sizeof(char *));
225 : 1 : nparams = 0;
226 : :
7868 bruce@momjian.us 227 : 1 : pos = pstr;
228 : :
5696 tgl@sss.pgh.pa.us 229 [ + - ]: 12 : while (*pos != '\0')
230 : : {
231 [ + + ]: 12 : if (nparams >= max_params)
232 : : {
233 : 1 : max_params *= 2;
234 : 1 : params = (const char **) repalloc(params,
3189 235 : 1 : (max_params + 1) * sizeof(char *));
236 : : }
5696 237 : 12 : params[nparams++] = pos;
7868 bruce@momjian.us 238 : 12 : pos = strstr(pos, nvsep);
239 [ + - ]: 12 : if (pos != NULL)
240 : : {
241 : 12 : *pos = '\0';
242 : 12 : pos++;
243 : : }
244 : : else
245 : : {
246 : : /* No equal sign, so ignore this "parameter" */
5696 tgl@sss.pgh.pa.us 247 :UBC 0 : nparams--;
7868 bruce@momjian.us 248 : 0 : break;
249 : : }
250 : :
251 : : /* since max_params is even, we still have nparams < max_params */
5696 tgl@sss.pgh.pa.us 252 :CBC 12 : params[nparams++] = pos;
7868 bruce@momjian.us 253 : 12 : pos = strstr(pos, itsep);
254 [ + + ]: 12 : if (pos != NULL)
255 : : {
256 : 11 : *pos = '\0';
257 : 11 : pos++;
258 : : }
259 : : else
260 : 1 : break;
261 : : }
262 : :
263 : : /* Add the terminator marker; we left room for it in the palloc's */
5696 tgl@sss.pgh.pa.us 264 : 1 : params[nparams] = NULL;
265 : :
266 : 1 : return params;
267 : : }
268 : :
269 : : #endif /* USE_LIBXSLT */
|