1 /* 2 * reserved comment block 3 * DO NOT REMOVE OR ALTER! 4 */ 5 /** 6 * Licensed to the Apache Software Foundation (ASF) under one 7 * or more contributor license agreements. See the NOTICE file 8 * distributed with this work for additional information 9 * regarding copyright ownership. The ASF licenses this file 10 * to you under the Apache License, Version 2.0 (the 11 * "License"); you may not use this file except in compliance 12 * with the License. You may obtain a copy of the License at 13 * 14 * http://www.apache.org/licenses/LICENSE-2.0 15 * 16 * Unless required by applicable law or agreed to in writing, 17 * software distributed under the License is distributed on an 18 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 19 * KIND, either express or implied. See the License for the 20 * specific language governing permissions and limitations 21 * under the License. 22 */ 23 package com.sun.org.apache.xml.internal.security.c14n.implementations; 24 25 import java.net.URI; 26 import java.net.URISyntaxException; 27 import java.util.ArrayList; 28 import java.util.Collection; 29 import java.util.HashMap; 30 import java.util.Iterator; 31 import java.util.List; 32 import java.util.Map; 33 34 import org.w3c.dom.Attr; 35 36 /** 37 * An XmlAttrStack that is shared between the Canonical XML 1.0 and 1.1 implementations. 38 */ 39 class XmlAttrStack { 40 41 private static final com.sun.org.slf4j.internal.Logger LOG = 42 com.sun.org.slf4j.internal.LoggerFactory.getLogger(XmlAttrStack.class); 43 44 static class XmlsStackElement { 45 int level; 46 boolean rendered = false; 47 List<Attr> nodes = new ArrayList<>(); 48 } 49 50 private int currentLevel = 0; 51 private int lastlevel = 0; 52 private XmlsStackElement cur; 53 private List<XmlsStackElement> levels = new ArrayList<>(); 54 private boolean c14n11; 55 XmlAttrStack(boolean c14n11)56 public XmlAttrStack(boolean c14n11) { 57 this.c14n11 = c14n11; 58 } 59 push(int level)60 void push(int level) { 61 currentLevel = level; 62 if (currentLevel == -1) { 63 return; 64 } 65 cur = null; 66 while (lastlevel >= currentLevel) { 67 levels.remove(levels.size() - 1); 68 int newSize = levels.size(); 69 if (newSize == 0) { 70 lastlevel = 0; 71 return; 72 } 73 lastlevel = levels.get(newSize - 1).level; 74 } 75 } 76 addXmlnsAttr(Attr n)77 void addXmlnsAttr(Attr n) { 78 if (cur == null) { 79 cur = new XmlsStackElement(); 80 cur.level = currentLevel; 81 levels.add(cur); 82 lastlevel = currentLevel; 83 } 84 cur.nodes.add(n); 85 } 86 getXmlnsAttr(Collection<Attr> col)87 void getXmlnsAttr(Collection<Attr> col) { 88 int size = levels.size() - 1; 89 if (cur == null) { 90 cur = new XmlsStackElement(); 91 cur.level = currentLevel; 92 lastlevel = currentLevel; 93 levels.add(cur); 94 } 95 boolean parentRendered = false; 96 XmlsStackElement e = null; 97 if (size == -1) { 98 parentRendered = true; 99 } else { 100 e = levels.get(size); 101 if (e.rendered && e.level + 1 == currentLevel) { 102 parentRendered = true; 103 } 104 } 105 if (parentRendered) { 106 col.addAll(cur.nodes); 107 cur.rendered = true; 108 return; 109 } 110 111 Map<String, Attr> loa = new HashMap<>(); 112 if (c14n11) { 113 List<Attr> baseAttrs = new ArrayList<>(); 114 boolean successiveOmitted = true; 115 for (; size >= 0; size--) { 116 e = levels.get(size); 117 if (e.rendered) { 118 successiveOmitted = false; 119 } 120 Iterator<Attr> it = e.nodes.iterator(); 121 while (it.hasNext() && successiveOmitted) { 122 Attr n = it.next(); 123 if (n.getLocalName().equals("base") && !e.rendered) { 124 baseAttrs.add(n); 125 } else if (!loa.containsKey(n.getName())) { 126 loa.put(n.getName(), n); 127 } 128 } 129 } 130 if (!baseAttrs.isEmpty()) { 131 Iterator<Attr> it = col.iterator(); 132 String base = null; 133 Attr baseAttr = null; 134 while (it.hasNext()) { 135 Attr n = it.next(); 136 if (n.getLocalName().equals("base")) { 137 base = n.getValue(); 138 baseAttr = n; 139 break; 140 } 141 } 142 it = baseAttrs.iterator(); 143 while (it.hasNext()) { 144 Attr n = it.next(); 145 if (base == null) { 146 base = n.getValue(); 147 baseAttr = n; 148 } else { 149 try { 150 base = joinURI(n.getValue(), base); 151 } catch (URISyntaxException ue) { 152 LOG.debug(ue.getMessage(), ue); 153 } 154 } 155 } 156 if (base != null && base.length() != 0) { 157 baseAttr.setValue(base); 158 col.add(baseAttr); 159 } 160 } 161 } else { 162 for (; size >= 0; size--) { 163 e = levels.get(size); 164 Iterator<Attr> it = e.nodes.iterator(); 165 while (it.hasNext()) { 166 Attr n = it.next(); 167 if (!loa.containsKey(n.getName())) { 168 loa.put(n.getName(), n); 169 } 170 } 171 } 172 } 173 174 cur.rendered = true; 175 col.addAll(loa.values()); 176 } 177 joinURI(String baseURI, String relativeURI)178 private static String joinURI(String baseURI, String relativeURI) throws URISyntaxException { 179 String bscheme = null; 180 String bauthority = null; 181 String bpath = ""; 182 String bquery = null; 183 184 // pre-parse the baseURI 185 if (baseURI != null) { 186 if (baseURI.endsWith("..")) { 187 baseURI = baseURI + "/"; 188 } 189 URI base = new URI(baseURI); 190 bscheme = base.getScheme(); 191 bauthority = base.getAuthority(); 192 bpath = base.getPath(); 193 bquery = base.getQuery(); 194 } 195 196 URI r = new URI(relativeURI); 197 String rscheme = r.getScheme(); 198 String rauthority = r.getAuthority(); 199 String rpath = r.getPath(); 200 String rquery = r.getQuery(); 201 202 String tscheme, tauthority, tpath, tquery; 203 if (rscheme != null && rscheme.equals(bscheme)) { 204 rscheme = null; 205 } 206 if (rscheme != null) { 207 tscheme = rscheme; 208 tauthority = rauthority; 209 tpath = removeDotSegments(rpath); 210 tquery = rquery; 211 } else { 212 if (rauthority != null) { 213 tauthority = rauthority; 214 tpath = removeDotSegments(rpath); 215 tquery = rquery; 216 } else { 217 if (rpath.length() == 0) { 218 tpath = bpath; 219 if (rquery != null) { 220 tquery = rquery; 221 } else { 222 tquery = bquery; 223 } 224 } else { 225 if (rpath.startsWith("/")) { 226 tpath = removeDotSegments(rpath); 227 } else { 228 if (bauthority != null && bpath.length() == 0) { 229 tpath = "/" + rpath; 230 } else { 231 int last = bpath.lastIndexOf('/'); 232 if (last == -1) { 233 tpath = rpath; 234 } else { 235 tpath = bpath.substring(0, last+1) + rpath; 236 } 237 } 238 tpath = removeDotSegments(tpath); 239 } 240 tquery = rquery; 241 } 242 tauthority = bauthority; 243 } 244 tscheme = bscheme; 245 } 246 return new URI(tscheme, tauthority, tpath, tquery, null).toString(); 247 } 248 removeDotSegments(String path)249 private static String removeDotSegments(String path) { 250 LOG.debug("STEP OUTPUT BUFFER\t\tINPUT BUFFER"); 251 252 // 1. The input buffer is initialized with the now-appended path 253 // components then replace occurrences of "//" in the input buffer 254 // with "/" until no more occurrences of "//" are in the input buffer. 255 String input = path; 256 while (input.indexOf("//") > -1) { 257 input = input.replaceAll("//", "/"); 258 } 259 260 // Initialize the output buffer with the empty string. 261 StringBuilder output = new StringBuilder(); 262 263 // If the input buffer starts with a root slash "/" then move this 264 // character to the output buffer. 265 if (input.charAt(0) == '/') { 266 output.append("/"); 267 input = input.substring(1); 268 } 269 270 printStep("1 ", output.toString(), input); 271 272 // While the input buffer is not empty, loop as follows 273 while (input.length() != 0) { 274 // 2A. If the input buffer begins with a prefix of "./", 275 // then remove that prefix from the input buffer 276 // else if the input buffer begins with a prefix of "../", then 277 // if also the output does not contain the root slash "/" only, 278 // then move this prefix to the end of the output buffer else 279 // remove that prefix 280 if (input.startsWith("./")) { 281 input = input.substring(2); 282 printStep("2A", output.toString(), input); 283 } else if (input.startsWith("../")) { 284 input = input.substring(3); 285 if (!output.toString().equals("/")) { 286 output.append("../"); 287 } 288 printStep("2A", output.toString(), input); 289 // 2B. if the input buffer begins with a prefix of "/./" or "/.", 290 // where "." is a complete path segment, then replace that prefix 291 // with "/" in the input buffer; otherwise, 292 } else if (input.startsWith("/./")) { 293 input = input.substring(2); 294 printStep("2B", output.toString(), input); 295 } else if (input.equals("/.")) { 296 // FIXME: what is complete path segment? 297 input = input.replaceFirst("/.", "/"); 298 printStep("2B", output.toString(), input); 299 // 2C. if the input buffer begins with a prefix of "/../" or "/..", 300 // where ".." is a complete path segment, then replace that prefix 301 // with "/" in the input buffer and if also the output buffer is 302 // empty, last segment in the output buffer equals "../" or "..", 303 // where ".." is a complete path segment, then append ".." or "/.." 304 // for the latter case respectively to the output buffer else 305 // remove the last segment and its preceding "/" (if any) from the 306 // output buffer and if hereby the first character in the output 307 // buffer was removed and it was not the root slash then delete a 308 // leading slash from the input buffer; otherwise, 309 } else if (input.startsWith("/../")) { 310 input = input.substring(3); 311 if (output.length() == 0) { 312 output.append("/"); 313 } else if (output.toString().endsWith("../")) { 314 output.append(".."); 315 } else if (output.toString().endsWith("..")) { 316 output.append("/.."); 317 } else { 318 int index = output.lastIndexOf("/"); 319 if (index == -1) { 320 output = new StringBuilder(); 321 if (input.charAt(0) == '/') { 322 input = input.substring(1); 323 } 324 } else { 325 output = output.delete(index, output.length()); 326 } 327 } 328 printStep("2C", output.toString(), input); 329 } else if (input.equals("/..")) { 330 // FIXME: what is complete path segment? 331 input = input.replaceFirst("/..", "/"); 332 if (output.length() == 0) { 333 output.append("/"); 334 } else if (output.toString().endsWith("../")) { 335 output.append(".."); 336 } else if (output.toString().endsWith("..")) { 337 output.append("/.."); 338 } else { 339 int index = output.lastIndexOf("/"); 340 if (index == -1) { 341 output = new StringBuilder(); 342 if (input.charAt(0) == '/') { 343 input = input.substring(1); 344 } 345 } else { 346 output = output.delete(index, output.length()); 347 } 348 } 349 printStep("2C", output.toString(), input); 350 // 2D. if the input buffer consists only of ".", then remove 351 // that from the input buffer else if the input buffer consists 352 // only of ".." and if the output buffer does not contain only 353 // the root slash "/", then move the ".." to the output buffer 354 // else delte it.; otherwise, 355 } else if (input.equals(".")) { 356 input = ""; 357 printStep("2D", output.toString(), input); 358 } else if (input.equals("..")) { 359 if (!output.toString().equals("/")) { 360 output.append(".."); 361 } 362 input = ""; 363 printStep("2D", output.toString(), input); 364 // 2E. move the first path segment (if any) in the input buffer 365 // to the end of the output buffer, including the initial "/" 366 // character (if any) and any subsequent characters up to, but not 367 // including, the next "/" character or the end of the input buffer. 368 } else { 369 int end = -1; 370 int begin = input.indexOf('/'); 371 if (begin == 0) { 372 end = input.indexOf('/', 1); 373 } else { 374 end = begin; 375 begin = 0; 376 } 377 String segment; 378 if (end == -1) { 379 segment = input.substring(begin); 380 input = ""; 381 } else { 382 segment = input.substring(begin, end); 383 input = input.substring(end); 384 } 385 output.append(segment); 386 printStep("2E", output.toString(), input); 387 } 388 } 389 390 // 3. Finally, if the only or last segment of the output buffer is 391 // "..", where ".." is a complete path segment not followed by a slash 392 // then append a slash "/". The output buffer is returned as the result 393 // of remove_dot_segments 394 if (output.toString().endsWith("..")) { 395 output.append("/"); 396 printStep("3 ", output.toString(), input); 397 } 398 399 return output.toString(); 400 } 401 printStep(String step, String output, String input)402 private static void printStep(String step, String output, String input) { 403 if (LOG.isDebugEnabled()) { 404 LOG.debug(" " + step + ": " + output); 405 if (output.length() == 0) { 406 LOG.debug("\t\t\t\t" + input); 407 } else { 408 LOG.debug("\t\t\t" + input); 409 } 410 } 411 } 412 } 413