/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

/*
 * XSEC
 *
 * DSIG_Reference := Class for handling a DSIG reference element
 *
 * $Id: DSIGReference.cpp 1833341 2018-06-11 16:25:41Z scantor $
 *
 */

// XSEC includes

#include <xsec/dsig/DSIGReference.hpp>
#include <xsec/transformers/TXFMChain.hpp>
#include <xsec/transformers/TXFMURL.hpp>
#include <xsec/transformers/TXFMDocObject.hpp>
#include <xsec/transformers/TXFMOutputFile.hpp>
#include <xsec/transformers/TXFMHash.hpp>
#include <xsec/transformers/TXFMBase64.hpp>
#include <xsec/transformers/TXFMSB.hpp>
#include <xsec/transformers/TXFMXPath.hpp>
#include <xsec/transformers/TXFMC14n.hpp>
#include <xsec/transformers/TXFMXSL.hpp>
#include <xsec/transformers/TXFMEnvelope.hpp>
#include <xsec/dsig/DSIGConstants.hpp>
#include <xsec/dsig/DSIGSignature.hpp>
#include <xsec/dsig/DSIGTransformList.hpp>
#include <xsec/dsig/DSIGTransformBase64.hpp>
#include <xsec/dsig/DSIGTransformEnvelope.hpp>
#include <xsec/dsig/DSIGTransformXPath.hpp>
#include <xsec/dsig/DSIGTransformXPathFilter.hpp>
#include <xsec/dsig/DSIGTransformXSL.hpp>
#include <xsec/dsig/DSIGTransformC14n.hpp>

#include <xsec/framework/XSECError.hpp>
#include <xsec/framework/XSECEnv.hpp>
#include <xsec/framework/XSECAlgorithmHandler.hpp>
#include <xsec/framework/XSECAlgorithmMapper.hpp>
#include <xsec/utils/XSECPlatformUtils.hpp>
#include <xsec/utils/XSECBinTXFMInputStream.hpp>

#include "../utils/XSECDOMUtils.hpp"

// Xerces

#include <xercesc/util/XMLNetAccessor.hpp>
#include <xercesc/util/XMLUniDefs.hpp>
#include <xercesc/util/Janitor.hpp>

XERCES_CPP_NAMESPACE_USE

#include <iostream>

// --------------------------------------------------------------------------------
//           Some useful strings
// --------------------------------------------------------------------------------


static const XMLCh  s_unicodeStrURI[] =
{
        XERCES_CPP_NAMESPACE_QUALIFIER chLatin_U,
        XERCES_CPP_NAMESPACE_QUALIFIER chLatin_R,
        XERCES_CPP_NAMESPACE_QUALIFIER chLatin_I,
        XERCES_CPP_NAMESPACE_QUALIFIER chNull
};

static const XMLCh  s_unicodeStrxpointer[] =
{
        XERCES_CPP_NAMESPACE_QUALIFIER chLatin_x,
        XERCES_CPP_NAMESPACE_QUALIFIER chLatin_p,
        XERCES_CPP_NAMESPACE_QUALIFIER chLatin_o,
        XERCES_CPP_NAMESPACE_QUALIFIER chLatin_i,
        XERCES_CPP_NAMESPACE_QUALIFIER chLatin_n,
        XERCES_CPP_NAMESPACE_QUALIFIER chLatin_t,
        XERCES_CPP_NAMESPACE_QUALIFIER chLatin_e,
        XERCES_CPP_NAMESPACE_QUALIFIER chLatin_r,
        XERCES_CPP_NAMESPACE_QUALIFIER chNull
};

static const XMLCh s_unicodeStrRootNode[] =
{
        XERCES_CPP_NAMESPACE_QUALIFIER chOpenParen,
        XERCES_CPP_NAMESPACE_QUALIFIER chForwardSlash,
        XERCES_CPP_NAMESPACE_QUALIFIER chCloseParen,
        XERCES_CPP_NAMESPACE_QUALIFIER chNull
};

// --------------------------------------------------------------------------------
//           Constructors and Destructors
// --------------------------------------------------------------------------------


DSIGReference::DSIGReference(const XSECEnv * env, DOMNode *dom) :
    mp_formatter(NULL),
    mp_referenceNode(dom),
    mp_preHash(NULL),
    mp_manifestList(NULL),
    mp_URI(NULL),
    m_isManifest(false),
    mp_transformsNode(NULL),
    mp_hashValueNode(NULL),
    mp_env(env),
    mp_transformList(NULL),
    mp_algorithmURI(NULL),
    m_loaded(false) {

    // Should throw an exception if the node is not a REFERENCE element

    XSECnew(mp_formatter, XSECSafeBufferFormatter("UTF-8",XMLFormatter::NoEscapes,
                                                XMLFormatter::UnRep_CharRef));
}

DSIGReference::DSIGReference(const XSECEnv * env) :
    mp_formatter(NULL),
    mp_referenceNode(NULL),
    mp_preHash(NULL),
    mp_manifestList(NULL),
    mp_URI(NULL),
    m_isManifest(false),
    mp_transformsNode(NULL),
    mp_hashValueNode(NULL),
    mp_env(env),
    mp_transformList(NULL),
    mp_algorithmURI(NULL),
    m_loaded(false) {

    XSECnew(mp_formatter, XSECSafeBufferFormatter("UTF-8",XMLFormatter::NoEscapes,
                                            XMLFormatter::UnRep_CharRef));
};

