| /** |
| * 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 |
| * |
| * TXFMXPathFilter := Class that performs XPath transforms |
| * |
| * $Id$ |
| * |
| */ |
| |
| |
| #include <xsec/dsig/DSIGConstants.hpp> |
| #include <xsec/dsig/DSIGXPathFilterExpr.hpp> |
| #include <xsec/dsig/DSIGXPathHere.hpp> |
| #include <xsec/framework/XSECError.hpp> |
| #include <xsec/transformers/TXFMXPathFilter.hpp> |
| #include <xsec/transformers/TXFMParser.hpp> |
| |
| XERCES_CPP_NAMESPACE_USE |
| |
| #ifdef XSEC_HAVE_XALAN |
| |
| #include "../utils/XSECDOMUtils.hpp" |
| |
| #include <xercesc/util/Janitor.hpp> |
| |
| #if defined(_MSC_VER) |
| # pragma warning(disable: 4267) |
| #endif |
| |
| #include <xalanc/XalanDOM/XalanDocument.hpp> |
| #include <xalanc/XalanDOM/XalanDOMString.hpp> |
| #include <xalanc/XercesParserLiaison/XercesDocumentWrapper.hpp> |
| #include <xalanc/XercesParserLiaison/XercesDOMSupport.hpp> |
| #include <xalanc/XercesParserLiaison/XercesParserLiaison.hpp> |
| #include <xalanc/XPath/XPathEvaluator.hpp> |
| #include <xalanc/XPath/XPathProcessorImpl.hpp> |
| #include <xalanc/XPath/XPathFactoryDefault.hpp> |
| #include <xalanc/XPath/NodeRefList.hpp> |
| #include <xalanc/XPath/XPathEnvSupportDefault.hpp> |
| #include <xalanc/XPath/XPathConstructionContextDefault.hpp> |
| #include <xalanc/XPath/ElementPrefixResolverProxy.hpp> |
| #include <xalanc/XPath/XObjectFactoryDefault.hpp> |
| #include <xalanc/XPath/XPathExecutionContextDefault.hpp> |
| #include <xalanc/XSLT/XSLTResultTarget.hpp> |
| |
| #if defined(_MSC_VER) |
| # pragma warning(default: 4267) |
| #endif |
| |
| // Xalan namespace usage |
| XALAN_USING_XALAN(XPathProcessorImpl) |
| XALAN_USING_XALAN(XalanDOMString) |
| XALAN_USING_XALAN(XercesDOMSupport) |
| XALAN_USING_XALAN(XercesParserLiaison) |
| XALAN_USING_XALAN(XercesDocumentWrapper) |
| XALAN_USING_XALAN(XercesWrapperNavigator) |
| XALAN_USING_XALAN(XPathEvaluator) |
| XALAN_USING_XALAN(XPathFactoryDefault) |
| XALAN_USING_XALAN(XPathConstructionContextDefault) |
| XALAN_USING_XALAN(XalanDocument) |
| XALAN_USING_XALAN(XalanNode) |
| XALAN_USING_XALAN(XalanDOMChar) |
| XALAN_USING_XALAN(XPathEnvSupportDefault) |
| XALAN_USING_XALAN(XObjectFactoryDefault) |
| XALAN_USING_XALAN(XObjectPtr) |
| XALAN_USING_XALAN(XPathExecutionContextDefault) |
| XALAN_USING_XALAN(ElementPrefixResolverProxy) |
| XALAN_USING_XALAN(XPath) |
| XALAN_USING_XALAN(NodeRefListBase) |
| XALAN_USING_XALAN(XSLTResultTarget) |
| XALAN_USING_XALAN(XSLException) |
| |
| #endif |
| |
| #ifdef XSEC_HAVE_XPATH |
| |
| #include <iostream> |
| |
| #define KLUDGE_PREFIX "berindsig" |
| |
| // Helper functions - come from DSIGXPath |
| |
| void setXPathNS(DOMDocument* d, |
| DOMNamedNodeMap* xAtts, |
| XSECXPathNodeList& addedNodes, |
| XSECSafeBufferFormatter* formatter, |
| XSECNameSpaceExpander* nse); |
| |
| void clearXPathNS(DOMDocument*d, |
| XSECXPathNodeList& toRemove, |
| XSECSafeBufferFormatter* formatter, |
| XSECNameSpaceExpander* nse); |
| |
| bool separator(unsigned char c); |
| XalanNode* findHereNodeFromXalan(XercesWrapperNavigator* xwn, XalanNode* n, DOMNode* h); |
| |
| |
| TXFMXPathFilter::TXFMXPathFilter(DOMDocument* doc) : |
| TXFMBase(doc) { |
| |
| document = NULL; |
| XSECnew(mp_formatter, XSECSafeBufferFormatter("UTF-8",XMLFormatter::NoEscapes, |
| XMLFormatter::UnRep_CharRef)); |
| } |
| |
| TXFMXPathFilter::~TXFMXPathFilter() { |
| |
| lstsVectorType::iterator i; |
| |
| for (i = m_lsts.begin(); i != m_lsts.end(); ++i) { |
| |
| if ((*i)->lst != NULL) |
| delete ((*i)->lst); |
| |
| delete (*i); |
| } |
| |
| if (mp_formatter != NULL) |
| delete mp_formatter; |
| } |
| |
| // Methods to set the inputs |
| |
| void TXFMXPathFilter::setInput(TXFMBase* newInput) { |
| |
| if (newInput->getOutputType() == TXFMBase::BYTE_STREAM) { |
| |
| // Need to parse into DOM_NODES |
| |
| TXFMParser * parser; |
| XSECnew(parser, TXFMParser(mp_expansionDoc)); |
| try{ |
| parser->setInput(newInput); |
| } |
| catch (...) { |
| delete parser; |
| input = newInput; |
| throw; |
| } |
| |
| input = parser; |
| parser->expandNameSpaces(); |
| } |
| else { |
| input = newInput; |
| } |
| |
| // Set up for the new document |
| document = input->getDocument(); |
| |
| // Expand if necessary |
| this->expandNameSpaces(); |
| |
| keepComments = input->getCommentsStatus(); |
| } |
| |
| XSECXPathNodeList* TXFMXPathFilter::evaluateSingleExpr(DSIGXPathFilterExpr* expr) { |
| |
| // Have a single expression that we wish to find the resultant nodeset for |
| |
| XSECXPathNodeList addedNodes; |
| setXPathNS(document, expr->mp_NSMap, addedNodes, mp_formatter, mp_nse); |
| |
| XPathProcessorImpl xppi; // The processor |
| XercesParserLiaison xpl; |
| XercesDOMSupport xds(xpl); |
| XPathEvaluator xpe; |
| XPathFactoryDefault xpf; |
| XPathConstructionContextDefault xpcc; |
| |
| XalanDocument * xd; |
| XalanNode * contextNode; |
| |
| // Xalan can throw exceptions in all functions, so do one broad catch point. |
| |
| try { |
| |
| // Map to Xalan |
| xd = xpl.createDocument(document); |
| |
| // For performing mapping |
| XercesDocumentWrapper *xdw = xpl.mapDocumentToWrapper(xd); |
| XercesWrapperNavigator xwn(xdw); |
| |
| // Map the "here" node |
| |
| XalanNode * hereNode = NULL; |
| |
| hereNode = xwn.mapNode(expr->mp_xpathFilterNode); |
| |
| if (hereNode == NULL) { |
| |
| hereNode = findHereNodeFromXalan(&xwn, xd, expr->mp_exprTextNode); |
| |
| if (hereNode == NULL) { |
| throw XSECException(XSECException::XPathFilterError, |
| "Unable to find here node in Xalan Wrapper map"); |
| } |
| } |
| |
| // Now work out what we have to set up in the new processing |
| |
| XalanDOMString cd; // For XPath Filter, the root is always the context node |
| |
| cd = XalanDOMString("/"); // Root node |
| |
| // The context node is the "root" node |
| contextNode = |
| xpe.selectSingleNode( |
| xds, |
| xd, |
| cd.c_str(), |
| xd->getDocumentElement()); |
| |
| XPathEnvSupportDefault xpesd; |
| XObjectFactoryDefault xof; |
| XPathExecutionContextDefault xpec(xpesd, xds, xof); |
| |
| ElementPrefixResolverProxy pr(xd->getDocumentElement(), xpesd, xds); |
| |
| // Work around the fact that the XPath implementation is designed for XSLT, so does |
| // not allow here() as a NCName. |
| |
| // THIS IS A KLUDGE AND SHOULD BE DONE BETTER |
| |
| safeBuffer k(KLUDGE_PREFIX); |
| k.sbStrcatIn(":"); |
| |
| // Map the expression into a local code page string (silly - should be XMLCh) |
| safeBuffer exprSB; |
| exprSB << (*mp_formatter << expr->m_expr.rawXMLChBuffer()); |
| |
| XMLSSize_t offset = exprSB.sbStrstr("here()"); |
| |
| while (offset >= 0) { |
| |
| if (offset == 0 || offset == 1 || |
| (!(exprSB[offset - 1] == ':' && exprSB[offset - 2] != ':') && |
| separator(exprSB[offset - 1]))) { |
| |
| exprSB.sbStrinsIn(k.rawCharBuffer(), offset); |
| } |
| |
| offset = exprSB.sbOffsetStrstr("here()", offset + 11); |
| } |
| |
| // Install the External function in the Environment handler |
| |
| if (hereNode != NULL) { |
| xpesd.installExternalFunctionLocal(XalanDOMString(URI_ID_DSIG), XalanDOMString("here"), DSIGXPathHere(hereNode)); |
| } |
| |
| XPath * xp = xpf.create(); |
| |
| XalanDOMString Xexpr((char *) exprSB.rawBuffer()); |
| xppi.initXPath(*xp, xpcc, Xexpr, pr); |
| |
| // Now resolve |
| |
| XObjectPtr xObj = xp->execute(contextNode, pr, xpec); |
| |
| // Now map to a list that others can use (naieve list at this time) |
| |
| const NodeRefListBase& lst = xObj->nodeset(); |
| |
| int size = (int) lst.getLength(); |
| const DOMNode *item; |
| |
| XSECXPathNodeList * ret; |
| XSECnew(ret, XSECXPathNodeList); |
| Janitor<XSECXPathNodeList> j_ret(ret); |
| |
| for (int i = 0; i < size; ++ i) { |
| |
| if (lst.item(i) == xd) |
| ret->addNode(document); |
| else { |
| item = xwn.mapNode(lst.item(i)); |
| ret->addNode(item); |
| } |
| } |
| |
| xpesd.uninstallExternalFunctionGlobal(XalanDOMString(URI_ID_DSIG), XalanDOMString("here")); |
| |
| clearXPathNS(document, addedNodes, mp_formatter, mp_nse); |
| |
| j_ret.release(); |
| return ret; |
| } |
| catch (const XSLException &e) { |
| |
| safeBuffer msg; |
| |
| // Whatever happens - fix any changes to the original document |
| clearXPathNS(document, addedNodes, mp_formatter, mp_nse); |
| |
| // Collate the exception message into an XSEC message. |
| msg.sbTranscodeIn("Xalan Exception : "); |
| msg.sbXMLChCat(e.getType()); |
| msg.sbXMLChCat(" caught. Message : "); |
| msg.sbXMLChCat(e.getMessage().c_str()); |
| |
| throw XSECException(XSECException::XPathFilterError, |
| msg.rawXMLChBuffer()); |
| } |
| catch (...) { |
| clearXPathNS(document, addedNodes, mp_formatter, mp_nse); |
| throw; |
| } |
| |
| return NULL; |
| } |
| |
| bool TXFMXPathFilter::checkNodeInInput(DOMNode* n, DOMNode* attParent) { |
| |
| if (mp_fragment != NULL) { |
| |
| DOMNode * p = n; |
| |
| /* Check attParent here to reduce cycles */ |
| if (attParent != NULL) { |
| if (p == mp_fragment) |
| return true; |
| p = attParent; |
| } |
| |
| while (p != NULL) { |
| |
| if (p == mp_fragment) |
| return true; |
| |
| p = p->getParentNode(); |
| } |
| |
| return false; |
| } |
| |
| return mp_inputList->hasNode(n); |
| } |
| |
| bool TXFMXPathFilter::checkNodeInScope(DOMNode* n) { |
| |
| // Walk backwards through the lists |
| lstsVectorType::iterator lstsIter; |
| |
| lstsIter = m_lsts.end(); |
| filterSetHolder *sh; |
| |
| while (lstsIter != m_lsts.begin()) { |
| |
| lstsIter--; |
| sh = *lstsIter; |
| if (sh->ancestorInScope != NULL) { |
| |
| // Have an ancestor in scope, so this node is in this list |
| if (sh->type == DSIGXPathFilterExpr::FILTER_UNION) |
| // Got this far, must be OK! |
| return true; |
| if (sh->type == DSIGXPathFilterExpr::FILTER_SUBTRACT) |
| return false; |
| } |
| else { |
| if (sh->type == DSIGXPathFilterExpr::FILTER_INTERSECT) |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| #if 1 |
| |
| void TXFMXPathFilter::walkDocument(DOMNode* n) { |
| |
| // Non-recursive version |
| |
| DOMNode * current = n; |
| DOMNode * next; |
| DOMNode * attParent = NULL; /* Assign NULL to remove spurious Forte warning */ |
| bool done = false; |
| bool treeUp = false; |
| DOMNamedNodeMap * atts = n->getAttributes(); |
| int attsSize = -1; |
| int currentAtt = -1; |
| lstsVectorType::iterator lstsIter; |
| |
| while (done == false && current != NULL) { |
| |
| if (treeUp == true) { |
| |
| if (current == n) { |
| // We are complete. |
| done = true; |
| } |
| else { |
| // Remove this node from the ancestor lists |
| for (lstsIter = m_lsts.begin(); lstsIter != m_lsts.end(); ++lstsIter) { |
| |
| if ((*lstsIter)->ancestorInScope == current) { |
| (*lstsIter)->ancestorInScope = NULL; |
| } |
| } |
| |
| // Check for another sibling |
| next = current->getNextSibling(); |
| |
| if (next == NULL) { |
| current = current->getParentNode(); |
| treeUp = true; |
| } |
| else { |
| current = next; |
| treeUp = false; |
| } |
| } |
| } /* treeUp == true */ |
| else { |
| // Check if the current node is in the result set. The walk the children |
| |
| // First check if this node is in any lists, and if so, |
| // set the appropriate ancestor nodes (if necessary) |
| |
| for (lstsIter = m_lsts.begin(); lstsIter != m_lsts.end(); ++lstsIter) { |
| |
| if ((*lstsIter)->ancestorInScope == NULL && |
| (*lstsIter)->lst->hasNode(current)) { |
| |
| (*lstsIter)->ancestorInScope = current; |
| } |
| } |
| |
| // Now that the ancestor setup is done, check to see if this node is |
| // in scope. |
| |
| if (checkNodeInScope(current) && |
| checkNodeInInput(current, (atts != NULL ? attParent : NULL))) { |
| |
| m_xpathFilterMap.addNode(current); |
| } |
| |
| // Now find the next node! |
| |
| if (atts != NULL) { |
| |
| // Working on an attribute list |
| currentAtt++; |
| |
| if (currentAtt == attsSize) { |
| |
| // Attribute list complete |
| atts = NULL; |
| current = attParent; |
| next = current->getFirstChild(); |
| if (next == NULL) |
| treeUp = true; |
| else { |
| current = next; |
| treeUp = false; |
| } |
| } |
| else { |
| current = atts->item(currentAtt); |
| } |
| } |
| else { |
| // Working on an element or other non-attribute node |
| atts = current->getAttributes(); |
| |
| if (atts != NULL && ((attsSize = atts->getLength()) > 0)) { |
| currentAtt = 0; |
| attParent = current; |
| current = atts->item(0); |
| treeUp = false; |
| } |
| else { |
| atts = NULL; |
| |
| next = current->getFirstChild(); |
| |
| if (next != NULL) { |
| current = next; |
| treeUp = false; |
| } |
| else { |
| treeUp = true; |
| } |
| } |
| } /* ! atts == NULL */ |
| } |
| } /* while */ |
| } |
| #endif |
| |
| #if 0 |
| |
| void TXFMXPathFilter::walkDocument(DOMNode * n) { |
| |
| // Check if the current node is in the result set. The walk the children |
| lstsVectorType::iterator lstsIter; |
| |
| // First check if this node is in any lists, and if so, |
| // set the appropriate ancestor nodes (if necessary) |
| |
| for (lstsIter = m_lsts.begin(); lstsIter != m_lsts.end(); ++lstsIter) { |
| |
| if ((*lstsIter)->ancestorInScope == NULL && (*lstsIter)->lst->hasNode(n)) { |
| (*lstsIter)->ancestorInScope = n; |
| } |
| } |
| |
| // Now that the ancestor setup is done, check to see if this node is |
| // in scope. |
| |
| if (checkNodeInScope(n) && checkNodeInInput(n)) { |
| m_xpathFilterMap.addNode(n); |
| } |
| |
| // Do any attributes |
| |
| DOMNamedNodeMap * atts = n->getAttributes(); |
| if (atts != NULL) { |
| unsigned int s = atts->getLength(); |
| for (unsigned int i = 0; i < s; ++i) { |
| walkDocument(atts->item(i)); |
| } |
| } |
| |
| // Do any childeren |
| |
| DOMNode * c = n->getFirstChild(); |
| |
| while (c != NULL) { |
| walkDocument(c); |
| c = c->getNextSibling(); |
| } |
| |
| // Now remove from ancestor lists if we are that ancestor |
| for (lstsIter = m_lsts.begin(); lstsIter != m_lsts.end(); ++lstsIter) { |
| |
| if ((*lstsIter)->ancestorInScope == n) { |
| (*lstsIter)->ancestorInScope = NULL; |
| } |
| } |
| } |
| #endif |
| |
| void TXFMXPathFilter::evaluateExprs(DSIGTransformXPathFilter::exprVectorType* exprs) { |
| |
| if (exprs == NULL || exprs->size() < 1) { |
| throw XSECException(XSECException::XPathFilterError, |
| "TXFMXPathFilter::evaluateExpr - no expression list set"); |
| } |
| |
| DSIGTransformXPathFilter::exprVectorType::iterator i; |
| |
| for (i = exprs->begin(); i != exprs->end(); ++i) { |
| |
| XSECXPathNodeList * lst = evaluateSingleExpr(*i); |
| filterSetHolder * sh; |
| XSECnew(sh, filterSetHolder); |
| |
| sh->lst = lst; |
| sh->type = (*i)->m_filterType; |
| sh->ancestorInScope = NULL; |
| |
| if (lst != NULL) { |
| m_lsts.push_back(sh); |
| } |
| } |
| |
| // Well we appear to have successfully run through all the nodelists! |
| |
| mp_fragment = NULL; |
| mp_inputList = NULL; |
| |
| // Find the input nodeset |
| TXFMBase::nodeType inputType = input->getNodeType(); |
| switch (inputType) { |
| |
| case DOM_NODE_DOCUMENT : |
| mp_fragment = document; |
| break; |
| |
| case DOM_NODE_DOCUMENT_FRAGMENT : |
| mp_fragment = input->getFragmentNode(); |
| break; |
| |
| case DOM_NODE_XPATH_NODESET : |
| mp_inputList = &(input->getXPathNodeList()); |
| break; |
| |
| default : |
| throw XSECException(XSECException::XPathFilterError, |
| "TXFMXPathFilter::evaluateExprs - unknown input type"); |
| |
| } |
| |
| // Now just recurse through each node in the document |
| walkDocument(document); |
| } |
| |
| |
| // Methods to get tranform output type and input requirement |
| |
| TXFMBase::ioType TXFMXPathFilter::getInputType() const { |
| return TXFMBase::DOM_NODES; |
| } |
| |
| TXFMBase::ioType TXFMXPathFilter::getOutputType() const { |
| return TXFMBase::DOM_NODES; |
| } |
| |
| TXFMBase::nodeType TXFMXPathFilter::getNodeType() const { |
| return TXFMBase::DOM_NODE_XPATH_NODESET; |
| } |
| |
| // Methods to get output data |
| |
| unsigned int TXFMXPathFilter::readBytes(XMLByte* const toFill, unsigned int maxToFill) { |
| return 0; |
| } |
| |
| DOMDocument* TXFMXPathFilter::getDocument() const { |
| return document; |
| } |
| |
| XSECXPathNodeList& TXFMXPathFilter::getXPathNodeList() { |
| return m_xpathFilterMap; |
| } |
| |
| #endif /* XSEC_HAVE_XPATH */ |
| |
| |
| |