src/share/classes/com/sun/org/apache/xml/internal/security/signature/XMLSignatureInputDebugger.java

Print this page


   1 /*
   2  * reserved comment block
   3  * DO NOT REMOVE OR ALTER!
   4  */
   5 /*
   6  * Copyright  1999-2004 The Apache Software Foundation.
   7  *
   8  *  Licensed under the Apache License, Version 2.0 (the "License");
   9  *  you may not use this file except in compliance with the License.
  10  *  You may obtain a copy of the License at


  11  *
  12  *      http://www.apache.org/licenses/LICENSE-2.0
  13  *
  14  *  Unless required by applicable law or agreed to in writing, software
  15  *  distributed under the License is distributed on an "AS IS" BASIS,
  16  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  17  *  See the License for the specific language governing permissions and
  18  *  limitations under the License.
  19  *
  20  */
  21 package com.sun.org.apache.xml.internal.security.signature;
  22 
  23 import java.io.IOException;
  24 import java.io.StringWriter;
  25 import java.io.Writer;
  26 import java.util.Arrays;
  27 import java.util.Set;
  28 
  29 import com.sun.org.apache.xml.internal.security.c14n.helper.AttrCompare;
  30 import com.sun.org.apache.xml.internal.security.utils.XMLUtils;
  31 import org.w3c.dom.Attr;
  32 import org.w3c.dom.Comment;
  33 import org.w3c.dom.Document;
  34 import org.w3c.dom.Element;
  35 import org.w3c.dom.NamedNodeMap;
  36 import org.w3c.dom.Node;
  37 import org.w3c.dom.ProcessingInstruction;
  38 
  39 /**
  40  * Class XMLSignatureInputDebugger
  41  *
  42  * @author $Author: mullan $
  43  * @version $Revision: 1.3 $
  44  */
  45 public class XMLSignatureInputDebugger {
  46 
  47 
  48 
  49         /** Field _xmlSignatureInput */
  50         private Set<Node> _xpathNodeSet;


  51 
  52         private Set<String> _inclusiveNamespaces;

  53 
  54         /** Field _doc */
  55         private Document _doc = null;
  56 
  57         /** Field _writer */
  58         private Writer _writer = null;
  59 
  60         // J-
  61         // public static final String HTMLPrefix = "<!DOCTYPE HTML PUBLIC
  62         // \"-//W3C//DTD HTML 4.01 Transitional//EN\"><html><head><style
  63         // type=\"text/css\"><!-- .INCLUDED { color: #000000; background-color:
  64         // #FFFFFF; font-weight: bold; } .EXCLUDED { color: #666666;
  65         // background-color: #999999; } .INCLUDEDINCLUSIVENAMESPACE { color:
  66         // #0000FF; background-color: #FFFFFF; font-weight: bold; font-style:
  67         // italic; } .EXCLUDEDINCLUSIVENAMESPACE { color: #0000FF; background-color:
  68         // #999999; font-style: italic; } --> </style> </head><body
  69         // bgcolor=\"#999999\"><pre>";
  70         /** The HTML Prefix* */
  71         static final String HTMLPrefix = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n"

  72                         + "<html>\n"
  73                         + "<head>\n"
  74                         + "<title>Caninical XML node set</title>\n"
  75                         + "<style type=\"text/css\">\n"
  76                         + "<!-- \n"
  77                         + ".INCLUDED { \n"
  78                         + "   color: #000000; \n"
  79                         + "   background-color: \n"
  80                         + "   #FFFFFF; \n"
  81                         + "   font-weight: bold; } \n"
  82                         + ".EXCLUDED { \n"
  83                         + "   color: #666666; \n"
  84                         + "   background-color: \n"
  85                         + "   #999999; } \n"
  86                         + ".INCLUDEDINCLUSIVENAMESPACE { \n"
  87                         + "   color: #0000FF; \n"
  88                         + "   background-color: #FFFFFF; \n"
  89                         + "   font-weight: bold; \n"
  90                         + "   font-style: italic; } \n"
  91                         + ".EXCLUDEDINCLUSIVENAMESPACE { \n"
  92                         + "   color: #0000FF; \n"
  93                         + "   background-color: #999999; \n"
  94                         + "   font-style: italic; } \n"
  95                         + "--> \n"
  96                         + "</style> \n"
  97                         + "</head>\n"
  98                         + "<body bgcolor=\"#999999\">\n"
  99                         + "<h1>Explanation of the output</h1>\n"
 100                         + "<p>The following text contains the nodeset of the given Reference before it is canonicalized. There exist four different styles to indicate how a given node is treated.</p>\n"
 101                         + "<ul>\n"
 102                         + "<li class=\"INCLUDED\">A node which is in the node set is labeled using the INCLUDED style.</li>\n"
 103                         + "<li class=\"EXCLUDED\">A node which is <em>NOT</em> in the node set is labeled EXCLUDED style.</li>\n"
 104                         + "<li class=\"INCLUDEDINCLUSIVENAMESPACE\">A namespace which is in the node set AND in the InclusiveNamespaces PrefixList is labeled using the INCLUDEDINCLUSIVENAMESPACE style.</li>\n"
 105                         + "<li class=\"EXCLUDEDINCLUSIVENAMESPACE\">A namespace which is in NOT the node set AND in the InclusiveNamespaces PrefixList is labeled using the INCLUDEDINCLUSIVENAMESPACE style.</li>\n"
 106                         + "</ul>\n" + "<h1>Output</h1>\n" + "<pre>\n";
 107 
 108         /** HTML Suffix * */
 109         static final String HTMLSuffix = "</pre></body></html>";
 110 
 111         static final String HTMLExcludePrefix = "<span class=\"EXCLUDED\">";
 112 
 113         static final String HTMLExcludeSuffix = "</span>";
 114 
 115         static final String HTMLIncludePrefix = "<span class=\"INCLUDED\">";
 116 
 117         static final String HTMLIncludeSuffix = "</span>";
 118 
 119         static final String HTMLIncludedInclusiveNamespacePrefix = "<span class=\"INCLUDEDINCLUSIVENAMESPACE\">";
 120 
 121         static final String HTMLIncludedInclusiveNamespaceSuffix = "</span>";
 122 
 123         static final String HTMLExcludedInclusiveNamespacePrefix = "<span class=\"EXCLUDEDINCLUSIVENAMESPACE\">";
 124 
 125         static final String HTMLExcludedInclusiveNamespaceSuffix = "</span>";
 126 
 127         private static final int NODE_BEFORE_DOCUMENT_ELEMENT = -1;
 128 
 129         private static final int NODE_NOT_BEFORE_OR_AFTER_DOCUMENT_ELEMENT = 0;
 130 
 131         private static final int NODE_AFTER_DOCUMENT_ELEMENT = 1;
 132 
 133         static final AttrCompare ATTR_COMPARE = new AttrCompare();
 134 
 135         // J+
 136         private XMLSignatureInputDebugger() {
 137                 // do nothing
 138         }
 139 
 140         /**
 141          * Constructor XMLSignatureInputDebugger
 142          *
 143          * @param xmlSignatureInput the signatur to pretty print
 144          */
 145         public XMLSignatureInputDebugger(
 146                         XMLSignatureInput xmlSignatureInput) {
 147 
 148                 if (!xmlSignatureInput.isNodeSet()) {
 149                         this._xpathNodeSet = null;
 150                 } else {
 151                         this._xpathNodeSet = xmlSignatureInput._inputNodeSet;
 152                 }
 153         }
 154 
 155         /**
 156          * Constructor XMLSignatureInputDebugger
 157          *
 158          * @param xmlSignatureInput the signatur to pretty print
 159          * @param inclusiveNamespace
 160          */
 161         public XMLSignatureInputDebugger(
 162                         XMLSignatureInput xmlSignatureInput, Set<String> inclusiveNamespace) {
 163 

 164                 this(xmlSignatureInput);
 165 
 166                 this._inclusiveNamespaces = inclusiveNamespace;
 167         }
 168 
 169         /**
 170          * Method getHTMLRepresentation
 171          *
 172          * @return The HTML Representation.
 173          * @throws XMLSignatureException
 174          */
 175         public String getHTMLRepresentation() throws XMLSignatureException {
 176 
 177                 if ((this._xpathNodeSet == null) || (this._xpathNodeSet.size() == 0)) {
 178                         return HTMLPrefix + "<blink>no node set, sorry</blink>"
 179                                         + HTMLSuffix;
 180                 }
 181 
 182                 {
 183 
 184                         // get only a single node as anchor to fetch the owner document
 185                         Node n = this._xpathNodeSet.iterator().next();
 186 
 187                         this._doc = XMLUtils.getOwnerDocument(n);
 188                 }
 189 
 190                 try {
 191                         this._writer = new StringWriter();
 192 
 193                         this.canonicalizeXPathNodeSet(this._doc);
 194                         this._writer.close();
 195 
 196                         return this._writer.toString();
 197                 } catch (IOException ex) {
 198                         throw new XMLSignatureException("empty", ex);
 199                 } finally {
 200                         this._xpathNodeSet = null;
 201                         this._doc = null;
 202                         this._writer = null;
 203                 }
 204         }
 205 
 206         /**
 207          * Method canonicalizeXPathNodeSet
 208          *
 209          * @param currentNode
 210          * @throws XMLSignatureException
 211          * @throws IOException
 212          */
 213         private void canonicalizeXPathNodeSet(Node currentNode)
 214                         throws XMLSignatureException, IOException {
 215 
 216                 int currentNodeType = currentNode.getNodeType();
 217                 switch (currentNodeType) {
 218 
 219                 case Node.DOCUMENT_TYPE_NODE:
 220                 default:
 221                         break;
 222 
 223                 case Node.ENTITY_NODE:
 224                 case Node.NOTATION_NODE:
 225                 case Node.DOCUMENT_FRAGMENT_NODE:
 226                 case Node.ATTRIBUTE_NODE:
 227                         throw new XMLSignatureException("empty");
 228                 case Node.DOCUMENT_NODE:
 229                         this._writer.write(HTMLPrefix);
 230 
 231                         for (Node currentChild = currentNode.getFirstChild(); currentChild != null; currentChild = currentChild
 232                                         .getNextSibling()) {
 233                                 this.canonicalizeXPathNodeSet(currentChild);
 234                         }
 235 
 236                         this._writer.write(HTMLSuffix);
 237                         break;
 238 
 239                 case Node.COMMENT_NODE:
 240                         if (this._xpathNodeSet.contains(currentNode)) {
 241                                 this._writer.write(HTMLIncludePrefix);
 242                         } else {
 243                                 this._writer.write(HTMLExcludePrefix);
 244                         }
 245 
 246                         int position = getPositionRelativeToDocumentElement(currentNode);
 247 
 248                         if (position == NODE_AFTER_DOCUMENT_ELEMENT) {
 249                                 this._writer.write("\n");
 250                         }
 251 
 252                         this.outputCommentToWriter((Comment) currentNode);
 253 
 254                         if (position == NODE_BEFORE_DOCUMENT_ELEMENT) {
 255                                 this._writer.write("\n");
 256                         }
 257 
 258                         if (this._xpathNodeSet.contains(currentNode)) {
 259                                 this._writer.write(HTMLIncludeSuffix);
 260                         } else {
 261                                 this._writer.write(HTMLExcludeSuffix);
 262                         }
 263                         break;
 264 
 265                 case Node.PROCESSING_INSTRUCTION_NODE:
 266                         if (this._xpathNodeSet.contains(currentNode)) {
 267                                 this._writer.write(HTMLIncludePrefix);
 268                         } else {
 269                                 this._writer.write(HTMLExcludePrefix);
 270                         }
 271 
 272                         position = getPositionRelativeToDocumentElement(currentNode);
 273 
 274                         if (position == NODE_AFTER_DOCUMENT_ELEMENT) {
 275                                 this._writer.write("\n");
 276                         }
 277 
 278                         this.outputPItoWriter((ProcessingInstruction) currentNode);
 279 
 280                         if (position == NODE_BEFORE_DOCUMENT_ELEMENT) {
 281                                 this._writer.write("\n");
 282                         }
 283 
 284                         if (this._xpathNodeSet.contains(currentNode)) {
 285                                 this._writer.write(HTMLIncludeSuffix);
 286                         } else {
 287                                 this._writer.write(HTMLExcludeSuffix);
 288                         }
 289                         break;
 290 
 291                 case Node.TEXT_NODE:
 292                 case Node.CDATA_SECTION_NODE:
 293                         if (this._xpathNodeSet.contains(currentNode)) {
 294                                 this._writer.write(HTMLIncludePrefix);
 295                         } else {
 296                                 this._writer.write(HTMLExcludePrefix);
 297                         }
 298 
 299                         outputTextToWriter(currentNode.getNodeValue());
 300 
 301                         for (Node nextSibling = currentNode.getNextSibling(); (nextSibling != null)
 302                                         && ((nextSibling.getNodeType() == Node.TEXT_NODE) || (nextSibling
 303                                                         .getNodeType() == Node.CDATA_SECTION_NODE)); nextSibling = nextSibling
 304                                         .getNextSibling()) {
 305 
 306                                 /*
 307                                  * The XPath data model allows to select only the first of a
 308                                  * sequence of mixed text and CDATA nodes. But we must output
 309                                  * them all, so we must search:
 310                                  *
 311                                  * @see http://nagoya.apache.org/bugzilla/show_bug.cgi?id=6329
 312                                  */
 313                                 this.outputTextToWriter(nextSibling.getNodeValue());
 314                         }
 315 
 316                         if (this._xpathNodeSet.contains(currentNode)) {
 317                                 this._writer.write(HTMLIncludeSuffix);
 318                         } else {
 319                                 this._writer.write(HTMLExcludeSuffix);
 320                         }
 321                         break;
 322 
 323                 case Node.ELEMENT_NODE:
 324                         Element currentElement = (Element) currentNode;
 325 
 326                         if (this._xpathNodeSet.contains(currentNode)) {
 327                                 this._writer.write(HTMLIncludePrefix);
 328                         } else {
 329                                 this._writer.write(HTMLExcludePrefix);
 330                         }
 331 
 332                         this._writer.write("&lt;");
 333                         this._writer.write(currentElement.getTagName());
 334 
 335                         if (this._xpathNodeSet.contains(currentNode)) {
 336                                 this._writer.write(HTMLIncludeSuffix);
 337                         } else {
 338                                 this._writer.write(HTMLExcludeSuffix);
 339                         }
 340 
 341                         // we output all Attrs which are available
 342                         NamedNodeMap attrs = currentElement.getAttributes();
 343                         int attrsLength = attrs.getLength();
 344                         Attr attrs2[] = new Attr[attrsLength];
 345 
 346                         for (int i = 0; i < attrsLength; i++) {
 347                                 attrs2[i] = (Attr)attrs.item(i);
 348                         }
 349 
 350                         Arrays.sort(attrs2, ATTR_COMPARE);
 351                         Object attrs3[] = attrs2;
 352 
 353                         for (int i = 0; i < attrsLength; i++) {
 354                                 Attr a = (Attr) attrs3[i];
 355                                 boolean included = this._xpathNodeSet.contains(a);
 356                                 boolean inclusive = this._inclusiveNamespaces.contains(a
 357                                                 .getName());
 358 
 359                                 if (included) {
 360                                         if (inclusive) {
 361 
 362                                                 // included and inclusive
 363                                                 this._writer
 364                                                                 .write(HTMLIncludedInclusiveNamespacePrefix);
 365                                         } else {
 366 
 367                                                 // included and not inclusive
 368                                                 this._writer.write(HTMLIncludePrefix);
 369                                         }
 370                                 } else {
 371                                         if (inclusive) {
 372 
 373                                                 // excluded and inclusive
 374                                                 this._writer
 375                                                                 .write(HTMLExcludedInclusiveNamespacePrefix);
 376                                         } else {
 377 
 378                                                 // excluded and not inclusive
 379                                                 this._writer.write(HTMLExcludePrefix);
 380                                         }
 381                                 }
 382 
 383                                 this.outputAttrToWriter(a.getNodeName(), a.getNodeValue());
 384 
 385                                 if (included) {
 386                                         if (inclusive) {
 387 
 388                                                 // included and inclusive
 389                                                 this._writer
 390                                                                 .write(HTMLIncludedInclusiveNamespaceSuffix);
 391                                         } else {
 392 
 393                                                 // included and not inclusive
 394                                                 this._writer.write(HTMLIncludeSuffix);
 395                                         }
 396                                 } else {
 397                                         if (inclusive) {
 398 
 399                                                 // excluded and inclusive
 400                                                 this._writer
 401                                                                 .write(HTMLExcludedInclusiveNamespaceSuffix);
 402                                         } else {
 403 
 404                                                 // excluded and not inclusive
 405                                                 this._writer.write(HTMLExcludeSuffix);
 406                                         }
 407                                 }
 408                         }
 409 
 410                         if (this._xpathNodeSet.contains(currentNode)) {
 411                                 this._writer.write(HTMLIncludePrefix);
 412                         } else {
 413                                 this._writer.write(HTMLExcludePrefix);
 414                         }
 415 
 416                         this._writer.write("&gt;");
 417 
 418                         if (this._xpathNodeSet.contains(currentNode)) {
 419                                 this._writer.write(HTMLIncludeSuffix);
 420                         } else {
 421                                 this._writer.write(HTMLExcludeSuffix);
 422                         }
 423 
 424                         // traversal
 425                         for (Node currentChild = currentNode.getFirstChild(); currentChild != null; currentChild = currentChild
 426                                         .getNextSibling()) {

 427                                 this.canonicalizeXPathNodeSet(currentChild);
 428                         }
 429 
 430                         if (this._xpathNodeSet.contains(currentNode)) {
 431                                 this._writer.write(HTMLIncludePrefix);
 432                         } else {
 433                                 this._writer.write(HTMLExcludePrefix);
 434                         }
 435 
 436                         this._writer.write("&lt;/");
 437                         this._writer.write(currentElement.getTagName());
 438                         this._writer.write("&gt;");
 439 
 440                         if (this._xpathNodeSet.contains(currentNode)) {
 441                                 this._writer.write(HTMLIncludeSuffix);
 442                         } else {
 443                                 this._writer.write(HTMLExcludeSuffix);
 444                         }
 445                         break;
 446                 }
 447         }
 448 
 449         /**
 450          * Checks whether a Comment or ProcessingInstruction is before or after the
 451          * document element. This is needed for prepending or appending "\n"s.
 452          *
 453          * @param currentNode
 454          *            comment or pi to check
 455          * @return NODE_BEFORE_DOCUMENT_ELEMENT,
 456          *         NODE_NOT_BEFORE_OR_AFTER_DOCUMENT_ELEMENT or
 457          *         NODE_AFTER_DOCUMENT_ELEMENT
 458          * @see #NODE_BEFORE_DOCUMENT_ELEMENT
 459          * @see #NODE_NOT_BEFORE_OR_AFTER_DOCUMENT_ELEMENT
 460          * @see #NODE_AFTER_DOCUMENT_ELEMENT
 461          */
 462         private int getPositionRelativeToDocumentElement(Node currentNode) {
 463 
 464                 if (currentNode == null) {
 465                         return NODE_NOT_BEFORE_OR_AFTER_DOCUMENT_ELEMENT;
 466                 }
 467 
 468                 Document doc = currentNode.getOwnerDocument();
 469 
 470                 if (currentNode.getParentNode() != doc) {
 471                         return NODE_NOT_BEFORE_OR_AFTER_DOCUMENT_ELEMENT;
 472                 }
 473 
 474                 Element documentElement = doc.getDocumentElement();
 475 
 476                 if (documentElement == null) {
 477                         return NODE_NOT_BEFORE_OR_AFTER_DOCUMENT_ELEMENT;
 478                 }
 479 
 480                 if (documentElement == currentNode) {
 481                         return NODE_NOT_BEFORE_OR_AFTER_DOCUMENT_ELEMENT;
 482                 }
 483 


 491         }
 492 
 493         /**
 494          * Normalizes an {@link Attr}ibute value
 495          *
 496          * The string value of the node is modified by replacing
 497          * <UL>
 498          * <LI>all ampersands (&) with <CODE>&amp;amp;</CODE></LI>
 499          * <LI>all open angle brackets (<) with <CODE>&amp;lt;</CODE></LI>
 500          * <LI>all quotation mark characters with <CODE>&amp;quot;</CODE></LI>
 501          * <LI>and the whitespace characters <CODE>#x9</CODE>, #xA, and #xD,
 502          * with character references. The character references are written in
 503          * uppercase hexadecimal with no leading zeroes (for example, <CODE>#xD</CODE>
 504          * is represented by the character reference <CODE>&amp;#xD;</CODE>)</LI>
 505          * </UL>
 506          *
 507          * @param name
 508          * @param value
 509          * @throws IOException
 510          */
 511         private void outputAttrToWriter(String name, String value)
 512                         throws IOException {
 513 
 514                 this._writer.write(" ");
 515                 this._writer.write(name);
 516                 this._writer.write("=\"");
 517 
 518                 int length = value.length();
 519 
 520                 for (int i = 0; i < length; i++) {
 521                         char c = value.charAt(i);
 522 
 523                         switch (c) {
 524 
 525                         case '&':
 526                                 this._writer.write("&amp;amp;");
 527                                 break;
 528 
 529                         case '<':
 530                                 this._writer.write("&amp;lt;");
 531                                 break;
 532 
 533                         case '"':
 534                                 this._writer.write("&amp;quot;");
 535                                 break;
 536 
 537                         case 0x09: // '\t'
 538                                 this._writer.write("&amp;#x9;");
 539                                 break;
 540 
 541                         case 0x0A: // '\n'
 542                                 this._writer.write("&amp;#xA;");
 543                                 break;
 544 
 545                         case 0x0D: // '\r'
 546                                 this._writer.write("&amp;#xD;");
 547                                 break;
 548 
 549                         default:
 550                                 this._writer.write(c);
 551                                 break;
 552                         }
 553                 }
 554 
 555                 this._writer.write("\"");
 556         }
 557 
 558         /**
 559          * Normalizes a {@link org.w3c.dom.Comment} value
 560          *
 561          * @param currentPI
 562          * @throws IOException
 563          */
 564         private void outputPItoWriter(ProcessingInstruction currentPI)
 565                         throws IOException {
 566 
 567                 if (currentPI == null) {
 568                         return;
 569                 }
 570 
 571                 this._writer.write("&lt;?");
 572 
 573                 String target = currentPI.getTarget();
 574                 int length = target.length();
 575 
 576                 for (int i = 0; i < length; i++) {
 577                         char c = target.charAt(i);
 578 
 579                         switch (c) {
 580 
 581                         case 0x0D:
 582                                 this._writer.write("&amp;#xD;");
 583                                 break;
 584 
 585                         case ' ':
 586                                 this._writer.write("&middot;");
 587                                 break;
 588 
 589                         case '\n':
 590                                 this._writer.write("&para;\n");
 591                                 break;
 592 
 593                         default:
 594                                 this._writer.write(c);
 595                                 break;
 596                         }
 597                 }
 598 
 599                 String data = currentPI.getData();
 600 
 601                 length = data.length();
 602 
 603                 if (length > 0) {
 604                     this._writer.write(" ");
 605 
 606                     for (int i = 0; i < length; i++) {
 607                         char c = data.charAt(i);
 608 
 609                         switch (c) {
 610 
 611                             case 0x0D:
 612                                 this._writer.write("&amp;#xD;");
 613                                 break;
 614 
 615                             default:
 616                                 this._writer.write(c);
 617                                 break;
 618                         }
 619                     }
 620                 }
 621 
 622                 this._writer.write("?&gt;");
 623         }
 624 
 625         /**
 626          * Method outputCommentToWriter
 627          *
 628          * @param currentComment
 629          * @throws IOException
 630          */
 631         private void outputCommentToWriter(Comment currentComment)
 632                         throws IOException {
 633 
 634                 if (currentComment == null) {
 635                         return;
 636                 }
 637 
 638                 this._writer.write("&lt;!--");
 639 
 640                 String data = currentComment.getData();
 641                 int length = data.length();
 642 
 643                 for (int i = 0; i < length; i++) {
 644                         char c = data.charAt(i);
 645 
 646                         switch (c) {
 647 
 648                         case 0x0D:
 649                                 this._writer.write("&amp;#xD;");
 650                                 break;
 651 
 652                         case ' ':
 653                                 this._writer.write("&middot;");
 654                                 break;
 655 
 656                         case '\n':
 657                                 this._writer.write("&para;\n");
 658                                 break;
 659 
 660                         default:
 661                                 this._writer.write(c);
 662                                 break;
 663                         }
 664                 }
 665 
 666                 this._writer.write("--&gt;");
 667         }
 668 
 669         /**
 670          * Method outputTextToWriter
 671          *
 672          * @param text
 673          * @throws IOException
 674          */
 675         private void outputTextToWriter(String text) throws IOException {
 676 
 677                 if (text == null) {
 678                         return;
 679                 }
 680 
 681                 int length = text.length();
 682 
 683                 for (int i = 0; i < length; i++) {
 684                         char c = text.charAt(i);
 685 
 686                         switch (c) {
 687 
 688                         case '&':
 689                                 this._writer.write("&amp;amp;");
 690                                 break;
 691 
 692                         case '<':
 693                                 this._writer.write("&amp;lt;");
 694                                 break;
 695 
 696                         case '>':
 697                                 this._writer.write("&amp;gt;");
 698                                 break;
 699 
 700                         case 0xD:
 701                                 this._writer.write("&amp;#xD;");
 702                                 break;
 703 
 704                         case ' ':
 705                                 this._writer.write("&middot;");
 706                                 break;
 707 
 708                         case '\n':
 709                                 this._writer.write("&para;\n");
 710                                 break;
 711 
 712                         default:
 713                                 this._writer.write(c);
 714                                 break;
 715                         }
 716                 }
 717         }
 718 }
   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.signature;
  24 
  25 import java.io.IOException;
  26 import java.io.StringWriter;
  27 import java.io.Writer;
  28 import java.util.Arrays;
  29 import java.util.Set;
  30 
  31 import com.sun.org.apache.xml.internal.security.c14n.helper.AttrCompare;
  32 import com.sun.org.apache.xml.internal.security.utils.XMLUtils;
  33 import org.w3c.dom.Attr;
  34 import org.w3c.dom.Comment;
  35 import org.w3c.dom.Document;
  36 import org.w3c.dom.Element;
  37 import org.w3c.dom.NamedNodeMap;
  38 import org.w3c.dom.Node;
  39 import org.w3c.dom.ProcessingInstruction;
  40 
  41 /**
  42  * Class XMLSignatureInputDebugger



  43  */
  44 public class XMLSignatureInputDebugger {
  45 


  46     /** Field _xmlSignatureInput */
  47     private Set<Node> xpathNodeSet;
  48 
  49     private Set<String> inclusiveNamespaces;
  50 
  51     /** Field doc */
  52     private Document doc = null;
  53 
  54     /** Field writer */
  55     private Writer writer = null;
  56 













  57     /** The HTML Prefix* */
  58     static final String HTMLPrefix = 
  59         "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n"
  60         + "<html>\n"
  61         + "<head>\n"
  62         + "<title>Caninical XML node set</title>\n"
  63         + "<style type=\"text/css\">\n"
  64         + "<!-- \n"
  65         + ".INCLUDED { \n"
  66         + "   color: #000000; \n"
  67         + "   background-color: \n"
  68         + "   #FFFFFF; \n"
  69         + "   font-weight: bold; } \n"
  70         + ".EXCLUDED { \n"
  71         + "   color: #666666; \n"
  72         + "   background-color: \n"
  73         + "   #999999; } \n"
  74         + ".INCLUDEDINCLUSIVENAMESPACE { \n"
  75         + "   color: #0000FF; \n"
  76         + "   background-color: #FFFFFF; \n"
  77         + "   font-weight: bold; \n"
  78         + "   font-style: italic; } \n"
  79         + ".EXCLUDEDINCLUSIVENAMESPACE { \n"
  80         + "   color: #0000FF; \n"
  81         + "   background-color: #999999; \n"
  82         + "   font-style: italic; } \n"
  83         + "--> \n"
  84         + "</style> \n"
  85         + "</head>\n"
  86         + "<body bgcolor=\"#999999\">\n"
  87         + "<h1>Explanation of the output</h1>\n"
  88         + "<p>The following text contains the nodeset of the given Reference before it is canonicalized. There exist four different styles to indicate how a given node is treated.</p>\n"
  89         + "<ul>\n"
  90         + "<li class=\"INCLUDED\">A node which is in the node set is labeled using the INCLUDED style.</li>\n"
  91         + "<li class=\"EXCLUDED\">A node which is <em>NOT</em> in the node set is labeled EXCLUDED style.</li>\n"
  92         + "<li class=\"INCLUDEDINCLUSIVENAMESPACE\">A namespace which is in the node set AND in the InclusiveNamespaces PrefixList is labeled using the INCLUDEDINCLUSIVENAMESPACE style.</li>\n"
  93         + "<li class=\"EXCLUDEDINCLUSIVENAMESPACE\">A namespace which is in NOT the node set AND in the InclusiveNamespaces PrefixList is labeled using the INCLUDEDINCLUSIVENAMESPACE style.</li>\n"
  94         + "</ul>\n" + "<h1>Output</h1>\n" + "<pre>\n";
  95 
  96     /** HTML Suffix * */
  97     static final String HTMLSuffix = "</pre></body></html>";
  98 
  99     static final String HTMLExcludePrefix = "<span class=\"EXCLUDED\">";
 100 


 101     static final String HTMLIncludePrefix = "<span class=\"INCLUDED\">";
 102 
 103     static final String HTMLIncludeOrExcludeSuffix = "</span>";
 104 
 105     static final String HTMLIncludedInclusiveNamespacePrefix = "<span class=\"INCLUDEDINCLUSIVENAMESPACE\">";
 106 


 107     static final String HTMLExcludedInclusiveNamespacePrefix = "<span class=\"EXCLUDEDINCLUSIVENAMESPACE\">";
 108 


 109     private static final int NODE_BEFORE_DOCUMENT_ELEMENT = -1;
 110 
 111     private static final int NODE_NOT_BEFORE_OR_AFTER_DOCUMENT_ELEMENT = 0;
 112 
 113     private static final int NODE_AFTER_DOCUMENT_ELEMENT = 1;
 114 
 115     static final AttrCompare ATTR_COMPARE = new AttrCompare();
 116 





 117     /**
 118      * Constructor XMLSignatureInputDebugger
 119      * 
 120      * @param xmlSignatureInput the signature to pretty print
 121      */
 122     public XMLSignatureInputDebugger(XMLSignatureInput xmlSignatureInput) {


 123         if (!xmlSignatureInput.isNodeSet()) {
 124             this.xpathNodeSet = null;
 125         } else {
 126             this.xpathNodeSet = xmlSignatureInput.getInputNodeSet();
 127         }
 128     }
 129 
 130     /**
 131      * Constructor XMLSignatureInputDebugger
 132      * 
 133      * @param xmlSignatureInput the signatur to pretty print
 134      * @param inclusiveNamespace
 135      */
 136     public XMLSignatureInputDebugger(
 137         XMLSignatureInput xmlSignatureInput, 
 138         Set<String> inclusiveNamespace
 139     ) {
 140         this(xmlSignatureInput);
 141         this.inclusiveNamespaces = inclusiveNamespace;

 142     }
 143 
 144     /**
 145      * Method getHTMLRepresentation
 146      * 
 147      * @return The HTML Representation.
 148      * @throws XMLSignatureException
 149      */
 150     public String getHTMLRepresentation() throws XMLSignatureException {
 151         if ((this.xpathNodeSet == null) || (this.xpathNodeSet.size() == 0)) {
 152             return HTMLPrefix + "<blink>no node set, sorry</blink>" + HTMLSuffix;


 153         }
 154 


 155         // get only a single node as anchor to fetch the owner document
 156         Node n = this.xpathNodeSet.iterator().next();
 157 
 158         this.doc = XMLUtils.getOwnerDocument(n);

 159 
 160         try {
 161             this.writer = new StringWriter();
 162 
 163             this.canonicalizeXPathNodeSet(this.doc);
 164             this.writer.close();
 165 
 166             return this.writer.toString();
 167         } catch (IOException ex) {
 168             throw new XMLSignatureException("empty", ex);
 169         } finally {
 170             this.xpathNodeSet = null;
 171             this.doc = null;
 172             this.writer = null;
 173         }
 174     }
 175 
 176     /**
 177      * Method canonicalizeXPathNodeSet
 178      * 
 179      * @param currentNode
 180      * @throws XMLSignatureException
 181      * @throws IOException
 182      */
 183     private void canonicalizeXPathNodeSet(Node currentNode)
 184         throws XMLSignatureException, IOException {
 185 
 186         int currentNodeType = currentNode.getNodeType();
 187         switch (currentNodeType) {
 188 



 189 
 190         case Node.ENTITY_NODE:
 191         case Node.NOTATION_NODE:
 192         case Node.DOCUMENT_FRAGMENT_NODE:
 193         case Node.ATTRIBUTE_NODE:
 194             throw new XMLSignatureException("empty");
 195         case Node.DOCUMENT_NODE:
 196             this.writer.write(HTMLPrefix);
 197 
 198             for (Node currentChild = currentNode.getFirstChild(); 
 199                 currentChild != null; currentChild = currentChild.getNextSibling()) {
 200                 this.canonicalizeXPathNodeSet(currentChild);
 201             }
 202 
 203             this.writer.write(HTMLSuffix);
 204             break;
 205 
 206         case Node.COMMENT_NODE:
 207             if (this.xpathNodeSet.contains(currentNode)) {
 208                 this.writer.write(HTMLIncludePrefix);
 209             } else {
 210                 this.writer.write(HTMLExcludePrefix);
 211             }
 212 
 213             int position = getPositionRelativeToDocumentElement(currentNode);
 214 
 215             if (position == NODE_AFTER_DOCUMENT_ELEMENT) {
 216                 this.writer.write("\n");
 217             }
 218 
 219             this.outputCommentToWriter((Comment) currentNode);
 220 
 221             if (position == NODE_BEFORE_DOCUMENT_ELEMENT) {
 222                 this.writer.write("\n");
 223             }
 224 
 225             this.writer.write(HTMLIncludeOrExcludeSuffix);




 226             break;
 227 
 228         case Node.PROCESSING_INSTRUCTION_NODE:
 229             if (this.xpathNodeSet.contains(currentNode)) {
 230                 this.writer.write(HTMLIncludePrefix);
 231             } else {
 232                 this.writer.write(HTMLExcludePrefix);
 233             }
 234 
 235             position = getPositionRelativeToDocumentElement(currentNode);
 236 
 237             if (position == NODE_AFTER_DOCUMENT_ELEMENT) {
 238                 this.writer.write("\n");
 239             }
 240 
 241             this.outputPItoWriter((ProcessingInstruction) currentNode);
 242 
 243             if (position == NODE_BEFORE_DOCUMENT_ELEMENT) {
 244                 this.writer.write("\n");
 245             }
 246 
 247             this.writer.write(HTMLIncludeOrExcludeSuffix);




 248             break;
 249 
 250         case Node.TEXT_NODE:
 251         case Node.CDATA_SECTION_NODE:
 252             if (this.xpathNodeSet.contains(currentNode)) {
 253                 this.writer.write(HTMLIncludePrefix);
 254             } else {
 255                 this.writer.write(HTMLExcludePrefix);
 256             }
 257 
 258             outputTextToWriter(currentNode.getNodeValue());
 259 
 260             for (Node nextSibling = currentNode.getNextSibling(); 
 261                 (nextSibling != null) 
 262                 && ((nextSibling.getNodeType() == Node.TEXT_NODE) 
 263                     || (nextSibling.getNodeType() == Node.CDATA_SECTION_NODE)); 
 264                 nextSibling = nextSibling.getNextSibling()) {
 265                 /*
 266                  * The XPath data model allows to select only the first of a
 267                  * sequence of mixed text and CDATA nodes. But we must output
 268                  * them all, so we must search:
 269                  * 
 270                  * @see http://nagoya.apache.org/bugzilla/show_bug.cgi?id=6329
 271                  */
 272                 this.outputTextToWriter(nextSibling.getNodeValue());
 273             }
 274 
 275             this.writer.write(HTMLIncludeOrExcludeSuffix);




 276             break;
 277 
 278         case Node.ELEMENT_NODE:
 279             Element currentElement = (Element) currentNode;
 280 
 281             if (this.xpathNodeSet.contains(currentNode)) {
 282                 this.writer.write(HTMLIncludePrefix);
 283             } else {
 284                 this.writer.write(HTMLExcludePrefix);
 285             }
 286 
 287             this.writer.write("&lt;");
 288             this.writer.write(currentElement.getTagName());
 289 
 290             this.writer.write(HTMLIncludeOrExcludeSuffix);




 291 
 292             // we output all Attrs which are available
 293             NamedNodeMap attrs = currentElement.getAttributes();
 294             int attrsLength = attrs.getLength();
 295             Attr attrs2[] = new Attr[attrsLength];
 296 
 297             for (int i = 0; i < attrsLength; i++) {
 298                 attrs2[i] = (Attr)attrs.item(i);
 299             }
 300 
 301             Arrays.sort(attrs2, ATTR_COMPARE);
 302             Object attrs3[] = attrs2;
 303 
 304             for (int i = 0; i < attrsLength; i++) {
 305                 Attr a = (Attr) attrs3[i];
 306                 boolean included = this.xpathNodeSet.contains(a);
 307                 boolean inclusive = this.inclusiveNamespaces.contains(a.getName());

 308 
 309                 if (included) {
 310                     if (inclusive) {

 311                         // included and inclusive
 312                         this.writer.write(HTMLIncludedInclusiveNamespacePrefix);

 313                     } else {

 314                         // included and not inclusive
 315                         this.writer.write(HTMLIncludePrefix);
 316                     }
 317                 } else {
 318                     if (inclusive) {

 319                         // excluded and inclusive
 320                         this.writer.write(HTMLExcludedInclusiveNamespacePrefix);

 321                     } else {

 322                         // excluded and not inclusive
 323                         this.writer.write(HTMLExcludePrefix);
 324                     }
 325                 }
 326 
 327                 this.outputAttrToWriter(a.getNodeName(), a.getNodeValue());
 328                 this.writer.write(HTMLIncludeOrExcludeSuffix);























 329             }
 330 
 331             if (this.xpathNodeSet.contains(currentNode)) {
 332                 this.writer.write(HTMLIncludePrefix);
 333             } else {
 334                 this.writer.write(HTMLExcludePrefix);
 335             }
 336 
 337             this.writer.write("&gt;");
 338 
 339             this.writer.write(HTMLIncludeOrExcludeSuffix);




 340 
 341             // traversal
 342             for (Node currentChild = currentNode.getFirstChild(); 
 343                 currentChild != null; 
 344                 currentChild = currentChild.getNextSibling()) {
 345                 this.canonicalizeXPathNodeSet(currentChild);
 346             }
 347 
 348             if (this.xpathNodeSet.contains(currentNode)) {
 349                 this.writer.write(HTMLIncludePrefix);
 350             } else {
 351                 this.writer.write(HTMLExcludePrefix);
 352             }
 353 
 354             this.writer.write("&lt;/");
 355             this.writer.write(currentElement.getTagName());
 356             this.writer.write("&gt;");
 357 
 358             this.writer.write(HTMLIncludeOrExcludeSuffix);
 359             break;
 360             
 361         case Node.DOCUMENT_TYPE_NODE:
 362         default:
 363             break;
 364         }
 365     }
 366 
 367     /**
 368      * Checks whether a Comment or ProcessingInstruction is before or after the
 369      * document element. This is needed for prepending or appending "\n"s.
 370      * 
 371      * @param currentNode
 372      *            comment or pi to check
 373      * @return NODE_BEFORE_DOCUMENT_ELEMENT,
 374      *         NODE_NOT_BEFORE_OR_AFTER_DOCUMENT_ELEMENT or
 375      *         NODE_AFTER_DOCUMENT_ELEMENT
 376      * @see #NODE_BEFORE_DOCUMENT_ELEMENT
 377      * @see #NODE_NOT_BEFORE_OR_AFTER_DOCUMENT_ELEMENT
 378      * @see #NODE_AFTER_DOCUMENT_ELEMENT
 379      */
 380     private int getPositionRelativeToDocumentElement(Node currentNode) {

 381         if (currentNode == null) {
 382             return NODE_NOT_BEFORE_OR_AFTER_DOCUMENT_ELEMENT;
 383         }
 384 
 385         Document doc = currentNode.getOwnerDocument();
 386 
 387         if (currentNode.getParentNode() != doc) {
 388             return NODE_NOT_BEFORE_OR_AFTER_DOCUMENT_ELEMENT;
 389         }
 390 
 391         Element documentElement = doc.getDocumentElement();
 392 
 393         if (documentElement == null) {
 394             return NODE_NOT_BEFORE_OR_AFTER_DOCUMENT_ELEMENT;
 395         }
 396 
 397         if (documentElement == currentNode) {
 398             return NODE_NOT_BEFORE_OR_AFTER_DOCUMENT_ELEMENT;
 399         }
 400 


 408     }
 409 
 410     /**
 411      * Normalizes an {@link Attr}ibute value
 412      * 
 413      * The string value of the node is modified by replacing
 414      * <UL>
 415      * <LI>all ampersands (&) with <CODE>&amp;amp;</CODE></LI>
 416      * <LI>all open angle brackets (<) with <CODE>&amp;lt;</CODE></LI>
 417      * <LI>all quotation mark characters with <CODE>&amp;quot;</CODE></LI>
 418      * <LI>and the whitespace characters <CODE>#x9</CODE>, #xA, and #xD,
 419      * with character references. The character references are written in
 420      * uppercase hexadecimal with no leading zeroes (for example, <CODE>#xD</CODE>
 421      * is represented by the character reference <CODE>&amp;#xD;</CODE>)</LI>
 422      * </UL>
 423      * 
 424      * @param name
 425      * @param value
 426      * @throws IOException
 427      */
 428     private void outputAttrToWriter(String name, String value) throws IOException {
 429         this.writer.write(" ");
 430         this.writer.write(name);
 431         this.writer.write("=\"");


 432 
 433         int length = value.length();
 434 
 435         for (int i = 0; i < length; i++) {
 436             char c = value.charAt(i);
 437 
 438             switch (c) {
 439 
 440             case '&':
 441                 this.writer.write("&amp;amp;");
 442                 break;
 443 
 444             case '<':
 445                 this.writer.write("&amp;lt;");
 446                 break;
 447 
 448             case '"':
 449                 this.writer.write("&amp;quot;");
 450                 break;
 451 
 452             case 0x09: // '\t'
 453                 this.writer.write("&amp;#x9;");
 454                 break;
 455 
 456             case 0x0A: // '\n'
 457                 this.writer.write("&amp;#xA;");
 458                 break;
 459 
 460             case 0x0D: // '\r'
 461                 this.writer.write("&amp;#xD;");
 462                 break;
 463 
 464             default:
 465                 this.writer.write(c);
 466                 break;
 467             }
 468         }
 469 
 470         this.writer.write("\"");
 471     }
 472 
 473     /**
 474      * Normalizes a {@link org.w3c.dom.Comment} value
 475      * 
 476      * @param currentPI
 477      * @throws IOException
 478      */
 479     private void outputPItoWriter(ProcessingInstruction currentPI) throws IOException {

 480 
 481         if (currentPI == null) {
 482             return;
 483         }
 484 
 485         this.writer.write("&lt;?");
 486 
 487         String target = currentPI.getTarget();
 488         int length = target.length();
 489 
 490         for (int i = 0; i < length; i++) {
 491             char c = target.charAt(i);
 492 
 493             switch (c) {
 494 
 495             case 0x0D:
 496                 this.writer.write("&amp;#xD;");
 497                 break;
 498 
 499             case ' ':
 500                 this.writer.write("&middot;");
 501                 break;
 502 
 503             case '\n':
 504                 this.writer.write("&para;\n");
 505                 break;
 506 
 507             default:
 508                 this.writer.write(c);
 509                 break;
 510             }
 511         }
 512 
 513         String data = currentPI.getData();
 514 
 515         length = data.length();
 516 
 517         if (length > 0) {
 518             this.writer.write(" ");
 519 
 520             for (int i = 0; i < length; i++) {
 521                 char c = data.charAt(i);
 522 
 523                 switch (c) {
 524 
 525                 case 0x0D:
 526                     this.writer.write("&amp;#xD;");
 527                     break;
 528 
 529                 default:
 530                     this.writer.write(c);
 531                     break;
 532                 }
 533             }
 534         }
 535 
 536         this.writer.write("?&gt;");
 537     }
 538 
 539     /**
 540      * Method outputCommentToWriter
 541      * 
 542      * @param currentComment
 543      * @throws IOException
 544      */
 545     private void outputCommentToWriter(Comment currentComment) throws IOException {

 546 
 547         if (currentComment == null) {
 548             return;
 549         }
 550 
 551         this.writer.write("&lt;!--");
 552 
 553         String data = currentComment.getData();
 554         int length = data.length();
 555 
 556         for (int i = 0; i < length; i++) {
 557             char c = data.charAt(i);
 558 
 559             switch (c) {
 560 
 561             case 0x0D:
 562                 this.writer.write("&amp;#xD;");
 563                 break;
 564 
 565             case ' ':
 566                 this.writer.write("&middot;");
 567                 break;
 568 
 569             case '\n':
 570                 this.writer.write("&para;\n");
 571                 break;
 572 
 573             default:
 574                 this.writer.write(c);
 575                 break;
 576             }
 577         }
 578 
 579         this.writer.write("--&gt;");
 580     }
 581 
 582     /**
 583      * Method outputTextToWriter
 584      * 
 585      * @param text
 586      * @throws IOException
 587      */
 588     private void outputTextToWriter(String text) throws IOException {

 589         if (text == null) {
 590             return;
 591         }
 592 
 593         int length = text.length();
 594 
 595         for (int i = 0; i < length; i++) {
 596             char c = text.charAt(i);
 597 
 598             switch (c) {
 599 
 600             case '&':
 601                 this.writer.write("&amp;amp;");
 602                 break;
 603 
 604             case '<':
 605                 this.writer.write("&amp;lt;");
 606                 break;
 607 
 608             case '>':
 609                 this.writer.write("&amp;gt;");
 610                 break;
 611 
 612             case 0xD:
 613                 this.writer.write("&amp;#xD;");
 614                 break;
 615 
 616             case ' ':
 617                 this.writer.write("&middot;");
 618                 break;
 619 
 620             case '\n':
 621                 this.writer.write("&para;\n");
 622                 break;
 623 
 624             default:
 625                 this.writer.write(c);
 626                 break;
 627             }
 628         }
 629     }
 630 }