DSIGReference::~DSIGReference() {

    // Destroy any associated transforms

    if (mp_transformList != NULL) {

        delete mp_transformList;
        mp_transformList = NULL;

    }

    if (mp_formatter != NULL)
        delete mp_formatter;

    if (mp_manifestList != NULL)
        delete mp_manifestList;

};

// --------------------------------------------------------------------------------
//           Creation of Transforms
// --------------------------------------------------------------------------------

void DSIGReference::createTransformList(void) {

    // Creates the transforms list
    safeBuffer str;
    const XMLCh * prefix;
    DOMDocument *doc = mp_env->getParentDocument();

    prefix = mp_env->getDSIGNSPrefix();

    if (mp_transformsNode == NULL) {

        // Need to create a transforms node
        makeQName(str, prefix, "Transforms");
        mp_transformsNode = doc->createElementNS(DSIGConstants::s_unicodeStrURIDSIG, str.rawXMLChBuffer());
        mp_referenceNode->insertBefore(mp_transformsNode, mp_referenceNode->getFirstChild());
        if (mp_env->getPrettyPrintFlag() == true)
            mp_referenceNode->insertBefore(doc->createTextNode(DSIGConstants::s_unicodeStrNL), mp_transformsNode);
        mp_env->doPrettyPrint(mp_transformsNode);

        // Create the list
        XSECnew(mp_transformList, DSIGTransformList());
    }

}

void DSIGReference::addTransform(DSIGTransform * txfm, DOMElement * txfmElt) {

    if (mp_transformList == NULL)
        createTransformList();

    mp_transformsNode->appendChild(txfmElt);
    mp_env->doPrettyPrint(mp_transformsNode);

    mp_transformList->addTransform(txfm);
}


DSIGTransformEnvelope * DSIGReference::appendEnvelopedSignatureTransform() {

    DOMElement *txfmElt;
    DSIGTransformEnvelope * txfm;

    XSECnew(txfm, DSIGTransformEnvelope(mp_env));
    txfmElt = txfm->createBlankTransform(mp_env->getParentDocument());

    addTransform(txfm, txfmElt);

    return txfm;

}

DSIGTransformBase64 * DSIGReference::appendBase64Transform() {

    DOMElement *txfmElt;
    DSIGTransformBase64 * txfm;

    XSECnew(txfm, DSIGTransformBase64(mp_env));
    txfmElt = txfm->createBlankTransform(mp_env->getParentDocument());

    addTransform(txfm, txfmElt);

    return txfm;

}

DSIGTransformXSL * DSIGReference::appendXSLTransform(DOMNode * stylesheet) {

    DOMElement *txfmElt;
    DSIGTransformXSL * txfm;

    XSECnew(txfm, DSIGTransformXSL(mp_env));
    txfmElt = txfm->createBlankTransform(mp_env->getParentDocument());
    txfm->setStylesheet(stylesheet);

    addTransform(txfm, txfmElt);

    return txfm;

}

DSIGTransformC14n * DSIGReference::appendCanonicalizationTransform(
        const XMLCh * canonicalizationAlgorithmURI) {

    DOMElement *txfmElt;
    DSIGTransformC14n * txfm;

    XSECnew(txfm, DSIGTransformC14n(mp_env));
    txfmElt = txfm->createBlankTransform(mp_env->getParentDocument());
    txfm->setCanonicalizationMethod(canonicalizationAlgorithmURI);

    addTransform(txfm, txfmElt);

    return txfm;

}

DSIGTransformXPath * DSIGReference::appendXPathTransform(const char * expr) {

    DOMElement *txfmElt;
    DSIGTransformXPath * txfm;

    XSECnew(txfm, DSIGTransformXPath(mp_env));
    txfmElt = txfm->createBlankTransform(mp_env->getParentDocument());
    txfm->setExpression(expr);

    addTransform(txfm, txfmElt);

    return txfm;
}

DSIGTransformXPathFilter * DSIGReference::appendXPathFilterTransform(void) {

    DOMElement *txfmElt;
    DSIGTransformXPathFilter * txfm;

    XSECnew(txfm, DSIGTransformXPathFilter(mp_env));
    txfmElt = txfm->createBlankTransform(mp_env->getParentDocument());

    addTransform(txfm, txfmElt);
    mp_env->doPrettyPrint(txfmElt);

    return txfm;
}



// --------------------------------------------------------------------------------
//           Creation of blanks
// --------------------------------------------------------------------------------


