xsec/canon/XSECC14n20010315.cpp (860 lines of code) (raw):

/** * 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> // If this isn't defined, we're on Xalan 1.12+ and require modern C++ #ifndef XALAN_USING_XALAN # define XALAN_USING_XALAN(NAME) using xalanc :: NAME; #endif // 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; }