/** 
 * XML Security Library (http://www.aleksey.com/xmlsec).
 *
 * Enveloped transform.
 *
 * 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_globals.h"
 
#include <stdlib.h>
#include <string.h>

#include <libxml2_tree.h>
#include <libxml2_xpath.h>
#include <libxml2_xpathinternals.h>

#include "xmlsec_xmlsec.h"
#include "xmlsec_xmltree.h"
#include "xmlsec_keys.h"
#include "xmlsec_transforms.h"
#include "xmlsec_errors.h"

/**************************************************************************
 *
 *  Enveloped transform 
 *
 *************************************************************************/
static int 	xmlSecTransformEnvelopedExecute		(xmlSecTransformPtr transform,
							 int last, 
							 xmlSecTransformCtxPtr transformCtx);


static xmlSecTransformKlass xmlSecTransformEnvelopedKlass = {
    /* klass/object sizes */
    sizeof(xmlSecTransformKlass),		/* xmlSecSize klassSize */
    sizeof(xmlSecTransform),			/* xmlSecSize objSize */

    xmlSecNameEnveloped,			/* const xmlChar* name; */  
    xmlSecHrefEnveloped,			/* const xmlChar* href; */
    xmlSecTransformUsageDSigTransform,		/* xmlSecTransformUsage	usage; */
    
    NULL,					/* xmlSecTransformInitializeMethod initialize; */
    NULL,					/* xmlSecTransformFinalizeMethod finalize; */
    NULL,					/* xmlSecTransformNodeReadMethod readNode; */
    NULL,					/* xmlSecTransformNodeWriteMethod writeNode; */    
    NULL,					/* xmlSecTransformSetKeyReqMethod setKeyReq; */
    NULL,					/* xmlSecTransformSetKeyMethod setKey; */
    NULL,					/* xmlSecTransformValidateMethod validate; */
    xmlSecTransformDefaultGetDataType,		/* xmlSecTransformGetDataTypeMethod getDataType; */
    NULL,					/* xmlSecTransformPushBinMethod pushBin; */
    NULL,					/* xmlSecTransformPopBinMethod popBin; */
    xmlSecTransformDefaultPushXml,		/* xmlSecTransformPushXmlMethod pushXml; */
    xmlSecTransformDefaultPopXml,		/* xmlSecTransformPopXmlMethod popXml; */
    xmlSecTransformEnvelopedExecute,		/* xmlSecTransformExecuteMethod execute; */

    NULL,					/* void* reserved0; */
    NULL,					/* void* reserved1; */
};

/**
 * xmlSecTransformEnvelopedGetKlass:
 *
 * The enveloped transform klass (http://www.w3.org/TR/xmldsig-core/#sec-EnvelopedSignature):
 *
 * An enveloped signature transform T removes the whole Signature element 
 * containing T from the digest calculation of the Reference element 
 * containing T. The entire string of characters used by an XML processor 
 * to match the Signature with the XML production element is removed. 
 * The output of the transform is equivalent to the output that would 
 * result from replacing T with an XPath transform containing the following 
 * XPath parameter element:
 *
 * <XPath xmlns:dsig="&dsig;">
 *   count(ancestor-or-self::dsig:Signature |
 *   here()/ancestor::dsig:Signature[1]) >
 *   count(ancestor-or-self::dsig:Signature)</XPath>
 *    
 * The input and output requirements of this transform are identical to 
 * those of the XPath transform, but may only be applied to a node-set from 
 * its parent XML document. Note that it is not necessary to use an XPath 
 * expression evaluator to create this transform. However, this transform 
 * MUST produce output in exactly the same manner as the XPath transform 
 * parameterized by the XPath expression above.
 *
 * Returns enveloped transform id.
 */
EXPORT_C
xmlSecTransformId 
xmlSecTransformEnvelopedGetKlass(void) {
    return(&xmlSecTransformEnvelopedKlass);
}

static int
xmlSecTransformEnvelopedExecute(xmlSecTransformPtr transform, int last, 
				 xmlSecTransformCtxPtr transformCtx) {
    xmlNodePtr node;
    xmlSecNodeSetPtr children;

    xmlSecAssert2(xmlSecTransformCheckId(transform, xmlSecTransformEnvelopedId), -1);
    xmlSecAssert2(transform->hereNode != NULL, -1);
    xmlSecAssert2(transform->outNodes == NULL, -1);
    xmlSecAssert2(last != 0, -1);
    xmlSecAssert2(transformCtx != NULL, -1);
    
    if((transform->inNodes != NULL) && (transform->inNodes->doc != transform->hereNode->doc)) {
	xmlSecError(XMLSEC_ERRORS_HERE,
		    xmlSecErrorsSafeString(xmlSecTransformGetName(transform)),
		    NULL,
		    XMLSEC_ERRORS_R_TRANSFORM_SAME_DOCUMENT_REQUIRED,
		    XMLSEC_ERRORS_NO_MESSAGE);
	return(-1);
    }
    
    /* find signature node and get all its children in the nodes set */
    node = xmlSecFindParent(transform->hereNode, xmlSecNodeSignature, xmlSecDSigNs);
    if(node == NULL) {
	xmlSecError(XMLSEC_ERRORS_HERE,
		    xmlSecErrorsSafeString(xmlSecTransformGetName(transform)),
		    xmlSecErrorsSafeString(xmlSecNodeSignature),
		    XMLSEC_ERRORS_R_NODE_NOT_FOUND,
		    XMLSEC_ERRORS_NO_MESSAGE);
	return(-1);
    }
    
    children = xmlSecNodeSetGetChildren(node->doc, node, 1, 1);
    if(children == NULL) {
	xmlSecError(XMLSEC_ERRORS_HERE,
		    xmlSecErrorsSafeString(xmlSecTransformGetName(transform)),
		    "xmlSecNodeSetGetChildren",
		    XMLSEC_ERRORS_R_XMLSEC_FAILED,
		    "node=%s",
		    xmlSecErrorsSafeString(xmlSecNodeGetName(node)));
	return(-1);
    }

    /* intersect <dsig:Signature/> node children with input nodes (if exist) */
    transform->outNodes = xmlSecNodeSetAdd(transform->inNodes, children, xmlSecNodeSetIntersection);
    if(transform->outNodes == NULL) {
	xmlSecError(XMLSEC_ERRORS_HERE,
		    xmlSecErrorsSafeString(xmlSecTransformGetName(transform)),
		    "xmlSecNodeSetAdd",		    
		    XMLSEC_ERRORS_R_XMLSEC_FAILED,
		    XMLSEC_ERRORS_NO_MESSAGE);
	xmlSecNodeSetDestroy(children);
	return(-1);
    }
    
    return(0);
}