DOMElement *DSIGReference::createBlankReference(const XMLCh * URI,
                                                const XMLCh * hashAlgorithmURI,
                                                const XMLCh * type) {

    // Reset this Reference just in case

    m_isManifest = false;
    mp_preHash = NULL;
    mp_manifestList = NULL;
    mp_transformsNode = NULL;
    mp_transformList = NULL;

    safeBuffer str;
    DOMDocument *doc = mp_env->getParentDocument();
    const XMLCh * prefix = mp_env->getDSIGNSPrefix();

    makeQName(str, prefix, "Reference");

    DOMElement *ret = doc->createElementNS(DSIGConstants::s_unicodeStrURIDSIG, str.rawXMLChBuffer());
    mp_referenceNode = ret;

    // Set type
    if (type != NULL)
        ret->setAttributeNS(NULL, MAKE_UNICODE_STRING("Type"), type);

    // Set URI
    if (URI != NULL) {
        ret->setAttributeNS(NULL, s_unicodeStrURI, URI);
        mp_URI = ret->getAttributeNS(NULL, s_unicodeStrURI); // Used later on as a pointer
    }
    else {
        // Anonymous reference
        mp_URI = NULL;
    }

    // Create hash and hashValue nodes
    makeQName(str, prefix, "DigestMethod");
    DOMElement *digestMethod = doc->createElementNS(DSIGConstants::s_unicodeStrURIDSIG, str.rawXMLChBuffer());
    mp_env->doPrettyPrint(ret);
    ret->appendChild(digestMethod);
    mp_env->doPrettyPrint(ret);

    digestMethod->setAttributeNS(NULL, DSIGConstants::s_unicodeStrAlgorithm,
        hashAlgorithmURI);

    // Retrieve the attribute value for later use
    mp_algorithmURI =
        digestMethod->getAttributeNS(NULL, DSIGConstants::s_unicodeStrAlgorithm);


    // DigestValue

    makeQName(str, prefix, "DigestValue");
    mp_hashValueNode = doc->createElementNS(DSIGConstants::s_unicodeStrURIDSIG, str.rawXMLChBuffer());
    ret->appendChild(mp_hashValueNode);
    mp_env->doPrettyPrint(ret);
    mp_hashValueNode->appendChild(doc->createTextNode(MAKE_UNICODE_STRING("Not yet calculated")));

    m_loaded = true;
    return ret;

}

// --------------------------------------------------------------------------------
//           setPreHashTransform - to set a transform to be used pre-hash
// --------------------------------------------------------------------------------


void DSIGReference::setPreHashTXFM(TXFMBase * t) {

    mp_preHash = t;

}

//           setId
// --------------------------------------------------------------------------------

void DSIGReference::setId(const XMLCh *id) {

    if (mp_referenceNode)
        ((DOMElement*)mp_referenceNode)->setAttributeNS(NULL, MAKE_UNICODE_STRING("Id"), id);

}

// --------------------------------------------------------------------------------
//           setType
// --------------------------------------------------------------------------------

void DSIGReference::setType(const XMLCh *type) {

    if (mp_referenceNode)
        ((DOMElement*)mp_referenceNode)->setAttributeNS(NULL, MAKE_UNICODE_STRING("Type"), type);

}

// --------------------------------------------------------------------------------

// --------------------------------------------------------------------------------
//           isManifest
// --------------------------------------------------------------------------------

bool DSIGReference::isManifest() const {

    return m_isManifest;

}

// --------------------------------------------------------------------------------
//           getURI
// --------------------------------------------------------------------------------

const XMLCh * DSIGReference::getURI() const {

    return mp_URI;

}

// --------------------------------------------------------------------------------
//           getManifestReferenceList
// --------------------------------------------------------------------------------

DSIGReferenceList * DSIGReference::getManifestReferenceList() const {

    return mp_manifestList;

}

// --------------------------------------------------------------------------------
//           getURIBaseTransform
// --------------------------------------------------------------------------------

TXFMBase * DSIGReference::getURIBaseTXFM(DOMDocument * doc,
                                         const XMLCh * URI,
                                         const XSECEnv * env) {

    // Determine if this is a full URL or a pointer to a URL

    if (URI == NULL || (URI[0] != 0 &&
        URI[0] != XERCES_CPP_NAMESPACE_QUALIFIER chPound)) {

        TXFMURL * retTransform;

        // Have a URL!

        XSECnew(retTransform, TXFMURL(doc, env->getURIResolver()));

        try {
            ((TXFMURL *) retTransform)->setInput(URI);
        }
        catch (...) {

            delete retTransform;
            throw;

        }

        return retTransform;

    }

    // Have a fragment URI from the local document
    TXFMDocObject * to;
    XSECnew(to, TXFMDocObject(doc));
    Janitor<TXFMDocObject> j_to(to);
    to->setEnv(env);

    // Find out what sort of object pointer this is.

    if (URI[0] == 0) {

        // empty pointer - use the document itself
        to->setInput(doc);
        to->stripComments();

    }

    else if (XMLString::compareNString(&URI[1], s_unicodeStrxpointer, 8) == 0) {

        // Have an xpointer
        if (strEquals(s_unicodeStrRootNode, &URI[9]) == true) {
            // Root node
            to->setInput(doc);

        }

        else if (URI[9] == XERCES_CPP_NAMESPACE_QUALIFIER chOpenParen &&
                URI[10] == XERCES_CPP_NAMESPACE_QUALIFIER chLatin_i &&
                URI[11] == XERCES_CPP_NAMESPACE_QUALIFIER chLatin_d &&
                URI[12] == XERCES_CPP_NAMESPACE_QUALIFIER chOpenParen &&
                URI[13] == XERCES_CPP_NAMESPACE_QUALIFIER chSingleQuote) {

            XMLSize_t len = XMLString::stringLen(&URI[14]);

            XMLCh* tmp = new XMLCh[len + 1];
            ArrayJanitor<XMLCh> j_tmp(tmp);

            XMLSize_t j = 14, i = 0;

            // Have an ID
            while (i < len && URI[j] != '\'') {
                tmp[i++] = URI[j++];
            }

            tmp[i] = XERCES_CPP_NAMESPACE_QUALIFIER chNull;

            if (URI[j] != '\'') {
                throw XSECException(XSECException::UnsupportedXpointerExpr);
            }

            to->setInput(doc, tmp);

        }

        else {

            throw XSECException(XSECException::UnsupportedXpointerExpr);

        }

        // Keep comments in these situations
        to->activateComments();
    }

    else {

        to->setInput(doc, &URI[1]);

        // Remove comments
        to->stripComments();

    }

    j_to.release();
    return to;

}

