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 "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 : :
7345 bruce@momjian.us 45 :CBC 4 : PG_FUNCTION_INFO_V1(xslt_process);
46 : :
47 : : Datum
7168 48 : 5 : xslt_process(PG_FUNCTION_ARGS)
49 : : {
50 : : #ifdef USE_LIBXSLT
51 : :
2590 noah@leadboat.com 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;
4652 tgl@sss.pgh.pa.us 58 : 5 : volatile xsltStylesheetPtr stylesheet = NULL;
59 : 5 : volatile xmlDocPtr doctree = NULL;
60 : 5 : volatile xmlDocPtr restree = NULL;
4261 61 : 5 : volatile xsltSecurityPrefsPtr xslt_sec_prefs = NULL;
62 : 5 : volatile xsltTransformContextPtr xslt_ctxt = NULL;
4652 63 : 5 : volatile int resstat = -1;
64 : 5 : xmlChar *resstr = NULL;
65 : 5 : int reslen = 0;
66 : :
7168 bruce@momjian.us 67 [ + + ]: 5 : if (fcinfo->nargs == 3)
68 : : {
2590 noah@leadboat.com 69 : 1 : paramstr = PG_GETARG_TEXT_PP(2);
4996 tgl@sss.pgh.pa.us 70 : 1 : params = parse_params(paramstr);
71 : : }
72 : : else
73 : : {
74 : : /* No parameters */
75 : 4 : params = (const char **) palloc(sizeof(char *));
7168 bruce@momjian.us 76 : 4 : params[0] = NULL;
77 : : }
78 : :
79 : : /* Setup parser */
4652 tgl@sss.pgh.pa.us 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 */
88 michael@paquier.xyz 88 [ - + ]:GNC 5 : doctree = xmlReadMemory((char *) VARDATA_ANY(doct),
89 [ - + - - : 5 : VARSIZE_ANY_EXHDR(doct), NULL, NULL,
- - - - -
+ ]
90 : : XML_PARSE_NOENT);
91 : :
4326 bruce@momjian.us 92 [ - + ]:CBC 5 : if (doctree == NULL)
4652 tgl@sss.pgh.pa.us 93 :UBC 0 : xml_ereport(xmlerrcxt, ERROR, ERRCODE_EXTERNAL_ROUTINE_EXCEPTION,
94 : : "error parsing XML document");
95 : :
96 : : /* Same for stylesheet */
88 michael@paquier.xyz 97 [ - + ]:GNC 5 : ssdoc = xmlReadMemory((char *) VARDATA_ANY(ssheet),
98 [ - + - - : 5 : VARSIZE_ANY_EXHDR(ssheet), NULL, NULL,
- - - - -
+ ]
99 : : XML_PARSE_NOENT);
100 : :
4261 tgl@sss.pgh.pa.us 101 [ - + ]:CBC 5 : if (ssdoc == NULL)
4261 tgl@sss.pgh.pa.us 102 :UBC 0 : xml_ereport(xmlerrcxt, ERROR, ERRCODE_EXTERNAL_ROUTINE_EXCEPTION,
103 : : "error parsing stylesheet as XML document");
104 : :
105 : : /* After this call we need not free ssdoc separately */
4261 tgl@sss.pgh.pa.us 106 :CBC 5 : stylesheet = xsltParseStylesheetDoc(ssdoc);
107 : :
4326 bruce@momjian.us 108 [ - + ]: 5 : if (stylesheet == NULL)
4326 bruce@momjian.us 109 :UBC 0 : xml_ereport(xmlerrcxt, ERROR, ERRCODE_EXTERNAL_ROUTINE_EXCEPTION,
110 : : "failed to parse stylesheet");
111 : :
4261 tgl@sss.pgh.pa.us 112 :CBC 5 : xslt_ctxt = xsltNewTransformContext(stylesheet, doctree);
113 : :
114 : 5 : xslt_sec_prefs_error = false;
115 [ - + ]: 5 : if ((xslt_sec_prefs = xsltNewSecurityPrefs()) == NULL)
4261 tgl@sss.pgh.pa.us 116 :UBC 0 : xslt_sec_prefs_error = true;
117 : :
4261 tgl@sss.pgh.pa.us 118 [ - + ]:CBC 5 : if (xsltSetSecurityPrefs(xslt_sec_prefs, XSLT_SECPREF_READ_FILE,
119 : : xsltSecurityForbid) != 0)
4261 tgl@sss.pgh.pa.us 120 :UBC 0 : xslt_sec_prefs_error = true;
4261 tgl@sss.pgh.pa.us 121 [ - + ]:CBC 5 : if (xsltSetSecurityPrefs(xslt_sec_prefs, XSLT_SECPREF_WRITE_FILE,
122 : : xsltSecurityForbid) != 0)
4261 tgl@sss.pgh.pa.us 123 :UBC 0 : xslt_sec_prefs_error = true;
4261 tgl@sss.pgh.pa.us 124 [ - + ]:CBC 5 : if (xsltSetSecurityPrefs(xslt_sec_prefs, XSLT_SECPREF_CREATE_DIRECTORY,
125 : : xsltSecurityForbid) != 0)
4261 tgl@sss.pgh.pa.us 126 :UBC 0 : xslt_sec_prefs_error = true;
4261 tgl@sss.pgh.pa.us 127 [ - + ]:CBC 5 : if (xsltSetSecurityPrefs(xslt_sec_prefs, XSLT_SECPREF_READ_NETWORK,
128 : : xsltSecurityForbid) != 0)
4261 tgl@sss.pgh.pa.us 129 :UBC 0 : xslt_sec_prefs_error = true;
4261 tgl@sss.pgh.pa.us 130 [ - + ]:CBC 5 : if (xsltSetSecurityPrefs(xslt_sec_prefs, XSLT_SECPREF_WRITE_NETWORK,
131 : : xsltSecurityForbid) != 0)
4261 tgl@sss.pgh.pa.us 132 :UBC 0 : xslt_sec_prefs_error = true;
4261 tgl@sss.pgh.pa.us 133 [ - + ]:CBC 5 : if (xsltSetCtxtSecurityPrefs(xslt_sec_prefs, xslt_ctxt) != 0)
4261 tgl@sss.pgh.pa.us 134 :UBC 0 : xslt_sec_prefs_error = true;
135 : :
4261 tgl@sss.pgh.pa.us 136 [ - + ]:CBC 5 : if (xslt_sec_prefs_error)
4261 tgl@sss.pgh.pa.us 137 [ # # ]:UBC 0 : ereport(ERROR,
138 : : (errmsg("could not set libxslt security preferences")));
139 : :
4261 tgl@sss.pgh.pa.us 140 :CBC 5 : restree = xsltApplyStylesheetUser(stylesheet, doctree, params,
141 : : NULL, NULL, xslt_ctxt);
142 : :
4326 bruce@momjian.us 143 [ + + ]: 5 : if (restree == NULL)
144 : 1 : xml_ereport(xmlerrcxt, ERROR, ERRCODE_EXTERNAL_ROUTINE_EXCEPTION,
145 : : "failed to apply stylesheet");
146 : :
147 : 4 : resstat = xsltSaveResultToString(&resstr, &reslen, restree, stylesheet);
148 : : }
4652 tgl@sss.pgh.pa.us 149 : 1 : PG_CATCH();
150 : : {
151 [ - + ]: 1 : if (restree != NULL)
4652 tgl@sss.pgh.pa.us 152 :UBC 0 : xmlFreeDoc(restree);
4261 tgl@sss.pgh.pa.us 153 [ + - ]:CBC 1 : if (xslt_ctxt != NULL)
154 : 1 : xsltFreeTransformContext(xslt_ctxt);
3426 155 [ + - ]: 1 : if (xslt_sec_prefs != NULL)
156 : 1 : xsltFreeSecurityPrefs(xslt_sec_prefs);
157 [ + - ]: 1 : if (stylesheet != NULL)
158 : 1 : xsltFreeStylesheet(stylesheet);
159 [ + - ]: 1 : if (doctree != NULL)
160 : 1 : xmlFreeDoc(doctree);
4652 161 : 1 : xsltCleanupGlobals();
162 : :
163 : 1 : pg_xml_done(xmlerrcxt, true);
164 : :
165 : 1 : PG_RE_THROW();
166 : : }
167 [ - + ]: 4 : PG_END_TRY();
168 : :
7168 bruce@momjian.us 169 : 4 : xmlFreeDoc(restree);
4261 tgl@sss.pgh.pa.us 170 : 4 : xsltFreeTransformContext(xslt_ctxt);
3426 171 : 4 : xsltFreeSecurityPrefs(xslt_sec_prefs);
172 : 4 : xsltFreeStylesheet(stylesheet);
173 : 4 : xmlFreeDoc(doctree);
7168 bruce@momjian.us 174 : 4 : xsltCleanupGlobals();
175 : :
4652 tgl@sss.pgh.pa.us 176 : 4 : pg_xml_done(xmlerrcxt, false);
177 : :
178 : : /* XXX this is pretty dubious, really ought to throw error instead */
7168 bruce@momjian.us 179 [ - + ]: 4 : if (resstat < 0)
7168 bruce@momjian.us 180 :UBC 0 : PG_RETURN_NULL();
181 : :
4332 tgl@sss.pgh.pa.us 182 :CBC 4 : result = cstring_to_text_with_len((char *) resstr, reslen);
183 : :
184 [ + - ]: 4 : if (resstr)
185 : 4 : xmlFree(resstr);
186 : :
187 : 4 : PG_RETURN_TEXT_P(result);
188 : : #else /* !USE_LIBXSLT */
189 : :
190 : : ereport(ERROR,
191 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
192 : : errmsg("xslt_process() is not available without libxslt")));
193 : : PG_RETURN_NULL();
194 : : #endif /* USE_LIBXSLT */
195 : : }
196 : :
197 : : #ifdef USE_LIBXSLT
198 : :
199 : : static const char **
4996 200 : 1 : parse_params(text *paramstr)
201 : : {
202 : : char *pos;
203 : : char *pstr;
7168 bruce@momjian.us 204 : 1 : char *nvsep = "=";
205 : 1 : char *itsep = ",";
206 : : const char **params;
207 : : int max_params;
208 : : int nparams;
209 : :
5864 tgl@sss.pgh.pa.us 210 : 1 : pstr = text_to_cstring(paramstr);
211 : :
4996 212 : 1 : max_params = 20; /* must be even! */
213 : 1 : params = (const char **) palloc((max_params + 1) * sizeof(char *));
214 : 1 : nparams = 0;
215 : :
7168 bruce@momjian.us 216 : 1 : pos = pstr;
217 : :
4996 tgl@sss.pgh.pa.us 218 [ + - ]: 12 : while (*pos != '\0')
219 : : {
220 [ + + ]: 12 : if (nparams >= max_params)
221 : : {
222 : 1 : max_params *= 2;
223 : 1 : params = (const char **) repalloc(params,
2489 224 : 1 : (max_params + 1) * sizeof(char *));
225 : : }
4996 226 : 12 : params[nparams++] = pos;
7168 bruce@momjian.us 227 : 12 : pos = strstr(pos, nvsep);
228 [ + - ]: 12 : if (pos != NULL)
229 : : {
230 : 12 : *pos = '\0';
231 : 12 : pos++;
232 : : }
233 : : else
234 : : {
235 : : /* No equal sign, so ignore this "parameter" */
4996 tgl@sss.pgh.pa.us 236 :UBC 0 : nparams--;
7168 bruce@momjian.us 237 : 0 : break;
238 : : }
239 : :
240 : : /* since max_params is even, we still have nparams < max_params */
4996 tgl@sss.pgh.pa.us 241 :CBC 12 : params[nparams++] = pos;
7168 bruce@momjian.us 242 : 12 : pos = strstr(pos, itsep);
243 [ + + ]: 12 : if (pos != NULL)
244 : : {
245 : 11 : *pos = '\0';
246 : 11 : pos++;
247 : : }
248 : : else
249 : 1 : break;
250 : : }
251 : :
252 : : /* Add the terminator marker; we left room for it in the palloc's */
4996 tgl@sss.pgh.pa.us 253 : 1 : params[nparams] = NULL;
254 : :
255 : 1 : return params;
256 : : }
257 : :
258 : : #endif /* USE_LIBXSLT */
|