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.c14n.implementations; 24 25 import java.io.ByteArrayOutputStream; 26 import java.io.IOException; 27 import java.io.OutputStream; 28 import java.io.UnsupportedEncodingException; 29 import java.util.ArrayList; 30 import java.util.HashMap; 31 import java.util.Iterator; 32 import java.util.List; 33 import java.util.ListIterator; 34 import java.util.Map; 35 import java.util.Set; 36 37 import javax.xml.parsers.ParserConfigurationException; 38 39 import com.sun.org.apache.xml.internal.security.c14n.CanonicalizationException; 40 import com.sun.org.apache.xml.internal.security.c14n.CanonicalizerSpi; 41 import com.sun.org.apache.xml.internal.security.c14n.helper.AttrCompare; 42 import com.sun.org.apache.xml.internal.security.signature.NodeFilter; 43 import com.sun.org.apache.xml.internal.security.signature.XMLSignatureInput; 44 import com.sun.org.apache.xml.internal.security.utils.Constants; 45 import com.sun.org.apache.xml.internal.security.utils.UnsyncByteArrayOutputStream; 46 import com.sun.org.apache.xml.internal.security.utils.XMLUtils; 47 import org.w3c.dom.Attr; 48 import org.w3c.dom.Comment; 49 import org.w3c.dom.DOMException; 50 import org.w3c.dom.Document; 51 import org.w3c.dom.Element; 52 import org.w3c.dom.NamedNodeMap; 53 import org.w3c.dom.Node; 54 import org.w3c.dom.ProcessingInstruction; 55 import org.xml.sax.SAXException; 56 57 /** 58 * Abstract base class for canonicalization algorithms. 59 * Please note that these implementations are NOT thread safe - please see the following JIRA for more information: 60 * https://issues.apache.org/jira/browse/SANTUARIO-463 61 */ 62 public abstract class CanonicalizerBase extends CanonicalizerSpi { 63 public static final String XML = "xml"; 64 public static final String XMLNS = "xmlns"; 65 public static final String XMLNS_URI = Constants.NamespaceSpecNS; 66 public static final String XML_LANG_URI = Constants.XML_LANG_SPACE_SpecNS; 67 68 protected static final AttrCompare COMPARE = new AttrCompare(); 69 70 // Make sure you clone the following mutable arrays before passing to 71 // potentially untrusted objects such as OutputStreams. 72 private static final byte[] END_PI = {'?','>'}; 73 private static final byte[] BEGIN_PI = {'<','?'}; 74 private static final byte[] END_COMM = {'-','-','>'}; 75 private static final byte[] BEGIN_COMM = {'<','!','-','-'}; 76 private static final byte[] XA = {'&','#','x','A',';'}; 77 private static final byte[] X9 = {'&','#','x','9',';'}; 78 private static final byte[] QUOT = {'&','q','u','o','t',';'}; 79 private static final byte[] XD = {'&','#','x','D',';'}; 80 private static final byte[] GT = {'&','g','t',';'}; 81 private static final byte[] LT = {'&','l','t',';'}; 82 private static final byte[] END_TAG = {'<','/'}; 83 private static final byte[] AMP = {'&','a','m','p',';'}; 84 private static final byte[] EQUALS_STR = {'=','\"'}; 85 86 protected static final int NODE_BEFORE_DOCUMENT_ELEMENT = -1; 87 protected static final int NODE_NOT_BEFORE_OR_AFTER_DOCUMENT_ELEMENT = 0; 88 protected static final int NODE_AFTER_DOCUMENT_ELEMENT = 1; 89 90 private List<NodeFilter> nodeFilter; 91 92 private boolean includeComments; 93 private Set<Node> xpathNodeSet; 94 95 /** 96 * The node to be skipped/excluded from the DOM tree 97 * in subtree canonicalizations. 98 */ 99 private Node excludeNode; 100 private OutputStream writer = new ByteArrayOutputStream(); 101 102 /** 103 * The null xmlns definition. 104 */ 105 private Attr nullNode; 106 107 /** 108 * Constructor CanonicalizerBase 109 * 110 * @param includeComments 111 */ CanonicalizerBase(boolean includeComments)112 public CanonicalizerBase(boolean includeComments) { 113 this.includeComments = includeComments; 114 } 115 116 /** 117 * Method engineCanonicalizeSubTree 118 * {@inheritDoc} 119 * @param rootNode 120 * @throws CanonicalizationException 121 */ engineCanonicalizeSubTree(Node rootNode)122 public byte[] engineCanonicalizeSubTree(Node rootNode) 123 throws CanonicalizationException { 124 return engineCanonicalizeSubTree(rootNode, (Node)null); 125 } 126 127 /** 128 * Method engineCanonicalizeXPathNodeSet 129 * {@inheritDoc} 130 * @param xpathNodeSet 131 * @throws CanonicalizationException 132 */ engineCanonicalizeXPathNodeSet(Set<Node> xpathNodeSet)133 public byte[] engineCanonicalizeXPathNodeSet(Set<Node> xpathNodeSet) 134 throws CanonicalizationException { 135 this.xpathNodeSet = xpathNodeSet; 136 return engineCanonicalizeXPathNodeSetInternal(XMLUtils.getOwnerDocument(this.xpathNodeSet)); 137 } 138 139 /** 140 * Canonicalizes a Subtree node. 141 * @param input the root of the subtree to canicalize 142 * @return The canonicalize stream. 143 * @throws CanonicalizationException 144 */ engineCanonicalize(XMLSignatureInput input)145 public byte[] engineCanonicalize(XMLSignatureInput input) throws CanonicalizationException { 146 try { 147 if (input.isExcludeComments()) { 148 includeComments = false; 149 } 150 if (input.isOctetStream()) { 151 return engineCanonicalize(input.getBytes()); 152 } 153 if (input.isElement()) { 154 return engineCanonicalizeSubTree(input.getSubNode(), input.getExcludeNode()); 155 } else if (input.isNodeSet()) { 156 nodeFilter = input.getNodeFilters(); 157 158 circumventBugIfNeeded(input); 159 160 if (input.getSubNode() != null) { 161 return engineCanonicalizeXPathNodeSetInternal(input.getSubNode()); 162 } else { 163 return engineCanonicalizeXPathNodeSet(input.getNodeSet()); 164 } 165 } 166 return null; 167 } catch (ParserConfigurationException ex) { 168 throw new CanonicalizationException(ex); 169 } catch (IOException ex) { 170 throw new CanonicalizationException(ex); 171 } catch (SAXException ex) { 172 throw new CanonicalizationException(ex); 173 } 174 } 175 176 /** 177 * @param writer The writer to set. 178 */ setWriter(OutputStream writer)179 public void setWriter(OutputStream writer) { 180 this.writer = writer; 181 } 182 getWriter()183 protected OutputStream getWriter() { 184 return writer; 185 } 186 187 /** 188 * Canonicalizes a Subtree node. 189 * 190 * @param rootNode 191 * the root of the subtree to canonicalize 192 * @param excludeNode 193 * a node to be excluded from the canonicalize operation 194 * @return The canonicalize stream. 195 * @throws CanonicalizationException 196 */ engineCanonicalizeSubTree(Node rootNode, Node excludeNode)197 protected byte[] engineCanonicalizeSubTree(Node rootNode, Node excludeNode) 198 throws CanonicalizationException { 199 this.excludeNode = excludeNode; 200 try { 201 NameSpaceSymbTable ns = new NameSpaceSymbTable(); 202 int nodeLevel = NODE_BEFORE_DOCUMENT_ELEMENT; 203 if (rootNode != null && Node.ELEMENT_NODE == rootNode.getNodeType()) { 204 //Fills the nssymbtable with the definitions of the parent of the root subnode 205 getParentNameSpaces((Element)rootNode, ns); 206 nodeLevel = NODE_NOT_BEFORE_OR_AFTER_DOCUMENT_ELEMENT; 207 } 208 this.canonicalizeSubTree(rootNode, ns, rootNode, nodeLevel); 209 this.writer.flush(); 210 if (this.writer instanceof ByteArrayOutputStream) { 211 byte[] result = ((ByteArrayOutputStream)this.writer).toByteArray(); 212 if (reset) { 213 ((ByteArrayOutputStream)this.writer).reset(); 214 } else { 215 this.writer.close(); 216 } 217 return result; 218 } else if (this.writer instanceof UnsyncByteArrayOutputStream) { 219 byte[] result = ((UnsyncByteArrayOutputStream)this.writer).toByteArray(); 220 if (reset) { 221 ((UnsyncByteArrayOutputStream)this.writer).reset(); 222 } else { 223 this.writer.close(); 224 } 225 return result; 226 } else { 227 this.writer.close(); 228 } 229 return null; 230 231 } catch (UnsupportedEncodingException ex) { 232 throw new CanonicalizationException(ex); 233 } catch (IOException ex) { 234 throw new CanonicalizationException(ex); 235 } 236 } 237 238 239 /** 240 * Method canonicalizeSubTree, this function is a recursive one. 241 * 242 * @param currentNode 243 * @param ns 244 * @param endnode 245 * @throws CanonicalizationException 246 * @throws IOException 247 */ canonicalizeSubTree( Node currentNode, NameSpaceSymbTable ns, Node endnode, int documentLevel )248 protected final void canonicalizeSubTree( 249 Node currentNode, NameSpaceSymbTable ns, Node endnode, int documentLevel 250 ) throws CanonicalizationException, IOException { 251 if (currentNode == null || isVisibleInt(currentNode) == -1) { 252 return; 253 } 254 Node sibling = null; 255 Node parentNode = null; 256 final OutputStream writer = this.writer; 257 final Node excludeNode = this.excludeNode; 258 final boolean includeComments = this.includeComments; 259 Map<String, byte[]> cache = new HashMap<>(); 260 do { 261 switch (currentNode.getNodeType()) { 262 263 case Node.ENTITY_NODE : 264 case Node.NOTATION_NODE : 265 case Node.ATTRIBUTE_NODE : 266 // illegal node type during traversal 267 throw new CanonicalizationException("empty", 268 new Object[]{"illegal node type during traversal"}); 269 270 case Node.DOCUMENT_FRAGMENT_NODE : 271 case Node.DOCUMENT_NODE : 272 ns.outputNodePush(); 273 sibling = currentNode.getFirstChild(); 274 break; 275 276 case Node.COMMENT_NODE : 277 if (includeComments) { 278 outputCommentToWriter((Comment) currentNode, writer, documentLevel); 279 } 280 break; 281 282 case Node.PROCESSING_INSTRUCTION_NODE : 283 outputPItoWriter((ProcessingInstruction) currentNode, writer, documentLevel); 284 break; 285 286 case Node.TEXT_NODE : 287 case Node.CDATA_SECTION_NODE : 288 outputTextToWriter(currentNode.getNodeValue(), writer); 289 break; 290 291 case Node.ELEMENT_NODE : 292 documentLevel = NODE_NOT_BEFORE_OR_AFTER_DOCUMENT_ELEMENT; 293 if (currentNode == excludeNode) { 294 break; 295 } 296 Element currentElement = (Element)currentNode; 297 //Add a level to the nssymbtable. So latter can be pop-back. 298 ns.outputNodePush(); 299 writer.write('<'); 300 String name = currentElement.getTagName(); 301 UtfHelpper.writeByte(name, writer, cache); 302 303 outputAttributesSubtree(currentElement, ns, cache); 304 305 writer.write('>'); 306 sibling = currentNode.getFirstChild(); 307 if (sibling == null) { 308 writer.write(END_TAG.clone()); 309 UtfHelpper.writeStringToUtf8(name, writer); 310 writer.write('>'); 311 //We finished with this level, pop to the previous definitions. 312 ns.outputNodePop(); 313 if (parentNode != null) { 314 sibling = currentNode.getNextSibling(); 315 } 316 } else { 317 parentNode = currentElement; 318 } 319 break; 320 321 case Node.DOCUMENT_TYPE_NODE : 322 default : 323 break; 324 } 325 while (sibling == null && parentNode != null) { 326 writer.write(END_TAG.clone()); 327 UtfHelpper.writeByte(((Element)parentNode).getTagName(), writer, cache); 328 writer.write('>'); 329 //We finished with this level, pop to the previous definitions. 330 ns.outputNodePop(); 331 if (parentNode == endnode) { 332 return; 333 } 334 sibling = parentNode.getNextSibling(); 335 parentNode = parentNode.getParentNode(); 336 if (parentNode == null || Node.ELEMENT_NODE != parentNode.getNodeType()) { 337 documentLevel = NODE_AFTER_DOCUMENT_ELEMENT; 338 parentNode = null; 339 } 340 } 341 if (sibling == null) { 342 return; 343 } 344 currentNode = sibling; 345 sibling = currentNode.getNextSibling(); 346 } while(true); 347 } 348 349 engineCanonicalizeXPathNodeSetInternal(Node doc)350 private byte[] engineCanonicalizeXPathNodeSetInternal(Node doc) 351 throws CanonicalizationException { 352 try { 353 this.canonicalizeXPathNodeSet(doc, doc); 354 this.writer.flush(); 355 if (this.writer instanceof ByteArrayOutputStream) { 356 byte[] sol = ((ByteArrayOutputStream)this.writer).toByteArray(); 357 if (reset) { 358 ((ByteArrayOutputStream)this.writer).reset(); 359 } else { 360 this.writer.close(); 361 } 362 return sol; 363 } else if (this.writer instanceof UnsyncByteArrayOutputStream) { 364 byte[] result = ((UnsyncByteArrayOutputStream)this.writer).toByteArray(); 365 if (reset) { 366 ((UnsyncByteArrayOutputStream)this.writer).reset(); 367 } else { 368 this.writer.close(); 369 } 370 return result; 371 } else { 372 this.writer.close(); 373 } 374 return null; 375 } catch (UnsupportedEncodingException ex) { 376 throw new CanonicalizationException(ex); 377 } catch (IOException ex) { 378 throw new CanonicalizationException(ex); 379 } 380 } 381 382 /** 383 * Canonicalizes all the nodes included in the currentNode and contained in the 384 * xpathNodeSet field. 385 * 386 * @param currentNode 387 * @param endnode 388 * @throws CanonicalizationException 389 * @throws IOException 390 */ canonicalizeXPathNodeSet(Node currentNode, Node endnode)391 protected final void canonicalizeXPathNodeSet(Node currentNode, Node endnode) 392 throws CanonicalizationException, IOException { 393 if (isVisibleInt(currentNode) == -1) { 394 return; 395 } 396 boolean currentNodeIsVisible = false; 397 NameSpaceSymbTable ns = new NameSpaceSymbTable(); 398 if (currentNode != null && Node.ELEMENT_NODE == currentNode.getNodeType()) { 399 getParentNameSpaces((Element)currentNode, ns); 400 } 401 if (currentNode == null) { 402 return; 403 } 404 Node sibling = null; 405 Node parentNode = null; 406 int documentLevel = NODE_BEFORE_DOCUMENT_ELEMENT; 407 Map<String, byte[]> cache = new HashMap<>(); 408 do { 409 switch (currentNode.getNodeType()) { 410 411 case Node.ENTITY_NODE : 412 case Node.NOTATION_NODE : 413 case Node.ATTRIBUTE_NODE : 414 // illegal node type during traversal 415 throw new CanonicalizationException("empty", 416 new Object[]{"illegal node type during traversal"}); 417 418 case Node.DOCUMENT_FRAGMENT_NODE : 419 case Node.DOCUMENT_NODE : 420 ns.outputNodePush(); 421 sibling = currentNode.getFirstChild(); 422 break; 423 424 case Node.COMMENT_NODE : 425 if (this.includeComments && isVisibleDO(currentNode, ns.getLevel()) == 1) { 426 outputCommentToWriter((Comment) currentNode, writer, documentLevel); 427 } 428 break; 429 430 case Node.PROCESSING_INSTRUCTION_NODE : 431 if (isVisible(currentNode)) { 432 outputPItoWriter((ProcessingInstruction) currentNode, writer, documentLevel); 433 } 434 break; 435 436 case Node.TEXT_NODE : 437 case Node.CDATA_SECTION_NODE : 438 if (isVisible(currentNode)) { 439 outputTextToWriter(currentNode.getNodeValue(), writer); 440 for (Node nextSibling = currentNode.getNextSibling(); 441 nextSibling != null && (nextSibling.getNodeType() == Node.TEXT_NODE 442 || nextSibling.getNodeType() == Node.CDATA_SECTION_NODE); 443 nextSibling = nextSibling.getNextSibling()) { 444 outputTextToWriter(nextSibling.getNodeValue(), writer); 445 currentNode = nextSibling; 446 sibling = currentNode.getNextSibling(); 447 } 448 } 449 break; 450 451 case Node.ELEMENT_NODE : 452 documentLevel = NODE_NOT_BEFORE_OR_AFTER_DOCUMENT_ELEMENT; 453 Element currentElement = (Element) currentNode; 454 //Add a level to the nssymbtable. So latter can be pop-back. 455 String name = null; 456 int i = isVisibleDO(currentNode, ns.getLevel()); 457 if (i == -1) { 458 sibling = currentNode.getNextSibling(); 459 break; 460 } 461 currentNodeIsVisible = i == 1; 462 if (currentNodeIsVisible) { 463 ns.outputNodePush(); 464 writer.write('<'); 465 name = currentElement.getTagName(); 466 UtfHelpper.writeByte(name, writer, cache); 467 } else { 468 ns.push(); 469 } 470 471 outputAttributes(currentElement, ns, cache); 472 473 if (currentNodeIsVisible) { 474 writer.write('>'); 475 } 476 sibling = currentNode.getFirstChild(); 477 478 if (sibling == null) { 479 if (currentNodeIsVisible) { 480 writer.write(END_TAG.clone()); 481 UtfHelpper.writeByte(name, writer, cache); 482 writer.write('>'); 483 //We finished with this level, pop to the previous definitions. 484 ns.outputNodePop(); 485 } else { 486 ns.pop(); 487 } 488 if (parentNode != null) { 489 sibling = currentNode.getNextSibling(); 490 } 491 } else { 492 parentNode = currentElement; 493 } 494 break; 495 496 case Node.DOCUMENT_TYPE_NODE : 497 default : 498 break; 499 } 500 while (sibling == null && parentNode != null) { 501 if (isVisible(parentNode)) { 502 writer.write(END_TAG.clone()); 503 UtfHelpper.writeByte(((Element)parentNode).getTagName(), writer, cache); 504 writer.write('>'); 505 //We finished with this level, pop to the previous definitions. 506 ns.outputNodePop(); 507 } else { 508 ns.pop(); 509 } 510 if (parentNode == endnode) { 511 return; 512 } 513 sibling = parentNode.getNextSibling(); 514 parentNode = parentNode.getParentNode(); 515 if (parentNode == null || Node.ELEMENT_NODE != parentNode.getNodeType()) { 516 parentNode = null; 517 documentLevel = NODE_AFTER_DOCUMENT_ELEMENT; 518 } 519 } 520 if (sibling == null) { 521 return; 522 } 523 currentNode = sibling; 524 sibling = currentNode.getNextSibling(); 525 } while(true); 526 } 527 isVisibleDO(Node currentNode, int level)528 protected int isVisibleDO(Node currentNode, int level) { 529 if (nodeFilter != null) { 530 Iterator<NodeFilter> it = nodeFilter.iterator(); 531 while (it.hasNext()) { 532 int i = it.next().isNodeIncludeDO(currentNode, level); 533 if (i != 1) { 534 return i; 535 } 536 } 537 } 538 if (this.xpathNodeSet != null && !this.xpathNodeSet.contains(currentNode)) { 539 return 0; 540 } 541 return 1; 542 } 543 isVisibleInt(Node currentNode)544 protected int isVisibleInt(Node currentNode) { 545 if (nodeFilter != null) { 546 Iterator<NodeFilter> it = nodeFilter.iterator(); 547 while (it.hasNext()) { 548 int i = it.next().isNodeInclude(currentNode); 549 if (i != 1) { 550 return i; 551 } 552 } 553 } 554 if (this.xpathNodeSet != null && !this.xpathNodeSet.contains(currentNode)) { 555 return 0; 556 } 557 return 1; 558 } 559 isVisible(Node currentNode)560 protected boolean isVisible(Node currentNode) { 561 if (nodeFilter != null) { 562 Iterator<NodeFilter> it = nodeFilter.iterator(); 563 while (it.hasNext()) { 564 if (it.next().isNodeInclude(currentNode) != 1) { 565 return false; 566 } 567 } 568 } 569 if (this.xpathNodeSet != null && !this.xpathNodeSet.contains(currentNode)) { 570 return false; 571 } 572 return true; 573 } 574 handleParent(Element e, NameSpaceSymbTable ns)575 protected void handleParent(Element e, NameSpaceSymbTable ns) { 576 if (!e.hasAttributes() && e.getNamespaceURI() == null) { 577 return; 578 } 579 NamedNodeMap attrs = e.getAttributes(); 580 int attrsLength = attrs.getLength(); 581 for (int i = 0; i < attrsLength; i++) { 582 Attr attribute = (Attr) attrs.item(i); 583 String NName = attribute.getLocalName(); 584 String NValue = attribute.getNodeValue(); 585 586 if (Constants.NamespaceSpecNS.equals(attribute.getNamespaceURI()) 587 && (!XML.equals(NName) || !Constants.XML_LANG_SPACE_SpecNS.equals(NValue))) { 588 ns.addMapping(NName, NValue, attribute); 589 } 590 } 591 if (e.getNamespaceURI() != null) { 592 String NName = e.getPrefix(); 593 String NValue = e.getNamespaceURI(); 594 String Name; 595 if (NName == null || NName.equals("")) { 596 NName = XMLNS; 597 Name = XMLNS; 598 } else { 599 Name = XMLNS + ":" + NName; 600 } 601 Attr n = e.getOwnerDocument().createAttributeNS("http://www.w3.org/2000/xmlns/", Name); 602 n.setValue(NValue); 603 ns.addMapping(NName, NValue, n); 604 } 605 } 606 607 /** 608 * Adds to ns the definitions from the parent elements of el 609 * @param el 610 * @param ns 611 */ getParentNameSpaces(Element el, NameSpaceSymbTable ns)612 protected final void getParentNameSpaces(Element el, NameSpaceSymbTable ns) { 613 Node n1 = el.getParentNode(); 614 if (n1 == null || Node.ELEMENT_NODE != n1.getNodeType()) { 615 return; 616 } 617 //Obtain all the parents of the element 618 List<Element> parents = new ArrayList<>(); 619 Node parent = n1; 620 while (parent != null && Node.ELEMENT_NODE == parent.getNodeType()) { 621 parents.add((Element)parent); 622 parent = parent.getParentNode(); 623 } 624 //Visit them in reverse order. 625 ListIterator<Element> it = parents.listIterator(parents.size()); 626 while (it.hasPrevious()) { 627 Element ele = it.previous(); 628 handleParent(ele, ns); 629 } 630 parents.clear(); 631 Attr nsprefix = ns.getMappingWithoutRendered(XMLNS); 632 if (nsprefix != null && "".equals(nsprefix.getValue())) { 633 ns.addMappingAndRender( 634 XMLNS, "", getNullNode(nsprefix.getOwnerDocument())); 635 } 636 } 637 638 /** 639 * Output the attributes for this node in XPathNodeSet c14n. 640 * 641 * @param element 642 * @param ns 643 * @param cache 644 * @throws CanonicalizationException, DOMException, IOException 645 */ outputAttributes(Element element, NameSpaceSymbTable ns, Map<String, byte[]> cache)646 abstract void outputAttributes(Element element, NameSpaceSymbTable ns, Map<String, byte[]> cache) 647 throws CanonicalizationException, DOMException, IOException; 648 649 /** 650 * Output the attributes for this node in a Subtree c14n. 651 * 652 * @param element 653 * @param ns 654 * @param cache 655 * @throws CanonicalizationException, DOMException, IOException 656 */ outputAttributesSubtree(Element element, NameSpaceSymbTable ns, Map<String, byte[]> cache)657 abstract void outputAttributesSubtree(Element element, NameSpaceSymbTable ns, Map<String, byte[]> cache) 658 throws CanonicalizationException, DOMException, IOException; 659 circumventBugIfNeeded(XMLSignatureInput input)660 abstract void circumventBugIfNeeded(XMLSignatureInput input) 661 throws CanonicalizationException, ParserConfigurationException, IOException, SAXException; 662 663 /** 664 * Outputs an Attribute to the internal Writer. 665 * 666 * The string value of the node is modified by replacing 667 * <UL> 668 * <LI>all ampersands with {@code &amp;}</LI> 669 * <LI>all open angle brackets with {@code &lt;}</LI> 670 * <LI>all quotation mark characters with {@code &quot;}</LI> 671 * <LI>and the whitespace characters {@code #x9}, #xA, and #xD, with character 672 * references. The character references are written in uppercase 673 * hexadecimal with no leading zeroes (for example, {@code #xD} is represented 674 * by the character reference {@code &#xD;})</LI> 675 * </UL> 676 * 677 * @param name 678 * @param value 679 * @param writer 680 * @throws IOException 681 */ outputAttrToWriter( final String name, final String value, final OutputStream writer, final Map<String, byte[]> cache )682 protected static final void outputAttrToWriter( 683 final String name, final String value, 684 final OutputStream writer, final Map<String, byte[]> cache 685 ) throws IOException { 686 writer.write(' '); 687 UtfHelpper.writeByte(name, writer, cache); 688 writer.write(EQUALS_STR.clone()); 689 byte[] toWrite; 690 final int length = value.length(); 691 int i = 0; 692 while (i < length) { 693 int c = value.codePointAt(i); 694 i += Character.charCount(c); 695 696 switch (c) { 697 698 case '&' : 699 toWrite = AMP.clone(); 700 break; 701 702 case '<' : 703 toWrite = LT.clone(); 704 break; 705 706 case '"' : 707 toWrite = QUOT.clone(); 708 break; 709 710 case 0x09 : // '\t' 711 toWrite = X9.clone(); 712 break; 713 714 case 0x0A : // '\n' 715 toWrite = XA.clone(); 716 break; 717 718 case 0x0D : // '\r' 719 toWrite = XD.clone(); 720 break; 721 722 default : 723 if (c < 0x80) { 724 writer.write(c); 725 } else { 726 UtfHelpper.writeCodePointToUtf8(c, writer); 727 } 728 continue; 729 } 730 writer.write(toWrite); 731 } 732 733 writer.write('\"'); 734 } 735 736 /** 737 * Outputs a PI to the internal Writer. 738 * 739 * @param currentPI 740 * @param writer where to write the things 741 * @throws IOException 742 */ outputPItoWriter( ProcessingInstruction currentPI, OutputStream writer, int position )743 protected void outputPItoWriter( 744 ProcessingInstruction currentPI, OutputStream writer, int position 745 ) throws IOException { 746 if (position == NODE_AFTER_DOCUMENT_ELEMENT) { 747 writer.write('\n'); 748 } 749 writer.write(BEGIN_PI.clone()); 750 751 final String target = currentPI.getTarget(); 752 int length = target.length(); 753 754 for (int i = 0; i < length; ) { 755 int c = target.codePointAt(i); 756 i += Character.charCount(c); 757 if (c == 0x0D) { 758 writer.write(XD.clone()); 759 } else { 760 if (c < 0x80) { 761 writer.write(c); 762 } else { 763 UtfHelpper.writeCodePointToUtf8(c, writer); 764 } 765 } 766 } 767 768 final String data = currentPI.getData(); 769 770 length = data.length(); 771 772 if (length > 0) { 773 writer.write(' '); 774 775 for (int i = 0; i < length; ) { 776 int c = data.codePointAt(i); 777 i += Character.charCount(c); 778 if (c == 0x0D) { 779 writer.write(XD.clone()); 780 } else { 781 UtfHelpper.writeCodePointToUtf8(c, writer); 782 } 783 } 784 } 785 786 writer.write(END_PI.clone()); 787 if (position == NODE_BEFORE_DOCUMENT_ELEMENT) { 788 writer.write('\n'); 789 } 790 } 791 792 /** 793 * Method outputCommentToWriter 794 * 795 * @param currentComment 796 * @param writer writer where to write the things 797 * @throws IOException 798 */ outputCommentToWriter( Comment currentComment, OutputStream writer, int position )799 protected void outputCommentToWriter( 800 Comment currentComment, OutputStream writer, int position 801 ) throws IOException { 802 if (position == NODE_AFTER_DOCUMENT_ELEMENT) { 803 writer.write('\n'); 804 } 805 writer.write(BEGIN_COMM.clone()); 806 807 final String data = currentComment.getData(); 808 final int length = data.length(); 809 810 for (int i = 0; i < length; ) { 811 int c = data.codePointAt(i); 812 i += Character.charCount(c); 813 if (c == 0x0D) { 814 writer.write(XD.clone()); 815 } else { 816 if (c < 0x80) { 817 writer.write(c); 818 } else { 819 UtfHelpper.writeCodePointToUtf8(c, writer); 820 } 821 } 822 } 823 824 writer.write(END_COMM.clone()); 825 if (position == NODE_BEFORE_DOCUMENT_ELEMENT) { 826 writer.write('\n'); 827 } 828 } 829 830 /** 831 * Outputs a Text of CDATA section to the internal Writer. 832 * 833 * @param text 834 * @param writer writer where to write the things 835 * @throws IOException 836 */ outputTextToWriter( final String text, final OutputStream writer )837 protected static final void outputTextToWriter( 838 final String text, final OutputStream writer 839 ) throws IOException { 840 final int length = text.length(); 841 byte[] toWrite; 842 for (int i = 0; i < length; ) { 843 int c = text.codePointAt(i); 844 i += Character.charCount(c); 845 846 switch (c) { 847 848 case '&' : 849 toWrite = AMP.clone(); 850 break; 851 852 case '<' : 853 toWrite = LT.clone(); 854 break; 855 856 case '>' : 857 toWrite = GT.clone(); 858 break; 859 860 case 0xD : 861 toWrite = XD.clone(); 862 break; 863 864 default : 865 if (c < 0x80) { 866 writer.write(c); 867 } else { 868 UtfHelpper.writeCodePointToUtf8(c, writer); 869 } 870 continue; 871 } 872 writer.write(toWrite); 873 } 874 } 875 876 // The null xmlns definition. getNullNode(Document ownerDocument)877 protected Attr getNullNode(Document ownerDocument) { 878 if (nullNode == null) { 879 try { 880 nullNode = ownerDocument.createAttributeNS( 881 Constants.NamespaceSpecNS, XMLNS); 882 nullNode.setValue(""); 883 } catch (Exception e) { 884 throw new RuntimeException("Unable to create nullNode: " + e); 885 } 886 } 887 return nullNode; 888 } 889 890 } 891