// --------------------------------------------------------------------------------
//           load
// --------------------------------------------------------------------------------

void DSIGReference::load(void) {

    // Load reference info from XML

    DOMNamedNodeMap *atts = mp_referenceNode->getAttributes();
    DOMNode *tmpElt;

    const XMLCh * name;
    safeBuffer sbName;

    if (atts != 0) {

        XMLSize_t size = atts->getLength();

        for (XMLSize_t i = 0; i < size; ++i) {

            name = atts->item(i)->getNodeName();
            sbName << (*mp_formatter << atts->item(i)->getNodeName());

            if (strEquals(name, s_unicodeStrURI)) {
                mp_URI = atts->item(i)->getNodeValue();
            }

            else if (strEquals(name, "Type")) {

                // Check if a manifest, otherwise ignore for now
                if (strEquals(atts->item(i)->getNodeValue(), DSIGConstants::s_unicodeStrURIMANIFEST))
                    m_isManifest = true;

            }

            else if (strEquals(name, "Id")) {

                // For now ignore

            }

            else if (sbName.sbStrncmp("xmlns", 5) == 0) {

                // Ignore name spaces

            }

            else {
                //safeBuffer tmp, error;

                //error << (*mp_formatter << name);
                //tmp.sbStrcpyIn("Unknown attribute in <Reference> Element : ");
                //tmp.sbStrcatIn(error);

                throw XSECException(XSECException::UnknownDSIGAttribute,
                    "Unknown attribute in <Reference> Element");

            }

        }

    }

    // Now check for Transforms
    tmpElt = mp_referenceNode->getFirstChild();

    while (tmpElt != 0 && (tmpElt->getNodeType() != DOMNode::ELEMENT_NODE)) {
        if (tmpElt->getNodeType() == DOMNode::ENTITY_REFERENCE_NODE) {
            throw XSECException(XSECException::ExpectedDSIGChildNotFound,
                "EntityReference nodes in <Reference> are unsupported.");
        }
        // Skip text and comments
        tmpElt = tmpElt->getNextSibling();
    }

    if (tmpElt == 0) {

            throw XSECException(XSECException::ExpectedDSIGChildNotFound,
                    "Expected <Transforms> or <DigestMethod> within <Reference>");

    }

    if (strEquals(getDSIGLocalName(tmpElt), "Transforms")) {

        // Store node for later use
        mp_transformsNode = tmpElt;

        // Load the transforms
        mp_transformList = loadTransforms(tmpElt, mp_formatter, mp_env);

        // Find next node
        tmpElt = tmpElt->getNextSibling();
        while (tmpElt != 0 && (tmpElt->getNodeType() != DOMNode::ELEMENT_NODE)) {
            if (tmpElt->getNodeType() == DOMNode::ENTITY_REFERENCE_NODE) {
                throw XSECException(XSECException::ExpectedDSIGChildNotFound,
                    "EntityReference nodes in <Reference> are unsupported.");
            }
            tmpElt = tmpElt->getNextSibling();
        }


    } /* if tmpElt node type = transforms */
    else {
        mp_transformList = NULL;
    }


    if (tmpElt == NULL || !strEquals(getDSIGLocalName(tmpElt), "DigestMethod")) {

        throw XSECException(XSECException::ExpectedDSIGChildNotFound,
                            "Expected <DigestMethod> element");

    }

    // Determine what the digest method actually is

    atts = tmpElt->getAttributes();
    unsigned int i;

    for (i = 0; i < atts->getLength() &&
        !strEquals(atts->item(i)->getNodeName(), DSIGConstants::s_unicodeStrAlgorithm); ++i);


    if (i == atts->getLength()) {

        throw XSECException(XSECException::ExpectedDSIGChildNotFound,
                            "Expected 'Algorithm' attribute in <DigestMethod>");

    }

    mp_algorithmURI = atts->item(i)->getNodeValue();

    // Find the hash value node

    tmpElt = tmpElt->getNextSibling();

    while (tmpElt != 0 &&
        (tmpElt->getNodeType() != DOMNode::ELEMENT_NODE || !strEquals(getDSIGLocalName(tmpElt), "DigestValue"))) {
        if (tmpElt->getNodeType() == DOMNode::ENTITY_REFERENCE_NODE) {
            throw XSECException(XSECException::ExpectedDSIGChildNotFound,
                "EntityReference nodes in <Reference> are unsupported.");
        }
        tmpElt = tmpElt->getNextSibling();
    }

    if (tmpElt == 0) {

        throw XSECException(XSECException::ExpectedDSIGChildNotFound,
            "Expected <DigestValue> within <Reference>");

    }

    mp_hashValueNode = tmpElt;

    // If we are a manifest, then we need to load the manifest references

    if (m_isManifest) {

        // Find the manifest node - we cheat and use a transform
        TXFMBase                * docObject;
        DOMNode                    * manifestNode, * referenceNode;

        docObject = getURIBaseTXFM(mp_referenceNode->getOwnerDocument(), mp_URI,
            mp_env);

        manifestNode = docObject->getFragmentNode();
        delete docObject;

        // Now search downwards to find a <Manifest>
        if (manifestNode == 0 || manifestNode->getNodeType() != DOMNode::ELEMENT_NODE ||
            (!strEquals(getDSIGLocalName(manifestNode), "Object") && !strEquals(getDSIGLocalName(manifestNode), "Manifest"))) {

            throw XSECException(XSECException::ExpectedDSIGChildNotFound,
                "Expected <Manifest> or <Object> URI for Manifest Type <Reference>");

        }

        if (strEquals(getDSIGLocalName(manifestNode), "Object")) {

            // Find Manifest child
            manifestNode = manifestNode->getFirstChild();
            while (manifestNode != 0 && manifestNode->getNodeType() != DOMNode::ELEMENT_NODE) {
                if (manifestNode->getNodeType() == DOMNode::ENTITY_REFERENCE_NODE) {
                    throw XSECException(XSECException::ExpectedDSIGChildNotFound,
                        "EntityReference nodes in <Reference> are unsupported.");
                }
                manifestNode = manifestNode->getNextSibling();
            }

            if (manifestNode == 0 || !strEquals(getDSIGLocalName(manifestNode), "Manifest"))
                throw XSECException(XSECException::ExpectedDSIGChildNotFound,
                "Expected <Manifest> as child of <Object> for Manifest Type <Reference>");

        }

        // Now have the manifest node, find the first reference and load!
        referenceNode = manifestNode->getFirstChild();

        while (referenceNode != 0 &&
            (referenceNode->getNodeType() != DOMNode::ELEMENT_NODE || !strEquals(getDSIGLocalName(referenceNode), "Reference"))) {
            if (referenceNode->getNodeType() == DOMNode::ENTITY_REFERENCE_NODE) {
                throw XSECException(XSECException::ExpectedDSIGChildNotFound,
                    "EntityReference nodes in <Reference> are unsupported.");
            }
            referenceNode = referenceNode->getNextSibling();
        }

        if (referenceNode == 0)
            throw XSECException(XSECException::ExpectedDSIGChildNotFound,
            "Expected <Reference> as child of <Manifest>");

        // Have reference node, so lets create a list!
        mp_manifestList = DSIGReference::loadReferenceListFromXML(mp_env, referenceNode);

    } /* m_isManifest */

    m_loaded = true;

}

