|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
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; |
|
|
|
|
|
|
|
*/ |
|
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<Attr> nodes = new ArrayList<>(); |
|
} |
|
|
|
private int currentLevel = 0; |
|
private int lastlevel = 0; |
|
private XmlsStackElement cur; |
|
private List<XmlsStackElement> 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<Attr> 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<String, Attr> loa = new HashMap<>(); |
|
if (c14n11) { |
|
List<Attr> baseAttrs = new ArrayList<>(); |
|
boolean successiveOmitted = true; |
|
for (; size >= 0; size--) { |
|
e = levels.get(size); |
|
if (e.rendered) { |
|
successiveOmitted = false; |
|
} |
|
Iterator<Attr> 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<Attr> 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<Attr> 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; |
|
|
|
|
|
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 |
|
|
|
String input = path; |
|
while (input.indexOf("//") > -1) { |
|
input = input.replaceAll("//", "/"); |
|
} |
|
|
|
|
|
StringBuilder output = new StringBuilder(); |
|
|
|
// If the input buffer starts with a root slash "/" then move this |
|
|
|
if (input.charAt(0) == '/') { |
|
output.append("/"); |
|
input = input.substring(1); |
|
} |
|
|
|
printStep("1 ", output.toString(), input); |
|
|
|
|
|
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 |
|
|
|
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("/.")) { |
|
|
|
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("/..")) { |
|
|
|
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 |
|
|
|
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); |
|
} |
|
} |
|
} |
|
} |