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