// --------------------------------------------------------------------------------
//           createReferenceListFromXML
// --------------------------------------------------------------------------------

DSIGReferenceList *DSIGReference::loadReferenceListFromXML(const XSECEnv * env, DOMNode *firstReference) {

    // Have the first reference element in the document,
    // so want to find and load them all

    DOMNode *tmpRef = firstReference;
    DSIGReferenceList * refList;
    DSIGReference * r;

    XSECnew(refList, DSIGReferenceList());
    Janitor<DSIGReferenceList> j_refList(refList);

    while (tmpRef != 0) {

        // Must be an element node

        if (tmpRef->getNodeType() != DOMNode::ELEMENT_NODE ||
            !strEquals(getDSIGLocalName(tmpRef), "Reference")) {

            throw XSECException(XSECException::ExpectedDSIGChildNotFound,
                "Expected <Reference> as child of <SignedInfo>");

        }

        XSECnew(r, DSIGReference(env, tmpRef));

        refList->addReference(r);

        // Load the reference before moving on
        r->load();

        // Find next element Node
        tmpRef = tmpRef->getNextSibling();

        while (tmpRef != 0 && tmpRef->getNodeType() != DOMNode::ELEMENT_NODE) {
            if (tmpRef->getNodeType() == DOMNode::ENTITY_REFERENCE_NODE) {
                throw XSECException(XSECException::ExpectedDSIGChildNotFound,
                    "EntityReference nodes in <Reference> are unsupported.");
            }
            tmpRef = tmpRef->getNextSibling();
        }

    }

    j_refList.release();
    return refList;

}
// --------------------------------------------------------------------------------
//           Get the Canonicalised BYTE_STREAM of the reference data (and TXFMS)
// --------------------------------------------------------------------------------

XSECBinTXFMInputStream * DSIGReference::makeBinInputStream(void) const {

    // First set up for input

    TXFMChain * txfmChain;
    TXFMBase * currentTxfm;

    if (m_loaded == false) {

        throw XSECException(XSECException::NotLoaded,
            "calculateHash() called in DSIGReference before load()");

    }

    // Find base transform
    currentTxfm = getURIBaseTXFM(mp_referenceNode->getOwnerDocument(), mp_URI,
        mp_env);

    // Set up the transform chain

    txfmChain = createTXFMChainFromList(currentTxfm, mp_transformList);
    Janitor<TXFMChain> j_txfmChain(txfmChain);

    DOMDocument *d = mp_referenceNode->getOwnerDocument();

    // All transforms done.  If necessary, change the type from nodes to bytes

    if (txfmChain->getLastTxfm()->getOutputType() == TXFMBase::DOM_NODES) {

        TXFMC14n * c14n;
        XSECnew(c14n, TXFMC14n(d));
        txfmChain->appendTxfm(c14n);

    }

    // Now create the InputStream

    XSECBinTXFMInputStream * ret;
    ret = new XSECBinTXFMInputStream(txfmChain);    // Probs with MSVC++ mean no XSECnew
    j_txfmChain.release();        // Now owned by "ret"

    return ret;

}

// --------------------------------------------------------------------------------
//           Hash a reference list
// --------------------------------------------------------------------------------


