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