1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one 3 * or more contributor license agreements. See the NOTICE file 4 * distributed with this work for additional information 5 * regarding copyright ownership. The ASF licenses this file 6 * to you under the Apache License, Version 2.0 (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, software 13 * distributed under the License is distributed on an "AS IS" BASIS, 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 * See the License for the specific language governing permissions and 16 * limitations under the License. 17 */ 18 /* 19 * $Id: SystemIDResolver.java 468655 2006-10-28 07:12:06Z minchau $ 20 */ 21 package org.apache.xml.utils; 22 23 import java.io.File; 24 25 import javax.xml.transform.TransformerException; 26 27 import org.apache.xml.utils.URI.MalformedURIException; 28 29 /** 30 * This class is used to resolve relative URIs and SystemID 31 * strings into absolute URIs. 32 * 33 * <p>This is a generic utility for resolving URIs, other than the 34 * fact that it's declared to throw TransformerException. Please 35 * see code comments for details on how resolution is performed.</p> 36 * @xsl.usage internal 37 */ 38 public class SystemIDResolver 39 { 40 41 /** 42 * Get an absolute URI from a given relative URI (local path). 43 * 44 * <p>The relative URI is a local filesystem path. The path can be 45 * absolute or relative. If it is a relative path, it is resolved relative 46 * to the system property "user.dir" if it is available; if not (i.e. in an 47 * Applet perhaps which throws SecurityException) then we just return the 48 * relative path. The space and backslash characters are also replaced to 49 * generate a good absolute URI.</p> 50 * 51 * @param localPath The relative URI to resolve 52 * 53 * @return Resolved absolute URI 54 */ getAbsoluteURIFromRelative(String localPath)55 public static String getAbsoluteURIFromRelative(String localPath) 56 { 57 if (localPath == null || localPath.length() == 0) 58 return ""; 59 60 // If the local path is a relative path, then it is resolved against 61 // the "user.dir" system property. 62 String absolutePath = localPath; 63 if (!isAbsolutePath(localPath)) 64 { 65 try 66 { 67 absolutePath = getAbsolutePathFromRelativePath(localPath); 68 } 69 // user.dir not accessible from applet 70 catch (SecurityException se) 71 { 72 return "file:" + localPath; 73 } 74 } 75 76 String urlString; 77 if (null != absolutePath) 78 { 79 if (absolutePath.startsWith(File.separator)) 80 urlString = "file://" + absolutePath; 81 else 82 urlString = "file:///" + absolutePath; 83 } 84 else 85 urlString = "file:" + localPath; 86 87 return replaceChars(urlString); 88 } 89 90 /** 91 * Return an absolute path from a relative path. 92 * 93 * @param relativePath A relative path 94 * @return The absolute path 95 */ getAbsolutePathFromRelativePath(String relativePath)96 private static String getAbsolutePathFromRelativePath(String relativePath) 97 { 98 return new File(relativePath).getAbsolutePath(); 99 } 100 101 /** 102 * Return true if the systemId denotes an absolute URI . 103 * 104 * @param systemId The systemId string 105 * @return true if the systemId is an an absolute URI 106 */ isAbsoluteURI(String systemId)107 public static boolean isAbsoluteURI(String systemId) 108 { 109 /** http://www.ietf.org/rfc/rfc2396.txt 110 * Authors should be aware that a path segment which contains a colon 111 * character cannot be used as the first segment of a relative URI path 112 * (e.g., "this:that"), because it would be mistaken for a scheme name. 113 **/ 114 /** 115 * %REVIEW% Can we assume here that systemId is a valid URI? 116 * It looks like we cannot ( See discussion of this common problem in 117 * Bugzilla Bug 22777 ). 118 **/ 119 //"fix" for Bugzilla Bug 22777 120 if(isWindowsAbsolutePath(systemId)){ 121 return false; 122 } 123 124 final int fragmentIndex = systemId.indexOf('#'); 125 final int queryIndex = systemId.indexOf('?'); 126 final int slashIndex = systemId.indexOf('/'); 127 final int colonIndex = systemId.indexOf(':'); 128 129 //finding substring before '#', '?', and '/' 130 int index = systemId.length() -1; 131 if(fragmentIndex > 0) 132 index = fragmentIndex; 133 if((queryIndex > 0) && (queryIndex <index)) 134 index = queryIndex; 135 if((slashIndex > 0) && (slashIndex <index)) 136 index = slashIndex; 137 // return true if there is ':' before '#', '?', and '/' 138 return ((colonIndex >0) && (colonIndex<index)); 139 140 } 141 142 /** 143 * Return true if the local path is an absolute path. 144 * 145 * @param systemId The path string 146 * @return true if the path is absolute 147 */ isAbsolutePath(String systemId)148 public static boolean isAbsolutePath(String systemId) 149 { 150 if(systemId == null) 151 return false; 152 final File file = new File(systemId); 153 return file.isAbsolute(); 154 155 } 156 157 /** 158 * Return true if the local path is a Windows absolute path. 159 * 160 * @param systemId The path string 161 * @return true if the path is a Windows absolute path 162 */ isWindowsAbsolutePath(String systemId)163 private static boolean isWindowsAbsolutePath(String systemId) 164 { 165 if(!isAbsolutePath(systemId)) 166 return false; 167 // On Windows, an absolute path starts with "[drive_letter]:\". 168 if (systemId.length() > 2 169 && systemId.charAt(1) == ':' 170 && Character.isLetter(systemId.charAt(0)) 171 && (systemId.charAt(2) == '\\' || systemId.charAt(2) == '/')) 172 return true; 173 else 174 return false; 175 } 176 177 /** 178 * Replace spaces with "%20" and backslashes with forward slashes in 179 * the input string to generate a well-formed URI string. 180 * 181 * @param str The input string 182 * @return The string after conversion 183 */ replaceChars(String str)184 private static String replaceChars(String str) 185 { 186 StringBuffer buf = new StringBuffer(str); 187 int length = buf.length(); 188 for (int i = 0; i < length; i++) 189 { 190 char currentChar = buf.charAt(i); 191 // Replace space with "%20" 192 if (currentChar == ' ') 193 { 194 buf.setCharAt(i, '%'); 195 buf.insert(i+1, "20"); 196 length = length + 2; 197 i = i + 2; 198 } 199 // Replace backslash with forward slash 200 else if (currentChar == '\\') 201 { 202 buf.setCharAt(i, '/'); 203 } 204 } 205 206 return buf.toString(); 207 } 208 209 /** 210 * Take a SystemID string and try to turn it into a good absolute URI. 211 * 212 * @param systemId A URI string, which may be absolute or relative. 213 * 214 * @return The resolved absolute URI 215 */ getAbsoluteURI(String systemId)216 public static String getAbsoluteURI(String systemId) 217 { 218 String absoluteURI = systemId; 219 if (isAbsoluteURI(systemId)) 220 { 221 // Only process the systemId if it starts with "file:". 222 if (systemId.startsWith("file:")) 223 { 224 String str = systemId.substring(5); 225 226 // Resolve the absolute path if the systemId starts with "file:///" 227 // or "file:/". Don't do anything if it only starts with "file://". 228 if (str != null && str.startsWith("/")) 229 { 230 if (str.startsWith("///") || !str.startsWith("//")) 231 { 232 // A Windows path containing a drive letter can be relative. 233 // A Unix path starting with "file:/" is always absolute. 234 int secondColonIndex = systemId.indexOf(':', 5); 235 if (secondColonIndex > 0) 236 { 237 String localPath = systemId.substring(secondColonIndex-1); 238 try { 239 if (!isAbsolutePath(localPath)) 240 absoluteURI = systemId.substring(0, secondColonIndex-1) + 241 getAbsolutePathFromRelativePath(localPath); 242 } 243 catch (SecurityException se) { 244 return systemId; 245 } 246 } 247 } 248 } 249 else 250 { 251 return getAbsoluteURIFromRelative(systemId.substring(5)); 252 } 253 254 return replaceChars(absoluteURI); 255 } 256 else 257 return systemId; 258 } 259 else 260 return getAbsoluteURIFromRelative(systemId); 261 262 } 263 264 265 /** 266 * Take a SystemID string and try to turn it into a good absolute URI. 267 * 268 * @param urlString SystemID string 269 * @param base The URI string used as the base for resolving the systemID 270 * 271 * @return The resolved absolute URI 272 * @throws TransformerException thrown if the string can't be turned into a URI. 273 */ getAbsoluteURI(String urlString, String base)274 public static String getAbsoluteURI(String urlString, String base) 275 throws TransformerException 276 { 277 if (base == null) 278 return getAbsoluteURI(urlString); 279 280 String absoluteBase = getAbsoluteURI(base); 281 URI uri = null; 282 try 283 { 284 URI baseURI = new URI(absoluteBase); 285 uri = new URI(baseURI, urlString); 286 } 287 catch (MalformedURIException mue) 288 { 289 throw new TransformerException(mue); 290 } 291 292 return replaceChars(uri.toString()); 293 } 294 295 } 296