| 
 | 
 | 
 | 
 | 
 | 
 | 
 */  | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 */  | 
 | 
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); | 
 | 
            }  | 
 | 
        }  | 
 | 
    }  | 
 | 
}  |