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