void DSIGReference::hashReferenceList(const DSIGReferenceList *lst, bool interlocking) {

// Run through a list of hashes and checkHash for each one

    DSIGReference * r;
    int i = (int) lst->getSize();
    safeBuffer errStr;
    errStr.sbXMLChIn(DSIGConstants::s_unicodeStrEmpty);

    // Run a VERY naieve process at the moment that assumes the list will "settle"
    // after N iterations through the list.  This will settle any inter-locking references
    // Where a hash in a later calculated reference could impact an already calculated hash
    // in a previous references
    //
    // If interlocking is set to false, assume there are no interacting <Reference> nodes

    do {

        for (int j = 0; j < i; ++j) {

            r = lst->item(j);

            // If this is a manifest we need to set all the references in the manifest as well

            if (r->isManifest())
                hashReferenceList(r->getManifestReferenceList());

            // Re-ordered as per suggestion by Peter Gubis to make it more likely
            // that hashes are correct on first pass when manifests are involved

            r->setHash();

        }

    } while (interlocking && !DSIGReference::verifyReferenceList(lst, errStr) && i-- >= 0);
}

// --------------------------------------------------------------------------------
//           Verify reference list
// --------------------------------------------------------------------------------

bool DSIGReference::verifyReferenceList(const DSIGReferenceList * lst, safeBuffer &errStr) {

    // Run through a list of hashes and checkHash for each one

    XSEC_USING_XERCES(NetAccessorException);

    DSIGReference * r;
    bool res = true;

    int size = (lst ? (int) lst->getSize() : 0);

    for (int i = 0; i < size; ++i) {

        r = lst->item(i);

        try {
            if (!r->checkHash()) {

                // Failed
                errStr.sbXMLChCat("Reference URI=\"");
                errStr.sbXMLChCat(r->getURI());
                errStr.sbXMLChCat("\" failed to verify\n");

                res = false;

            }
        }
        catch (const NetAccessorException&) {

            res = false;

            errStr.sbXMLChCat("Error accessing network URI=\"");
            errStr.sbXMLChCat(r->getURI());
            errStr.sbXMLChCat("\".  Reference failed to verify\n");

        }
        catch (const XSECException& e) {

            if (e.getType() != XSECException::HTTPURIInputStreamError)
                throw;

            res = false;

            errStr.sbXMLChCat("Error accessing network URI=\"");
            errStr.sbXMLChCat(r->getURI());
            errStr.sbXMLChCat("\".  Reference failed to verify\n");

        }

        // if a manifest, check the manifest list
        if (r->isManifest())
            res = res & verifyReferenceList(r->getManifestReferenceList(), errStr);

    }

    return res;
}

// --------------------------------------------------------------------------------
//           processTransforms
// --------------------------------------------------------------------------------

TXFMChain * DSIGReference::createTXFMChainFromList(TXFMBase * input,
                            DSIGTransformList * lst) {

    TXFMChain * ret;
    XSECnew(ret, TXFMChain(input));

    if (lst == NULL)
        return ret;

    Janitor<TXFMChain> j_ret(ret);

    DSIGTransformList::TransformListVectorType::size_type size, i;

    size = lst->getSize();

    if (size > 0) {

        // Iterate through the list

        for (i = 0; i < size; ++i) {

            lst->item(i)->appendTransformer(ret);

        }

    }

    j_ret.release();
    return ret;

}

// --------------------------------------------------------------------------------
//           loadTransforms
// --------------------------------------------------------------------------------

