| /* FileURLConnection.java -- URLConnection class for "file" protocol | |
| Copyright (C) 1998, 1999, 2003 Free Software Foundation, Inc. | |
| This file is part of GNU Classpath. | |
| GNU Classpath is free software; you can redistribute it and/or modify | |
| it under the terms of the GNU General Public License as published by | |
| the Free Software Foundation; either version 2, or (at your option) | |
| any later version. | |
| GNU Classpath is distributed in the hope that it will be useful, but | |
| WITHOUT ANY WARRANTY; without even the implied warranty of | |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| General Public License for more details. | |
| You should have received a copy of the GNU General Public License | |
| along with GNU Classpath; see the file COPYING. If not, write to the | |
| Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | |
| 02110-1301 USA. | |
| Linking this library statically or dynamically with other modules is | |
| making a combined work based on this library. Thus, the terms and | |
| conditions of the GNU General Public License cover the whole | |
| combination. | |
| As a special exception, the copyright holders of this library give you | |
| permission to link this library with independent modules to produce an | |
| executable, regardless of the license terms of these independent | |
| modules, and to copy and distribute the resulting executable under | |
| terms of your choice, provided that you also meet, for each linked | |
| independent module, the terms and conditions of the license of that | |
| module. An independent module is a module which is not derived from | |
| or based on this library. If you modify this library, you may extend | |
| this exception to your version of the library, but you are not | |
| obligated to do so. If you do not wish to do so, delete this | |
| exception statement from your version. */ | |
| package gnu.java.net.protocol.file; | |
| import gnu.classpath.SystemProperties; | |
| import java.io.BufferedInputStream; | |
| import java.io.BufferedOutputStream; | |
| import java.io.ByteArrayInputStream; | |
| import java.io.ByteArrayOutputStream; | |
| import java.io.File; | |
| import java.io.FileInputStream; | |
| import java.io.FileOutputStream; | |
| import java.io.FilePermission; | |
| import java.io.IOException; | |
| import java.io.InputStream; | |
| import java.io.OutputStream; | |
| import java.io.OutputStreamWriter; | |
| import java.io.Writer; | |
| import java.net.ProtocolException; | |
| import java.net.URL; | |
| import java.net.URLConnection; | |
| import java.security.Permission; | |
| import java.text.SimpleDateFormat; | |
| import java.util.Date; | |
| import java.util.Locale; | |
| import java.net.MalformedURLException; | |
| /** | |
| * This subclass of java.net.URLConnection models a URLConnection via | |
| * the "file" protocol. | |
| * | |
| * @author Aaron M. Renn (arenn@urbanophile.com) | |
| * @author Nic Ferrier (nferrier@tapsellferrier.co.uk) | |
| * @author Warren Levy (warrenl@cygnus.com) | |
| */ | |
| public class Connection extends URLConnection | |
| { | |
| /** | |
| * Default permission for a file | |
| */ | |
| private static final String DEFAULT_PERMISSION = "read"; | |
| private static class StaticData | |
| { | |
| /** | |
| * HTTP-style DateFormat, used to format the last-modified header. | |
| */ | |
| static SimpleDateFormat dateFormat | |
| = new SimpleDateFormat("EEE, dd MMM yyyy hh:mm:ss 'GMT'", | |
| new Locale ("En", "Us", "Unix")); | |
| static String lineSeparator = | |
| SystemProperties.getProperty("line.separator"); | |
| } | |
| /** | |
| * This is a File object for this connection | |
| */ | |
| private File file; | |
| /** | |
| * If a directory, contains a list of files in the directory. | |
| */ | |
| private byte[] directoryListing; | |
| /** | |
| * InputStream if we are reading from the file | |
| */ | |
| private InputStream inputStream; | |
| /** | |
| * OutputStream if we are writing to the file | |
| */ | |
| private OutputStream outputStream; | |
| /** | |
| * FilePermission to read the file | |
| */ | |
| private FilePermission permission; | |
| /** | |
| * Calls superclass constructor to initialize. | |
| */ | |
| public Connection(URL url) | |
| { | |
| super (url); | |
| permission = new FilePermission(getURL().getFile(), DEFAULT_PERMISSION); | |
| } | |
| /** | |
| * Unquote "%" + hex quotes characters | |
| * | |
| * @param str The string to unquote or null. | |
| * | |
| * @return The unquoted string or null if str was null. | |
| * | |
| * @exception MalformedURLException If the given string contains invalid | |
| * escape sequences. | |
| * | |
| */ | |
| public static String unquote(String str) throws MalformedURLException | |
| { | |
| if (str == null) | |
| return null; | |
| final int MAX_BYTES_PER_UTF_8_CHAR = 3; | |
| byte[] buf = new byte[str.length()*MAX_BYTES_PER_UTF_8_CHAR]; | |
| int pos = 0; | |
| for (int i = 0; i < str.length(); i++) | |
| { | |
| char c = str.charAt(i); | |
| if (c == '%') | |
| { | |
| if (i + 2 >= str.length()) | |
| throw new MalformedURLException(str + " : Invalid quoted character"); | |
| int hi = Character.digit(str.charAt(++i), 16); | |
| int lo = Character.digit(str.charAt(++i), 16); | |
| if (lo < 0 || hi < 0) | |
| throw new MalformedURLException(str + " : Invalid quoted character"); | |
| buf[pos++] = (byte) (hi * 16 + lo); | |
| } | |
| else if (c > 127) { | |
| try { | |
| byte [] c_as_bytes = Character.toString(c).getBytes("utf-8"); | |
| final int c_length = c_as_bytes.length; | |
| System.arraycopy(c_as_bytes, 0, buf, pos, c_length); | |
| pos += c_length; | |
| } | |
| catch (java.io.UnsupportedEncodingException x2) { | |
| throw (Error) new InternalError().initCause(x2); | |
| } | |
| } | |
| else | |
| buf[pos++] = (byte) c; | |
| } | |
| try | |
| { | |
| return new String(buf, 0, pos, "utf-8"); | |
| } | |
| catch (java.io.UnsupportedEncodingException x2) | |
| { | |
| throw (Error) new InternalError().initCause(x2); | |
| } | |
| } | |
| /** | |
| * "Connects" to the file by opening it. | |
| */ | |
| public void connect() throws IOException | |
| { | |
| // Call is ignored if already connected. | |
| if (connected) | |
| return; | |
| // If not connected, then file needs to be openned. | |
| file = new File (unquote(getURL().getFile())); | |
| if (! file.isDirectory()) | |
| { | |
| if (doInput) | |
| inputStream = new BufferedInputStream(new FileInputStream(file)); | |
| if (doOutput) | |
| outputStream = new BufferedOutputStream(new FileOutputStream(file)); | |
| } | |
| else | |
| { | |
| if (doInput) | |
| { | |
| inputStream = new ByteArrayInputStream(getDirectoryListing()); | |
| } | |
| if (doOutput) | |
| throw new ProtocolException | |
| ("file: protocol does not support output on directories"); | |
| } | |
| connected = true; | |
| } | |
| /** | |
| * Populates the <code>directoryListing</code> field with a byte array | |
| * containing a representation of the directory listing. | |
| */ | |
| byte[] getDirectoryListing() | |
| throws IOException | |
| { | |
| if (directoryListing == null) | |
| { | |
| ByteArrayOutputStream sink = new ByteArrayOutputStream(); | |
| // NB uses default character encoding for this system | |
| Writer writer = new OutputStreamWriter(sink); | |
| String[] files = file.list(); | |
| for (int i = 0; i < files.length; i++) | |
| { | |
| writer.write(files[i]); | |
| writer.write(StaticData.lineSeparator); | |
| } | |
| directoryListing = sink.toByteArray(); | |
| } | |
| return directoryListing; | |
| } | |
| /** | |
| * Opens the file for reading and returns a stream for it. | |
| * | |
| * @return An InputStream for this connection. | |
| * | |
| * @exception IOException If an error occurs | |
| */ | |
| public InputStream getInputStream() | |
| throws IOException | |
| { | |
| if (!doInput) | |
| throw new ProtocolException("Can't open InputStream if doInput is false"); | |
| if (!connected) | |
| connect(); | |
| return inputStream; | |
| } | |
| /** | |
| * Opens the file for writing and returns a stream for it. | |
| * | |
| * @return An OutputStream for this connection. | |
| * | |
| * @exception IOException If an error occurs. | |
| */ | |
| public OutputStream getOutputStream() | |
| throws IOException | |
| { | |
| if (!doOutput) | |
| throw new | |
| ProtocolException("Can't open OutputStream if doOutput is false"); | |
| if (!connected) | |
| connect(); | |
| return outputStream; | |
| } | |
| /** | |
| * Get the last modified time of the resource. | |
| * | |
| * @return the time since epoch that the resource was modified. | |
| */ | |
| public long getLastModified() | |
| { | |
| try | |
| { | |
| if (!connected) | |
| connect(); | |
| return file.lastModified(); | |
| } | |
| catch (IOException e) | |
| { | |
| return -1; | |
| } | |
| } | |
| /** | |
| * Get an http-style header field. Just handle a few common ones. | |
| */ | |
| public String getHeaderField(String field) | |
| { | |
| try | |
| { | |
| if (!connected) | |
| connect(); | |
| if (field.equals("content-type")) | |
| return guessContentTypeFromName(file.getName()); | |
| else if (field.equals("content-length")) | |
| { | |
| if (file.isDirectory()) | |
| { | |
| return Integer.toString(getContentLength()); | |
| } | |
| return Long.toString(file.length()); | |
| } | |
| else if (field.equals("last-modified")) | |
| { | |
| synchronized (StaticData.dateFormat) | |
| { | |
| return StaticData.dateFormat.format( | |
| new Date(file.lastModified())); | |
| } | |
| } | |
| } | |
| catch (IOException e) | |
| { | |
| // Fall through. | |
| } | |
| return null; | |
| } | |
| /** | |
| * Get the length of content. | |
| * | |
| * @return the length of the content. | |
| */ | |
| public int getContentLength() | |
| { | |
| try | |
| { | |
| if (!connected) | |
| connect(); | |
| if (file.isDirectory()) | |
| { | |
| return getDirectoryListing().length; | |
| } | |
| return (int) file.length(); | |
| } | |
| catch (IOException e) | |
| { | |
| return -1; | |
| } | |
| } | |
| /** | |
| * This method returns a <code>Permission</code> object representing the | |
| * permissions required to access this URL. This method returns a | |
| * <code>java.io.FilePermission</code> for the file's path with a read | |
| * permission. | |
| * | |
| * @return A Permission object | |
| */ | |
| public Permission getPermission() throws IOException | |
| { | |
| return permission; | |
| } | |
| } |