comparison src/org/eclipse/jetty/util/resource/Resource.java @ 825:7fb7c1915788

remove jetty.util.B64Code
author Franklin Schmidt <fschmidt@gmail.com>
date Wed, 14 Sep 2016 16:38:33 -0600
parents 8e9db0bbf4f9
children f89abbfb3a8f
comparison
equal deleted inserted replaced
824:90bf40ba1ec2 825:7fb7c1915788
29 import java.net.URLConnection; 29 import java.net.URLConnection;
30 import java.text.DateFormat; 30 import java.text.DateFormat;
31 import java.util.Arrays; 31 import java.util.Arrays;
32 import java.util.Date; 32 import java.util.Date;
33 33
34 import org.eclipse.jetty.util.B64Code; 34 import java.util.Base64;
35 import org.eclipse.jetty.util.IO; 35 import org.eclipse.jetty.util.IO;
36 import org.eclipse.jetty.util.Loader; 36 import org.eclipse.jetty.util.Loader;
37 import org.eclipse.jetty.util.StringUtil; 37 import org.eclipse.jetty.util.StringUtil;
38 import org.eclipse.jetty.util.URIUtil; 38 import org.eclipse.jetty.util.URIUtil;
39 import org.slf4j.Logger; 39 import org.slf4j.Logger;
44 /** 44 /**
45 * Abstract resource class. 45 * Abstract resource class.
46 */ 46 */
47 public abstract class Resource implements ResourceFactory 47 public abstract class Resource implements ResourceFactory
48 { 48 {
49 private static final Logger LOG = LoggerFactory.getLogger(Resource.class); 49 private static final Logger LOG = LoggerFactory.getLogger(Resource.class);
50 public static boolean __defaultUseCaches = true; 50 public static boolean __defaultUseCaches = true;
51 volatile Object _associate; 51 volatile Object _associate;
52 52
53 /* ------------------------------------------------------------ */ 53 /* ------------------------------------------------------------ */
54 /** 54 /**
55 * Change the default setting for url connection caches. 55 * Change the default setting for url connection caches.
56 * Subsequent URLConnections will use this default. 56 * Subsequent URLConnections will use this default.
57 * @param useCaches 57 * @param useCaches
58 */ 58 */
59 public static void setDefaultUseCaches (boolean useCaches) 59 public static void setDefaultUseCaches (boolean useCaches)
60 { 60 {
61 __defaultUseCaches=useCaches; 61 __defaultUseCaches=useCaches;
62 } 62 }
63 63
64 /* ------------------------------------------------------------ */ 64 /* ------------------------------------------------------------ */
65 public static boolean getDefaultUseCaches () 65 public static boolean getDefaultUseCaches ()
66 { 66 {
67 return __defaultUseCaches; 67 return __defaultUseCaches;
68 } 68 }
69 69
70 /* ------------------------------------------------------------ */ 70 /* ------------------------------------------------------------ */
71 /** Construct a resource from a uri. 71 /** Construct a resource from a uri.
72 * @param uri A URI. 72 * @param uri A URI.
73 * @return A Resource object. 73 * @return A Resource object.
74 * @throws IOException Problem accessing URI 74 * @throws IOException Problem accessing URI
75 */ 75 */
76 public static Resource newResource(URI uri) 76 public static Resource newResource(URI uri)
77 throws IOException 77 throws IOException
78 { 78 {
79 return newResource(uri.toURL()); 79 return newResource(uri.toURL());
80 } 80 }
81 81
82 /* ------------------------------------------------------------ */ 82 /* ------------------------------------------------------------ */
83 /** Construct a resource from a url. 83 /** Construct a resource from a url.
84 * @param url A URL. 84 * @param url A URL.
85 * @return A Resource object. 85 * @return A Resource object.
86 * @throws IOException Problem accessing URL 86 * @throws IOException Problem accessing URL
87 */ 87 */
88 public static Resource newResource(URL url) 88 public static Resource newResource(URL url)
89 throws IOException 89 throws IOException
90 { 90 {
91 return newResource(url, __defaultUseCaches); 91 return newResource(url, __defaultUseCaches);
92 } 92 }
93 93
94 /* ------------------------------------------------------------ */ 94 /* ------------------------------------------------------------ */
95 /** 95 /**
96 * Construct a resource from a url. 96 * Construct a resource from a url.
97 * @param url the url for which to make the resource 97 * @param url the url for which to make the resource
98 * @param useCaches true enables URLConnection caching if applicable to the type of resource 98 * @param useCaches true enables URLConnection caching if applicable to the type of resource
99 * @return 99 * @return
100 */ 100 */
101 static Resource newResource(URL url, boolean useCaches) 101 static Resource newResource(URL url, boolean useCaches)
102 { 102 {
103 if (url==null) 103 if (url==null)
104 return null; 104 return null;
105 105
106 String url_string=url.toExternalForm(); 106 String url_string=url.toExternalForm();
107 if( url_string.startsWith( "file:")) 107 if( url_string.startsWith( "file:"))
108 { 108 {
109 try 109 try
110 { 110 {
111 FileResource fileResource= new FileResource(url); 111 FileResource fileResource= new FileResource(url);
112 return fileResource; 112 return fileResource;
113 } 113 }
114 catch(Exception e) 114 catch(Exception e)
115 { 115 {
116 LOG.debug("EXCEPTION",e); 116 LOG.debug("EXCEPTION",e);
117 return new BadResource(url,e.toString()); 117 return new BadResource(url,e.toString());
118 } 118 }
119 } 119 }
120 else if( url_string.startsWith( "jar:file:")) 120 else if( url_string.startsWith( "jar:file:"))
121 { 121 {
122 return new JarFileResource(url, useCaches); 122 return new JarFileResource(url, useCaches);
123 } 123 }
124 else if( url_string.startsWith( "jar:")) 124 else if( url_string.startsWith( "jar:"))
125 { 125 {
126 return new JarResource(url, useCaches); 126 return new JarResource(url, useCaches);
127 } 127 }
128 128
129 return new URLResource(url,null,useCaches); 129 return new URLResource(url,null,useCaches);
130 } 130 }
131 131
132 132
133 133
134 /* ------------------------------------------------------------ */ 134 /* ------------------------------------------------------------ */
135 /** Construct a resource from a string. 135 /** Construct a resource from a string.
136 * @param resource A URL or filename. 136 * @param resource A URL or filename.
137 * @return A Resource object. 137 * @return A Resource object.
138 */ 138 */
139 public static Resource newResource(String resource) 139 public static Resource newResource(String resource)
140 throws MalformedURLException, IOException 140 throws MalformedURLException, IOException
141 { 141 {
142 return newResource(resource, __defaultUseCaches); 142 return newResource(resource, __defaultUseCaches);
143 } 143 }
144 144
145 /* ------------------------------------------------------------ */ 145 /* ------------------------------------------------------------ */
146 /** Construct a resource from a string. 146 /** Construct a resource from a string.
147 * @param resource A URL or filename. 147 * @param resource A URL or filename.
148 * @param useCaches controls URLConnection caching 148 * @param useCaches controls URLConnection caching
149 * @return A Resource object. 149 * @return A Resource object.
150 */ 150 */
151 public static Resource newResource (String resource, boolean useCaches) 151 public static Resource newResource (String resource, boolean useCaches)
152 throws MalformedURLException, IOException 152 throws MalformedURLException, IOException
153 { 153 {
154 URL url=null; 154 URL url=null;
155 try 155 try
156 { 156 {
157 // Try to format as a URL? 157 // Try to format as a URL?
158 url = new URL(resource); 158 url = new URL(resource);
159 } 159 }
160 catch(MalformedURLException e) 160 catch(MalformedURLException e)
161 { 161 {
162 if(!resource.startsWith("ftp:") && 162 if(!resource.startsWith("ftp:") &&
163 !resource.startsWith("file:") && 163 !resource.startsWith("file:") &&
164 !resource.startsWith("jar:")) 164 !resource.startsWith("jar:"))
165 { 165 {
166 try 166 try
167 { 167 {
168 // It's a file. 168 // It's a file.
169 if (resource.startsWith("./")) 169 if (resource.startsWith("./"))
170 resource=resource.substring(2); 170 resource=resource.substring(2);
171 171
172 File file=new File(resource).getCanonicalFile(); 172 File file=new File(resource).getCanonicalFile();
173 url=Resource.toURL(file); 173 url=Resource.toURL(file);
174 174
175 URLConnection connection=url.openConnection(); 175 URLConnection connection=url.openConnection();
176 connection.setUseCaches(useCaches); 176 connection.setUseCaches(useCaches);
177 return new FileResource(url,connection,file); 177 return new FileResource(url,connection,file);
178 } 178 }
179 catch(Exception e2) 179 catch(Exception e2)
180 { 180 {
181 LOG.debug("EXCEPTION",e2); 181 LOG.debug("EXCEPTION",e2);
182 throw e; 182 throw e;
183 } 183 }
184 } 184 }
185 else 185 else
186 { 186 {
187 LOG.warn("Bad Resource: "+resource); 187 LOG.warn("Bad Resource: "+resource);
188 throw e; 188 throw e;
189 } 189 }
190 } 190 }
191 191
192 return newResource(url); 192 return newResource(url);
193 } 193 }
194 194
195 /* ------------------------------------------------------------ */ 195 /* ------------------------------------------------------------ */
196 public static Resource newResource (File file) 196 public static Resource newResource (File file)
197 throws MalformedURLException, IOException 197 throws MalformedURLException, IOException
198 { 198 {
199 file = file.getCanonicalFile(); 199 file = file.getCanonicalFile();
200 URL url = Resource.toURL(file); 200 URL url = Resource.toURL(file);
201 201
202 URLConnection connection = url.openConnection(); 202 URLConnection connection = url.openConnection();
203 FileResource fileResource = new FileResource(url, connection, file); 203 FileResource fileResource = new FileResource(url, connection, file);
204 return fileResource; 204 return fileResource;
205 } 205 }
206 206
207 /* ------------------------------------------------------------ */ 207 /* ------------------------------------------------------------ */
208 /** Construct a system resource from a string. 208 /** Construct a system resource from a string.
209 * The resource is tried as classloader resource before being 209 * The resource is tried as classloader resource before being
210 * treated as a normal resource. 210 * treated as a normal resource.
211 * @param resource Resource as string representation 211 * @param resource Resource as string representation
212 * @return The new Resource 212 * @return The new Resource
213 * @throws IOException Problem accessing resource. 213 * @throws IOException Problem accessing resource.
214 */ 214 */
215 public static Resource newSystemResource(String resource) 215 public static Resource newSystemResource(String resource)
216 throws IOException 216 throws IOException
217 { 217 {
218 URL url=null; 218 URL url=null;
219 // Try to format as a URL? 219 // Try to format as a URL?
220 ClassLoader loader=Thread.currentThread().getContextClassLoader(); 220 ClassLoader loader=Thread.currentThread().getContextClassLoader();
221 if (loader!=null) 221 if (loader!=null)
222 { 222 {
223 try 223 try
224 { 224 {
225 url = loader.getResource(resource); 225 url = loader.getResource(resource);
226 if (url == null && resource.startsWith("/")) 226 if (url == null && resource.startsWith("/"))
227 url = loader.getResource(resource.substring(1)); 227 url = loader.getResource(resource.substring(1));
228 } 228 }
229 catch (IllegalArgumentException e) 229 catch (IllegalArgumentException e)
230 { 230 {
231 // Catches scenario where a bad Windows path like "C:\dev" is 231 // Catches scenario where a bad Windows path like "C:\dev" is
232 // improperly escaped, which various downstream classloaders 232 // improperly escaped, which various downstream classloaders
233 // tend to have a problem with 233 // tend to have a problem with
234 url = null; 234 url = null;
235 } 235 }
236 } 236 }
237 if (url==null) 237 if (url==null)
238 { 238 {
239 loader=Resource.class.getClassLoader(); 239 loader=Resource.class.getClassLoader();
240 if (loader!=null) 240 if (loader!=null)
241 { 241 {
242 url=loader.getResource(resource); 242 url=loader.getResource(resource);
243 if (url==null && resource.startsWith("/")) 243 if (url==null && resource.startsWith("/"))
244 url=loader.getResource(resource.substring(1)); 244 url=loader.getResource(resource.substring(1));
245 } 245 }
246 } 246 }
247 247
248 if (url==null) 248 if (url==null)
249 { 249 {
250 url=ClassLoader.getSystemResource(resource); 250 url=ClassLoader.getSystemResource(resource);
251 if (url==null && resource.startsWith("/")) 251 if (url==null && resource.startsWith("/"))
252 url=ClassLoader.getSystemResource(resource.substring(1)); 252 url=ClassLoader.getSystemResource(resource.substring(1));
253 } 253 }
254 254
255 if (url==null) 255 if (url==null)
256 return null; 256 return null;
257 257
258 return newResource(url); 258 return newResource(url);
259 } 259 }
260 260
261 /* ------------------------------------------------------------ */ 261 /* ------------------------------------------------------------ */
262 /** Find a classpath resource. 262 /** Find a classpath resource.
263 */ 263 */
264 public static Resource newClassPathResource(String resource) 264 public static Resource newClassPathResource(String resource)
265 { 265 {
266 return newClassPathResource(resource,true,false); 266 return newClassPathResource(resource,true,false);
267 } 267 }
268 268
269 /* ------------------------------------------------------------ */ 269 /* ------------------------------------------------------------ */
270 /** Find a classpath resource. 270 /** Find a classpath resource.
271 * The {@link java.lang.Class#getResource(String)} method is used to lookup the resource. If it is not 271 * The {@link java.lang.Class#getResource(String)} method is used to lookup the resource. If it is not
272 * found, then the {@link Loader#getResource(Class, String, boolean)} method is used. 272 * found, then the {@link Loader#getResource(Class, String, boolean)} method is used.
273 * If it is still not found, then {@link ClassLoader#getSystemResource(String)} is used. 273 * If it is still not found, then {@link ClassLoader#getSystemResource(String)} is used.
274 * Unlike {@link ClassLoader#getSystemResource(String)} this method does not check for normal resources. 274 * Unlike {@link ClassLoader#getSystemResource(String)} this method does not check for normal resources.
275 * @param name The relative name of the resource 275 * @param name The relative name of the resource
276 * @param useCaches True if URL caches are to be used. 276 * @param useCaches True if URL caches are to be used.
277 * @param checkParents True if forced searching of parent Classloaders is performed to work around 277 * @param checkParents True if forced searching of parent Classloaders is performed to work around
278 * loaders with inverted priorities 278 * loaders with inverted priorities
279 * @return Resource or null 279 * @return Resource or null
280 */ 280 */
281 public static Resource newClassPathResource(String name,boolean useCaches,boolean checkParents) 281 public static Resource newClassPathResource(String name,boolean useCaches,boolean checkParents)
282 { 282 {
283 URL url=Resource.class.getResource(name); 283 URL url=Resource.class.getResource(name);
284 284
285 if (url==null) 285 if (url==null)
286 url=Loader.getResource(Resource.class,name,checkParents); 286 url=Loader.getResource(Resource.class,name,checkParents);
287 if (url==null) 287 if (url==null)
288 return null; 288 return null;
289 return newResource(url,useCaches); 289 return newResource(url,useCaches);
290 } 290 }
291 291
292 /* ------------------------------------------------------------ */ 292 /* ------------------------------------------------------------ */
293 public static boolean isContainedIn (Resource r, Resource containingResource) throws MalformedURLException 293 public static boolean isContainedIn (Resource r, Resource containingResource) throws MalformedURLException
294 { 294 {
295 return r.isContainedIn(containingResource); 295 return r.isContainedIn(containingResource);
296 } 296 }
297 297
298 /* ------------------------------------------------------------ */ 298 /* ------------------------------------------------------------ */
299 @Override 299 @Override
300 protected void finalize() 300 protected void finalize()
301 { 301 {
302 release(); 302 release();
303 } 303 }
304 304
305 /* ------------------------------------------------------------ */ 305 /* ------------------------------------------------------------ */
306 public abstract boolean isContainedIn (Resource r) throws MalformedURLException; 306 public abstract boolean isContainedIn (Resource r) throws MalformedURLException;
307 307
308 308
309 /* ------------------------------------------------------------ */ 309 /* ------------------------------------------------------------ */
310 /** Release any temporary resources held by the resource. 310 /** Release any temporary resources held by the resource.
311 */ 311 */
312 public abstract void release(); 312 public abstract void release();
313 313
314 314
315 /* ------------------------------------------------------------ */ 315 /* ------------------------------------------------------------ */
316 /** 316 /**
317 * Returns true if the respresened resource exists. 317 * Returns true if the respresened resource exists.
318 */ 318 */
319 public abstract boolean exists(); 319 public abstract boolean exists();
320 320
321 321
322 /* ------------------------------------------------------------ */ 322 /* ------------------------------------------------------------ */
323 /** 323 /**
324 * Returns true if the respresenetd resource is a container/directory. 324 * Returns true if the respresenetd resource is a container/directory.
325 * If the resource is not a file, resources ending with "/" are 325 * If the resource is not a file, resources ending with "/" are
326 * considered directories. 326 * considered directories.
327 */ 327 */
328 public abstract boolean isDirectory(); 328 public abstract boolean isDirectory();
329 329
330 /* ------------------------------------------------------------ */ 330 /* ------------------------------------------------------------ */
331 /** 331 /**
332 * Returns the last modified time 332 * Returns the last modified time
333 */ 333 */
334 public abstract long lastModified(); 334 public abstract long lastModified();
335 335
336 336
337 /* ------------------------------------------------------------ */ 337 /* ------------------------------------------------------------ */
338 /** 338 /**
339 * Return the length of the resource 339 * Return the length of the resource
340 */ 340 */
341 public abstract long length(); 341 public abstract long length();
342 342
343 343
344 /* ------------------------------------------------------------ */ 344 /* ------------------------------------------------------------ */
345 /** 345 /**
346 * Returns an URL representing the given resource 346 * Returns an URL representing the given resource
347 */ 347 */
348 public abstract URL getURL(); 348 public abstract URL getURL();
349 349
350 /* ------------------------------------------------------------ */ 350 /* ------------------------------------------------------------ */
351 /** 351 /**
352 * Returns an URI representing the given resource 352 * Returns an URI representing the given resource
353 */ 353 */
354 public URI getURI() 354 public URI getURI()
355 { 355 {
356 try 356 try
357 { 357 {
358 return getURL().toURI(); 358 return getURL().toURI();
359 } 359 }
360 catch(Exception e) 360 catch(Exception e)
361 { 361 {
362 throw new RuntimeException(e); 362 throw new RuntimeException(e);
363 } 363 }
364 } 364 }
365 365
366 366
367 /* ------------------------------------------------------------ */ 367 /* ------------------------------------------------------------ */
368 /** 368 /**
369 * Returns an File representing the given resource or NULL if this 369 * Returns an File representing the given resource or NULL if this
370 * is not possible. 370 * is not possible.
371 */ 371 */
372 public abstract File getFile() 372 public abstract File getFile()
373 throws IOException; 373 throws IOException;
374 374
375 375
376 /* ------------------------------------------------------------ */ 376 /* ------------------------------------------------------------ */
377 /** 377 /**
378 * Returns the name of the resource 378 * Returns the name of the resource
379 */ 379 */
380 public abstract String getName(); 380 public abstract String getName();
381 381
382 382
383 /* ------------------------------------------------------------ */ 383 /* ------------------------------------------------------------ */
384 /** 384 /**
385 * Returns an input stream to the resource 385 * Returns an input stream to the resource
386 */ 386 */
387 public abstract InputStream getInputStream() 387 public abstract InputStream getInputStream()
388 throws java.io.IOException; 388 throws java.io.IOException;
389 389
390 /* ------------------------------------------------------------ */ 390 /* ------------------------------------------------------------ */
391 /** 391 /**
392 * Returns an output stream to the resource 392 * Returns an output stream to the resource
393 */ 393 */
394 public abstract OutputStream getOutputStream() 394 public abstract OutputStream getOutputStream()
395 throws java.io.IOException, SecurityException; 395 throws java.io.IOException, SecurityException;
396 396
397 /* ------------------------------------------------------------ */ 397 /* ------------------------------------------------------------ */
398 /** 398 /**
399 * Deletes the given resource 399 * Deletes the given resource
400 */ 400 */
401 public abstract boolean delete() 401 public abstract boolean delete()
402 throws SecurityException; 402 throws SecurityException;
403 403
404 /* ------------------------------------------------------------ */ 404 /* ------------------------------------------------------------ */
405 /** 405 /**
406 * Rename the given resource 406 * Rename the given resource
407 */ 407 */
408 public abstract boolean renameTo( Resource dest) 408 public abstract boolean renameTo( Resource dest)
409 throws SecurityException; 409 throws SecurityException;
410 410
411 /* ------------------------------------------------------------ */ 411 /* ------------------------------------------------------------ */
412 /** 412 /**
413 * Returns a list of resource names contained in the given resource 413 * Returns a list of resource names contained in the given resource
414 * The resource names are not URL encoded. 414 * The resource names are not URL encoded.
415 */ 415 */
416 public abstract String[] list(); 416 public abstract String[] list();
417 417
418 /* ------------------------------------------------------------ */ 418 /* ------------------------------------------------------------ */
419 /** 419 /**
420 * Returns the resource contained inside the current resource with the 420 * Returns the resource contained inside the current resource with the
421 * given name. 421 * given name.
422 * @param path The path segment to add, which should be encoded by the 422 * @param path The path segment to add, which should be encoded by the
423 * encode method. 423 * encode method.
424 */ 424 */
425 public abstract Resource addPath(String path) 425 public abstract Resource addPath(String path)
426 throws IOException,MalformedURLException; 426 throws IOException,MalformedURLException;
427 427
428 /* ------------------------------------------------------------ */ 428 /* ------------------------------------------------------------ */
429 /** Get a resource from withing this resource. 429 /** Get a resource from withing this resource.
430 * <p> 430 * <p>
431 * This method is essentially an alias for {@link #addPath(String)}, but without checked exceptions. 431 * This method is essentially an alias for {@link #addPath(String)}, but without checked exceptions.
432 * This method satisfied the {@link ResourceFactory} interface. 432 * This method satisfied the {@link ResourceFactory} interface.
433 * @see org.eclipse.jetty.util.resource.ResourceFactory#getResource(java.lang.String) 433 * @see org.eclipse.jetty.util.resource.ResourceFactory#getResource(java.lang.String)
434 */ 434 */
435 public Resource getResource(String path) 435 public Resource getResource(String path)
436 { 436 {
437 try 437 try
438 { 438 {
439 return addPath(path); 439 return addPath(path);
440 } 440 }
441 catch(Exception e) 441 catch(Exception e)
442 { 442 {
443 LOG.debug("",e); 443 LOG.debug("",e);
444 return null; 444 return null;
445 } 445 }
446 } 446 }
447 447
448 /* ------------------------------------------------------------ */ 448 /* ------------------------------------------------------------ */
449 /** Encode according to this resource type. 449 /** Encode according to this resource type.
450 * The default implementation calls URI.encodePath(uri) 450 * The default implementation calls URI.encodePath(uri)
451 * @param uri 451 * @param uri
452 * @return String encoded for this resource type. 452 * @return String encoded for this resource type.
453 */ 453 */
454 public String encode(String uri) 454 public String encode(String uri)
455 { 455 {
456 return URIUtil.encodePath(uri); 456 return URIUtil.encodePath(uri);
457 } 457 }
458 458
459 /* ------------------------------------------------------------ */ 459 /* ------------------------------------------------------------ */
460 public Object getAssociate() 460 public Object getAssociate()
461 { 461 {
462 return _associate; 462 return _associate;
463 } 463 }
464 464
465 /* ------------------------------------------------------------ */ 465 /* ------------------------------------------------------------ */
466 public void setAssociate(Object o) 466 public void setAssociate(Object o)
467 { 467 {
468 _associate=o; 468 _associate=o;
469 } 469 }
470 470
471 /* ------------------------------------------------------------ */ 471 /* ------------------------------------------------------------ */
472 /** 472 /**
473 * @return The canonical Alias of this resource or null if none. 473 * @return The canonical Alias of this resource or null if none.
474 */ 474 */
475 public URL getAlias() 475 public URL getAlias()
476 { 476 {
477 return null; 477 return null;
478 } 478 }
479 479
480 /* ------------------------------------------------------------ */ 480 /* ------------------------------------------------------------ */
481 /** Get the resource list as a HTML directory listing. 481 /** Get the resource list as a HTML directory listing.
482 * @param base The base URL 482 * @param base The base URL
483 * @param parent True if the parent directory should be included 483 * @param parent True if the parent directory should be included
484 * @return String of HTML 484 * @return String of HTML
485 */ 485 */
486 public String getListHTML(String base,boolean parent) 486 public String getListHTML(String base,boolean parent)
487 throws IOException 487 throws IOException
488 { 488 {
489 base=URIUtil.canonicalPath(base); 489 base=URIUtil.canonicalPath(base);
490 if (base==null || !isDirectory()) 490 if (base==null || !isDirectory())
491 return null; 491 return null;
492 492
493 String[] ls = list(); 493 String[] ls = list();
494 if (ls==null) 494 if (ls==null)
495 return null; 495 return null;
496 Arrays.sort(ls); 496 Arrays.sort(ls);
497 497
498 String decodedBase = URIUtil.decodePath(base); 498 String decodedBase = URIUtil.decodePath(base);
499 String title = "Directory: "+deTag(decodedBase); 499 String title = "Directory: "+deTag(decodedBase);
500 500
501 StringBuilder buf=new StringBuilder(4096); 501 StringBuilder buf=new StringBuilder(4096);
502 buf.append("<HTML><HEAD>"); 502 buf.append("<HTML><HEAD>");
503 buf.append("<LINK HREF=\"").append("jetty-dir.css").append("\" REL=\"stylesheet\" TYPE=\"text/css\"/><TITLE>"); 503 buf.append("<LINK HREF=\"").append("jetty-dir.css").append("\" REL=\"stylesheet\" TYPE=\"text/css\"/><TITLE>");
504 buf.append(title); 504 buf.append(title);
505 buf.append("</TITLE></HEAD><BODY>\n<H1>"); 505 buf.append("</TITLE></HEAD><BODY>\n<H1>");
506 buf.append(title); 506 buf.append(title);
507 buf.append("</H1>\n<TABLE BORDER=0>\n"); 507 buf.append("</H1>\n<TABLE BORDER=0>\n");
508 508
509 if (parent) 509 if (parent)
510 { 510 {
511 buf.append("<TR><TD><A HREF=\""); 511 buf.append("<TR><TD><A HREF=\"");
512 buf.append(URIUtil.addPaths(base,"../")); 512 buf.append(URIUtil.addPaths(base,"../"));
513 buf.append("\">Parent Directory</A></TD><TD></TD><TD></TD></TR>\n"); 513 buf.append("\">Parent Directory</A></TD><TD></TD><TD></TD></TR>\n");
514 } 514 }
515 515
516 String encodedBase = hrefEncodeURI(base); 516 String encodedBase = hrefEncodeURI(base);
517 517
518 DateFormat dfmt=DateFormat.getDateTimeInstance(DateFormat.MEDIUM, 518 DateFormat dfmt=DateFormat.getDateTimeInstance(DateFormat.MEDIUM,
519 DateFormat.MEDIUM); 519 DateFormat.MEDIUM);
520 for (int i=0 ; i< ls.length ; i++) 520 for (int i=0 ; i< ls.length ; i++)
521 { 521 {
522 Resource item = addPath(ls[i]); 522 Resource item = addPath(ls[i]);
523 523
524 buf.append("\n<TR><TD><A HREF=\""); 524 buf.append("\n<TR><TD><A HREF=\"");
525 String path=URIUtil.addPaths(encodedBase,URIUtil.encodePath(ls[i])); 525 String path=URIUtil.addPaths(encodedBase,URIUtil.encodePath(ls[i]));
526 526
527 buf.append(path); 527 buf.append(path);
528 528
529 if (item.isDirectory() && !path.endsWith("/")) 529 if (item.isDirectory() && !path.endsWith("/"))
530 buf.append(URIUtil.SLASH); 530 buf.append(URIUtil.SLASH);
531 531
532 // URIUtil.encodePath(buf,path); 532 // URIUtil.encodePath(buf,path);
533 buf.append("\">"); 533 buf.append("\">");
534 buf.append(deTag(ls[i])); 534 buf.append(deTag(ls[i]));
535 buf.append("&nbsp;"); 535 buf.append("&nbsp;");
536 buf.append("</A></TD><TD ALIGN=right>"); 536 buf.append("</A></TD><TD ALIGN=right>");
537 buf.append(item.length()); 537 buf.append(item.length());
538 buf.append(" bytes&nbsp;</TD><TD>"); 538 buf.append(" bytes&nbsp;</TD><TD>");
539 buf.append(dfmt.format(new Date(item.lastModified()))); 539 buf.append(dfmt.format(new Date(item.lastModified())));
540 buf.append("</TD></TR>"); 540 buf.append("</TD></TR>");
541 } 541 }
542 buf.append("</TABLE>\n"); 542 buf.append("</TABLE>\n");
543 buf.append("</BODY></HTML>\n"); 543 buf.append("</BODY></HTML>\n");
544 544
545 return buf.toString(); 545 return buf.toString();
546 } 546 }
547 547
548 /** 548 /**
549 * Encode any characters that could break the URI string in an HREF. 549 * Encode any characters that could break the URI string in an HREF.
550 * Such as <a href="/path/to;<script>Window.alert("XSS"+'%20'+"here");</script>">Link</a> 550 * Such as <a href="/path/to;<script>Window.alert("XSS"+'%20'+"here");</script>">Link</a>
551 * 551 *
552 * The above example would parse incorrectly on various browsers as the "<" or '"' characters 552 * The above example would parse incorrectly on various browsers as the "<" or '"' characters
553 * would end the href attribute value string prematurely. 553 * would end the href attribute value string prematurely.
554 * 554 *
555 * @param raw the raw text to encode. 555 * @param raw the raw text to encode.
556 * @return the defanged text. 556 * @return the defanged text.
557 */ 557 */
558 private static String hrefEncodeURI(String raw) 558 private static String hrefEncodeURI(String raw)
559 { 559 {
560 StringBuffer buf = null; 560 StringBuffer buf = null;
561 561
562 loop: 562 loop:
563 for (int i=0;i<raw.length();i++) 563 for (int i=0;i<raw.length();i++)
564 { 564 {
565 char c=raw.charAt(i); 565 char c=raw.charAt(i);
566 switch(c) 566 switch(c)
567 { 567 {
568 case '\'': 568 case '\'':
569 case '"': 569 case '"':
570 case '<': 570 case '<':
571 case '>': 571 case '>':
572 buf=new StringBuffer(raw.length()<<1); 572 buf=new StringBuffer(raw.length()<<1);
573 break loop; 573 break loop;
574 } 574 }
575 } 575 }
576 if (buf==null) 576 if (buf==null)
577 return raw; 577 return raw;
578 578
579 for (int i=0;i<raw.length();i++) 579 for (int i=0;i<raw.length();i++)
580 { 580 {
581 char c=raw.charAt(i); 581 char c=raw.charAt(i);
582 switch(c) 582 switch(c)
583 { 583 {
584 case '"': 584 case '"':
585 buf.append("%22"); 585 buf.append("%22");
586 continue; 586 continue;
587 case '\'': 587 case '\'':
588 buf.append("%27"); 588 buf.append("%27");
589 continue; 589 continue;
590 case '<': 590 case '<':
591 buf.append("%3C"); 591 buf.append("%3C");
592 continue; 592 continue;
593 case '>': 593 case '>':
594 buf.append("%3E"); 594 buf.append("%3E");
595 continue; 595 continue;
596 default: 596 default:
597 buf.append(c); 597 buf.append(c);
598 continue; 598 continue;
599 } 599 }
600 } 600 }
601 601
602 return buf.toString(); 602 return buf.toString();
603 } 603 }
604 604
605 private static String deTag(String raw) 605 private static String deTag(String raw)
606 { 606 {
607 return StringUtil.replace( StringUtil.replace(raw,"<","&lt;"), ">", "&gt;"); 607 return StringUtil.replace( StringUtil.replace(raw,"<","&lt;"), ">", "&gt;");
608 } 608 }
609 609
610 /* ------------------------------------------------------------ */ 610 /* ------------------------------------------------------------ */
611 /** 611 /**
612 * @param out 612 * @param out
613 * @param start First byte to write 613 * @param start First byte to write
614 * @param count Bytes to write or -1 for all of them. 614 * @param count Bytes to write or -1 for all of them.
615 */ 615 */
616 public void writeTo(OutputStream out,long start,long count) 616 public void writeTo(OutputStream out,long start,long count)
617 throws IOException 617 throws IOException
618 { 618 {
619 InputStream in = getInputStream(); 619 InputStream in = getInputStream();
620 try 620 try
621 { 621 {
622 in.skip(start); 622 in.skip(start);
623 if (count<0) 623 if (count<0)
624 IO.copy(in,out); 624 IO.copy(in,out);
625 else 625 else
626 IO.copy(in,out,count); 626 IO.copy(in,out,count);
627 } 627 }
628 finally 628 finally
629 { 629 {
630 in.close(); 630 in.close();
631 } 631 }
632 } 632 }
633 633
634 /* ------------------------------------------------------------ */ 634 /* ------------------------------------------------------------ */
635 public void copyTo(File destination) 635 public void copyTo(File destination)
636 throws IOException 636 throws IOException
637 { 637 {
638 if (destination.exists()) 638 if (destination.exists())
639 throw new IllegalArgumentException(destination+" exists"); 639 throw new IllegalArgumentException(destination+" exists");
640 writeTo(new FileOutputStream(destination),0,-1); 640 writeTo(new FileOutputStream(destination),0,-1);
641 } 641 }
642 642
643 /* ------------------------------------------------------------ */ 643 /* ------------------------------------------------------------ */
644 public String getWeakETag() 644 public String getWeakETag()
645 { 645 {
646 try 646 StringBuilder b = new StringBuilder(32);
647 { 647 b.append("W/\"");
648 StringBuilder b = new StringBuilder(32); 648
649 b.append("W/\""); 649 long lhash = lastModified() ^ getName().hashCode() ^ length();
650 650 byte[] a = new byte[Long.BYTES];
651 String name=getName(); 651 for( int i=0; i<a.length; i++ ) {
652 int length=name.length(); 652 a[i] = (byte)lhash;
653 long lhash=0; 653 lhash >>= 8;
654 for (int i=0; i<length;i++) 654 }
655 lhash=31*lhash+name.charAt(i); 655 b.append( Base64.getEncoder().encodeToString(a) );
656 656
657 B64Code.encode(lastModified()^lhash,b); 657 b.append('"');
658 B64Code.encode(length()^lhash,b); 658 return b.toString();
659 b.append('"'); 659 }
660 return b.toString(); 660
661 } 661 /* ------------------------------------------------------------ */
662 catch (IOException e) 662 /** Generate a properly encoded URL from a {@link File} instance.
663 { 663 * @param file Target file.
664 throw new RuntimeException(e); 664 * @return URL of the target file.
665 } 665 * @throws MalformedURLException
666 } 666 */
667 667 public static URL toURL(File file) throws MalformedURLException
668 /* ------------------------------------------------------------ */ 668 {
669 /** Generate a properly encoded URL from a {@link File} instance. 669 return file.toURI().toURL();
670 * @param file Target file. 670 }
671 * @return URL of the target file.
672 * @throws MalformedURLException
673 */
674 public static URL toURL(File file) throws MalformedURLException
675 {
676 return file.toURI().toURL();
677 }
678 } 671 }