DSIGTransformList * DSIGReference::loadTransforms(
                            DOMNode *transformsNode,
                            XSECSafeBufferFormatter * formatter,
                            const XSECEnv * env) {

    // This is defined as a static function, not because it makes use of any static variables
    // in the DSIGReference class, but to neatly link it to the other users

    if (transformsNode == 0 || (!strEquals(getDSIGLocalName(transformsNode), "Transforms") &&
        !strEquals(getXENCLocalName(transformsNode), "Transforms"))) {

            throw XSECException(XSECException::ExpectedDSIGChildNotFound,
                    "Expected <Transforms> in function DSIGReference::processTransforms");

    }

    // Create the list
    DSIGTransformList * lst;
    XSECnew(lst, DSIGTransformList());
    Janitor<DSIGTransformList> j_lst(lst);

    // Find First transform

    DOMNode * transforms = transformsNode->getFirstChild();
    while (transforms != NULL && transforms->getNodeType() != DOMNode::ELEMENT_NODE)
        transforms = transforms->getNextSibling();

    while (transforms != NULL) {

        // Process each transform in turn
        if (!strEquals(getDSIGLocalName(transforms), "Transform")) {

            // Not what we expected to see!
            safeBuffer tmp, error;

            error << (*formatter << getDSIGLocalName(transforms));
            tmp.sbStrcpyIn("Unknown attribute in <Transforms> - Expected <Transform> found ");
            tmp.sbStrcatIn(error);
            tmp.sbStrcatIn(">.");

            throw XSECException(XSECException::ExpectedDSIGChildNotFound, error.rawCharBuffer());

        }

        DOMNamedNodeMap * transformAtts = transforms->getAttributes();

        unsigned int i;

        for (i = 0; i < transformAtts->getLength() &&
                !strEquals(transformAtts->item(i)->getNodeName(), DSIGConstants::s_unicodeStrAlgorithm); ++i);

        if (i == transformAtts->getLength()) {

            throw XSECException(XSECException::ExpectedDSIGChildNotFound,
                "Algorithm attribute not found in <Transform> element");

        }

        safeBuffer algorithm;
        algorithm << (*formatter << transformAtts->item(i)->getNodeValue());

        // Determine what the transform is

        if (algorithm.sbStrcmp(URI_ID_BASE64) == 0) {

            DSIGTransformBase64 * b;
            XSECnew(b, DSIGTransformBase64(env, transforms));
            lst->addTransform(b);
            b->load();
        }

        else if (algorithm.sbStrcmp(URI_ID_XPATH) == 0) {

            DSIGTransformXPath * x;
            XSECnew(x, DSIGTransformXPath(env, transforms));
            lst->addTransform(x);
            x->load();
        }

        else if (algorithm.sbStrcmp(URI_ID_XPF) == 0) {

            DSIGTransformXPathFilter * xpf;

            XSECnew(xpf, DSIGTransformXPathFilter(env, transforms));
            lst->addTransform(xpf);
            xpf->load();

        }

        else if (algorithm.sbStrcmp(URI_ID_ENVELOPE) == 0) {

            DSIGTransformEnvelope * e;
            XSECnew(e, DSIGTransformEnvelope(env, transforms));
            lst->addTransform(e);
            e->load();
        }

        else if (algorithm.sbStrcmp(URI_ID_XSLT) == 0) {

            DSIGTransformXSL * x;
            XSECnew(x, DSIGTransformXSL(env, transforms));
            lst->addTransform(x);
            x->load();

        }


        else if (algorithm.sbStrcmp(URI_ID_C14N_COM) == 0 ||
                 algorithm.sbStrcmp(URI_ID_C14N_NOC) == 0 ||
                 algorithm.sbStrcmp(URI_ID_C14N11_COM) == 0 ||
                 algorithm.sbStrcmp(URI_ID_C14N11_NOC) == 0 ||
                 algorithm.sbStrcmp(URI_ID_EXC_C14N_COM) == 0 ||
                 algorithm.sbStrcmp(URI_ID_EXC_C14N_NOC) == 0) {

            DSIGTransformC14n * c;
            XSECnew(c, DSIGTransformC14n(env, transforms));
            lst->addTransform(c);
            c->load();

        }

        else {

            // Not what we expected to see!
            safeBuffer tmp;

            tmp.sbStrcpyIn("Unknown transform : ");
            tmp.sbStrcatIn(algorithm);
            tmp.sbStrcatIn(" found.");

            throw XSECException(XSECException::UnknownTransform, tmp.rawCharBuffer());
        }

        // Now find next element

        transforms = transforms->getNextSibling();
        while (transforms != NULL && transforms->getNodeType() != DOMNode::ELEMENT_NODE)
            transforms = transforms->getNextSibling();

    } /* while (transforms != NULL) */

    j_lst.release();
    return lst;
}

// --------------------------------------------------------------------------------
//           Set hash
// --------------------------------------------------------------------------------

void DSIGReference::setHash() {

    // Set up

    if (mp_hashValueNode == 0) {
        throw XSECException(XSECException::NotLoaded,
            "setHash() called in DSIGReference before load()");
    }

    unsigned int maxHashSize = XSECPlatformUtils::g_cryptoProvider->getMaxHashSize();

    XSECCryptoBase64 *    b64 = XSECPlatformUtils::g_cryptoProvider->base64();
    if (!b64) {
        throw XSECException(XSECException::CryptoProviderError,
                "Error requesting Base64 object from Crypto Provider");
    }

    Janitor<XSECCryptoBase64> j_b64(b64);

    // First determine the hash value
    XMLByte* calculatedHashVal = new XMLByte[maxHashSize];
    XMLByte* base64Hash = new XMLByte[maxHashSize * 2];

    unsigned int base64HashLen = 0;

    try {
        unsigned int calculatedHashLen = calculateHash(calculatedHashVal, maxHashSize);

        // Calculate the base64 value
        b64->encodeInit();
        base64HashLen = b64->encode(calculatedHashVal,
            calculatedHashLen,
            base64Hash,
            maxHashSize * 2);
        base64HashLen += b64->encodeFinish(&base64Hash[base64HashLen],
            (maxHashSize * 2) - base64HashLen);
    }
    catch (...) {
        delete[] calculatedHashVal;
        delete[] base64Hash;
        throw;
    }

    delete[] calculatedHashVal;

    // Ensure the string is terminated
    if (base64Hash[base64HashLen-1] == '\n')
        base64Hash[base64HashLen-1] = '\0';
    else
        base64Hash[base64HashLen] = '\0';

    // Now find the correct text node to re-set

    DOMNode* tmpElt = mp_hashValueNode->getFirstChild();

    while (tmpElt != NULL && tmpElt->getNodeType() != DOMNode::TEXT_NODE)
        tmpElt = tmpElt->getNextSibling();

    if (tmpElt == NULL) {
        // Need to create the underlying TEXT_NODE
        DOMDocument *doc = mp_referenceNode->getOwnerDocument();
        tmpElt = doc->createTextNode(MAKE_UNICODE_STRING((char *) base64Hash));
        mp_hashValueNode->appendChild(tmpElt);
    }
    else {
        tmpElt->setNodeValue(MAKE_UNICODE_STRING((char *) base64Hash));
    }

    delete[] base64Hash;
}




// --------------------------------------------------------------------------------
//           Create hash
// --------------------------------------------------------------------------------

