--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/xmlsecurityengine/xmlsec/src/xmlsec_xslt.c Thu Dec 17 09:29:21 2009 +0200
@@ -0,0 +1,521 @@
+/**
+ * XML Security Library (http://www.aleksey.com/xmlsec).
+ *
+ * XSLT Transform (http://www.w3.org/TR/xmldsig-core/#sec-XSLT)
+ *
+ * This is free software; see Copyright file in the source
+ * distribution for preciese wording.
+ *
+ * Copyright (C) 2002-2003 Aleksey Sanin <aleksey@aleksey.com>
+ * Portion Copyright © 2009 Nokia Corporation and/or its subsidiary(-ies). All rights reserved.
+ */
+#include "xmlsec_config.h"
+#ifndef XMLSEC_NO_XSLT
+#include "xmlsec_globals.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <libxml2_tree.h>
+#include <libxml2_xmlmemory.h> //added for symbian port
+#include <libxslt/libxslt_xslt.h>
+#include <libxslt/libxslt_xsltinternals.h>
+#include <libxslt/libxslt_transform.h>
+#include <libxslt/libxslt_xsltutils.h>
+
+#include "xmlsec_xmlsec.h"
+#include "xmlsec_xmltree.h"
+#include "xmlsec_keys.h"
+#include "xmlsec_transforms.h"
+#include "xmlsec_keys.h"
+#include "xmlsec_parser.h"
+#include "xmlsec_errors.h"
+
+/**************************************************************************
+ *
+ * Internal xslt ctx
+ *
+ *****************************************************************************/
+typedef struct _xmlSecXsltCtx xmlSecXsltCtx, *xmlSecXsltCtxPtr;
+struct _xmlSecXsltCtx {
+ xsltStylesheetPtr xslt;
+ xmlParserCtxtPtr parserCtx;
+};
+
+/****************************************************************************
+ *
+ * XSLT transform
+ *
+ * xmlSecXsltCtx is located after xmlSecTransform
+ *
+ ***************************************************************************/
+#define xmlSecXsltSize \
+ (sizeof(xmlSecTransform) + sizeof(xmlSecXsltCtx))
+#define xmlSecXsltGetCtx(transform) \
+ ((xmlSecXsltCtxPtr)(((xmlSecByte*)(transform)) + sizeof(xmlSecTransform)))
+
+static int xmlSecXsltInitialize (xmlSecTransformPtr transform);
+static void xmlSecXsltFinalize (xmlSecTransformPtr transform);
+static int xmlSecXsltReadNode (xmlSecTransformPtr transform,
+ xmlNodePtr node,
+ xmlSecTransformCtxPtr transformCtx);
+static int xmlSecXsltPushBin (xmlSecTransformPtr transform,
+ const xmlSecByte* data,
+ xmlSecSize dataSize,
+ int final,
+ xmlSecTransformCtxPtr transformCtx);
+static int xmlSecXsltExecute (xmlSecTransformPtr transform,
+ int last,
+ xmlSecTransformCtxPtr transformCtx);
+static int xmlSecXslProcess (xmlSecBufferPtr in,
+ xmlSecBufferPtr out,
+ xsltStylesheetPtr stylesheet);
+static xmlSecTransformKlass xmlSecXsltKlass = {
+ /* klass/object sizes */
+ sizeof(xmlSecTransformKlass), /* xmlSecSize klassSize */
+ xmlSecXsltSize, /* xmlSecSize objSize */
+
+ xmlSecNameXslt, /* const xmlChar* name; */
+ xmlSecHrefXslt, /* const xmlChar* href; */
+ xmlSecTransformUsageDSigTransform, /* xmlSecAlgorithmUsage usage; */
+
+ xmlSecXsltInitialize, /* xmlSecTransformInitializeMethod initialize; */
+ xmlSecXsltFinalize, /* xmlSecTransformFinalizeMethod finalize; */
+ xmlSecXsltReadNode, /* xmlSecTransformNodeReadMethod readNode; */
+ NULL, /* xmlSecTransformNodeWriteMethod writeNode; */
+ NULL, /* xmlSecTransformSetKeyReqMethod setKeyReq; */
+ NULL, /* xmlSecTransformSetKeyMethod setKey; */
+ NULL, /* xmlSecTransformValidateMethod validate; */
+ xmlSecTransformDefaultGetDataType, /* xmlSecTransformGetDataTypeMethod getDataType; */
+ xmlSecXsltPushBin, /* xmlSecTransformPushBinMethod pushBin; */
+ xmlSecTransformDefaultPopBin, /* xmlSecTransformPopBinMethod popBin; */
+ NULL, /* xmlSecTransformPushXmlMethod pushXml; */
+ NULL, /* xmlSecTransformPopXmlMethod popXml; */
+ xmlSecXsltExecute, /* xmlSecTransformExecuteMethod execute; */
+
+ NULL, /* void* reserved0; */
+ NULL, /* void* reserved1; */
+};
+
+/**
+ * xmlSecTransformXsltGetKlass:
+ *
+ * XSLT transform klass (http://www.w3.org/TR/xmldsig-core/#sec-XSLT):
+ *
+ * The normative specification for XSL Transformations is [XSLT].
+ * Specification of a namespace-qualified stylesheet element, which MUST be
+ * the sole child of the Transform element, indicates that the specified style
+ * sheet should be used. Whether this instantiates in-line processing of local
+ * XSLT declarations within the resource is determined by the XSLT processing
+ * model; the ordered application of multiple stylesheet may require multiple
+ * Transforms. No special provision is made for the identification of a remote
+ * stylesheet at a given URI because it can be communicated via an xsl:include
+ * or xsl:import within the stylesheet child of the Transform.
+ *
+ * This transform requires an octet stream as input. If the actual input is an
+ * XPath node-set, then the signature application should attempt to convert it
+ * to octets (apply Canonical XML]) as described in the Reference Processing
+ * Model (section 4.3.3.2).]
+ *
+ * The output of this transform is an octet stream. The processing rules for
+ * the XSL style sheet or transform element are stated in the XSLT specification
+ * [XSLT]. We RECOMMEND that XSLT transform authors use an output method of xml
+ * for XML and HTML. As XSLT implementations do not produce consistent
+ * serializations of their output, we further RECOMMEND inserting a transform
+ * after the XSLT transform to canonicalize the output. These steps will help
+ * to ensure interoperability of the resulting signatures among applications
+ * that support the XSLT transform. Note that if the output is actually HTML,
+ * then the result of these steps is logically equivalent [XHTML].
+ *
+ * Returns pointer to XSLT transform klass.
+ */
+xmlSecTransformId
+xmlSecTransformXsltGetKlass(void) {
+ return(&xmlSecXsltKlass);
+}
+
+static int
+xmlSecXsltInitialize(xmlSecTransformPtr transform) {
+ xmlSecXsltCtxPtr ctx;
+
+ xmlSecAssert2(xmlSecTransformCheckId(transform, xmlSecTransformXsltId), -1);
+ xmlSecAssert2(xmlSecTransformCheckSize(transform, xmlSecXsltSize), -1);
+
+ ctx = xmlSecXsltGetCtx(transform);
+ xmlSecAssert2(ctx != NULL, -1);
+
+ /* initialize context */
+ memset(ctx, 0, sizeof(xmlSecXsltCtx));
+ return(0);
+}
+
+static void
+xmlSecXsltFinalize(xmlSecTransformPtr transform) {
+ xmlSecXsltCtxPtr ctx;
+
+ xmlSecAssert(xmlSecTransformCheckId(transform, xmlSecTransformXsltId));
+ xmlSecAssert(xmlSecTransformCheckSize(transform, xmlSecXsltSize));
+
+ ctx = xmlSecXsltGetCtx(transform);
+ xmlSecAssert(ctx != NULL);
+
+ if(ctx->xslt != NULL) {
+ xsltFreeStylesheet(ctx->xslt);
+ }
+ if(ctx->parserCtx != NULL) {
+ xmlFreeParserCtxt(ctx->parserCtx);
+ }
+ memset(ctx, 0, sizeof(xmlSecXsltCtx));
+}
+
+/**
+ * xmlSecXsltReadNode:
+ */
+static int
+xmlSecXsltReadNode(xmlSecTransformPtr transform, xmlNodePtr node, xmlSecTransformCtxPtr transformCtx) {
+ xmlSecXsltCtxPtr ctx;
+ xmlBufferPtr buffer;
+ xmlDocPtr doc;
+ xmlNodePtr cur;
+
+ xmlSecAssert2(xmlSecTransformCheckId(transform, xmlSecTransformXsltId), -1);
+ xmlSecAssert2(xmlSecTransformCheckSize(transform, xmlSecXsltSize), -1);
+ xmlSecAssert2(node != NULL, -1);
+ xmlSecAssert2(transformCtx != NULL, -1);
+
+ ctx = xmlSecXsltGetCtx(transform);
+ xmlSecAssert2(ctx != NULL, -1);
+ xmlSecAssert2(ctx->xslt == NULL, -1);
+
+ /* read content in the buffer */
+ buffer = xmlBufferCreate();
+ if(buffer == NULL) {
+ xmlSecError(XMLSEC_ERRORS_HERE,
+ xmlSecErrorsSafeString(xmlSecTransformGetName(transform)),
+ "xmlBufferCreate",
+ XMLSEC_ERRORS_R_XML_FAILED,
+ XMLSEC_ERRORS_NO_MESSAGE);
+ return(-1);
+ }
+ cur = node->children;
+ while(cur != NULL) {
+ xmlNodeDump(buffer, cur->doc, cur, 0, 0);
+ cur = cur->next;
+ }
+
+ /* parse the buffer */
+ doc = xmlSecParseMemory(xmlBufferContent(buffer),
+ xmlBufferLength(buffer), 1);
+ if(doc == NULL) {
+ xmlSecError(XMLSEC_ERRORS_HERE,
+ xmlSecErrorsSafeString(xmlSecTransformGetName(transform)),
+ "xmlSecParseMemory",
+ XMLSEC_ERRORS_R_XMLSEC_FAILED,
+ XMLSEC_ERRORS_NO_MESSAGE);
+ xmlBufferFree(buffer);
+ return(-1);
+ }
+
+ /* pre-process stylesheet */
+ ctx->xslt = xsltParseStylesheetDoc(doc);
+ if(ctx->xslt == NULL) {
+ xmlSecError(XMLSEC_ERRORS_HERE,
+ xmlSecErrorsSafeString(xmlSecTransformGetName(transform)),
+ "xsltParseStylesheetDoc",
+ XMLSEC_ERRORS_R_XSLT_FAILED,
+ XMLSEC_ERRORS_NO_MESSAGE);
+ /* after parsing stylesheet doc is assigned
+ * to it and will be freed by xsltFreeStylesheet() */
+ xmlFreeDoc(doc);
+ xmlBufferFree(buffer);
+ return(-1);
+ }
+
+ xmlBufferFree(buffer);
+ return(0);
+}
+
+static int
+xmlSecXsltPushBin(xmlSecTransformPtr transform, const xmlSecByte* data,
+ xmlSecSize dataSize, int final, xmlSecTransformCtxPtr transformCtx) {
+ xmlSecXsltCtxPtr ctx;
+ int ret;
+
+ xmlSecAssert2(xmlSecTransformCheckId(transform, xmlSecTransformXsltId), -1);
+ xmlSecAssert2(xmlSecTransformCheckSize(transform, xmlSecXsltSize), -1);
+ xmlSecAssert2(transformCtx != NULL, -1);
+
+ ctx = xmlSecXsltGetCtx(transform);
+ xmlSecAssert2(ctx != NULL, -1);
+ xmlSecAssert2(ctx->xslt != NULL, -1);
+
+ /* check/update current transform status */
+ if(transform->status == xmlSecTransformStatusNone) {
+ xmlSecAssert2(ctx->parserCtx == NULL, -1);
+
+ ctx->parserCtx = xmlCreatePushParserCtxt(NULL, NULL, NULL, 0, NULL);
+ if(ctx->parserCtx == NULL) {
+ xmlSecError(XMLSEC_ERRORS_HERE,
+ xmlSecErrorsSafeString(xmlSecTransformGetName(transform)),
+ "xmlCreatePushParserCtxt",
+ XMLSEC_ERRORS_R_XML_FAILED,
+ XMLSEC_ERRORS_NO_MESSAGE);
+ return(-1);
+ }
+
+ /* required for c14n! */
+ ctx->parserCtx->loadsubset = XML_DETECT_IDS | XML_COMPLETE_ATTRS;
+ ctx->parserCtx->replaceEntities = 1;
+
+ transform->status = xmlSecTransformStatusWorking;
+ } else if(transform->status == xmlSecTransformStatusFinished) {
+ return(0);
+ } else if(transform->status != xmlSecTransformStatusWorking) {
+ xmlSecError(XMLSEC_ERRORS_HERE,
+ xmlSecErrorsSafeString(xmlSecTransformGetName(transform)),
+ NULL,
+ XMLSEC_ERRORS_R_INVALID_STATUS,
+ "status=%d", transform->status);
+ return(-1);
+ }
+ xmlSecAssert2(transform->status == xmlSecTransformStatusWorking, -1);
+ xmlSecAssert2(ctx->parserCtx != NULL, -1);
+
+ /* push data to the input buffer */
+ if((data != NULL) && (dataSize > 0)) {
+ ret = xmlParseChunk(ctx->parserCtx, (const char*)data, dataSize, 0);
+ if(ret != 0) {
+ xmlSecError(XMLSEC_ERRORS_HERE,
+ xmlSecErrorsSafeString(xmlSecTransformGetName(transform)),
+ "xmlParseChunk",
+ XMLSEC_ERRORS_R_XML_FAILED,
+ "size=%d", dataSize);
+ return(-1);
+ }
+ }
+
+ /* finish parsing, apply xslt transforms and push to next in the chain */
+ if(final != 0) {
+ xmlDocPtr docIn;
+ xmlDocPtr docOut;
+ xmlOutputBufferPtr output;
+
+ /* finalize */
+ ret = xmlParseChunk(ctx->parserCtx, NULL, 0, 1);
+ if((ret != 0) || (ctx->parserCtx->myDoc == NULL)) {
+ xmlSecError(XMLSEC_ERRORS_HERE,
+ xmlSecErrorsSafeString(xmlSecTransformGetName(transform)),
+ "xmlParseChunk",
+ XMLSEC_ERRORS_R_XML_FAILED,
+ XMLSEC_ERRORS_NO_MESSAGE);
+ return(-1);
+ }
+
+ docIn = ctx->parserCtx->myDoc;
+ ctx->parserCtx->myDoc = NULL;
+
+ docOut = xsltApplyStylesheet(ctx->xslt, docIn, NULL);
+ if(docOut == NULL) {
+ xmlSecError(XMLSEC_ERRORS_HERE,
+ xmlSecErrorsSafeString(xmlSecTransformGetName(transform)),
+ "xsltApplyStylesheet",
+ XMLSEC_ERRORS_R_XSLT_FAILED,
+ XMLSEC_ERRORS_NO_MESSAGE);
+ xmlFreeDoc(docIn);
+ return(-1);
+ }
+ xmlFreeDoc(docIn);
+
+ if(transform->next != NULL) {
+ output = xmlSecTransformCreateOutputBuffer(transform->next, transformCtx);
+ if(output == NULL) {
+ xmlSecError(XMLSEC_ERRORS_HERE,
+ xmlSecErrorsSafeString(xmlSecTransformGetName(transform)),
+ "xmlSecTransformCreateOutputBuffer",
+ XMLSEC_ERRORS_R_XMLSEC_FAILED,
+ XMLSEC_ERRORS_NO_MESSAGE);
+ xmlFreeDoc(docOut);
+ return(-1);
+ }
+ } else {
+ output = xmlSecBufferCreateOutputBuffer(&(transform->outBuf));
+ if(output == NULL) {
+ xmlSecError(XMLSEC_ERRORS_HERE,
+ xmlSecErrorsSafeString(xmlSecTransformGetName(transform)),
+ "xmlSecBufferCreateOutputBuffer",
+ XMLSEC_ERRORS_R_XMLSEC_FAILED,
+ XMLSEC_ERRORS_NO_MESSAGE);
+ xmlFreeDoc(docOut);
+ return(-1);
+ }
+ }
+
+ ret = xsltSaveResultTo(output, docOut, ctx->xslt);
+ if(ret < 0) {
+ xmlSecError(XMLSEC_ERRORS_HERE,
+ xmlSecErrorsSafeString(xmlSecTransformGetName(transform)),
+ "xsltSaveResultTo",
+ XMLSEC_ERRORS_R_XSLT_FAILED,
+ XMLSEC_ERRORS_NO_MESSAGE);
+ xmlOutputBufferClose(output);
+ xmlFreeDoc(docOut);
+ return(-1);
+ }
+ ret = xmlOutputBufferClose(output);
+ if(ret < 0) {
+ xmlSecError(XMLSEC_ERRORS_HERE,
+ xmlSecErrorsSafeString(xmlSecTransformGetName(transform)),
+ "xmlOutputBufferClose",
+ XMLSEC_ERRORS_R_XML_FAILED,
+ XMLSEC_ERRORS_NO_MESSAGE);
+ xmlFreeDoc(docOut);
+ return(-1);
+ }
+ xmlFreeDoc(docOut);
+
+ transform->status = xmlSecTransformStatusFinished;
+ }
+
+ return(0);
+}
+
+static int
+xmlSecXsltExecute(xmlSecTransformPtr transform, int last, xmlSecTransformCtxPtr transformCtx) {
+ xmlSecXsltCtxPtr ctx;
+ xmlSecBufferPtr in, out;
+ xmlSecSize inSize, outSize;
+ int ret;
+
+ xmlSecAssert2(xmlSecTransformCheckId(transform, xmlSecTransformXsltId), -1);
+ xmlSecAssert2(xmlSecTransformCheckSize(transform, xmlSecXsltSize), -1);
+ xmlSecAssert2(transformCtx != NULL, -1);
+
+ ctx = xmlSecXsltGetCtx(transform);
+ xmlSecAssert2(ctx != NULL, -1);
+ xmlSecAssert2(ctx->xslt != NULL, -1);
+
+ in = &(transform->inBuf);
+ out = &(transform->outBuf);
+ inSize = xmlSecBufferGetSize(in);
+ outSize = xmlSecBufferGetSize(out);
+
+ if(transform->status == xmlSecTransformStatusNone) {
+ transform->status = xmlSecTransformStatusWorking;
+ }
+
+ if((transform->status == xmlSecTransformStatusWorking) && (last == 0)) {
+ /* just do nothing */
+ xmlSecAssert2(outSize == 0, -1);
+
+ } else if((transform->status == xmlSecTransformStatusWorking) && (last != 0)) {
+ xmlSecAssert2(outSize == 0, -1);
+
+ ret = xmlSecXslProcess(in, out, ctx->xslt);
+ if(ret < 0) {
+ xmlSecError(XMLSEC_ERRORS_HERE,
+ xmlSecErrorsSafeString(xmlSecTransformGetName(transform)),
+ "xmlSecXslProcess",
+ XMLSEC_ERRORS_R_XMLSEC_FAILED,
+ XMLSEC_ERRORS_NO_MESSAGE);
+ return(-1);
+ }
+
+ ret = xmlSecBufferRemoveHead(in, inSize);
+ if(ret < 0) {
+ xmlSecError(XMLSEC_ERRORS_HERE,
+ xmlSecErrorsSafeString(xmlSecTransformGetName(transform)),
+ "xmlSecBufferRemoveHead",
+ XMLSEC_ERRORS_R_XMLSEC_FAILED,
+ "size=%d", inSize);
+ return(-1);
+ }
+
+ transform->status = xmlSecTransformStatusFinished;
+ } else if(transform->status == xmlSecTransformStatusFinished) {
+ /* the only way we can get here is if there is no input */
+ xmlSecAssert2(inSize == 0, -1);
+ } else {
+ xmlSecError(XMLSEC_ERRORS_HERE,
+ xmlSecErrorsSafeString(xmlSecTransformGetName(transform)),
+ NULL,
+ XMLSEC_ERRORS_R_INVALID_STATUS,
+ "status=%d", transform->status);
+ return(-1);
+ }
+ return(0);
+}
+
+static int
+xmlSecXslProcess(xmlSecBufferPtr in, xmlSecBufferPtr out, xsltStylesheetPtr stylesheet) {
+ xmlDocPtr docIn = NULL;
+ xmlDocPtr docOut = NULL;
+ xmlOutputBufferPtr output = NULL;
+ int res = -1;
+ int ret;
+
+ xmlSecAssert2(in != NULL, -1);
+ xmlSecAssert2(out != NULL, -1);
+ xmlSecAssert2(stylesheet != NULL, -1);
+
+ docIn = xmlSecParseMemory(xmlSecBufferGetData(in), xmlSecBufferGetSize(in), 1);
+ if(docIn == NULL) {
+ xmlSecError(XMLSEC_ERRORS_HERE,
+ NULL,
+ "xmlSecParseMemory",
+ XMLSEC_ERRORS_R_XMLSEC_FAILED,
+ XMLSEC_ERRORS_NO_MESSAGE);
+ goto done;
+ }
+
+ docOut = xsltApplyStylesheet(stylesheet, docIn, NULL);
+ if(docOut == NULL) {
+ xmlSecError(XMLSEC_ERRORS_HERE,
+ NULL,
+ "xsltApplyStylesheet",
+ XMLSEC_ERRORS_R_XSLT_FAILED,
+ XMLSEC_ERRORS_NO_MESSAGE);
+ goto done;
+ }
+
+ output = xmlSecBufferCreateOutputBuffer(out);
+ if(output == NULL) {
+ xmlSecError(XMLSEC_ERRORS_HERE,
+ NULL,
+ "xmlSecBufferCreateOutputBuffer",
+ XMLSEC_ERRORS_R_XMLSEC_FAILED,
+ XMLSEC_ERRORS_NO_MESSAGE);
+ goto done;
+ }
+
+ ret = xsltSaveResultTo(output, docOut, stylesheet);
+ if(ret < 0) {
+ xmlSecError(XMLSEC_ERRORS_HERE,
+ NULL,
+ "xsltSaveResultTo",
+ XMLSEC_ERRORS_R_XSLT_FAILED,
+ XMLSEC_ERRORS_NO_MESSAGE);
+ goto done;
+ }
+
+ ret = xmlOutputBufferClose(output);
+ output = NULL;
+ if(ret < 0) {
+ xmlSecError(XMLSEC_ERRORS_HERE,
+ NULL,
+ "xmlOutputBufferClose",
+ XMLSEC_ERRORS_R_XML_FAILED,
+ XMLSEC_ERRORS_NO_MESSAGE);
+ return(-1);
+ }
+
+ res = 0;
+
+done:
+ if(output != NULL) xmlOutputBufferClose(output);
+ if(docIn != NULL) xmlFreeDoc(docIn);
+ if(docOut != NULL) xmlFreeDoc(docOut);
+ return(res);
+}
+
+#endif /* XMLSEC_NO_XSLT */
+