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.resolver.implementations;
  24 
  25 import com.sun.org.apache.xml.internal.security.signature.XMLSignatureInput;
  26 import com.sun.org.apache.xml.internal.security.utils.XMLUtils;
  27 import com.sun.org.apache.xml.internal.security.utils.resolver.ResourceResolverContext;
  28 import com.sun.org.apache.xml.internal.security.utils.resolver.ResourceResolverException;
  29 import com.sun.org.apache.xml.internal.security.utils.resolver.ResourceResolverSpi;
  30 import org.w3c.dom.Document;
  31 import org.w3c.dom.Element;
  32 import org.w3c.dom.Node;
  33 
  34 /**
  35  * Handles barename XPointer Reference URIs.
  36  * <BR />
  37  * To retain comments while selecting an element by an identifier ID,
  38  * use the following full XPointer: URI='#xpointer(id('ID'))'.
  39  * <BR />
  40  * To retain comments while selecting the entire document,
  41  * use the following full XPointer: URI='#xpointer(/)'.
  42  * This XPointer contains a simple XPath expression that includes
  43  * the root node, which the second to last step above replaces with all
  44  * nodes of the parse tree (all descendants, plus all attributes,
  45  * plus all namespaces nodes).
  46  *
  47  * @author $Author: coheigea $
  48  */
  49 public class ResolverXPointer extends ResourceResolverSpi {
  50 
  51     /** {@link org.apache.commons.logging} logging facility */
  52     private static java.util.logging.Logger log = 
  53         java.util.logging.Logger.getLogger(ResolverXPointer.class.getName());
  54     
  55     private static final String XP = "#xpointer(id(";
  56     private static final int XP_LENGTH = XP.length();
  57 
  58     @Override
  59     public boolean engineIsThreadSafe() {
  60         return true;
  61     }
  62     
  63     /**
  64      * @inheritDoc
  65      */
  66     @Override
  67     public XMLSignatureInput engineResolveURI(ResourceResolverContext context)
  68         throws ResourceResolverException {
  69 
  70         Node resultNode = null;
  71         Document doc = context.attr.getOwnerElement().getOwnerDocument();
  72 
  73         if (isXPointerSlash(context.uriToResolve)) {
  74             resultNode = doc;
  75         } else if (isXPointerId(context.uriToResolve)) {
  76             String id = getXPointerId(context.uriToResolve);
  77             resultNode = doc.getElementById(id);
  78             
  79             if (context.secureValidation) {
  80                 Element start = context.attr.getOwnerDocument().getDocumentElement();
  81                 if (!XMLUtils.protectAgainstWrappingAttack(start, id)) {
  82                     Object exArgs[] = { id };
  83                     throw new ResourceResolverException(
  84                         "signature.Verification.MultipleIDs", exArgs, context.attr, context.baseUri
  85                     );
  86                 }
  87             }
  88 
  89             if (resultNode == null) {
  90                 Object exArgs[] = { id };
  91 
  92                 throw new ResourceResolverException(
  93                     "signature.Verification.MissingID", exArgs, context.attr, context.baseUri
  94                 );
  95             }
  96         }
  97 
  98         XMLSignatureInput result = new XMLSignatureInput(resultNode);
  99 
 100         result.setMIMEType("text/xml");
 101         if (context.baseUri != null && context.baseUri.length() > 0) {
 102             result.setSourceURI(context.baseUri.concat(context.uriToResolve));      
 103         } else {
 104             result.setSourceURI(context.uriToResolve);      
 105         }
 106 
 107         return result;
 108     }
 109 
 110     /**
 111      * @inheritDoc
 112      */
 113     public boolean engineCanResolveURI(ResourceResolverContext context) {
 114         if (context.uriToResolve == null) {
 115             return false;
 116         }
 117         if (isXPointerSlash(context.uriToResolve) || isXPointerId(context.uriToResolve)) {
 118             return true;
 119         }
 120 
 121         return false;
 122     }
 123 
 124     /**
 125      * Method isXPointerSlash
 126      *
 127      * @param uri
 128      * @return true if begins with xpointer
 129      */
 130     private static boolean isXPointerSlash(String uri) {
 131         if (uri.equals("#xpointer(/)")) {
 132             return true;
 133         }
 134 
 135         return false;
 136     }
 137 
 138     /**
 139      * Method isXPointerId
 140      *
 141      * @param uri
 142      * @return whether it has an xpointer id
 143      */
 144     private static boolean isXPointerId(String uri) {
 145         if (uri.startsWith(XP) && uri.endsWith("))")) {
 146             String idPlusDelim = uri.substring(XP_LENGTH, uri.length() - 2);
 147 
 148             int idLen = idPlusDelim.length() -1;
 149             if (((idPlusDelim.charAt(0) == '"') && (idPlusDelim.charAt(idLen) == '"')) 
 150                 || ((idPlusDelim.charAt(0) == '\'') && (idPlusDelim.charAt(idLen) == '\''))) {
 151                 if (log.isLoggable(java.util.logging.Level.FINE)) {
 152                     log.log(java.util.logging.Level.FINE, "Id = " + idPlusDelim.substring(1, idLen));
 153                 }
 154                 return true;
 155             }
 156         }
 157 
 158         return false;
 159     }
 160 
 161     /**
 162      * Method getXPointerId
 163      *
 164      * @param uri
 165      * @return xpointerId to search.
 166      */
 167     private static String getXPointerId(String uri) {
 168         if (uri.startsWith(XP) && uri.endsWith("))")) {
 169             String idPlusDelim = uri.substring(XP_LENGTH,uri.length() - 2);
 170             
 171             int idLen = idPlusDelim.length() -1;
 172             if (((idPlusDelim.charAt(0) == '"') && (idPlusDelim.charAt(idLen) == '"')) 
 173                 || ((idPlusDelim.charAt(0) == '\'') && (idPlusDelim.charAt(idLen) == '\''))) {
 174                 return idPlusDelim.substring(1, idLen);
 175             }
 176         }
 177 
 178         return null;
 179     }
 180 }