blob: 53913f9695975862e7a38a15fb8308bc574b8bb5 [file] [log] [blame]
/**
* 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
*
* XSECC14n20010315 := Canonicaliser object to process XML document in line with
* RFC 3076
*
* Author(s): Berin Lautenbach
*
* $Id$
*
*/
//XSEC includes
#include <xsec/framework/XSECDefs.hpp>
#include <xsec/framework/XSECError.hpp>
#include <xsec/canon/XSECC14n20010315.hpp>
#include <xsec/utils/XSECSafeBufferFormatter.hpp>
#include "../utils/XSECDOMUtils.hpp"
// Xerces includes
#include <xercesc/dom/DOMElement.hpp>
#include <xercesc/dom/DOMNamedNodeMap.hpp>
#include <xercesc/util/Janitor.hpp>
#include <xercesc/util/XMLUniDefs.hpp>
XERCES_CPP_NAMESPACE_USE
#ifdef XSEC_HAVE_XALAN
// Xalan includes
#include <xalanc/XalanDOM/XalanDocument.hpp>
#include <xalanc/XercesParserLiaison/XercesDocumentWrapper.hpp>
#include <xalanc/XercesParserLiaison/XercesDOMSupport.hpp>
#include <xalanc/XercesParserLiaison/XercesParserLiaison.hpp>
#include <xalanc/XPath/XPathEvaluator.hpp>
#include <xalanc/XPath/NodeRefList.hpp>
// Namespace definitions
XALAN_USING_XALAN(XPathEvaluator)
XALAN_USING_XALAN(XercesDOMSupport)
XALAN_USING_XALAN(XercesParserLiaison)
XALAN_USING_XALAN(XalanDocument)
XALAN_USING_XALAN(XalanNode)
XALAN_USING_XALAN(XalanElement)
XALAN_USING_XALAN(XalanDOMString)
XALAN_USING_XALAN(XalanDOMChar)
XALAN_USING_XALAN(NodeRefList)
XALAN_USING_XALAN(XercesDocumentWrapper)
XALAN_USING_XALAN(XercesWrapperNavigator)
XALAN_USING_XALAN(c_wstr)
#endif
// General includes
#include <stdlib.h>
#include <string.h>
#include <iostream>
// --------------------------------------------------------------------------------
// Some useful utilities
// --------------------------------------------------------------------------------
// Find a node in an XSECNodeList
bool NodeInList(const XSECNodeListElt * lst, const DOMNode * toFind) {
const XSECNodeListElt * tmp = lst;
while (tmp != NULL) {
if (tmp->element == toFind)
return true;
tmp = tmp->next;
}
return false;
}
XSECNodeListElt * insertNodeIntoList(XSECNodeListElt * lst, XSECNodeListElt *toIns) {
XSECNodeListElt *tmp, *last;
if (lst == NULL) {
// Goes at start
toIns->next = NULL;
toIns->last = NULL;
return toIns;
} /* if mp_attributes == NULL */
// Need to run through start of list
tmp = lst;
last = NULL;
int res = -1; // Used to remove a gcc warning
while ((tmp != NULL) &&
((res = toIns->sortString.sbStrcmp(tmp->sortString)) >= 0)) {
last = tmp;
tmp = tmp->next;
} /* while */
if (res ==0) {
// Already exists!
delete toIns;
return lst;
}
if (last == NULL) {
// It sits before first element
toIns->next = lst;
toIns->last = NULL;
lst->last = tmp;
return toIns;
} /* if last == NULL */
// We have found where it goes
toIns->next = tmp;
toIns->last = last;
if (tmp != NULL)
tmp->last = toIns;
last->next = toIns;
return lst;
}
// --------------------------------------------------------------------------------
// Exclusive Canonicalisation Methods
// --------------------------------------------------------------------------------
bool visiblyUtilises(DOMNode *node, safeBuffer &ns) {
// Test whether the node uses the name space passed in
if (strEquals(node->getPrefix(), (char *) ns.rawBuffer()))
return true;
if (ns.sbStrcmp("") == 0)
return false; // Attributes are never in default namespace
// Check the attributes
DOMNamedNodeMap *atts = node->getAttributes();
if (atts == NULL)
return false;
XMLSize_t size = atts->getLength();
for (XMLSize_t i = 0; i < size; ++i) {
if (strEquals(atts->item(i)->getPrefix(), (char *) ns.rawBuffer()) &&
!strEquals(atts->item(i)->getLocalName(), "xmlns"))
return true;
}
return false;
}
bool XSECC14n20010315::inNonExclNSList(safeBuffer &ns) {
int size = (int) m_exclNSList.size();
for (int i = 0; i < size; ++i) {
if (!strcmp((char *) ns.rawBuffer(), m_exclNSList[i]))
return true;
}
return false;
}
void XSECC14n20010315::setInclusive11(void) {
m_incl11 = true;
m_exclusive = false;
m_exclusiveDefault = false;
}
void XSECC14n20010315::setExclusive(void) {
m_exclusive = true;
m_exclusiveDefault = true;
m_incl11 = false;
}
void XSECC14n20010315::setExclusive(char * xmlnsList) {
char * nsBuf;
setExclusive();
// Set up the define non-exclusive prefixes
nsBuf = new char [strlen(xmlnsList) + 1];
if (nsBuf == NULL) {
throw XSECException (XSECException::MemoryAllocationFail,
"Error allocating a string buffer in XSECC14n20010315::setExclusive");
}
ArrayJanitor<char> j_nsBuf(nsBuf);
int i, j;
i = 0;
while (xmlnsList[i] != '\0') {
while (xmlnsList[i] == ' ' ||
xmlnsList[i] == '\t' ||
xmlnsList[i] == '\r' ||
xmlnsList[i] == '\n') {
++i; // Skip white space
}
j = 0;
while (!(xmlnsList[i] == ' ' ||
xmlnsList[i] == '\0' ||
xmlnsList[i] == '\t' ||
xmlnsList[i] == '\r' ||
xmlnsList[i] == '\n')) {
nsBuf[j++] = xmlnsList[i++]; // Copy name
}
// Terminate the string
nsBuf[j] = '\0';
if (strcmp(nsBuf, "#default") == 0) {
// Default is not to be exclusive
m_exclusiveDefault = false;
}
else {
// Add this to the list
m_exclNSList.push_back(strdup(nsBuf));
}
}
}
// --------------------------------------------------------------------------------
// XSECC14n20010315 methods
// --------------------------------------------------------------------------------
void XSECC14n20010315::stackInit(DOMNode * n) {
if (n == NULL)
return;
stackInit(n->getParentNode());
m_nsStack.pushElement(n);
XMLSize_t size;
DOMNamedNodeMap *tmpAtts = n->getAttributes();
safeBuffer currentName;
if (tmpAtts != NULL)
size = tmpAtts->getLength();
else
size = 0;
XMLSize_t i;
for (i = 0; i < size; ++i) {
currentName << (*mp_formatter << tmpAtts->item(i)->getNodeName());
if (currentName.sbStrncmp("xmlns", 5) == 0)
m_nsStack.addNamespace(tmpAtts->item(i));
}
}
// Constructors
void XSECC14n20010315::init() {
// This does the work of setting us up and checks to make sure everyhing is OK
// Set up the Xerces formatter
XSECnew(mp_formatter, XSECSafeBufferFormatter("UTF-8",XMLFormatter::NoEscapes,
XMLFormatter::UnRep_CharRef));
// Set up for first attribute list
mp_attributes = mp_currentAttribute = mp_firstNonNsAttribute = NULL;
// By default process comments
m_processComments = true;
// Set up for tree walking
m_returnedFromChild = false;
mp_firstElementNode = mp_startNode;
m_firstElementProcessed = false;
// XPath setup
m_XPathSelection = false;
m_XPathMap.clear();
// Exclusive Canonicalisation setup
m_exclNSList.clear();
m_exclusive = false;
m_exclusiveDefault = false;
// Default to 1.0 mode
m_incl11 = false;
// Namespace handling
m_useNamespaceStack = true;
// INitialise the stack - even if we don't use it later, at least this sets us up
if (mp_startNode != NULL) {
stackInit(mp_startNode->getParentNode());
}
}
XSECC14n20010315::XSECC14n20010315() {};
XSECC14n20010315::XSECC14n20010315(DOMDocument *newDoc) : XSECCanon(newDoc) {
// Just call the init function;
init();
};
XSECC14n20010315::XSECC14n20010315(DOMDocument *newDoc,
DOMNode *newStartNode) : XSECCanon(newDoc, newStartNode) {
// Just call the init function
init();
}
XSECC14n20010315::~XSECC14n20010315() {
if (mp_formatter != NULL)
delete mp_formatter;
// Clear out the exclusive namespace list
int size = (int) m_exclNSList.size();
for (int i = 0; i < size; ++i) {
free(m_exclNSList[i]);
}
m_exclNSList.clear();
while (mp_attributes != NULL) {
mp_currentAttribute = mp_attributes->next;
delete mp_attributes;
mp_attributes = mp_currentAttribute;
}
mp_attributes = mp_currentAttribute = mp_firstNonNsAttribute = NULL;
}
// --------------------------------------------------------------------------------
// XSECC14n20010315 Comments procesing
// --------------------------------------------------------------------------------
void XSECC14n20010315::setCommentsProcessing(bool onoff) {
m_processComments = onoff;
}
bool XSECC14n20010315::getCommentsProcessing(void) {
return m_processComments;
}
// --------------------------------------------------------------------------------
// XSECC14n20010315 XPathSelectNodes method
// --------------------------------------------------------------------------------
// Use an XPath expression to select a subset of nodes from the document
int XSECC14n20010315::XPathSelectNodes(const char * XPathExpr) {
#ifndef XSEC_HAVE_XPATH
throw XSECException(XSECException::UnsupportedFunction,
"This library has been compiled without XPath support");
#else
XPathEvaluator::initialize();
// We use Xalan to process the Xerces DOM tree and get the XPath nodes
XercesParserLiaison theParserLiaison;
XercesDOMSupport theDOMSupport(theParserLiaison);
if (mp_doc == 0) {
throw XSECException(XSECException::UnsupportedFunction,
"XPath selection only supported in C14n for full documents");
}
DOMElement* theXercesNode = mp_doc->createElement(c_wstr(XalanDOMString("ns")));
theXercesNode->setAttribute(c_wstr(XalanDOMString("xmlns:ietf")), c_wstr(XalanDOMString("http://www.ietf.org")));
XalanDocument* theDoc = theParserLiaison.createDocument(mp_doc);
// Set up the XPath evaluator
XPathEvaluator theEvaluator;
// OK, let's find the context node...
XalanDOMString cd = XalanDOMString("/"); // For the moment assume the root is the context
const XalanDOMChar * cexpr = cd.c_str();
XalanNode* const theContextNode =
theEvaluator.selectSingleNode(
theDOMSupport,
theDoc,
cexpr,
theDoc->getDocumentElement());
if (theContextNode == 0)
{
// No appropriate nodes.
return 0;
}
// OK, let's evaluate the expression...
XalanDOMString ed = XalanDOMString(XPathExpr);
const XalanDOMChar * expr = ed.c_str();
NodeRefList output;
NodeRefList theResult(
theEvaluator.selectNodeList(
output,
theDOMSupport,
theContextNode,
expr,
theDoc->getElementById(XalanDOMString("ns"))));
XercesDocumentWrapper *theWrapper = theParserLiaison.mapDocumentToWrapper(theDoc);
XercesWrapperNavigator theWrapperNavigator(theWrapper);
int size = (int) theResult.getLength();
const DOMNode *item;
for (int i = 0; i < size; ++ i) {
item = theWrapperNavigator.mapNode(theResult.item(i));
m_XPathMap.addNode(item);
//tmp->element = theBridgeNavigator.mapNode(theResult.item(i));
}
m_XPathSelection = true;
return size;
#endif
}
void XSECC14n20010315::setXPathMap(const XSECXPathNodeList & map) {
// XPath already done!
m_XPathMap = map;
m_XPathSelection = true;
}
// --------------------------------------------------------------------------------
// XSECC14n20010315 processNextNode method
// --------------------------------------------------------------------------------
safeBuffer c14nCleanText(safeBuffer &input) {
/* c14n Requires :
& -> &amp
< -> &lt
> -> &gt
LF -> &#xD
*/
XMLSize_t len = input.sbStrlen();
safeBuffer ret;
XMLSize_t i, j;
unsigned char c;
j = 0;
for (i = 0; i < len; ++i) {
c = input[i];
switch (c) {
case '&' :
ret[j++] = '&';
ret[j++] = 'a';
ret[j++] = 'm';
ret[j++] = 'p';
ret[j++] = ';';
break;
case '<' :
ret[j++] = '&';
ret[j++] = 'l';
ret[j++] = 't';
ret[j++] = ';';
break;
case '>' :
ret[j++] = '&';
ret[j++] = 'g';
ret[j++] = 't';
ret[j++] = ';';
break;
case 0xD :
ret[j++] = '&';
ret[j++] = '#';
ret[j++] = 'x';
ret[j++] = 'D';
ret[j++] = ';';
break;
default :
ret[j++] = c;
}
}
// final character:
ret[j] = '\0';
ret.setBufferType(safeBuffer::BUFFER_CHAR);
return ret;
}
safeBuffer c14nCleanAttribute(safeBuffer &input) {
/* c14n Requires :
& -> &amp
< -> &lt
" -> &quot
#x9 ->&#x9
#xA ->&#xA
LF -> &#xD
*/
XMLSize_t len = input.sbStrlen();
safeBuffer ret;
XMLSize_t i, j;
unsigned char c;
j = 0;
for (i = 0; i < len; ++i) {
c = input[i];
switch (c) {
case '&' :
ret[j++] = '&';
ret[j++] = 'a';
ret[j++] = 'm';
ret[j++] = 'p';
ret[j++] = ';';
break;
case '"' :
ret[j++] = '&';
ret[j++] = 'q';
ret[j++] = 'u';
ret[j++] = 'o';
ret[j++] = 't';
ret[j++] = ';';
break;
case '<' :
ret[j++] = '&';
ret[j++] = 'l';
ret[j++] = 't';
ret[j++] = ';';
break;
case 0x9 :
ret[j++] = '&';
ret[j++] = '#';
ret[j++] = 'x';
ret[j++] = '9';
ret[j++] = ';';
break;
case 0xA :
ret[j++] = '&';
ret[j++] = '#';
ret[j++] = 'x';
ret[j++] = 'A';
ret[j++] = ';';
break;
case 0xD :
ret[j++] = '&';
ret[j++] = '#';
ret[j++] = 'x';
ret[j++] = 'D';
ret[j++] = ';';
break;
default :
ret[j++] = c;
}
}
// final character:
ret[j] = '\0';
ret.setBufferType(safeBuffer::BUFFER_CHAR);
return ret;
}
bool XSECC14n20010315::checkRenderNameSpaceNode(DOMNode *e, DOMNode *a) {
DOMNode *parent;
DOMNode *att;
DOMNamedNodeMap *atts;
// If XPath and node not selected, then never print
if (m_XPathSelection && ! m_XPathMap.hasNode(a))
return false;
// BUGFIX: we need to skip xmlns:xml if the value is http://www.w3.org/XML/1998/namespace
if (strEquals(a->getLocalName(), "xml") && strEquals(a->getNodeValue(), "http://www.w3.org/XML/1998/namespace"))
return false;
// First - are we exclusive?
safeBuffer localName;
bool processAsExclusive = false;
if (m_exclusive) {
if (strEquals(a->getNodeName(), "xmlns")) {
processAsExclusive = m_exclusiveDefault;
}
else {
localName << (*mp_formatter << a->getLocalName());
processAsExclusive = !inNonExclNSList(localName);
}
}
if (processAsExclusive) {
// Is the parent in the node-set?
if (m_XPathSelection && !m_XPathMap.hasNode(e))
return false;
// Is the name space visibly utilised?
localName << (*mp_formatter << a->getLocalName());
if (localName.sbStrcmp("xmlns") == 0)
localName[0] = '\0'; // Is this correct or should Xerces return "" for default?
if (!visiblyUtilises(e, localName))
return false;
// If we are the top node, then this has never been printer
if (e == mp_firstElementNode)
return true;
// Make sure previous nodes do not use the name space (and have it printed)
parent = e->getParentNode();
while (parent != NULL) {
if (!m_XPathSelection || m_XPathMap.hasNode(parent)) {
// An output ancestor
if (visiblyUtilises(parent, localName)) {
// Have a hit!
while (parent != NULL) {
atts = parent->getAttributes();
att = (atts != NULL) ? atts->getNamedItem(a->getNodeName()) : NULL;
if (att != NULL && (!m_XPathSelection || m_XPathMap.hasNode(att))) {
// Check URI is the same
if (strEquals(att->getNodeValue(), a->getNodeValue()))
return false;
return true;
}
// If we are using the namespace stack, we need to go up until
// we found the defining attribute for this node
if (m_useNamespaceStack) {
parent = parent->getParentNode();
}
else
return true;
}
// Even if we are using the namespace stack, we have never
// printed this namespace
return true;
}
}
if (parent == mp_firstElementNode)
parent = NULL;
else
parent = parent->getParentNode();
}
// Didn't find it rendered!
return true;
}
// Either we are now in non-exclusive mode, or the name space in question
// Is to be treated as non-exclusive
// Never directly render a default
if (strEquals(a->getNodeName(), "xmlns") && strEquals(a->getNodeValue(), ""))
return false;
// If using a namespace stack, then we need to check whether the current node is in the nodeset
// Only really necessary for envelope txfms in boundary conditions
if (m_useNamespaceStack && m_XPathSelection && !m_XPathMap.hasNode(e))
return false;
// Otherwise, of node is at base of selected document, then print
if (e == mp_firstElementNode)
return true;
if (m_useNamespaceStack) {
// In this case, we need to go up until we find the namespace definition
// in question
parent = e->getParentNode();
while (parent != NULL) {
if (!m_XPathSelection || m_XPathMap.hasNode(parent)) {
DOMNamedNodeMap *pmap = parent->getAttributes();
DOMNode *pns;
if (pmap)
pns = pmap->getNamedItem(a->getNodeName());
else
pns = NULL;
if (pns != NULL) {
// Note we don't check XPath inclusion, as we shouldn't be
// using the namespace stack for XPath expressions
if (strEquals(pns->getNodeValue(), a->getNodeValue()))
return false;
else
return true; // Was defined but differently
}
}
parent = parent->getParentNode();
}
// Obviously we haven't found it!
return true;
}
// Find the parent and check if the node is already defined or if the node
// was out of scope
parent = e->getParentNode();
// if (m_XPathSelection && !m_XPathMap.hasNode(parent))
// return true;
while (m_XPathSelection && parent != NULL && !m_XPathMap.hasNode(parent))
parent = parent->getParentNode();
if (parent == NULL)
return true;
DOMNamedNodeMap *pmap = parent->getAttributes();
DOMNode *pns = pmap->getNamedItem(a->getNodeName());
if (pns != NULL) {
if (m_XPathSelection && !m_XPathMap.hasNode(pns))
return true; // Not printed in previous node
if (strEquals(pns->getNodeValue(), a->getNodeValue()))
return false;
else
return true; // Was defined but differently
}
return true; // Not defined in previous node
}
// Check an attribute to see if we should output
// This is the main worker function of this class
XMLSize_t XSECC14n20010315::processNextNode() {
// The information currently in the buffer has all been used. We now process the
// next node.
DOMNode *next; // For working (had *ns)
DOMNamedNodeMap *tmpAtts; // " "
safeBuffer currentName(128), currentValue(1024), sbWork;
bool done, xmlnsFound;
if (m_allNodesDone) {
return 0; // No bytes copied because nothing more to be done
}
// Always zeroise buffers to make work simpler
m_bufferLength = m_bufferPoint = 0;
m_buffer.sbStrcpyIn("");
// Find out if this is a node to process
bool processNode;
int nodeT;
if (mp_nextNode == 0) {
// Dummy element - we need to insert a default namespace
nodeT = DOMNode::ATTRIBUTE_NODE;
processNode = true;
}
else {
processNode = ((!m_XPathSelection) || (m_XPathMap.hasNode(mp_nextNode)));
nodeT = mp_nextNode->getNodeType();
}
switch (nodeT) {
case DOMNode::DOCUMENT_NODE : // Start of a document
// Check if finished
if (m_returnedFromChild) {
// All done!
m_allNodesDone = true;
return 0;
}
// In c14n we don't actually do anything for a document node except
// process the childeren
mp_firstElementNode = ((DOMDocument *) mp_nextNode)->getDocumentElement();
next = mp_nextNode->getFirstChild();
if (next == NULL) {
// Empty document?
m_allNodesDone = true;
}
mp_nextNode = next;
m_bufferLength = m_bufferPoint = 0; // To ensure nobody copies "nothing"
return 0;
case DOMNode::DOCUMENT_TYPE_NODE : // Ignore me
m_returnedFromChild = true;
m_buffer.sbStrcpyIn("");
break;
case DOMNode::PROCESSING_INSTRUCTION_NODE : // Just print
if (processNode) {
if ((mp_nextNode->getParentNode() == mp_doc) && m_firstElementProcessed) {
// this is a top level node and first element done
m_buffer.sbStrcpyIn("\x00A<?");
}
else
m_buffer.sbStrcpyIn("<?");
m_formatBuffer << (*mp_formatter << mp_nextNode->getNodeName());
m_buffer.sbStrcatIn(m_formatBuffer);
m_formatBuffer << (*mp_formatter << ((DOMProcessingInstruction *) mp_nextNode)->getData());
if (m_formatBuffer.sbStrlen() > 0) {
m_buffer.sbStrcatIn(" ");
m_buffer.sbStrcatIn(m_formatBuffer);
}
m_buffer.sbStrcatIn("?>");
if ((mp_nextNode->getParentNode() == mp_doc) && !m_firstElementProcessed) {
// this is a top level node and first element done
m_buffer.sbStrcatIn("\x00A");
}
}
// Node fully processed
m_returnedFromChild = true;
break;
case DOMNode::COMMENT_NODE : // Just print out
if (processNode && m_processComments) {
if ((mp_nextNode->getParentNode() == mp_doc) && m_firstElementProcessed) {
// this is a top level node and first element done
m_buffer.sbStrcpyIn("\x00A<!--");
}
else
m_buffer.sbStrcpyIn("<!--");
m_formatBuffer << (*mp_formatter << mp_nextNode->getNodeValue());
if (m_formatBuffer.sbStrlen() > 0) {
m_buffer.sbStrcatIn(m_formatBuffer);
}
m_buffer.sbStrcatIn("-->");
if ((mp_nextNode->getParentNode() == mp_doc) && !m_firstElementProcessed) {
// this is a top level node and first element done
m_buffer.sbStrcatIn("\x00A");
}
}
m_returnedFromChild = true; // Fool the tree processor
break;
case DOMNode::CDATA_SECTION_NODE :
case DOMNode::TEXT_NODE : // Straight copy for now
if (processNode) {
m_formatBuffer << (*mp_formatter << mp_nextNode->getNodeValue());
// Do c14n cleaning on the text string
m_buffer = c14nCleanText(m_formatBuffer);
}
// Fall through
m_returnedFromChild = true; // Fool the tree processor
break;
case DOMNode::ELEMENT_NODE : // This is an element that we can easily process
// If we are going "up" then we simply close off the element
if (m_returnedFromChild) {
if (processNode) {
m_buffer.sbStrcpyIn ("</");
m_formatBuffer << (*mp_formatter << mp_nextNode->getNodeName());
m_buffer.sbStrcatIn(m_formatBuffer);
m_buffer.sbStrcatIn(">");
}
if (m_useNamespaceStack)
m_nsStack.popElement();
break;
}
if (processNode) {
m_buffer.sbStrcpyIn("<");
m_formatBuffer << (*mp_formatter << mp_nextNode->getNodeName());
m_buffer.sbStrcatIn(m_formatBuffer);
}
// We now set up for attributes and name spaces
if (m_useNamespaceStack)
m_nsStack.pushElement(mp_nextNode);
mp_attributes = NULL;
tmpAtts = mp_nextNode->getAttributes();
next = mp_nextNode;
done = false;
xmlnsFound = false;
while (!done) {
// Need to sort the attributes
XMLSize_t size;
if (tmpAtts != NULL)
size = tmpAtts->getLength();
else
size = 0;
XSECNodeListElt *toIns;
XMLSize_t i;
for (i = 0; i < size; ++i) {
// Get the name and value of the attribute
currentName << (*mp_formatter << tmpAtts->item(i)->getNodeName());
currentValue << (*mp_formatter << tmpAtts->item(i)->getNodeValue());
// Build the string used to sort this node
if ((next == mp_nextNode) && currentName.sbStrncmp("xmlns", 5) == 0) {
// Are we using the namespace stack? If so - store this for later
// processing
if (m_useNamespaceStack) {
m_nsStack.addNamespace(tmpAtts->item(i));
}
else {
// Is this the default?
if (currentName.sbStrcmp("xmlns") == 0 &&
(!m_XPathSelection || m_XPathMap.hasNode(tmpAtts->item(i))) &&
!currentValue.sbStrcmp("") == 0)
xmlnsFound = true;
// A namespace node - See if we need to output
if (checkRenderNameSpaceNode(mp_nextNode, tmpAtts->item(i))) {
// Add to the list
m_formatBuffer << (*mp_formatter << tmpAtts->item(i)->getNodeName());
if (m_formatBuffer[5] == ':')
currentName.sbStrcpyIn((char *) &m_formatBuffer[6]);
else
currentName.sbStrcpyIn("");
toIns = new XSECNodeListElt;
toIns->element = tmpAtts->item(i);
// Build and insert name space node
toIns->sortString.sbStrcpyIn(XMLNS_PREFIX);
toIns->sortString.sbStrcatIn(currentName);
// Insert node
mp_attributes = insertNodeIntoList(mp_attributes, toIns);
}
}
}
else {
// A "normal" attribute - only process if selected or no XPath or is an
// XML node from a previously un-printed Element node
bool XMLElement = (next != mp_nextNode) && (!m_exclusive) && !currentName.sbStrncmp("xml:", 4) &&
(!m_incl11 || currentName.sbStrcmp("xml:id"));
// If we have an XML element, make sure it was not printed between this
// node and the node currently being worked on
if (XMLElement) {
DOMNode *t = mp_nextNode->getParentNode();
if (m_XPathSelection && m_XPathMap.hasNode(t))
XMLElement = false;
else {
// This is a real node that we have to check
t = mp_nextNode;
while (t != next) {
DOMNamedNodeMap *ta;
XMLSize_t sz;
ta = t->getAttributes();
if (ta != NULL)
sz = ta->getLength();
else
sz = 0;
for (XMLSize_t j = 0; j < sz; ++j) {
if (strEquals(ta->item(j)->getNodeName(),
tmpAtts->item(i)->getNodeName()) == true) {
XMLElement = false;
break;
}
}
t = t->getParentNode();
}
}
}
if ((!m_XPathSelection && next == mp_nextNode) || XMLElement || ((next == mp_nextNode) && m_XPathMap.hasNode(tmpAtts->item(i)))) {
toIns = new XSECNodeListElt;
toIns->element = tmpAtts->item(i);
// First the correct prefix to ensure will be sorted
// in correct placing against XMLNS nodes
toIns->sortString.sbStrcpyIn(ATTRIBUTE_PREFIX);
// Find the namespace URI
const XMLCh * nsURI =
tmpAtts->item(i)->getNamespaceURI();
if (nsURI == NULL) {
toIns->sortString.sbStrcatIn(NOURI_PREFIX);
}
else {
m_formatBuffer << (*mp_formatter << nsURI);
toIns->sortString.sbStrcatIn(HAVEURI_PREFIX);
toIns->sortString.sbStrcatIn(m_formatBuffer);
}
// Append the local name as the secondary key
const XMLCh * ln = tmpAtts->item(i)->getNodeName();
int index = XMLString::indexOf(ln, chColon);
if (index >= 0)
ln = &ln[index+1];
m_formatBuffer << (*mp_formatter << ln);
toIns->sortString.sbStrcatIn(m_formatBuffer);
// Insert node
mp_attributes = insertNodeIntoList(mp_attributes, toIns);
} /* else (sbStrCmp xmlns) */
}
} /* for */
#if 1
// Now go upwards and find parent for xml name spaces
if (processNode && (m_XPathSelection || mp_nextNode == mp_firstElementNode)) {
next = next->getParentNode();
if (next == 0) // || NodeInList(mp_XPathMap, next))
done = true;
else
tmpAtts = next->getAttributes();
}
else
#endif
done = true;
} /* while tmpAtts != NULL */
// Now add namespace nodes - but only if we are using the namespace stack
// (They have already been added otherwise
if (m_useNamespaceStack) {
DOMNode * nsnode = m_nsStack.getFirstNamespace();
while (nsnode != NULL) {
// Get the name and value of the attribute
currentName << (*mp_formatter << nsnode->getNodeName());
currentValue << (*mp_formatter << nsnode->getNodeValue());
// Is this the default?
if (currentName.sbStrcmp("xmlns") == 0 &&
(!m_XPathSelection || m_XPathMap.hasNode(nsnode)) &&
!currentValue.sbStrcmp("") == 0)
xmlnsFound = true;
// A namespace node - See if we need to output
if (checkRenderNameSpaceNode(mp_nextNode, nsnode)) {
// Add to the list
XSECNodeListElt *toIns;
m_formatBuffer << (*mp_formatter << nsnode->getNodeName());
if (m_formatBuffer[5] == ':')
currentName.sbStrcpyIn((char *) &m_formatBuffer[6]);
else
currentName.sbStrcpyIn("");
toIns = new XSECNodeListElt;
toIns->element = nsnode;
// Build and insert name space node
toIns->sortString.sbStrcpyIn(XMLNS_PREFIX);
toIns->sortString.sbStrcatIn(currentName);
// Insert node
mp_attributes = insertNodeIntoList(mp_attributes, toIns);
// Mark as printed in the NS Stack
m_nsStack.printNamespace(nsnode, mp_nextNode);
}
nsnode = m_nsStack.getNextNamespace();
}
// Fix for bug#47353, make sure we set xmlnsFound regardless of what the printing process saw.
if (!xmlnsFound)
xmlnsFound = m_nsStack.isNonEmptyDefaultNS();
} /* if (m_useNamespaceStack) */
// Check to see if we add xmlns=""
if (processNode && !xmlnsFound && mp_nextNode != mp_firstElementNode) {
// Is this exclusive?
safeBuffer sbLocalName("");
if (m_exclusiveDefault) {
if (visiblyUtilises(mp_nextNode, sbLocalName)) {
// May have to output!
next = mp_nextNode->getParentNode();
while (next != NULL) {
if (!m_XPathSelection || m_useNamespaceStack || m_XPathMap.hasNode(next)) {
DOMNode *tmpAtt;
// An output ancestor
if (visiblyUtilises(next, sbLocalName)) {
DOMNode * nextAttParent = next;
while (nextAttParent != NULL) {
// Have a hit!
tmpAtts = nextAttParent->getAttributes();
if (tmpAtts != NULL)
tmpAtt = tmpAtts->getNamedItem(DSIGConstants::s_unicodeStrXmlns);
if (tmpAtts != NULL && tmpAtt != NULL && (!m_XPathSelection || m_useNamespaceStack || m_XPathMap.hasNode(tmpAtt))) {
// Check URI is the same
if (!strEquals(tmpAtt->getNodeValue(), "")) {
xmlnsFound = true;
nextAttParent = NULL;
}
}
else {
// Doesn't have a default namespace in the node-set
next = nextAttParent = NULL;
break;
}
if (m_useNamespaceStack && nextAttParent)
nextAttParent = nextAttParent->getParentNode();
else
nextAttParent = NULL;
}
}
}
if (next)
next = next->getParentNode();
}
}
} /* m_exclusiveDefault */
else {
//DOM_Node next;
next = mp_nextNode->getParentNode();
while (!xmlnsFound && next != NULL) {
while (next != NULL && !m_useNamespaceStack && (m_XPathSelection && !m_XPathMap.hasNode(next)))
next = next->getParentNode();
XMLSize_t size;
if (next != NULL)
tmpAtts = next->getAttributes();
if (next != NULL && tmpAtts != NULL)
size = tmpAtts->getLength();
else
size = 0;
for (XMLSize_t i = 0; i < size; ++i) {
currentName << (*mp_formatter << tmpAtts->item(i)->getNodeName());
currentValue << (*mp_formatter << tmpAtts->item(i)->getNodeValue());
if ((currentName.sbStrcmp("xmlns") == 0) &&
(m_useNamespaceStack || !m_XPathSelection || m_XPathMap.hasNode(tmpAtts->item(i)))) {
if (currentValue.sbStrcmp("") != 0) {
xmlnsFound = true;
}
else {
xmlnsFound = false;
next = NULL;
}
}
}
if (m_useNamespaceStack && next != NULL)
next = next->getParentNode();
else
next = NULL;
}
}
// Did we find a non empty namespace?
if (xmlnsFound) {
currentName.sbStrcpyIn(""); // Don't include xmlns prefix
XSECNodeListElt * toIns;
toIns = new XSECNodeListElt;
toIns->element = NULL; // To trigger the state engine
// Build and insert name space node
toIns->sortString.sbStrcpyIn(XMLNS_PREFIX);
toIns->sortString.sbStrcatIn(currentName);
// Insert node
mp_attributes = insertNodeIntoList(mp_attributes, toIns);
}
}
if (mp_attributes != NULL) {
// Now we have set up the attribute list, set next node and return!
mp_attributeParent = mp_nextNode;
mp_nextNode = mp_attributes->element;
mp_currentAttribute = mp_attributes;
m_bufferLength = m_buffer.sbStrlen();
m_bufferPoint = 0;
return m_bufferLength;
} /* attrributes != NULL */
if (processNode)
m_buffer.sbStrcatIn(">");
// Fall through to find next node
break;
case DOMNode::ATTRIBUTE_NODE : // Output attr_name="value"
// Always process an attribute node as we have already checked they should
// be printed
m_buffer.sbStrcpyIn(" ");
if (mp_nextNode != 0) {
m_formatBuffer << (*mp_formatter << mp_nextNode->getNodeName());
m_buffer.sbStrcatIn(m_formatBuffer);
m_buffer.sbStrcatIn("=\"");
m_formatBuffer << (*mp_formatter << mp_nextNode->getNodeValue());
sbWork = c14nCleanAttribute(m_formatBuffer);
m_buffer.sbStrcatIn(sbWork);
m_buffer.sbStrcatIn("\"");
}
else {
m_buffer.sbStrcatIn("xmlns");
m_buffer.sbStrcatIn("=\"");
m_buffer.sbStrcatIn("\"");
}
// Now see if next node is an attribute
mp_currentAttribute = mp_currentAttribute->next;
if (mp_currentAttribute != NULL) {
// Easy case
mp_nextNode = mp_currentAttribute->element;
m_bufferLength = m_buffer.sbStrlen();
m_bufferPoint = 0;
return m_bufferLength;
} /* if mp_currentAttributes != NULL) */
// need to clear out the node list
while (mp_attributes != NULL) {
mp_currentAttribute = mp_attributes->next;
delete mp_attributes;
mp_attributes = mp_currentAttribute;
}
mp_attributes = mp_currentAttribute = mp_firstNonNsAttribute = NULL;
// return us to the element node
mp_nextNode = mp_attributeParent;
// End the element definition
if (!m_XPathSelection || (m_XPathMap.hasNode(mp_nextNode)))
m_buffer.sbStrcatIn(">");
m_returnedFromChild = false;
break;
default:
break;
}
// A node has fallen through to the default case for finding the next node.
m_bufferLength = m_buffer.sbStrlen();;
m_bufferPoint = 0;
// Firstly, was the last piece of processing because we "came up" from a child node?
if (m_returnedFromChild) {
if (mp_nextNode == mp_startNode) {
// we have closed off the document!
m_allNodesDone = true;
return m_bufferLength;
}
if (mp_nextNode == mp_firstElementNode) {
// we have closed off the main mp_doc elt
m_firstElementProcessed = true;
}
}
else {
// Going down - so check for children nodes
next = mp_nextNode->getFirstChild();
if (next != NULL)
mp_nextNode = next;
else
// No children, so need to close this node off!
m_returnedFromChild = true;
return m_bufferLength;
}
// If we get here all childeren (if there are any) are done
next = mp_nextNode->getNextSibling();
if (next != NULL) {
m_returnedFromChild = false;
mp_nextNode = next;
return m_bufferLength;
}
// No more nodes at this level either!
mp_nextNode = mp_nextNode->getParentNode();
m_returnedFromChild = true;
return m_bufferLength;
}