/* * reserved comment block * DO NOT REMOVE OR ALTER! */ /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package com.sun.org.apache.xml.internal.security.c14n.implementations; import java.net.URI; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import org.w3c.dom.Attr; /** * An XmlAttrStack that is shared between the Canonical XML 1.0 and 1.1 implementations. */ class XmlAttrStack { private static final com.sun.org.slf4j.internal.Logger LOG = com.sun.org.slf4j.internal.LoggerFactory.getLogger(XmlAttrStack.class); static class XmlsStackElement { int level; boolean rendered = false; List nodes = new ArrayList<>(); } private int currentLevel = 0; private int lastlevel = 0; private XmlsStackElement cur; private List levels = new ArrayList<>(); private boolean c14n11; public XmlAttrStack(boolean c14n11) { this.c14n11 = c14n11; } void push(int level) { currentLevel = level; if (currentLevel == -1) { return; } cur = null; while (lastlevel >= currentLevel) { levels.remove(levels.size() - 1); int newSize = levels.size(); if (newSize == 0) { lastlevel = 0; return; } lastlevel = levels.get(newSize - 1).level; } } void addXmlnsAttr(Attr n) { if (cur == null) { cur = new XmlsStackElement(); cur.level = currentLevel; levels.add(cur); lastlevel = currentLevel; } cur.nodes.add(n); } void getXmlnsAttr(Collection col) { int size = levels.size() - 1; if (cur == null) { cur = new XmlsStackElement(); cur.level = currentLevel; lastlevel = currentLevel; levels.add(cur); } boolean parentRendered = false; XmlsStackElement e = null; if (size == -1) { parentRendered = true; } else { e = levels.get(size); if (e.rendered && e.level + 1 == currentLevel) { parentRendered = true; } } if (parentRendered) { col.addAll(cur.nodes); cur.rendered = true; return; } Map loa = new HashMap<>(); if (c14n11) { List baseAttrs = new ArrayList<>(); boolean successiveOmitted = true; for (; size >= 0; size--) { e = levels.get(size); if (e.rendered) { successiveOmitted = false; } Iterator it = e.nodes.iterator(); while (it.hasNext() && successiveOmitted) { Attr n = it.next(); if (n.getLocalName().equals("base") && !e.rendered) { baseAttrs.add(n); } else if (!loa.containsKey(n.getName())) { loa.put(n.getName(), n); } } } if (!baseAttrs.isEmpty()) { Iterator it = col.iterator(); String base = null; Attr baseAttr = null; while (it.hasNext()) { Attr n = it.next(); if (n.getLocalName().equals("base")) { base = n.getValue(); baseAttr = n; break; } } it = baseAttrs.iterator(); while (it.hasNext()) { Attr n = it.next(); if (base == null) { base = n.getValue(); baseAttr = n; } else { try { base = joinURI(n.getValue(), base); } catch (URISyntaxException ue) { LOG.debug(ue.getMessage(), ue); } } } if (base != null && base.length() != 0) { baseAttr.setValue(base); col.add(baseAttr); } } } else { for (; size >= 0; size--) { e = levels.get(size); Iterator it = e.nodes.iterator(); while (it.hasNext()) { Attr n = it.next(); if (!loa.containsKey(n.getName())) { loa.put(n.getName(), n); } } } } cur.rendered = true; col.addAll(loa.values()); } private static String joinURI(String baseURI, String relativeURI) throws URISyntaxException { String bscheme = null; String bauthority = null; String bpath = ""; String bquery = null; // pre-parse the baseURI if (baseURI != null) { if (baseURI.endsWith("..")) { baseURI = baseURI + "/"; } URI base = new URI(baseURI); bscheme = base.getScheme(); bauthority = base.getAuthority(); bpath = base.getPath(); bquery = base.getQuery(); } URI r = new URI(relativeURI); String rscheme = r.getScheme(); String rauthority = r.getAuthority(); String rpath = r.getPath(); String rquery = r.getQuery(); String tscheme, tauthority, tpath, tquery; if (rscheme != null && rscheme.equals(bscheme)) { rscheme = null; } if (rscheme != null) { tscheme = rscheme; tauthority = rauthority; tpath = removeDotSegments(rpath); tquery = rquery; } else { if (rauthority != null) { tauthority = rauthority; tpath = removeDotSegments(rpath); tquery = rquery; } else { if (rpath.length() == 0) { tpath = bpath; if (rquery != null) { tquery = rquery; } else { tquery = bquery; } } else { if (rpath.startsWith("/")) { tpath = removeDotSegments(rpath); } else { if (bauthority != null && bpath.length() == 0) { tpath = "/" + rpath; } else { int last = bpath.lastIndexOf('/'); if (last == -1) { tpath = rpath; } else { tpath = bpath.substring(0, last+1) + rpath; } } tpath = removeDotSegments(tpath); } tquery = rquery; } tauthority = bauthority; } tscheme = bscheme; } return new URI(tscheme, tauthority, tpath, tquery, null).toString(); } private static String removeDotSegments(String path) { LOG.debug("STEP OUTPUT BUFFER\t\tINPUT BUFFER"); // 1. The input buffer is initialized with the now-appended path // components then replace occurrences of "//" in the input buffer // with "/" until no more occurrences of "//" are in the input buffer. String input = path; while (input.indexOf("//") > -1) { input = input.replaceAll("//", "/"); } // Initialize the output buffer with the empty string. StringBuilder output = new StringBuilder(); // If the input buffer starts with a root slash "/" then move this // character to the output buffer. if (input.charAt(0) == '/') { output.append("/"); input = input.substring(1); } printStep("1 ", output.toString(), input); // While the input buffer is not empty, loop as follows while (input.length() != 0) { // 2A. If the input buffer begins with a prefix of "./", // then remove that prefix from the input buffer // else if the input buffer begins with a prefix of "../", then // if also the output does not contain the root slash "/" only, // then move this prefix to the end of the output buffer else // remove that prefix if (input.startsWith("./")) { input = input.substring(2); printStep("2A", output.toString(), input); } else if (input.startsWith("../")) { input = input.substring(3); if (!output.toString().equals("/")) { output.append("../"); } printStep("2A", output.toString(), input); // 2B. if the input buffer begins with a prefix of "/./" or "/.", // where "." is a complete path segment, then replace that prefix // with "/" in the input buffer; otherwise, } else if (input.startsWith("/./")) { input = input.substring(2); printStep("2B", output.toString(), input); } else if (input.equals("/.")) { // FIXME: what is complete path segment? input = input.replaceFirst("/.", "/"); printStep("2B", output.toString(), input); // 2C. if the input buffer begins with a prefix of "/../" or "/..", // where ".." is a complete path segment, then replace that prefix // with "/" in the input buffer and if also the output buffer is // empty, last segment in the output buffer equals "../" or "..", // where ".." is a complete path segment, then append ".." or "/.." // for the latter case respectively to the output buffer else // remove the last segment and its preceding "/" (if any) from the // output buffer and if hereby the first character in the output // buffer was removed and it was not the root slash then delete a // leading slash from the input buffer; otherwise, } else if (input.startsWith("/../")) { input = input.substring(3); if (output.length() == 0) { output.append("/"); } else if (output.toString().endsWith("../")) { output.append(".."); } else if (output.toString().endsWith("..")) { output.append("/.."); } else { int index = output.lastIndexOf("/"); if (index == -1) { output = new StringBuilder(); if (input.charAt(0) == '/') { input = input.substring(1); } } else { output = output.delete(index, output.length()); } } printStep("2C", output.toString(), input); } else if (input.equals("/..")) { // FIXME: what is complete path segment? input = input.replaceFirst("/..", "/"); if (output.length() == 0) { output.append("/"); } else if (output.toString().endsWith("../")) { output.append(".."); } else if (output.toString().endsWith("..")) { output.append("/.."); } else { int index = output.lastIndexOf("/"); if (index == -1) { output = new StringBuilder(); if (input.charAt(0) == '/') { input = input.substring(1); } } else { output = output.delete(index, output.length()); } } printStep("2C", output.toString(), input); // 2D. if the input buffer consists only of ".", then remove // that from the input buffer else if the input buffer consists // only of ".." and if the output buffer does not contain only // the root slash "/", then move the ".." to the output buffer // else delte it.; otherwise, } else if (input.equals(".")) { input = ""; printStep("2D", output.toString(), input); } else if (input.equals("..")) { if (!output.toString().equals("/")) { output.append(".."); } input = ""; printStep("2D", output.toString(), input); // 2E. move the first path segment (if any) in the input buffer // to the end of the output buffer, including the initial "/" // character (if any) and any subsequent characters up to, but not // including, the next "/" character or the end of the input buffer. } else { int end = -1; int begin = input.indexOf('/'); if (begin == 0) { end = input.indexOf('/', 1); } else { end = begin; begin = 0; } String segment; if (end == -1) { segment = input.substring(begin); input = ""; } else { segment = input.substring(begin, end); input = input.substring(end); } output.append(segment); printStep("2E", output.toString(), input); } } // 3. Finally, if the only or last segment of the output buffer is // "..", where ".." is a complete path segment not followed by a slash // then append a slash "/". The output buffer is returned as the result // of remove_dot_segments if (output.toString().endsWith("..")) { output.append("/"); printStep("3 ", output.toString(), input); } return output.toString(); } private static void printStep(String step, String output, String input) { if (LOG.isDebugEnabled()) { LOG.debug(" " + step + ": " + output); if (output.length() == 0) { LOG.debug("\t\t\t\t" + input); } else { LOG.debug("\t\t\t" + input); } } } }