1 /*
   2  * reserved comment block
   3  * DO NOT REMOVE OR ALTER!
   4  */
   5 /**
   6  * Licensed to the Apache Software Foundation (ASF) under one
   7  * or more contributor license agreements. See the NOTICE file
   8  * distributed with this work for additional information
   9  * regarding copyright ownership. The ASF licenses this file
  10  * to you under the Apache License, Version 2.0 (the
  11  * "License"); you may not use this file except in compliance
  12  * with the License. You may obtain a copy of the License at
  13  *
  14  * http://www.apache.org/licenses/LICENSE-2.0
  15  *
  16  * Unless required by applicable law or agreed to in writing,
  17  * software distributed under the License is distributed on an
  18  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  19  * KIND, either express or implied. See the License for the
  20  * specific language governing permissions and limitations
  21  * under the License.
  22  */
  23 package com.sun.org.apache.xml.internal.security.utils;
  24 
  25 import java.lang.reflect.Constructor;
  26 import java.lang.reflect.Method;
  27 import java.lang.reflect.Modifier;
  28 
  29 import javax.xml.transform.ErrorListener;
  30 import javax.xml.transform.SourceLocator;
  31 import javax.xml.transform.TransformerException;
  32 
  33 import com.sun.org.apache.xml.internal.security.transforms.implementations.FuncHere;
  34 import com.sun.org.apache.xml.internal.utils.PrefixResolver;
  35 import com.sun.org.apache.xml.internal.utils.PrefixResolverDefault;
  36 import com.sun.org.apache.xpath.internal.Expression;
  37 import com.sun.org.apache.xpath.internal.XPath;
  38 import com.sun.org.apache.xpath.internal.XPathContext;
  39 import com.sun.org.apache.xpath.internal.compiler.FunctionTable;
  40 import com.sun.org.apache.xpath.internal.objects.XObject;
  41 import org.w3c.dom.Document;
  42 import org.w3c.dom.Node;
  43 import org.w3c.dom.NodeList;
  44 
  45 /**
  46  * An implementation of XPathAPI using Xalan. This supports the "here()" function defined in the digital
  47  * signature spec.
  48  */
  49 public class XalanXPathAPI implements XPathAPI {
  50 
  51     private static java.util.logging.Logger log =
  52         java.util.logging.Logger.getLogger(XalanXPathAPI.class.getName());
  53 
  54     private String xpathStr = null;
  55 
  56     private XPath xpath = null;
  57 
  58     private static FunctionTable funcTable = null;
  59     
  60     private static boolean installed;
  61     
  62     private XPathContext context;
  63     
  64     static {
  65         fixupFunctionTable();
  66     }
  67     
  68 
  69     /**
  70      *  Use an XPath string to select a nodelist.
  71      *  XPath namespace prefixes are resolved from the namespaceNode.
  72      *
  73      *  @param contextNode The node to start searching from.
  74      *  @param xpathnode
  75      *  @param str
  76      *  @param namespaceNode The node from which prefixes in the XPath will be resolved to namespaces.
  77      *  @return A NodeIterator, should never be null.
  78      *
  79      * @throws TransformerException
  80      */
  81     public NodeList selectNodeList(
  82         Node contextNode, Node xpathnode, String str, Node namespaceNode
  83     ) throws TransformerException {
  84 
  85         // Execute the XPath, and have it return the result
  86         XObject list = eval(contextNode, xpathnode, str, namespaceNode);
  87 
  88         // Return a NodeList.
  89         return list.nodelist();
  90     }
  91     
  92     /**
  93      * Evaluate an XPath string and return true if the output is to be included or not.
  94      *  @param contextNode The node to start searching from.
  95      *  @param xpathnode The XPath node
  96      *  @param str The XPath expression
  97      *  @param namespaceNode The node from which prefixes in the XPath will be resolved to namespaces.
  98      */
  99     public boolean evaluate(Node contextNode, Node xpathnode, String str, Node namespaceNode)
 100         throws TransformerException {
 101         XObject object = eval(contextNode, xpathnode, str, namespaceNode);
 102         return object.bool();
 103     }
 104     
 105     /**
 106      * Clear any context information from this object
 107      */
 108     public void clear() {
 109         xpathStr = null;
 110         xpath = null;
 111         context = null;
 112     }
 113     
 114     public synchronized static boolean isInstalled() {
 115         return installed;
 116     }
 117 
 118     private XObject eval(Node contextNode, Node xpathnode, String str, Node namespaceNode)
 119         throws TransformerException {
 120         if (context == null) {
 121             context = new XPathContext(xpathnode);
 122             context.setSecureProcessing(true);
 123         }
 124 
 125         // Create an object to resolve namespace prefixes.
 126         // XPath namespaces are resolved from the input context node's document element
 127         // if it is a root node, or else the current context node (for lack of a better
 128         // resolution space, given the simplicity of this sample code).
 129         Node resolverNode = 
 130             (namespaceNode.getNodeType() == Node.DOCUMENT_NODE)
 131                 ? ((Document) namespaceNode).getDocumentElement() : namespaceNode;
 132         PrefixResolverDefault prefixResolver = new PrefixResolverDefault(resolverNode);
 133 
 134         if (!str.equals(xpathStr)) {
 135             if (str.indexOf("here()") > 0) {
 136                 context.reset();
 137             }
 138             xpath = createXPath(str, prefixResolver);
 139             xpathStr = str;
 140         }
 141 
 142         // Execute the XPath, and have it return the result
 143         int ctxtNode = context.getDTMHandleFromNode(contextNode);
 144 
 145         return xpath.execute(context, ctxtNode, prefixResolver);
 146     }
 147 
 148     private XPath createXPath(String str, PrefixResolver prefixResolver) throws TransformerException {
 149         XPath xpath = null;
 150         Class<?>[] classes = new Class<?>[]{String.class, SourceLocator.class, PrefixResolver.class, int.class,
 151                                       ErrorListener.class, FunctionTable.class};
 152         Object[] objects = 
 153             new Object[]{str, null, prefixResolver, Integer.valueOf(XPath.SELECT), null, funcTable};
 154         try {
 155             Constructor<?> constructor = XPath.class.getConstructor(classes);
 156             xpath = (XPath) constructor.newInstance(objects);
 157         } catch (Exception ex) {
 158             if (log.isLoggable(java.util.logging.Level.FINE)) {
 159                 log.log(java.util.logging.Level.FINE, ex.getMessage(), ex);
 160             }
 161         }
 162         if (xpath == null) {
 163             xpath = new XPath(str, null, prefixResolver, XPath.SELECT, null);
 164         }
 165         return xpath;
 166     }
 167 
 168     private synchronized static void fixupFunctionTable() {
 169         installed = false;
 170         if (log.isLoggable(java.util.logging.Level.FINE)) {
 171             log.log(java.util.logging.Level.FINE, "Registering Here function");
 172         }
 173         /**
 174          * Try to register our here() implementation as internal function.
 175          */
 176         try {
 177             Class<?>[] args = {String.class, Expression.class};
 178             Method installFunction = FunctionTable.class.getMethod("installFunction", args);
 179             if ((installFunction.getModifiers() & Modifier.STATIC) != 0) {
 180                 Object[] params = {"here", new FuncHere()};
 181                 installFunction.invoke(null, params);
 182                 installed = true;
 183             }
 184         } catch (Exception ex) {
 185             log.log(java.util.logging.Level.FINE, "Error installing function using the static installFunction method", ex);
 186         }
 187         if (!installed) {
 188             try {
 189                 funcTable = new FunctionTable();
 190                 Class<?>[] args = {String.class, Class.class};
 191                 Method installFunction = FunctionTable.class.getMethod("installFunction", args);
 192                 Object[] params = {"here", FuncHere.class};
 193                 installFunction.invoke(funcTable, params);
 194                 installed = true;
 195             } catch (Exception ex) {
 196                 log.log(java.util.logging.Level.FINE, "Error installing function using the static installFunction method", ex);
 197             }
 198         }
 199         if (log.isLoggable(java.util.logging.Level.FINE)) {
 200             if (installed) {
 201                 log.log(java.util.logging.Level.FINE, "Registered class " + FuncHere.class.getName()
 202                           + " for XPath function 'here()' function in internal table");
 203             } else {
 204                 log.log(java.util.logging.Level.FINE, "Unable to register class " + FuncHere.class.getName()
 205                           + " for XPath function 'here()' function in internal table");
 206             }
 207         }
 208     }
 209     
 210 }