]> WPIA git - gigi.git/blob - lib/jetty/org/eclipse/jetty/util/resource/FileResource.java
Update notes about password security
[gigi.git] / lib / jetty / org / eclipse / jetty / util / resource / FileResource.java
1 //
2 //  ========================================================================
3 //  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
4 //  ------------------------------------------------------------------------
5 //  All rights reserved. This program and the accompanying materials
6 //  are made available under the terms of the Eclipse Public License v1.0
7 //  and Apache License v2.0 which accompanies this distribution.
8 //
9 //      The Eclipse Public License is available at
10 //      http://www.eclipse.org/legal/epl-v10.html
11 //
12 //      The Apache License v2.0 is available at
13 //      http://www.opensource.org/licenses/apache2.0.php
14 //
15 //  You may elect to redistribute this code under either of these licenses.
16 //  ========================================================================
17 //
18
19 package org.eclipse.jetty.util.resource;
20
21 import java.io.File;
22 import java.io.FileInputStream;
23 import java.io.IOException;
24 import java.io.InputStream;
25 import java.net.MalformedURLException;
26 import java.net.URI;
27 import java.net.URISyntaxException;
28 import java.net.URL;
29 import java.net.URLConnection;
30 import java.nio.channels.FileChannel;
31 import java.nio.channels.ReadableByteChannel;
32 import java.nio.file.StandardOpenOption;
33 import java.security.Permission;
34
35 import org.eclipse.jetty.util.IO;
36 import org.eclipse.jetty.util.URIUtil;
37 import org.eclipse.jetty.util.log.Log;
38 import org.eclipse.jetty.util.log.Logger;
39
40
41 /* ------------------------------------------------------------ */
42 /** File Resource.
43  *
44  * Handle resources of implied or explicit file type.
45  * This class can check for aliasing in the filesystem (eg case
46  * insensitivity).  By default this is turned on, or it can be controlled 
47  * by calling the static method @see FileResource#setCheckAliases(boolean)
48  * 
49  */
50 public class FileResource extends Resource
51 {
52     private static final Logger LOG = Log.getLogger(FileResource.class);
53
54     /* ------------------------------------------------------------ */
55     private final File _file;
56     private final String _uri;
57     private final URI _alias;
58     
59     /* -------------------------------------------------------- */
60     public FileResource(URL url)
61         throws IOException, URISyntaxException
62     {
63         File file;
64         try
65         {
66             // Try standard API to convert URL to file.
67             file =new File(url.toURI());
68         }
69         catch (URISyntaxException e) 
70         {
71             throw e;
72         }
73         catch (Exception e)
74         {
75             if (!url.toString().startsWith("file:"))
76                 throw new IllegalArgumentException("!file:");
77             
78             LOG.ignore(e);
79             try
80             {
81                 // Assume that File.toURL produced unencoded chars. So try encoding them.
82                 String file_url="file:"+URIUtil.encodePath(url.toString().substring(5));           
83                 URI uri = new URI(file_url);
84                 if (uri.getAuthority()==null) 
85                     file = new File(uri);
86                 else
87                     file = new File("//"+uri.getAuthority()+URIUtil.decodePath(url.getFile()));
88             }
89             catch (Exception e2)
90             {
91                 LOG.ignore(e2);
92                 // Still can't get the file.  Doh! try good old hack!
93                 URLConnection connection=url.openConnection();
94                 Permission perm = connection.getPermission();
95                 file = new File(perm==null?url.getFile():perm.getName());
96             }
97         }
98         
99         _file=file;
100         _uri=normalizeURI(_file,url.toURI());
101         _alias=checkAlias(_file);
102     }
103
104     /* -------------------------------------------------------- */
105     public FileResource(URI uri)
106     {
107         File file=new File(uri);
108         _file=file;
109         URI file_uri=_file.toURI();
110         _uri=normalizeURI(_file,uri);
111         
112         if (!_uri.equals(file_uri.toString()))
113         {
114             // URI and File URI are different.  Is it just an encoding difference?
115             if (!file_uri.toString().equals(URIUtil.decodePath(uri.toString())))
116                  _alias=_file.toURI();
117             else
118                 _alias=checkAlias(_file);
119         }
120         else
121             _alias=checkAlias(_file);
122     }
123
124     /* -------------------------------------------------------- */
125     FileResource(File file)
126     {
127         _file=file;
128         _uri=normalizeURI(_file,_file.toURI());
129         _alias=checkAlias(_file);
130     }
131
132     /* -------------------------------------------------------- */
133     private static String normalizeURI(File file, URI uri)
134     {
135         String u =uri.toASCIIString();
136         if (file.isDirectory())
137         {
138             if(!u.endsWith("/"))
139                 u+="/";
140         } 
141         else if (file.exists() && u.endsWith("/"))
142             u=u.substring(0,u.length()-1);
143         return u;
144     }
145
146     /* -------------------------------------------------------- */
147     private static URI checkAlias(File file)
148     {
149         try
150         {
151             String abs=file.getAbsolutePath();
152             String can=file.getCanonicalPath();
153
154             if (!abs.equals(can))
155             {
156                 LOG.debug("ALIAS abs={} can={}",abs,can);
157
158                 URI alias=new File(can).toURI();
159                 // Have to encode the path as File.toURI does not!
160                 return new URI("file://"+URIUtil.encodePath(alias.getPath()));  
161             }
162         }
163         catch(Exception e)
164         {
165             LOG.warn("bad alias for {}: {}",file,e.toString());
166             LOG.debug(e);
167             try
168             {
169                 return new URI("http://eclipse.org/bad/canonical/alias");
170             }
171             catch(Exception e2)
172             {
173                 LOG.ignore(e2);
174                 throw new RuntimeException(e);
175             }
176         }
177
178         return null;
179     }
180     
181     /* -------------------------------------------------------- */
182     @Override
183     public Resource addPath(String path)
184         throws IOException,MalformedURLException
185     {
186         path = org.eclipse.jetty.util.URIUtil.canonicalPath(path);
187
188         if (path==null)
189             throw new MalformedURLException();   
190         
191         if ("/".equals(path))
192             return this;
193         
194         path=URIUtil.encodePath(path);
195         // The encoded path should be a suffix of the resource (give or take a directory / )
196         URI uri;
197         try
198         {
199             if (_file.isDirectory())
200             {
201                 // treat all paths being added as relative
202                 uri=new URI(URIUtil.addPaths(_uri,path));
203             }
204             else
205             {
206                 uri=new URI(_uri+path);
207             }
208         }
209         catch(final URISyntaxException e)
210         {
211             throw new MalformedURLException(){{initCause(e);}};
212         }
213
214         return new FileResource(uri);
215     }
216    
217     
218     /* ------------------------------------------------------------ */
219     @Override
220     public URI getAlias()
221     {
222         return _alias;
223     }
224     
225     /* -------------------------------------------------------- */
226     /**
227      * Returns true if the resource exists.
228      */
229     @Override
230     public boolean exists()
231     {
232         return _file.exists();
233     }
234         
235     /* -------------------------------------------------------- */
236     /**
237      * Returns the last modified time
238      */
239     @Override
240     public long lastModified()
241     {
242         return _file.lastModified();
243     }
244
245     /* -------------------------------------------------------- */
246     /**
247      * Returns true if the resource is a container/directory.
248      */
249     @Override
250     public boolean isDirectory()
251     {
252         return _file.exists() && _file.isDirectory() || _uri.endsWith("/");
253     }
254
255     /* --------------------------------------------------------- */
256     /**
257      * Return the length of the resource
258      */
259     @Override
260     public long length()
261     {
262         return _file.length();
263     }
264         
265
266     /* --------------------------------------------------------- */
267     /**
268      * Returns the name of the resource
269      */
270     @Override
271     public String getName()
272     {
273         return _file.getAbsolutePath();
274     }
275         
276     /* ------------------------------------------------------------ */
277     /**
278      * Returns an File representing the given resource or NULL if this
279      * is not possible.
280      */
281     @Override
282     public File getFile()
283     {
284         return _file;
285     }
286         
287     /* --------------------------------------------------------- */
288     /**
289      * Returns an input stream to the resource
290      */
291     @Override
292     public InputStream getInputStream() throws IOException
293     {
294         return new FileInputStream(_file);
295     }
296
297     /* ------------------------------------------------------------ */
298     @Override
299     public ReadableByteChannel getReadableByteChannel() throws IOException
300     {
301         return FileChannel.open(_file.toPath(),StandardOpenOption.READ);
302     }
303         
304     /* --------------------------------------------------------- */
305     /**
306      * Deletes the given resource
307      */
308     @Override
309     public boolean delete()
310         throws SecurityException
311     {
312         return _file.delete();
313     }
314
315     /* --------------------------------------------------------- */
316     /**
317      * Rename the given resource
318      */
319     @Override
320     public boolean renameTo( Resource dest)
321         throws SecurityException
322     {
323         if( dest instanceof FileResource)
324             return _file.renameTo( ((FileResource)dest)._file);
325         else
326             return false;
327     }
328
329     /* --------------------------------------------------------- */
330     /**
331      * Returns a list of resources contained in the given resource
332      */
333     @Override
334     public String[] list()
335     {
336         String[] list =_file.list();
337         if (list==null)
338             return null;
339         for (int i=list.length;i-->0;)
340         {
341             if (new File(_file,list[i]).isDirectory() &&
342                 !list[i].endsWith("/"))
343                 list[i]+="/";
344         }
345         return list;
346     }
347     
348     /* ------------------------------------------------------------ */
349     /** 
350      * @param o
351      * @return <code>true</code> of the object <code>o</code> is a {@link FileResource} pointing to the same file as this resource. 
352      */
353     @Override
354     public boolean equals( Object o)
355     {
356         if (this == o)
357             return true;
358
359         if (null == o || ! (o instanceof FileResource))
360             return false;
361
362         FileResource f=(FileResource)o;
363         return f._file == _file || (null != _file && _file.equals(f._file));
364     }
365
366     /* ------------------------------------------------------------ */
367     /**
368      * @return the hashcode.
369      */
370     @Override
371     public int hashCode()
372     {
373        return null == _file ? super.hashCode() : _file.hashCode();
374     }
375     
376     /* ------------------------------------------------------------ */
377     @Override
378     public void copyTo(File destination)
379         throws IOException
380     {
381         if (isDirectory())
382         {
383             IO.copyDir(getFile(),destination);
384         }
385         else
386         {
387             if (destination.exists())
388                 throw new IllegalArgumentException(destination+" exists");
389             IO.copy(getFile(),destination);
390         }
391     }
392
393     @Override
394     public boolean isContainedIn(Resource r) throws MalformedURLException
395     {
396         return false;
397     }
398
399     @Override
400     public void close()
401     {
402     }
403
404     @Override
405     public URL getURL()
406     {
407         try
408         {
409             return new URL(_uri);
410         }
411         catch (MalformedURLException e)
412         {
413             throw new IllegalStateException(e);
414         }
415     }
416     
417     @Override
418     public URI getURI()
419     {
420         return _file.toURI();
421     }
422
423     @Override
424     public String toString()
425     {
426         return _uri;
427     }
428
429 }