/* * 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 = xmlReadMemory((char *) VARDATA_ANY(doct), VARSIZE_ANY_EXHDR(doct), NULL, NULL, XML_PARSE_NOENT); if (doctree == NULL) xml_ereport(xmlerrcxt, ERROR, ERRCODE_EXTERNAL_ROUTINE_EXCEPTION, "error parsing XML document"); /* Same for stylesheet */ ssdoc = xmlReadMemory((char *) VARDATA_ANY(ssheet), VARSIZE_ANY_EXHDR(ssheet), NULL, NULL, XML_PARSE_NOENT); 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 */