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.math.BigInteger;
  26 import java.util.concurrent.ConcurrentHashMap;
  27 import java.util.Map;
  28 
  29 import com.sun.org.apache.xml.internal.security.exceptions.Base64DecodingException;
  30 import com.sun.org.apache.xml.internal.security.exceptions.XMLSecurityException;
  31 import org.w3c.dom.Attr;
  32 import org.w3c.dom.Document;
  33 import org.w3c.dom.Element;
  34 import org.w3c.dom.Node;
  35 import org.w3c.dom.NodeList;
  36 import org.w3c.dom.Text;
  37 
  38 /**
  39  * This is the base class to all Objects which have a direct 1:1 mapping to an
  40  * Element in a particular namespace.
  41  */
  42 public abstract class ElementProxy {
  43 
  44     protected static final java.util.logging.Logger log =
  45         java.util.logging.Logger.getLogger(ElementProxy.class.getName());
  46 
  47     /** Field constructionElement */
  48     protected Element _constructionElement = null;
  49 
  50     /** Field baseURI */
  51     protected String _baseURI = null;
  52 
  53     /** Field doc */
  54     protected Document _doc = null;
  55 
  56     /** Field prefixMappings */
  57     private static Map<String, String> prefixMappings = new ConcurrentHashMap<String, String>();
  58 
  59     /**
  60      * Constructor ElementProxy
  61      *
  62      */
  63     public ElementProxy() {
  64     }
  65 
  66     /**
  67      * Constructor ElementProxy
  68      *
  69      * @param doc
  70      */
  71     public ElementProxy(Document doc) {
  72         if (doc == null) {
  73             throw new RuntimeException("Document is null");
  74         }
  75 
  76         this._doc = doc;
  77         this._constructionElement =
  78             createElementForFamilyLocal(this._doc, this.getBaseNamespace(), this.getBaseLocalName());
  79     }
  80 
  81     /**
  82      * Constructor ElementProxy
  83      *
  84      * @param element
  85      * @param BaseURI
  86      * @throws XMLSecurityException
  87      */
  88     public ElementProxy(Element element, String BaseURI) throws XMLSecurityException {
  89         if (element == null) {
  90             throw new XMLSecurityException("ElementProxy.nullElement");
  91         }
  92 
  93         if (log.isLoggable(java.util.logging.Level.FINE)) {
  94             log.log(java.util.logging.Level.FINE, "setElement(\"" + element.getTagName() + "\", \"" + BaseURI + "\")");
  95         }
  96 
  97         this._doc = element.getOwnerDocument();
  98         this._constructionElement = element;
  99         this._baseURI = BaseURI;
 100 
 101         this.guaranteeThatElementInCorrectSpace();
 102     }
 103 
 104     /**
 105      * Returns the namespace of the Elements of the sub-class.
 106      *
 107      * @return the namespace of the Elements of the sub-class.
 108      */
 109     public abstract String getBaseNamespace();
 110 
 111     /**
 112      * Returns the localname of the Elements of the sub-class.
 113      *
 114      * @return the localname of the Elements of the sub-class.
 115      */
 116     public abstract String getBaseLocalName();
 117 
 118 
 119     protected Element createElementForFamilyLocal(
 120         Document doc, String namespace, String localName
 121     ) {
 122         Element result = null;
 123         if (namespace == null) {
 124             result = doc.createElementNS(null, localName);
 125         } else {
 126             String baseName = this.getBaseNamespace();
 127             String prefix = ElementProxy.getDefaultPrefix(baseName);
 128             if ((prefix == null) || (prefix.length() == 0)) {
 129                 result = doc.createElementNS(namespace, localName);
 130                 result.setAttributeNS(Constants.NamespaceSpecNS, "xmlns", namespace);
 131             } else {
 132                 result = doc.createElementNS(namespace, prefix + ":" + localName);
 133                 result.setAttributeNS(Constants.NamespaceSpecNS, "xmlns:" + prefix, namespace);
 134             }
 135         }
 136         return result;
 137     }
 138 
 139 
 140     /**
 141      * This method creates an Element in a given namespace with a given localname.
 142      * It uses the {@link ElementProxy#getDefaultPrefix} method to decide whether
 143      * a particular prefix is bound to that namespace.
 144      * <BR />
 145      * This method was refactored out of the constructor.
 146      *
 147      * @param doc
 148      * @param namespace
 149      * @param localName
 150      * @return The element created.
 151      */
 152     public static Element createElementForFamily(Document doc, String namespace, String localName) {
 153         Element result = null;
 154         String prefix = ElementProxy.getDefaultPrefix(namespace);
 155 
 156         if (namespace == null) {
 157             result = doc.createElementNS(null, localName);
 158         } else {
 159             if ((prefix == null) || (prefix.length() == 0)) {
 160                 result = doc.createElementNS(namespace, localName);
 161                 result.setAttributeNS(Constants.NamespaceSpecNS, "xmlns", namespace);
 162             } else {
 163                 result = doc.createElementNS(namespace, prefix + ":" + localName);
 164                 result.setAttributeNS(Constants.NamespaceSpecNS, "xmlns:" + prefix, namespace);
 165             }
 166         }
 167 
 168         return result;
 169     }
 170 
 171     /**
 172      * Method setElement
 173      *
 174      * @param element
 175      * @param BaseURI
 176      * @throws XMLSecurityException
 177      */
 178     public void setElement(Element element, String BaseURI) throws XMLSecurityException {
 179         if (element == null) {
 180             throw new XMLSecurityException("ElementProxy.nullElement");
 181         }
 182 
 183         if (log.isLoggable(java.util.logging.Level.FINE)) {
 184             log.log(java.util.logging.Level.FINE, "setElement(" + element.getTagName() + ", \"" + BaseURI + "\"");
 185         }
 186 
 187         this._doc = element.getOwnerDocument();
 188         this._constructionElement = element;
 189         this._baseURI = BaseURI;
 190     }
 191 
 192 
 193     /**
 194      * Returns the Element which was constructed by the Object.
 195      *
 196      * @return the Element which was constructed by the Object.
 197      */
 198     public final Element getElement() {
 199         return this._constructionElement;
 200     }
 201 
 202     /**
 203      * Returns the Element plus a leading and a trailing CarriageReturn Text node.
 204      *
 205      * @return the Element which was constructed by the Object.
 206      */
 207     public final NodeList getElementPlusReturns() {
 208 
 209         HelperNodeList nl = new HelperNodeList();
 210 
 211         nl.appendChild(this._doc.createTextNode("\n"));
 212         nl.appendChild(this.getElement());
 213         nl.appendChild(this._doc.createTextNode("\n"));
 214 
 215         return nl;
 216     }
 217 
 218     /**
 219      * Method getDocument
 220      *
 221      * @return the Document where this element is contained.
 222      */
 223     public Document getDocument() {
 224         return this._doc;
 225     }
 226 
 227     /**
 228      * Method getBaseURI
 229      *
 230      * @return the base uri of the namespace of this element
 231      */
 232     public String getBaseURI() {
 233         return this._baseURI;
 234     }
 235 
 236     /**
 237      * Method guaranteeThatElementInCorrectSpace
 238      *
 239      * @throws XMLSecurityException
 240      */
 241     void guaranteeThatElementInCorrectSpace() throws XMLSecurityException {
 242 
 243         String expectedLocalName = this.getBaseLocalName();
 244         String expectedNamespaceUri = this.getBaseNamespace();
 245 
 246         String actualLocalName = this._constructionElement.getLocalName();
 247         String actualNamespaceUri = this._constructionElement.getNamespaceURI();
 248 
 249         if(!expectedNamespaceUri.equals(actualNamespaceUri)
 250             && !expectedLocalName.equals(actualLocalName)) {
 251             Object exArgs[] = { actualNamespaceUri + ":" + actualLocalName,
 252                                 expectedNamespaceUri + ":" + expectedLocalName};
 253             throw new XMLSecurityException("xml.WrongElement", exArgs);
 254         }
 255     }
 256 
 257     /**
 258      * Method addBigIntegerElement
 259      *
 260      * @param bi
 261      * @param localname
 262      */
 263     public void addBigIntegerElement(BigInteger bi, String localname) {
 264         if (bi != null) {
 265             Element e = XMLUtils.createElementInSignatureSpace(this._doc, localname);
 266 
 267             Base64.fillElementWithBigInteger(e, bi);
 268             this._constructionElement.appendChild(e);
 269             XMLUtils.addReturnToElement(this._constructionElement);
 270         }
 271     }
 272 
 273     /**
 274      * Method addBase64Element
 275      *
 276      * @param bytes
 277      * @param localname
 278      */
 279     public void addBase64Element(byte[] bytes, String localname) {
 280         if (bytes != null) {
 281             Element e = Base64.encodeToElement(this._doc, localname, bytes);
 282 
 283             this._constructionElement.appendChild(e);
 284             if (!XMLUtils.ignoreLineBreaks()) {
 285                 this._constructionElement.appendChild(this._doc.createTextNode("\n"));
 286             }
 287         }
 288     }
 289 
 290     /**
 291      * Method addTextElement
 292      *
 293      * @param text
 294      * @param localname
 295      */
 296     public void addTextElement(String text, String localname) {
 297         Element e = XMLUtils.createElementInSignatureSpace(this._doc, localname);
 298         Text t = this._doc.createTextNode(text);
 299 
 300         e.appendChild(t);
 301         this._constructionElement.appendChild(e);
 302         XMLUtils.addReturnToElement(this._constructionElement);
 303     }
 304 
 305     /**
 306      * Method addBase64Text
 307      *
 308      * @param bytes
 309      */
 310     public void addBase64Text(byte[] bytes) {
 311         if (bytes != null) {
 312             Text t = XMLUtils.ignoreLineBreaks()
 313                 ? this._doc.createTextNode(Base64.encode(bytes))
 314                 : this._doc.createTextNode("\n" + Base64.encode(bytes) + "\n");
 315             this._constructionElement.appendChild(t);
 316         }
 317     }
 318 
 319     /**
 320      * Method addText
 321      *
 322      * @param text
 323      */
 324     public void addText(String text) {
 325         if (text != null) {
 326             Text t = this._doc.createTextNode(text);
 327 
 328             this._constructionElement.appendChild(t);
 329         }
 330     }
 331 
 332     /**
 333      * Method getVal
 334      *
 335      * @param localname
 336      * @param namespace
 337      * @return The biginteger contained in the given element
 338      * @throws Base64DecodingException
 339      */
 340     public BigInteger getBigIntegerFromChildElement(
 341         String localname, String namespace
 342     ) throws Base64DecodingException {
 343         return Base64.decodeBigIntegerFromText(
 344             XMLUtils.selectNodeText(
 345                 this._constructionElement.getFirstChild(), namespace, localname, 0
 346             )
 347         );
 348     }
 349 
 350     /**
 351      * Method getBytesFromChildElement
 352      * @deprecated
 353      * @param localname
 354      * @param namespace
 355      * @return the bytes
 356      * @throws XMLSecurityException
 357      */
 358     @Deprecated
 359     public byte[] getBytesFromChildElement(String localname, String namespace)
 360         throws XMLSecurityException {
 361         Element e =
 362             XMLUtils.selectNode(
 363                 this._constructionElement.getFirstChild(), namespace, localname, 0
 364             );
 365 
 366         return Base64.decode(e);
 367     }
 368 
 369     /**
 370      * Method getTextFromChildElement
 371      *
 372      * @param localname
 373      * @param namespace
 374      * @return the Text of the textNode
 375      */
 376     public String getTextFromChildElement(String localname, String namespace) {
 377         return XMLUtils.selectNode(
 378                 this._constructionElement.getFirstChild(),
 379                 namespace,
 380                 localname,
 381                 0).getTextContent();
 382     }
 383 
 384     /**
 385      * Method getBytesFromTextChild
 386      *
 387      * @return The base64 bytes from the text children of this element
 388      * @throws XMLSecurityException
 389      */
 390     public byte[] getBytesFromTextChild() throws XMLSecurityException {
 391         return Base64.decode(XMLUtils.getFullTextChildrenFromElement(this._constructionElement));
 392     }
 393 
 394     /**
 395      * Method getTextFromTextChild
 396      *
 397      * @return the Text obtained by concatenating all the text nodes of this
 398      *    element
 399      */
 400     public String getTextFromTextChild() {
 401         return XMLUtils.getFullTextChildrenFromElement(this._constructionElement);
 402     }
 403 
 404     /**
 405      * Method length
 406      *
 407      * @param namespace
 408      * @param localname
 409      * @return the number of elements {namespace}:localname under this element
 410      */
 411     public int length(String namespace, String localname) {
 412         int number = 0;
 413         Node sibling = this._constructionElement.getFirstChild();
 414         while (sibling != null) {
 415             if (localname.equals(sibling.getLocalName())
 416                 && namespace.equals(sibling.getNamespaceURI())) {
 417                 number++;
 418             }
 419             sibling = sibling.getNextSibling();
 420         }
 421         return number;
 422     }
 423 
 424     /**
 425      * Adds an xmlns: definition to the Element. This can be called as follows:
 426      *
 427      * <PRE>
 428      * // set namespace with ds prefix
 429      * xpathContainer.setXPathNamespaceContext("ds", "http://www.w3.org/2000/09/xmldsig#");
 430      * xpathContainer.setXPathNamespaceContext("xmlns:ds", "http://www.w3.org/2000/09/xmldsig#");
 431      * </PRE>
 432      *
 433      * @param prefix
 434      * @param uri
 435      * @throws XMLSecurityException
 436      */
 437     public void setXPathNamespaceContext(String prefix, String uri)
 438         throws XMLSecurityException {
 439         String ns;
 440 
 441         if ((prefix == null) || (prefix.length() == 0)) {
 442             throw new XMLSecurityException("defaultNamespaceCannotBeSetHere");
 443         } else if (prefix.equals("xmlns")) {
 444             throw new XMLSecurityException("defaultNamespaceCannotBeSetHere");
 445         } else if (prefix.startsWith("xmlns:")) {
 446             ns = prefix;//"xmlns:" + prefix.substring("xmlns:".length());
 447         } else {
 448             ns = "xmlns:" + prefix;
 449         }
 450 
 451         Attr a = this._constructionElement.getAttributeNodeNS(Constants.NamespaceSpecNS, ns);
 452 
 453         if (a != null) {
 454             if (!a.getNodeValue().equals(uri)) {
 455                 Object exArgs[] = { ns, this._constructionElement.getAttributeNS(null, ns) };
 456 
 457                 throw new XMLSecurityException("namespacePrefixAlreadyUsedByOtherURI", exArgs);
 458             }
 459             return;
 460         }
 461 
 462         this._constructionElement.setAttributeNS(Constants.NamespaceSpecNS, ns, uri);
 463     }
 464 
 465     /**
 466      * Method setDefaultPrefix
 467      *
 468      * @param namespace
 469      * @param prefix
 470      * @throws XMLSecurityException
 471      */
 472     public static void setDefaultPrefix(String namespace, String prefix)
 473         throws XMLSecurityException {
 474         if (prefixMappings.containsValue(prefix)) {
 475             String storedPrefix = prefixMappings.get(namespace);
 476             if (!storedPrefix.equals(prefix)) {
 477                 Object exArgs[] = { prefix, namespace, storedPrefix };
 478 
 479                 throw new XMLSecurityException("prefix.AlreadyAssigned", exArgs);
 480             }
 481         }
 482 
 483         if (Constants.SignatureSpecNS.equals(namespace)) {
 484             XMLUtils.setDsPrefix(prefix);
 485         }
 486         if (EncryptionConstants.EncryptionSpecNS.equals(namespace)) {
 487             XMLUtils.setXencPrefix(prefix);
 488         }
 489         prefixMappings.put(namespace, prefix);
 490     }
 491 
 492     /**
 493      * This method registers the default prefixes.
 494      */
 495     public static void registerDefaultPrefixes() throws XMLSecurityException {
 496         setDefaultPrefix("http://www.w3.org/2000/09/xmldsig#", "ds");
 497         setDefaultPrefix("http://www.w3.org/2001/04/xmlenc#", "xenc");
 498         setDefaultPrefix("http://www.w3.org/2009/xmlenc11#", "xenc11");
 499         setDefaultPrefix("http://www.xmlsecurity.org/experimental#", "experimental");
 500         setDefaultPrefix("http://www.w3.org/2002/04/xmldsig-filter2", "dsig-xpath-old");
 501         setDefaultPrefix("http://www.w3.org/2002/06/xmldsig-filter2", "dsig-xpath");
 502         setDefaultPrefix("http://www.w3.org/2001/10/xml-exc-c14n#", "ec");
 503         setDefaultPrefix(
 504             "http://www.nue.et-inf.uni-siegen.de/~geuer-pollmann/#xpathFilter", "xx"
 505         );
 506     }
 507 
 508     /**
 509      * Method getDefaultPrefix
 510      *
 511      * @param namespace
 512      * @return the default prefix bind to this element.
 513      */
 514     public static String getDefaultPrefix(String namespace) {
 515         return prefixMappings.get(namespace);
 516     }
 517 
 518     protected void setLocalIdAttribute(String attrName, String value) {
 519 
 520         if (value != null) {
 521             Attr attr = getDocument().createAttributeNS(null, attrName);
 522             attr.setValue(value);
 523             getElement().setAttributeNodeNS(attr);
 524             getElement().setIdAttributeNode(attr, true);
 525         }
 526         else {
 527             getElement().removeAttributeNS(null, attrName);
 528         }
 529     }
 530 }