unsigned int DSIGReference::calculateHash(XMLByte *toFill, unsigned int maxToFill) const {

    // Determine the hash value of the element

    // First set up for input

    TXFMBase * currentTxfm;
    TXFMChain * chain;

    unsigned int size;

    if (m_loaded == false) {

        throw XSECException(XSECException::NotLoaded,
            "calculateHash() called in DSIGReference before load()");

    }

    // Find base transform
    currentTxfm = getURIBaseTXFM(mp_referenceNode->getOwnerDocument(), mp_URI,
        mp_env);

    // Now build the transforms list
    // Note this passes ownership of currentTxfm to the function, so it is the
    // responsibility of createTXFMChain to ensure it gets deleted if this throws.

    chain = createTXFMChainFromList(currentTxfm, mp_transformList);
    Janitor<TXFMChain> j_chain(chain);

    DOMDocument *d = mp_referenceNode->getOwnerDocument();

    // All transforms done.  If necessary, change the type from nodes to bytes

    if (chain->getLastTxfm()->getOutputType() == TXFMBase::DOM_NODES) {

        TXFMC14n * c14n;
        XSECnew(c14n, TXFMC14n(d));
        chain->appendTxfm(c14n);

    }

    // Check to see if there is a final "application" transform prior to the hash

    if (mp_preHash != NULL) {

        chain->appendTxfm(mp_preHash);
        mp_preHash = NULL;    // Can't be re-used

    }

    // Check for debugging sink for the data
    TXFMBase* sink = XSECPlatformUtils::GetReferenceLoggingSink(d);
    if (sink)
        chain->appendTxfm(sink);


    // Get the mapping for the hash transform

    const XSECAlgorithmHandler* handler =
        XSECPlatformUtils::g_algorithmMapper->mapURIToHandler(mp_algorithmURI);

    if (handler == NULL) {
        throw XSECException(XSECException::SigVfyError,
            "Hash method unknown in DSIGReference::calculateHash()");
    }

    if (!handler->appendHashTxfm(chain, mp_algorithmURI)) {
        throw XSECException(XSECException::SigVfyError,
            "Unexpected error in handler whilst appending Hash transform");
    }

    // Now we have the hashing transform, run it.

    size = chain->getLastTxfm()->readBytes(toFill, maxToFill);

    // Clean out document if necessary
    chain->getLastTxfm()->deleteExpandedNameSpaces();

    return size;

}

// --------------------------------------------------------------------------------
//           Read hash
// --------------------------------------------------------------------------------

unsigned int DSIGReference::readHash(XMLByte *toFill, unsigned int maxToFill) const {

    // Determine the hash value stored in the reference

    // First set up for input

    unsigned int size;
    DOMNode *tmpElt;
    //const XMLCh * stringHash;

    TXFMBase * nextInput;

    DOMDocument *d = mp_referenceNode->getOwnerDocument();

    safeBuffer b64HashVal;

    // Find the hash value

    tmpElt = mp_referenceNode->getFirstChild();

    while (tmpElt != 0 && !strEquals(getDSIGLocalName(tmpElt), "DigestValue"))
        tmpElt = tmpElt->getNextSibling();

    if (tmpElt == NULL)
        // ERROR
        return 0;

    // Now read the DOMString of the hash

    tmpElt = tmpElt->getFirstChild();
    while (tmpElt != NULL && tmpElt->getNodeType() != DOMNode::TEXT_NODE)
        tmpElt = tmpElt->getNextSibling();

    if (tmpElt == NULL)
        // Something wrong with the underlying XML if no text was found
        throw XSECException(XSECException::NoHashFoundInDigestValue);

    b64HashVal << (*mp_formatter << tmpElt->getNodeValue());

    // Now have the value of the string - create a transform around it

    XSECnew(nextInput, TXFMSB(d));
    ((TXFMSB *) nextInput)->setInput(b64HashVal);

    // Create a transform chain (really as a janitor for the entire list)
    TXFMChain * chain;
    XSECnew(chain, TXFMChain(nextInput));
    Janitor<TXFMChain> j_chain(chain);

    // Now create the base64 transform

    XSECnew(nextInput, TXFMBase64(d));
    chain->appendTxfm(nextInput);

    // Now get the value

    size = chain->getLastTxfm()->readBytes(toFill, maxToFill);

    // Clear any documentat modifications

    chain->getLastTxfm()->deleteExpandedNameSpaces();

    return size;

}



// --------------------------------------------------------------------------------
//           Check a reference
// --------------------------------------------------------------------------------

bool DSIGReference::checkHash() const {

    // Determine the hash value of the element and check if matches that stored in the
    // DigestValue part of the element

    // First set up for input

    unsigned int maxHashSize = XSECPlatformUtils::g_cryptoProvider->getMaxHashSize();

    XMLByte* calculatedHashVal = new XMLByte[maxHashSize];        // The hash that we determined

    unsigned int calculatedHashSize;

    try {
        if ((calculatedHashSize = calculateHash(calculatedHashVal, maxHashSize)) == 0)
            return false;
    }
    catch (...) {
        delete[] calculatedHashVal;
        throw;
    }

    XMLByte* readHashVal = new XMLByte [maxHashSize];            // The hash in the element

    if (readHash(readHashVal, maxHashSize) != calculatedHashSize) {
        delete[] calculatedHashVal;
        delete[] readHashVal;
        return false;
    }

    for (unsigned int i = 0; i < calculatedHashSize; ++i) {
        if (calculatedHashVal[i] != readHashVal[i]) {
            delete[] calculatedHashVal;
            delete[] readHashVal;
            return false;
        }

    }

    delete[] calculatedHashVal;
    delete[] readHashVal;

    // Got through with flying colours!
    return true;
}
