diff options
Diffstat (limited to '')
-rw-r--r-- | contrib/xml2/xslt_proc.c | 256 |
1 files changed, 256 insertions, 0 deletions
diff --git a/contrib/xml2/xslt_proc.c b/contrib/xml2/xslt_proc.c new file mode 100644 index 0000000..2189bca --- /dev/null +++ b/contrib/xml2/xslt_proc.c @@ -0,0 +1,256 @@ +/* + * contrib/xml2/xslt_proc.c + * + * XSLT processing functions (requiring libxslt) + * + * John Gray, for Torchbox 2003-04-01 + */ +#include "postgres.h" + +#include "executor/spi.h" +#include "fmgr.h" +#include "funcapi.h" +#include "miscadmin.h" +#include "utils/builtins.h" +#include "utils/xml.h" + +#ifdef USE_LIBXSLT + +/* libxml includes */ + +#include <libxml/xpath.h> +#include <libxml/tree.h> +#include <libxml/xmlmemory.h> + +/* libxslt includes */ + +#include <libxslt/xslt.h> +#include <libxslt/xsltInternals.h> +#include <libxslt/security.h> +#include <libxslt/transform.h> +#include <libxslt/xsltutils.h> +#endif /* USE_LIBXSLT */ + + +#ifdef USE_LIBXSLT + +/* declarations to come from xpath.c */ +extern PgXmlErrorContext *pgxml_parser_init(PgXmlStrictness strictness); + +/* local defs */ +static const char **parse_params(text *paramstr); +#endif /* USE_LIBXSLT */ + + +PG_FUNCTION_INFO_V1(xslt_process); + +Datum +xslt_process(PG_FUNCTION_ARGS) +{ +#ifdef USE_LIBXSLT + + text *doct = PG_GETARG_TEXT_PP(0); + text *ssheet = PG_GETARG_TEXT_PP(1); + text *result; + text *paramstr; + const char **params; + PgXmlErrorContext *xmlerrcxt; + volatile xsltStylesheetPtr stylesheet = NULL; + volatile xmlDocPtr doctree = NULL; + volatile xmlDocPtr restree = NULL; + volatile xsltSecurityPrefsPtr xslt_sec_prefs = NULL; + volatile xsltTransformContextPtr xslt_ctxt = NULL; + volatile int resstat = -1; + xmlChar *resstr = NULL; + int reslen = 0; + + if (fcinfo->nargs == 3) + { + paramstr = PG_GETARG_TEXT_PP(2); + params = parse_params(paramstr); + } + else + { + /* No parameters */ + params = (const char **) palloc(sizeof(char *)); + params[0] = NULL; + } + + /* Setup parser */ + xmlerrcxt = pgxml_parser_init(PG_XML_STRICTNESS_LEGACY); + + PG_TRY(); + { + xmlDocPtr ssdoc; + bool xslt_sec_prefs_error; + + /* Parse document */ + doctree = xmlParseMemory((char *) VARDATA_ANY(doct), + VARSIZE_ANY_EXHDR(doct)); + + if (doctree == NULL) + xml_ereport(xmlerrcxt, ERROR, ERRCODE_EXTERNAL_ROUTINE_EXCEPTION, + "error parsing XML document"); + + /* Same for stylesheet */ + ssdoc = xmlParseMemory((char *) VARDATA_ANY(ssheet), + VARSIZE_ANY_EXHDR(ssheet)); + + if (ssdoc == NULL) + xml_ereport(xmlerrcxt, ERROR, ERRCODE_EXTERNAL_ROUTINE_EXCEPTION, + "error parsing stylesheet as XML document"); + + /* After this call we need not free ssdoc separately */ + stylesheet = xsltParseStylesheetDoc(ssdoc); + + if (stylesheet == NULL) + xml_ereport(xmlerrcxt, ERROR, ERRCODE_EXTERNAL_ROUTINE_EXCEPTION, + "failed to parse stylesheet"); + + xslt_ctxt = xsltNewTransformContext(stylesheet, doctree); + + xslt_sec_prefs_error = false; + if ((xslt_sec_prefs = xsltNewSecurityPrefs()) == NULL) + xslt_sec_prefs_error = true; + + if (xsltSetSecurityPrefs(xslt_sec_prefs, XSLT_SECPREF_READ_FILE, + xsltSecurityForbid) != 0) + xslt_sec_prefs_error = true; + if (xsltSetSecurityPrefs(xslt_sec_prefs, XSLT_SECPREF_WRITE_FILE, + xsltSecurityForbid) != 0) + xslt_sec_prefs_error = true; + if (xsltSetSecurityPrefs(xslt_sec_prefs, XSLT_SECPREF_CREATE_DIRECTORY, + xsltSecurityForbid) != 0) + xslt_sec_prefs_error = true; + if (xsltSetSecurityPrefs(xslt_sec_prefs, XSLT_SECPREF_READ_NETWORK, + xsltSecurityForbid) != 0) + xslt_sec_prefs_error = true; + if (xsltSetSecurityPrefs(xslt_sec_prefs, XSLT_SECPREF_WRITE_NETWORK, + xsltSecurityForbid) != 0) + xslt_sec_prefs_error = true; + if (xsltSetCtxtSecurityPrefs(xslt_sec_prefs, xslt_ctxt) != 0) + xslt_sec_prefs_error = true; + + if (xslt_sec_prefs_error) + ereport(ERROR, + (errmsg("could not set libxslt security preferences"))); + + restree = xsltApplyStylesheetUser(stylesheet, doctree, params, + NULL, NULL, xslt_ctxt); + + if (restree == NULL) + xml_ereport(xmlerrcxt, ERROR, ERRCODE_EXTERNAL_ROUTINE_EXCEPTION, + "failed to apply stylesheet"); + + resstat = xsltSaveResultToString(&resstr, &reslen, restree, stylesheet); + } + PG_CATCH(); + { + if (restree != NULL) + xmlFreeDoc(restree); + if (xslt_ctxt != NULL) + xsltFreeTransformContext(xslt_ctxt); + if (xslt_sec_prefs != NULL) + xsltFreeSecurityPrefs(xslt_sec_prefs); + if (stylesheet != NULL) + xsltFreeStylesheet(stylesheet); + if (doctree != NULL) + xmlFreeDoc(doctree); + xsltCleanupGlobals(); + + pg_xml_done(xmlerrcxt, true); + + PG_RE_THROW(); + } + PG_END_TRY(); + + xmlFreeDoc(restree); + xsltFreeTransformContext(xslt_ctxt); + xsltFreeSecurityPrefs(xslt_sec_prefs); + xsltFreeStylesheet(stylesheet); + xmlFreeDoc(doctree); + xsltCleanupGlobals(); + + pg_xml_done(xmlerrcxt, false); + + /* XXX this is pretty dubious, really ought to throw error instead */ + if (resstat < 0) + PG_RETURN_NULL(); + + result = cstring_to_text_with_len((char *) resstr, reslen); + + if (resstr) + xmlFree(resstr); + + PG_RETURN_TEXT_P(result); +#else /* !USE_LIBXSLT */ + + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("xslt_process() is not available without libxslt"))); + PG_RETURN_NULL(); +#endif /* USE_LIBXSLT */ +} + +#ifdef USE_LIBXSLT + +static const char ** +parse_params(text *paramstr) +{ + char *pos; + char *pstr; + char *nvsep = "="; + char *itsep = ","; + const char **params; + int max_params; + int nparams; + + pstr = text_to_cstring(paramstr); + + max_params = 20; /* must be even! */ + params = (const char **) palloc((max_params + 1) * sizeof(char *)); + nparams = 0; + + pos = pstr; + + while (*pos != '\0') + { + if (nparams >= max_params) + { + max_params *= 2; + params = (const char **) repalloc(params, + (max_params + 1) * sizeof(char *)); + } + params[nparams++] = pos; + pos = strstr(pos, nvsep); + if (pos != NULL) + { + *pos = '\0'; + pos++; + } + else + { + /* No equal sign, so ignore this "parameter" */ + nparams--; + break; + } + + /* since max_params is even, we still have nparams < max_params */ + params[nparams++] = pos; + pos = strstr(pos, itsep); + if (pos != NULL) + { + *pos = '\0'; + pos++; + } + else + break; + } + + /* Add the terminator marker; we left room for it in the palloc's */ + params[nparams] = NULL; + + return params; +} + +#endif /* USE_LIBXSLT */ |