| /** |
| * 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 : |
| |
| & -> & |
| < -> < |
| > -> > |
| LF -> 
 |
| |
| */ |
| |
| 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 : |
| |
| & -> & |
| < -> < |
| " -> " |
| #x9 ->	 |
| #xA ->
 |
| LF -> 
 |
| |
| */ |
| |
| 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; |
| |
| } |