diff options
author | upstream source tree <ports@midipix.org> | 2015-03-15 20:14:05 -0400 |
---|---|---|
committer | upstream source tree <ports@midipix.org> | 2015-03-15 20:14:05 -0400 |
commit | 554fd8c5195424bdbcabf5de30fdc183aba391bd (patch) | |
tree | 976dc5ab7fddf506dadce60ae936f43f58787092 /libjava/classpath/gnu/java/net | |
download | cbb-gcc-4.6.4-554fd8c5195424bdbcabf5de30fdc183aba391bd.tar.bz2 cbb-gcc-4.6.4-554fd8c5195424bdbcabf5de30fdc183aba391bd.tar.xz |
obtained gcc-4.6.4.tar.bz2 from upstream website;upstream
verified gcc-4.6.4.tar.bz2.sig;
imported gcc-4.6.4 source tree from verified upstream tarball.
downloading a git-generated archive based on the 'upstream' tag
should provide you with a source tree that is binary identical
to the one extracted from the above tarball.
if you have obtained the source via the command 'git clone',
however, do note that line-endings of files in your working
directory might differ from line-endings of the respective
files in the upstream repository.
Diffstat (limited to 'libjava/classpath/gnu/java/net')
68 files changed, 14027 insertions, 0 deletions
diff --git a/libjava/classpath/gnu/java/net/CRLFInputStream.java b/libjava/classpath/gnu/java/net/CRLFInputStream.java new file mode 100644 index 000000000..5e394ae2c --- /dev/null +++ b/libjava/classpath/gnu/java/net/CRLFInputStream.java @@ -0,0 +1,178 @@ +/* CRLFInputStream.java -- + Copyright (C) 2002, 2003, 2004, 2006 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; + +import java.io.BufferedInputStream; +import java.io.IOException; +import java.io.InputStream; + +/** + * An input stream that filters out CR/LF pairs into LFs. + * + * @author Chris Burdess (dog@gnu.org) + */ +public class CRLFInputStream + extends InputStream +{ + /** + * The CR octet. + */ + public static final int CR = 13; + + /** + * The LF octet. + */ + public static final int LF = 10; + + /** + * The underlying input stream. + */ + protected InputStream in; + + private boolean doReset; + + /** + * Constructs a CR/LF input stream connected to the specified input + * stream. + */ + public CRLFInputStream(InputStream in) + { + this.in = in.markSupported() ? in : new BufferedInputStream(in); + } + + /** + * Reads the next byte of data from this input stream. + * Returns -1 if the end of the stream has been reached. + * @exception IOException if an I/O error occurs + */ + public int read() + throws IOException + { + int c = in.read(); + if (c == CR) + { + in.mark(1); + int d = in.read(); + if (d == LF) + { + c = d; + } + else + { + in.reset(); + } + } + return c; + } + + /** + * Reads up to b.length bytes of data from this input stream into + * an array of bytes. + * Returns -1 if the end of the stream has been reached. + * @exception IOException if an I/O error occurs + */ + public int read(byte[] b) + throws IOException + { + return read(b, 0, b.length); + } + + /** + * Reads up to len bytes of data from this input stream into an + * array of bytes, starting at the specified offset. + * Returns -1 if the end of the stream has been reached. + * @exception IOException if an I/O error occurs + */ + public int read(byte[] b, int off, int len) + throws IOException + { + in.mark(len + 1); + int l = in.read(b, off, len); + if (l > 0) + { + int i = indexOfCRLF(b, off, l); + if (doReset) + { + in.reset(); + if (i != -1) + { + l = in.read(b, off, (i + 1) - off); // read to CR + in.read(); // skip LF + b[i] = LF; // fix CR as LF + } + else + { + l = in.read(b, off, len); // CR(s) but no LF + } + } + } + return l; + } + + private int indexOfCRLF(byte[] b, int off, int len) + throws IOException + { + doReset = false; + int end = off + len; + int em1 = end - 1; + for (int i = off; i < end; i++) + { + if (b[i] == CR) + { + int d; + if (i == em1) + { + d = in.read(); + doReset = true; + } + else + { + d = b[i + 1]; + } + if (d == LF) + { + doReset = true; + return i; + } + } + } + return -1; + } + +} diff --git a/libjava/classpath/gnu/java/net/CRLFOutputStream.java b/libjava/classpath/gnu/java/net/CRLFOutputStream.java new file mode 100644 index 000000000..0e9428373 --- /dev/null +++ b/libjava/classpath/gnu/java/net/CRLFOutputStream.java @@ -0,0 +1,182 @@ +/* CRLFOutputStream.java -- + Copyright (C) 2002, 2003, 2004 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; + +import java.io.FilterOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; + +/** + * An output stream that filters LFs into CR/LF pairs. + * + * @author Chris Burdess (dog@gnu.org) + */ +public class CRLFOutputStream + extends FilterOutputStream +{ + static final String US_ASCII = "US-ASCII"; + + /** + * The CR octet. + */ + public static final int CR = 13; + + /** + * The LF octet. + */ + public static final int LF = 10; + + /** + * The CR/LF pair. + */ + public static final byte[] CRLF = { CR, LF }; + + /** + * The last byte read. + */ + protected int last; + + /** + * Constructs a CR/LF output stream connected to the specified output stream. + */ + public CRLFOutputStream(OutputStream out) + { + super(out); + last = -1; + } + + /** + * Writes a character to the underlying stream. + * @exception IOException if an I/O error occurred + */ + public void write(int ch) throws IOException + { + if (ch == CR) + { + out.write(CRLF); + } + else if (ch == LF) + { + if (last != CR) + { + out.write(CRLF); + } + } + else + { + out.write(ch); + } + last = ch; + } + + /** + * Writes a byte array to the underlying stream. + * @exception IOException if an I/O error occurred + */ + public void write(byte[] b) + throws IOException + { + write(b, 0, b.length); + } + + /** + * Writes a portion of a byte array to the underlying stream. + * @exception IOException if an I/O error occurred + */ + public void write(byte[] b, int off, int len) + throws IOException + { + int d = off; + len += off; + for (int i = off; i < len; i++) + { + switch (b[i]) + { + case CR: + out.write (b, d, i - d); + out.write (CRLF, 0, 2); + d = i + 1; + break; + case LF: + if (last != CR) + { + out.write (b, d, i - d); + out.write (CRLF, 0, 2); + } + d = i + 1; + break; + } + last = b[i]; + } + if (len - d > 0) + { + out.write (b, d, len - d); + } + } + + /** + * Writes the specified ASCII string to the underlying stream. + * @exception IOException if an I/O error occurred + */ + public void write(String text) + throws IOException + { + try + { + byte[] bytes = text.getBytes(US_ASCII); + write(bytes, 0, bytes.length); + } + catch (UnsupportedEncodingException e) + { + throw new IOException("The US-ASCII encoding is not supported " + + "on this system"); + } + } + + /** + * Writes a newline to the underlying stream. + * @exception IOException if an I/O error occurred + */ + public void writeln() + throws IOException + { + out.write(CRLF, 0, 2); + } +} diff --git a/libjava/classpath/gnu/java/net/DefaultContentHandlerFactory.java b/libjava/classpath/gnu/java/net/DefaultContentHandlerFactory.java new file mode 100644 index 000000000..1d113b935 --- /dev/null +++ b/libjava/classpath/gnu/java/net/DefaultContentHandlerFactory.java @@ -0,0 +1,94 @@ +/* DefaultContentHandlerFactory.java + Copyright (C) 2004 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; + +import java.io.IOException; +import java.net.ContentHandler; +import java.net.ContentHandlerFactory; +import java.net.URLConnection; +import java.util.Arrays; +import java.util.HashSet; + +/** Content Handler for Image types, using the AWT Toolkit's image decoder. */ +class ImageHandler extends ContentHandler +{ + static ImageHandler instance = new ImageHandler(); + + public Object getContent(URLConnection urlc) throws IOException + { + // FIXME: implement using ImageIO + // ClasspathToolkit tk = (ClasspathToolkit) Toolkit.getDefaultToolkit(); + // java.awt.image.ImageProducer ip = tk.createImageProducer(urlc.getURL()); + // return ip; + return null; + } +} + +/** + */ +public class DefaultContentHandlerFactory implements ContentHandlerFactory +{ + /** For now, hard code the list of types that we assume should + * be supported by the Toolkit. ClasspathToolkit should perhaps provide + * an API to express what Image MIME types the Toolkit understands. + */ + private static String[] known_image_types = + { + "image/bmp", + "image/gif", + "image/jpeg", + "image/png", + "image/tiff", + "image/x-portable-anymap", + "image/x-cmu-raster", + "image/x-xbitmap", + "image/x-xpixmap" + }; + + private static HashSet<String> imageTypes + = new HashSet<String>(Arrays.asList(known_image_types)); + + public ContentHandler createContentHandler(String mimeType) + { + if (imageTypes.contains(mimeType)) + return ImageHandler.instance; + // Currently, only image types are handled. + return null; + } +} diff --git a/libjava/classpath/gnu/java/net/DefaultProxySelector.java b/libjava/classpath/gnu/java/net/DefaultProxySelector.java new file mode 100644 index 000000000..f0215a054 --- /dev/null +++ b/libjava/classpath/gnu/java/net/DefaultProxySelector.java @@ -0,0 +1,80 @@ +/* DefaultProxySelector.java -- + Copyright (C) 2006 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; + +import java.io.IOException; +import java.net.Proxy; +import java.net.ProxySelector; +import java.net.SocketAddress; +import java.net.URI; +import java.util.ArrayList; +import java.util.List; + +public final class DefaultProxySelector + extends ProxySelector +{ + private static final List<Proxy> proxies = new ArrayList<Proxy>(); + + static + { + // The default proxy selector supports only direct connections. + proxies.add(Proxy.NO_PROXY); + } + + public DefaultProxySelector() + { + // Do nothing by default. + } + + public void connectFailed(URI uri, SocketAddress sa, IOException ioe) + { + if (uri == null || sa == null || ioe == null) + throw new IllegalArgumentException(); + + // Do nothing by default. + } + + public List<Proxy> select(URI uri) + { + if (uri == null) + throw new IllegalArgumentException(); + + return proxies; + } +} diff --git a/libjava/classpath/gnu/java/net/EmptyX509TrustManager.java b/libjava/classpath/gnu/java/net/EmptyX509TrustManager.java new file mode 100644 index 000000000..466b49ef8 --- /dev/null +++ b/libjava/classpath/gnu/java/net/EmptyX509TrustManager.java @@ -0,0 +1,69 @@ +/* EmptyX509TrustManager.java -- + Copyright (C) 2004 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; + +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; + +import javax.net.ssl.X509TrustManager; + +/** + * Empty implementation of an X509 trust manager. + * This implementation does not check any certificates in the chain. + * + * @author Chris Burdess (dog@gnu.org) + */ +public class EmptyX509TrustManager + implements X509TrustManager +{ + public void checkClientTrusted(X509Certificate[] chain, String authType) + throws CertificateException + { + } + + public void checkServerTrusted(X509Certificate[] chain, String authType) + throws CertificateException + { + } + + public X509Certificate[] getAcceptedIssuers() + { + return new X509Certificate[0]; + } +} diff --git a/libjava/classpath/gnu/java/net/GetLocalHostAction.java b/libjava/classpath/gnu/java/net/GetLocalHostAction.java new file mode 100644 index 000000000..1e18877de --- /dev/null +++ b/libjava/classpath/gnu/java/net/GetLocalHostAction.java @@ -0,0 +1,64 @@ +/* GetLocalHostAction.java -- + Copyright (C) 2003, 2004 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; + +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.security.PrivilegedAction; + +/** + * Privileged action to retrieve the local host InetAddress. + * + * @author Chris Burdess (dog@gnu.org) + */ +public class GetLocalHostAction + implements PrivilegedAction<InetAddress> +{ + public InetAddress run() + { + try + { + return InetAddress.getLocalHost(); + } + catch (UnknownHostException e) + { + return null; + } + } +} diff --git a/libjava/classpath/gnu/java/net/HeaderFieldHelper.java b/libjava/classpath/gnu/java/net/HeaderFieldHelper.java new file mode 100644 index 000000000..ca3b9d2dd --- /dev/null +++ b/libjava/classpath/gnu/java/net/HeaderFieldHelper.java @@ -0,0 +1,136 @@ +/* HeaderFieldHelper.java -- Helps manage headers fields + Copyright (C) 1998, 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; + +import java.util.HashMap; +import java.util.Map; +import java.util.Vector; + +/** + * This class manages header field keys and values. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class HeaderFieldHelper +{ + private Vector<String> headerFieldKeys; + private Vector<String> headerFieldValues; + + public HeaderFieldHelper() + { + this (10); + } + + public HeaderFieldHelper (int size) + { + headerFieldKeys = new Vector<String> (size); + headerFieldValues = new Vector<String> (size); + } + + public void addHeaderField (String key, String value) + { + headerFieldKeys.addElement (key); + headerFieldValues.addElement (value); + } + + public String getHeaderFieldKeyByIndex (int index) + { + String key = null; + + try + { + key = headerFieldKeys.elementAt (index); + } + catch (ArrayIndexOutOfBoundsException e) + { + } + + return key; + } + + public String getHeaderFieldValueByIndex(int index) + { + String value = null; + + try + { + value = headerFieldValues.elementAt (index); + } + catch (ArrayIndexOutOfBoundsException e) + { + } + + return value; + } + + public String getHeaderFieldValueByKey(String key) + { + String value = null; + + try + { + value = headerFieldValues.elementAt(headerFieldKeys.indexOf(key)); + } + catch (ArrayIndexOutOfBoundsException e) + { + } + + return value; + } + + public Map<String, String> getHeaderFields() + { + HashMap<String, String> headers = new HashMap<String, String>(); + int max = headerFieldKeys.size(); + + for (int index = 0; index < max; index++) + { + headers.put(headerFieldKeys.elementAt(index), + headerFieldValues.elementAt(index)); + } + + return headers; + } + + public int getNumberOfEntries() + { + return headerFieldKeys.size(); + } + +} // class HeaderFieldHelper diff --git a/libjava/classpath/gnu/java/net/IndexListParser.java b/libjava/classpath/gnu/java/net/IndexListParser.java new file mode 100644 index 000000000..bf274b172 --- /dev/null +++ b/libjava/classpath/gnu/java/net/IndexListParser.java @@ -0,0 +1,183 @@ +/* IndexListParser.java -- + Copyright (C) 2006 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; + +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.net.URL; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.Set; +import java.util.jar.JarFile; + +/** + * The INDEX.LIST file contains sections each separated by a blank line. + * Each section defines the content of a jar, with a + * header defining the jar file path name, followed by a list of paths. + * The jar file paths are relative to the codebase of the root jar. + * + Specification + index file : version-info blankline section* + version-info : JarIndex-Version: version-number + version-number : digit+{.digit+}* + section : body blankline + body : header name* + header : char+.jar newline + name : char+ newline + + * @author langel at redhat dot com + */ +public class IndexListParser +{ + public static final String JAR_INDEX_FILE = "META-INF/INDEX.LIST"; + public static final String JAR_INDEX_VERSION_KEY = "JarIndex-Version: "; + + double versionNumber; + // Map each jar to the prefixes defined for the jar. + // This is intentionally kept in insertion order. + LinkedHashMap<URL, Set<String>> prefixes + = new LinkedHashMap<URL, Set<String>>(); + + /** + * Parses the given jarfile's INDEX.LIST file if it exists. + * + * @param jarfile - the given jar file + * @param baseJarURL - the codebase of the jar file + * @param baseURL - the base url for the headers + */ + public IndexListParser(JarFile jarfile, URL baseJarURL, URL baseURL) + { + try + { + // Parse INDEX.LIST if it exists + if (jarfile.getEntry(JAR_INDEX_FILE) != null) + { + BufferedReader br = new BufferedReader(new InputStreamReader(new URL(baseJarURL, + JAR_INDEX_FILE).openStream())); + + // Must start with version info + String line = br.readLine(); + if (!line.startsWith(JAR_INDEX_VERSION_KEY)) + return; + versionNumber = Double.parseDouble(line.substring(JAR_INDEX_VERSION_KEY.length()).trim()); + + // Blank line must be next + line = br.readLine(); + if (! "".equals(line)) + { + clearAll(); + return; + } + + // May contain sections. + while ((line = br.readLine()) != null) + { + URL jarURL = new URL(baseURL, line); + HashSet<String> values = new HashSet<String>(); + + // Read the names in the section. + while ((line = br.readLine()) != null) + { + // Stop at section boundary. + if ("".equals(line)) + break; + values.add(line.trim()); + } + prefixes.put(jarURL, values); + // Might have seen an early EOF. + if (line == null) + break; + } + + br.close(); + } + else + { + // INDEX.LIST does not exist + clearAll(); + } + } + catch (Exception ex) + { + clearAll(); + } + } + + /** + * Clears all the variables. This is called when parsing fails. + */ + void clearAll() + { + versionNumber = 0; + prefixes = null; + } + + /** + * Gets the version info for the file. + * + * @return the version info. + */ + public String getVersionInfo() + { + return JAR_INDEX_VERSION_KEY + getVersionNumber(); + } + + /** + * Gets the version number of the file. + * + * @return the version number. + */ + public double getVersionNumber() + { + return versionNumber; + } + + /** + * Gets the map of all the headers found in the file. + * The keys in the map are URLs of jars. The values in the map + * are Sets of package prefixes (and top-level file names), as + * specifed in INDEX.LIST. + * + * @return an map of all the headers, or null if no INDEX.LIST was found + */ + public LinkedHashMap<URL, Set<String>> getHeaders() + { + return prefixes; + } +} diff --git a/libjava/classpath/gnu/java/net/LineInputStream.java b/libjava/classpath/gnu/java/net/LineInputStream.java new file mode 100644 index 000000000..8bd4b69cd --- /dev/null +++ b/libjava/classpath/gnu/java/net/LineInputStream.java @@ -0,0 +1,223 @@ +/* LineInputStream.java -- + Copyright (C) 2002, 2003, 2004, 2005, 2006 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; + +import java.io.BufferedInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; + +/** + * An input stream that can read lines of input. + * + * @author Chris Burdess (dog@gnu.org) + */ +public class LineInputStream + extends InputStream +{ + + /** + * The underlying input stream. + */ + protected InputStream in; + + /* + * Line buffer. + */ + private ByteArrayOutputStream buf; + + /* + * Encoding to use when translating bytes to characters. + */ + private String encoding; + + /* + * End-of-stream flag. + */ + private boolean eof; + + /** + * Whether we can use block reads. + */ + private final boolean blockReads; + + /** + * Constructor using the US-ASCII character encoding. + * @param in the underlying input stream + */ + public LineInputStream(InputStream in) + { + this(in, "US-ASCII"); + } + + /** + * Constructor. + * @param in the underlying input stream + * @param encoding the character encoding to use + */ + public LineInputStream(InputStream in, String encoding) + { + this.in = in; + buf = new ByteArrayOutputStream(); + this.encoding = encoding; + eof = false; + // If it is already buffered, additional buffering gains nothing. + blockReads = !(in instanceof BufferedInputStream) && in.markSupported(); + } + + public int read() + throws IOException + { + return in.read(); + } + + public int read(byte[] buf) + throws IOException + { + return in.read(buf); + } + + public int read(byte[] buf, int off, int len) + throws IOException + { + return in.read(buf, off, len); + } + + /** + * Read a line of input. + */ + public String readLine() + throws IOException + { + if (eof) + { + return null; + } + do + { + if (blockReads) + { + // Use mark and reset to read chunks of bytes + final int MAX_LENGTH = 1024; + int len, pos; + + len = in.available(); + if (len == 0 || len > MAX_LENGTH) + len = MAX_LENGTH; + byte[] b = new byte[len]; + in.mark(len); + // Read into buffer b + len = in.read(b, 0, len); + // Handle EOF + if (len == -1) + { + eof = true; + if (buf.size() == 0) + { + return null; + } + else + { + // We don't care about resetting buf + return buf.toString(encoding); + } + } + // Get index of LF in b + pos = indexOf(b, len, (byte) 0x0a); + if (pos != -1) + { + // Write pos bytes to buf + buf.write(b, 0, pos); + // Reset stream, and read pos + 1 bytes + in.reset(); + pos += 1; + while (pos > 0) + { + len = in.read(b, 0, pos); + pos = (len == -1) ? -1 : pos - len; + } + // Return line + String ret = buf.toString(encoding); + buf.reset(); + return ret; + } + else + { + // Append everything to buf and fall through to re-read. + buf.write(b, 0, len); + } + } + else + { + // We must use character reads in order not to read too much + // from the underlying stream. + int c = in.read(); + switch (c) + { + case -1: + eof = true; + if (buf.size() == 0) + { + return null; + } + // Fall through and return contents of buffer. + case 0x0a: // LF + String ret = buf.toString(encoding); + buf.reset(); + return ret; + default: + buf.write(c); + } + } + } + while (true); + } + + private int indexOf(byte[] b, int len, byte c) + { + for (int pos = 0; pos < len; pos++) + { + if (b[pos] == c) + { + return pos; + } + } + return -1; + } +} diff --git a/libjava/classpath/gnu/java/net/PlainDatagramSocketImpl.java b/libjava/classpath/gnu/java/net/PlainDatagramSocketImpl.java new file mode 100644 index 000000000..639c9ff55 --- /dev/null +++ b/libjava/classpath/gnu/java/net/PlainDatagramSocketImpl.java @@ -0,0 +1,486 @@ +/* PlainDatagramSocketImpl.java -- Default DatagramSocket implementation + Copyright (C) 1998, 1999, 2001, 2003, 2004, 2005, 2006 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; + +import gnu.java.nio.VMChannel; + +import java.io.IOException; +import java.io.InterruptedIOException; +import java.net.DatagramPacket; +import java.net.DatagramSocketImpl; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.NetworkInterface; +import java.net.SocketAddress; +import java.net.SocketException; +import java.net.SocketTimeoutException; +import java.nio.ByteBuffer; + +/** + * Written using on-line Java Platform 1.2 API Specification, as well + * as "The Java Class Libraries", 2nd edition (Addison-Wesley, 1998). + * Status: Believed complete and correct. + */ + +/** + * This is the default socket implementation for datagram sockets. + * It makes native calls to C routines that implement BSD style + * SOCK_DGRAM sockets in the AF_INET family. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Warren Levy (warrenl@cygnus.com) + */ +public final class PlainDatagramSocketImpl extends DatagramSocketImpl +{ + private final VMChannel channel; + + /** + * The platform-specific socket implementation. + */ + private final VMPlainSocketImpl impl; + + /** + * Lock object to serialize threads wanting to receive + */ + private final Object RECEIVE_LOCK = new Object(); + + /** + * Lock object to serialize threads wanting to send + */ + private final Object SEND_LOCK = new Object(); + + /** + * Default do nothing constructor + */ + public PlainDatagramSocketImpl() throws IOException + { + channel = new VMChannel(); + impl = new VMPlainSocketImpl(channel); + } + + /*protected void finalize() throws Throwable + { + synchronized (this) + { + if (channel.getState().isValid()) + close(); + } + super.finalize(); + }*/ + + /*public int getNativeFD() + { + return native_fd; + }*/ + + /** + * Binds this socket to a particular port and interface + * + * @param port The port to bind to + * @param addr The address to bind to + * + * @exception SocketException If an error occurs + */ + protected synchronized void bind(int port, InetAddress addr) + throws SocketException + { + try + { + impl.bind(new InetSocketAddress(addr, port)); + } + catch (SocketException se) + { + throw se; + } + catch (IOException ioe) + { + SocketException se = new SocketException(); + se.initCause(ioe); + throw se; + } + } + + /** + * Creates a new datagram socket + * + * @exception SocketException If an error occurs + */ + protected synchronized void create() throws SocketException + { + try + { + channel.initSocket(false); + } + catch (SocketException se) + { + throw se; + } + catch (IOException ioe) + { + SocketException se = new SocketException(); + se.initCause(ioe); + throw se; + } + } + + /** + * Connects to the remote address and port specified as arguments. + * + * @param addr The remote address to connect to + * @param port The remote port to connect to + * + * @exception SocketException If an error occurs + */ + protected void connect(InetAddress addr, int port) throws SocketException + { + channel.connect(new InetSocketAddress(addr, port), 0); + } + + /** + * Disconnects the socket. + * + * @since 1.4 + */ + protected void disconnect() + { + synchronized (this) + { + try + { + if (channel.getState().isValid()) + channel.disconnect(); + } + catch (IOException ioe) + { + } + } + } + + /** + * Sets the Time to Live value for the socket + * + * @param ttl The new TTL value + * + * @exception IOException If an error occurs + */ + protected synchronized void setTimeToLive(int ttl) throws IOException + { + impl.setTimeToLive(ttl); + } + + /** + * Gets the Time to Live value for the socket + * + * @return The TTL value + * + * @exception IOException If an error occurs + */ + protected synchronized int getTimeToLive() throws IOException + { + return impl.getTimeToLive(); + } + + protected int getLocalPort() + { + if (channel == null) + return -1; + + try + { + InetSocketAddress local = channel.getLocalAddress(); + if (local == null) + return -1; + return local.getPort(); + } + catch (IOException ioe) + { + return -1; + } + } + + /** + * Sends a packet of data to a remote host + * + * @param packet The packet to send + * + * @exception IOException If an error occurs + */ + protected void send(DatagramPacket packet) throws IOException + { + synchronized (SEND_LOCK) + { + ByteBuffer buf = ByteBuffer.wrap(packet.getData(), + packet.getOffset(), + packet.getLength()); + InetAddress remote = packet.getAddress(); + int port = packet.getPort(); + if (remote == null) + throw new NullPointerException(); + if (port <= 0) + throw new SocketException("invalid port " + port); + while (true) + { + try + { + channel.send(buf, new InetSocketAddress(remote, port)); + break; + } + catch (InterruptedIOException ioe) + { + // Ignore; interrupted system call. + } + } + } + } + + /** + * Receives a UDP packet from the network + * + * @param packet The packet to fill in with the data received + * + * @exception IOException IOException If an error occurs + */ + protected void receive(DatagramPacket packet) + throws IOException + { + synchronized(RECEIVE_LOCK) + { + ByteBuffer buf = ByteBuffer.wrap(packet.getData(), + packet.getOffset(), + packet.getLength()); + SocketAddress addr = null; + while (true) + { + try + { + addr = channel.receive(buf); + break; + } + catch (SocketTimeoutException ste) + { + throw ste; + } + catch (InterruptedIOException iioe) + { + // Ignore. Loop. + } + } + if (addr != null) + packet.setSocketAddress(addr); + packet.setLength(buf.position() - packet.getOffset()); + } + } + + + /** + * Sets the value of an option on the socket + * + * @param optionId The identifier of the option to set + * @param value The value of the option to set + * + * @exception SocketException If an error occurs + */ + public synchronized void setOption(int optionId, Object value) + throws SocketException + { + switch (optionId) + { + case IP_MULTICAST_IF: + case IP_MULTICAST_IF2: + impl.setMulticastInterface(optionId, (InetAddress) value); + break; + + case IP_MULTICAST_LOOP: + case SO_BROADCAST: + case SO_KEEPALIVE: + case SO_OOBINLINE: + case TCP_NODELAY: + case IP_TOS: + case SO_LINGER: + case SO_RCVBUF: + case SO_SNDBUF: + case SO_TIMEOUT: + case SO_REUSEADDR: + impl.setOption(optionId, value); + return; + + default: + throw new SocketException("cannot set option " + optionId); + } + } + + /** + * Retrieves the value of an option on the socket + * + * @param optionId The identifier of the option to retrieve + * + * @return The value of the option + * + * @exception SocketException If an error occurs + */ + public synchronized Object getOption(int optionId) + throws SocketException + { + if (optionId == SO_BINDADDR) + { + try + { + InetSocketAddress local = channel.getLocalAddress(); + if (local == null) + return null; + return local.getAddress(); + } + catch (SocketException se) + { + throw se; + } + catch (IOException ioe) + { + SocketException se = new SocketException(); + se.initCause(ioe); + throw se; + } + } + if (optionId == IP_MULTICAST_IF || optionId == IP_MULTICAST_IF2) + return impl.getMulticastInterface(optionId); + + return impl.getOption(optionId); + } + + /** + * Closes the socket + */ + protected synchronized void close() + { + try + { + if (channel.getState().isValid()) + channel.close(); + } + catch (IOException ioe) + { + } + } + + /** + * Gets the Time to Live value for the socket + * + * @return The TTL value + * + * @exception IOException If an error occurs + * + * @deprecated 1.2 + */ + protected synchronized byte getTTL() throws IOException + { + return (byte) getTimeToLive(); + } + + /** + * Sets the Time to Live value for the socket + * + * @param ttl The new TTL value + * + * @exception IOException If an error occurs + * + * @deprecated 1.2 + */ + protected synchronized void setTTL(byte ttl) throws IOException + { + setTimeToLive(((int) ttl) & 0xFF); + } + + /** + * Joins a multicast group + * + * @param addr The group to join + * + * @exception IOException If an error occurs + */ + protected synchronized void join(InetAddress addr) throws IOException + { + impl.join(addr); + } + + /** + * Leaves a multicast group + * + * @param addr The group to leave + * + * @exception IOException If an error occurs + */ + protected synchronized void leave(InetAddress addr) throws IOException + { + impl.leave(addr); + } + + /** + * What does this method really do? + */ + protected synchronized int peek(InetAddress addr) throws IOException + { + throw new IOException("Not Implemented Yet"); + } + + public int peekData(DatagramPacket packet) + { + throw new InternalError + ("PlainDatagramSocketImpl::peekData is not implemented"); + } + + public void joinGroup(SocketAddress address, NetworkInterface netIf) + throws IOException + { + if (address == null) + throw new NullPointerException(); + if (!(address instanceof InetSocketAddress)) + throw new SocketException("unknown address type"); + impl.joinGroup((InetSocketAddress) address, netIf); + } + + public void leaveGroup(SocketAddress address, NetworkInterface netIf) + throws IOException + { + if (address == null) + throw new NullPointerException(); + if (!(address instanceof InetSocketAddress)) + throw new SocketException("unknown address type"); + impl.leaveGroup((InetSocketAddress) address, netIf); + } +} diff --git a/libjava/classpath/gnu/java/net/PlainSocketImpl.java b/libjava/classpath/gnu/java/net/PlainSocketImpl.java new file mode 100644 index 000000000..72db53186 --- /dev/null +++ b/libjava/classpath/gnu/java/net/PlainSocketImpl.java @@ -0,0 +1,678 @@ +/* PlainSocketImpl.java -- Default socket implementation + Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005 + 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; + +import gnu.java.nio.SocketChannelImpl; +import gnu.java.nio.VMChannel; + +import java.io.InputStream; +import java.io.IOException; +import java.io.InterruptedIOException; +import java.io.OutputStream; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.net.SocketException; +import java.net.SocketImpl; +import java.net.SocketTimeoutException; +import java.nio.ByteBuffer; + +/** + * Written using on-line Java Platform 1.2 API Specification, as well + * as "The Java Class Libraries", 2nd edition (Addison-Wesley, 1998). + * Status: Believed complete and correct. + */ + +/** + * Unless the application installs its own SocketImplFactory, this is the + * default socket implemetation that will be used. It simply uses a + * combination of Java and native routines to implement standard BSD + * style sockets of family AF_INET and types SOCK_STREAM and SOCK_DGRAM + * + * @author Per Bothner (bothner@cygnus.com) + * @author Nic Ferrier (nferrier@tapsellferrier.co.uk) + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class PlainSocketImpl extends SocketImpl +{ + + /** + * The underlying plain socket VM implementation. + */ + protected VMPlainSocketImpl impl; + + /** + * A cached copy of the in stream for reading from the socket. + */ + private InputStream in; + + /** + * A cached copy of the out stream for writing to the socket. + */ + private OutputStream out; + + /** + * Indicates whether a channel initiated whatever operation + * is being invoked on this socket. + */ + private boolean inChannelOperation; + + /** + * The socket channel we use for IO operation. Package-private for + * use by inner classes. + */ + SocketChannelImpl channel; + + /** + * Indicates whether we should ignore whether any associated + * channel is set to non-blocking mode. Certain operations + * throw an <code>IllegalBlockingModeException</code> if the + * associated channel is in non-blocking mode, <i>except</i> + * if the operation is invoked by the channel itself. + */ + public final boolean isInChannelOperation() + { + return inChannelOperation; + } + + /** + * Sets our indicator of whether an I/O operation is being + * initiated by a channel. + */ + public final void setInChannelOperation(boolean b) + { + inChannelOperation = b; + } + + /** + * Default do nothing constructor. + */ + public PlainSocketImpl() + { + this.impl = new VMPlainSocketImpl(); + } + + /** + * Sets the specified option on a socket to the passed in object. For + * options that take an integer argument, the passed in object is an + * Integer. The option_id parameter is one of the defined constants in + * this interface. + * + * @param optionId The identifier of the option + * @param value The value to set the option to + * + * @throws SocketException if an error occurs + */ + public void setOption(int optionId, Object value) throws SocketException + { + switch (optionId) + { + case SO_LINGER: + case IP_MULTICAST_LOOP: + case SO_BROADCAST: + case SO_KEEPALIVE: + case SO_OOBINLINE: + case TCP_NODELAY: + case IP_TOS: + case SO_RCVBUF: + case SO_SNDBUF: + case SO_TIMEOUT: + case SO_REUSEADDR: + impl.setOption(optionId, value); + return; + default: + throw new SocketException("Unrecognized TCP option: " + optionId); + } + } + + /** + * Returns the current setting of the specified option. The Object returned + * will be an Integer for options that have integer values. The option_id + * is one of the defined constants in this interface. + * + * @param optionId the option identifier + * + * @return the current value of the option + * + * @throws SocketException if an error occurs + */ + public Object getOption(int optionId) throws SocketException + { + if (optionId == SO_BINDADDR) + { + try + { + return channel.getVMChannel().getLocalAddress().getAddress(); + } + catch (IOException ioe) + { + SocketException se = new SocketException(); + se.initCause(ioe); + throw se; + } + } + + // This filters options which are invalid for TCP. + switch (optionId) + { + case SO_LINGER: + case IP_MULTICAST_LOOP: + case SO_BROADCAST: + case SO_KEEPALIVE: + case SO_OOBINLINE: + case TCP_NODELAY: + case IP_TOS: + case SO_RCVBUF: + case SO_SNDBUF: + case SO_TIMEOUT: + case SO_REUSEADDR: + return impl.getOption(optionId); + default: + throw new SocketException("Unrecognized TCP option: " + optionId); + } + + } + + public void shutdownInput() throws IOException + { + impl.shutdownInput(); + } + + public void shutdownOutput() throws IOException + { + impl.shutdownOutput(); + } + + /** + * Creates a new socket that is not bound to any local address/port and + * is not connected to any remote address/port. The stream parameter will be + * ignored since PlainSocketImpl always is a stream socket. Datagram sockets + * are handled by PlainDatagramSocketImpl. + * + * @param stream <code>true</code> for stream sockets, <code>false</code> for + * datagram sockets + */ + protected synchronized void create(boolean stream) throws IOException + { + channel = new SocketChannelImpl(false); + VMChannel vmchannel = channel.getVMChannel(); + vmchannel.initSocket(stream); + channel.configureBlocking(true); + impl.getState().setChannelFD(vmchannel.getState()); + } + + /** + * Connects to the remote hostname and port specified as arguments. + * + * @param hostname the remote hostname to connect to + * @param port the remote port to connect to + * + * @throws IOException If an error occurs + */ + protected synchronized void connect(String hostname, int port) + throws IOException + { + connect(InetAddress.getByName(hostname), port); + } + + /** + * Connects to the remote address and port specified as arguments. + * + * @param addr the remote address to connect to + * @param port the remote port to connect to + * + * @throws IOException If an error occurs + */ + protected void connect(InetAddress addr, int port) throws IOException + { + connect(new InetSocketAddress(addr, port), 0); + } + + /** + * Connects to the remote socket address with a specified timeout. + * + * @param address the remote address to connect to + * @param timeout the timeout to use for this connect, 0 means infinite. + * + * @throws IOException If an error occurs + */ + protected synchronized void connect(SocketAddress address, int timeout) + throws IOException + { + if (channel == null) + create(true); + boolean connected = channel.connect(address, timeout); + if (!connected) + throw new SocketTimeoutException("connect timed out"); + + // Using the given SocketAddress is important to preserve + // hostnames given by the caller. + InetSocketAddress addr = (InetSocketAddress) address; + this.address = addr.getAddress(); + this.port = addr.getPort(); + } + + /** + * Binds to the specified port on the specified addr. Note that this addr + * must represent a local IP address. **** How bind to INADDR_ANY? **** + * + * @param addr the address to bind to + * @param port the port number to bind to + * + * @throws IOException if an error occurs + */ + protected synchronized void bind(InetAddress addr, int port) + throws IOException + { + if (channel == null) + create(true); + impl.bind(new InetSocketAddress(addr, port)); + localport = channel.getVMChannel().getLocalAddress().getPort(); + } + + /** + * Starts listening for connections on a socket. The queuelen parameter + * is how many pending connections will queue up waiting to be serviced + * before being accept'ed. If the queue of pending requests exceeds this + * number, additional connections will be refused. + * + * @param queuelen The length of the pending connection queue + * + * @throws IOException If an error occurs + */ + protected synchronized void listen(int queuelen) + throws IOException + { + impl.listen(queuelen); + } + + /** + * Accepts a new connection on this socket and returns in in the + * passed in SocketImpl. + * + * @param impl The SocketImpl object to accept this connection. + */ + protected synchronized void accept(SocketImpl impl) + throws IOException + { + if (channel == null) + create(true); + if (!(impl instanceof PlainSocketImpl)) + throw new IOException("incompatible SocketImpl: " + + impl.getClass().getName()); + PlainSocketImpl that = (PlainSocketImpl) impl; + VMChannel c = channel.getVMChannel().accept(); + that.impl.getState().setChannelFD(c.getState()); + that.channel = new SocketChannelImpl(c); + that.setOption(SO_REUSEADDR, Boolean.TRUE); + // Reset the inherited timeout. + that.setOption(SO_TIMEOUT, Integer.valueOf(0)); + + } + + /** + * Returns the number of bytes that the caller can read from this socket + * without blocking. + * + * @return the number of readable bytes before blocking + * + * @throws IOException if an error occurs + */ + protected int available() throws IOException + { + if (channel == null) + throw new SocketException("not connected"); + return channel.getVMChannel().available(); + } + + /** + * Closes the socket. This will cause any InputStream or OutputStream + * objects for this Socket to be closed as well. + * + * <p> + * Note that if the SO_LINGER option is set on this socket, then the + * operation could block. + * </p> + * + * @throws IOException if an error occurs + */ + protected void close() throws IOException + { + if (impl.getState().isValid()) + impl.close(); + + address = null; + port = -1; + } + + public void sendUrgentData(int data) throws IOException + { + impl.sendUrgentData(data); + } + + /** + * Returns an InputStream object for reading from this socket. This will + * be an instance of SocketInputStream. + * + * @return An input stream attached to the socket. + * + * @exception IOException If an error occurs + */ + protected synchronized InputStream getInputStream() throws IOException + { + if (in == null) + in = new SocketInputStream(); + + return in; + } + + /** + * Returns an OutputStream object for writing to this socket. This will + * be an instance of SocketOutputStream. + * + * @return An output stream attached to the socket. + * + * @exception IOException If an error occurs + */ + protected synchronized OutputStream getOutputStream() throws IOException + { + if (out == null) + out = new SocketOutputStream(); + + return out; + } + + public VMChannel getVMChannel() + { + if (channel == null) + return null; + return channel.getVMChannel(); + } + + /* (non-Javadoc) + * @see java.net.SocketImpl#getInetAddress() + */ + protected InetAddress getInetAddress() + { + if (channel == null) + return null; + + try + { + InetSocketAddress remote = channel.getVMChannel().getPeerAddress(); + if (remote == null) + return null; + // To mimic behavior of the RI the InetAddress instance which was + // used to establish the connection is returned instead of one that + // was created by the native layer (this preserves exact hostnames). + if (address != null) + return address; + + return remote.getAddress(); + } + catch (IOException ioe) + { + return null; + } + } + + /* (non-Javadoc) + * @see java.net.SocketImpl#getLocalPort() + */ + protected int getLocalPort() + { + if (channel == null) + return -1; + try + { + InetSocketAddress local = channel.getVMChannel().getLocalAddress(); + if (local == null) + return -1; + return local.getPort(); + } + catch (IOException ioe) + { + return -1; + } + } + + public InetSocketAddress getLocalAddress() + { + if (channel == null) + return null; + try + { + return channel.getVMChannel().getLocalAddress(); + } + catch (IOException ioe) + { + return null; + } + } + + /* (non-Javadoc) + * @see java.net.SocketImpl#getPort() + */ + protected int getPort() + { + if (channel == null) + return -1; + + try + { + InetSocketAddress remote = channel.getVMChannel().getPeerAddress(); + if (remote == null) + return -1; + return remote.getPort(); + } + catch (IOException ioe) + { + return -1; + } + } + + /** + * This class contains an implementation of <code>InputStream</code> for + * sockets. It in an internal only class used by <code>PlainSocketImpl</code>. + * + * @author Nic Ferrier <nferrier@tapsellferrier.co.uk> + */ + final class SocketInputStream + extends InputStream + { + /** + * Returns the number of bytes available to be read before blocking + */ + public int available() throws IOException + { + return PlainSocketImpl.this.available(); + } + + /** + * This method not only closes the stream, it closes the underlying socket + * (and thus any connection) and invalidates any other Input/Output streams + * for the underlying impl object + */ + public void close() throws IOException + { + PlainSocketImpl.this.close(); + } + + /** + * Reads the next byte of data and returns it as an int. + * + * @return The byte read (as an int) or -1 if end of stream); + * + * @exception IOException If an error occurs. + */ + public int read() throws IOException + { + if (channel == null) + throw new SocketException("not connected"); + while (true) + { + try + { + return channel.getVMChannel().read(); + } + catch (SocketTimeoutException ste) + { + throw ste; + } + catch (InterruptedIOException iioe) + { + // Ignore; NIO may throw this; net io shouldn't + } + } + } + + /** + * Reads up to len bytes of data into the caller supplied buffer starting + * at offset bytes from the start of the buffer + * + * @param buf The buffer + * @param offset Offset into the buffer to start reading from + * @param len The number of bytes to read + * + * @return The number of bytes actually read or -1 if end of stream + * + * @exception IOException If an error occurs. + */ + public int read (byte[] buf, int offset, int len) throws IOException + { + if (channel == null) + throw new SocketException("not connected"); + ByteBuffer b = ByteBuffer.wrap(buf, offset, len); + while (true) + { + try + { + return channel.read(b); + } + catch (SocketTimeoutException ste) + { + throw ste; + } + catch (InterruptedIOException iioe) + { + // Ignored; NIO may throw this; net IO not. + } + } + } + } + + /** + * This class is used internally by <code>PlainSocketImpl</code> to be the + * <code>OutputStream</code> subclass returned by its + * <code>getOutputStream method</code>. It expects only to be used in that + * context. + * + * @author Nic Ferrier <nferrier@tapsellferrier.co.uk> + */ + final class SocketOutputStream + extends OutputStream + { + /** + * This method closes the stream and the underlying socket connection. This + * action also effectively closes any other InputStream or OutputStream + * object associated with the connection. + * + * @exception IOException If an error occurs + */ + public void close() throws IOException + { + PlainSocketImpl.this.close(); + } + + /** + * Writes a byte (passed in as an int) to the given output stream + * + * @param b The byte to write + * + * @exception IOException If an error occurs + */ + public void write(int b) throws IOException + { + if (channel == null) + throw new SocketException("not connected"); + while (true) + { + try + { + channel.getVMChannel().write(b); + return; + } + catch (InterruptedIOException iioe) + { + // Ignored. + } + } + } + + /** + * Writes len number of bytes from the array buf to the stream starting + * at offset bytes into the buffer. + * + * @param buf The buffer + * @param offset Offset into the buffer to start writing from + * @param len The number of bytes to write + * + * @exception IOException If an error occurs. + */ + public void write (byte[] buf, int offset, int len) throws IOException + { + if (channel == null) + throw new SocketException("not connected"); + ByteBuffer b = ByteBuffer.wrap(buf, offset, len); + while (b.hasRemaining()) + { + try + { + if (channel.write(b) == -1) + throw new IOException("channel has been closed"); + } + catch (InterruptedIOException iioe) + { + // Ignored. + } + } + } + } +} diff --git a/libjava/classpath/gnu/java/net/URLParseError.java b/libjava/classpath/gnu/java/net/URLParseError.java new file mode 100644 index 000000000..90907480f --- /dev/null +++ b/libjava/classpath/gnu/java/net/URLParseError.java @@ -0,0 +1,57 @@ +/* URLParseError.java -- Helps bypassing the exception limitation for + URLStreamHandler.parseURL(). + Copyright (C) 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; + +/** + * This class helps the people writing protocols to report URL parse + * errors in parseUrl as this method cannot report other exceptions + * than Errors. + * + * The main drawback is that it uses the Error mechanism which should not + * be used for that type of error reporting. + * + * @author Guilhem Lavaux (guilhem@kaffe.org) + */ +public class URLParseError extends Error +{ + public URLParseError(String msg) + { + super(msg); + } +} diff --git a/libjava/classpath/gnu/java/net/loader/FileResource.java b/libjava/classpath/gnu/java/net/loader/FileResource.java new file mode 100644 index 000000000..471584023 --- /dev/null +++ b/libjava/classpath/gnu/java/net/loader/FileResource.java @@ -0,0 +1,82 @@ +/* FileResource.java -- a Resource for file URLs + Copyright (C) 2006 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.loader; + + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URL; + +public final class FileResource extends Resource +{ + final File file; + + public FileResource(FileURLLoader loader, File file) + { + super(loader); + this.file = file; + } + + public InputStream getInputStream() throws IOException + { + return new FileInputStream(file); + } + + public int getLength() + { + return (int) file.length(); + } + + public URL getURL() + { + try + { + return file.toURL(); + } + catch (MalformedURLException e) + { + InternalError ie = new InternalError(); + ie.initCause(e); + throw ie; + } + } +} diff --git a/libjava/classpath/gnu/java/net/loader/FileURLLoader.java b/libjava/classpath/gnu/java/net/loader/FileURLLoader.java new file mode 100644 index 000000000..58b6dcf05 --- /dev/null +++ b/libjava/classpath/gnu/java/net/loader/FileURLLoader.java @@ -0,0 +1,145 @@ +/* FileURLLoader.java -- a URLLoader for file URLs + Copyright (C) 2006 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.loader; + + +import java.io.File; +import java.io.IOException; +import java.net.URL; +import java.net.URLClassLoader; +import java.net.URLStreamHandlerFactory; +import java.util.StringTokenizer; + +/** + * A <code>FileURLLoader</code> is a type of <code>URLLoader</code> + * only loading from file url. + */ +public final class FileURLLoader extends URLLoader +{ + File dir; //the file for this file url + + public FileURLLoader(URLClassLoader classloader, + URLStreamHandlerCache cache, + URLStreamHandlerFactory factory, + URL url, URL absoluteUrl) + { + super(classloader, cache, factory, url, absoluteUrl); + dir = new File(absoluteUrl.getFile()); + } + + /** get resource with the name "name" in the file url */ + public Resource getResource(String name) + { + try + { + // Make sure that all components in name are valid by walking through + // them + File file = walkPathComponents(name); + + if (file == null) + return null; + + return new FileResource(this, file); + } + catch (IOException e) + { + // Fall through... + } + return null; + } + + /** + * Walk all path tokens and check them for validity. At no moment, we are + * allowed to reach a directory located "above" the root directory, stored + * in "dir" property. We are also not allowed to enter a non existing + * directory or a non directory component (plain file, symbolic link, ...). + * An empty or null path is valid. Pathnames components are separated by + * <code>File.separatorChar</code> + * + * @param resourceFileName the name to be checked for validity. + * @return the canonical file pointed by the resourceFileName or null if the + * walking failed + * @throws IOException in case of issue when creating the canonical + * resulting file + * @see File#separatorChar + */ + private File walkPathComponents(String resourceFileName) throws IOException + { + StringTokenizer stringTokenizer = new StringTokenizer(resourceFileName, File.separator); + File currentFile = dir; + int tokenCount = stringTokenizer.countTokens(); + + for (int i = 0; i < tokenCount - 1; i++) + { + String currentToken = stringTokenizer.nextToken(); + + // If we are at the root directory and trying to go up, the walking is + // finished with an error + if ("..".equals(currentToken) && currentFile.equals(dir)) + return null; + + currentFile = new File(currentFile, currentToken); + + // If the current file doesn't exist or is not a directory, the walking is + // finished with an error + if (! (currentFile.exists() && currentFile.isDirectory())) + return null; + + } + + // Treat the last token differently, if it exists, because it does not need + // to be a directory + if (tokenCount > 0) + { + String currentToken = stringTokenizer.nextToken(); + + if ("..".equals(currentToken) && currentFile.equals(dir)) + return null; + + currentFile = new File(currentFile, currentToken); + + // If the current file doesn't exist, the walking is + // finished with an error + if (! currentFile.exists()) + return null; + } + + return currentFile.getCanonicalFile(); + } +} diff --git a/libjava/classpath/gnu/java/net/loader/JarURLLoader.java b/libjava/classpath/gnu/java/net/loader/JarURLLoader.java new file mode 100644 index 000000000..6f06ad3b2 --- /dev/null +++ b/libjava/classpath/gnu/java/net/loader/JarURLLoader.java @@ -0,0 +1,215 @@ +package gnu.java.net.loader; + +import gnu.java.net.IndexListParser; + +import java.io.IOException; +import java.net.JarURLConnection; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.net.URLStreamHandlerFactory; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Set; +import java.util.StringTokenizer; +import java.util.jar.Attributes; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.jar.Manifest; + +/** + * A <code>JarURLLoader</code> is a type of <code>URLLoader</code> + * only loading from jar url. + */ +public final class JarURLLoader extends URLLoader +{ + // True if we've initialized -- i.e., tried open the jar file. + boolean initialized; + // The jar file for this url. + JarFile jarfile; + // Base jar: url for all resources loaded from jar. + final URL baseJarURL; + // The "Class-Path" attribute of this Jar's manifest. + ArrayList<URLLoader> classPath; + // If not null, a mapping from INDEX.LIST for this jar only. + // This is a set of all prefixes and top-level files that + // ought to be available in this jar. + Set indexSet; + + // This constructor is used internally. It purposely does not open + // the jar file -- it defers this until later. This allows us to + // implement INDEX.LIST lazy-loading semantics. + private JarURLLoader(URLClassLoader classloader, URLStreamHandlerCache cache, + URLStreamHandlerFactory factory, + URL baseURL, URL absoluteUrl, + Set indexSet) + { + super(classloader, cache, factory, baseURL, absoluteUrl); + + URL newBaseURL = null; + try + { + // Cache url prefix for all resources in this jar url. + String base = baseURL.toExternalForm() + "!/"; + newBaseURL = new URL("jar", "", -1, base, cache.get(factory, "jar")); + } + catch (MalformedURLException ignore) + { + // Ignore. + } + this.baseJarURL = newBaseURL; + this.classPath = null; + this.indexSet = indexSet; + } + + // This constructor is used by URLClassLoader. It will immediately + // try to read the jar file, in case we've found an index or a class-path + // setting. FIXME: it would be nice to defer this as well, but URLClassLoader + // makes this hard. + public JarURLLoader(URLClassLoader classloader, URLStreamHandlerCache cache, + URLStreamHandlerFactory factory, + URL baseURL, URL absoluteUrl) + { + this(classloader, cache, factory, baseURL, absoluteUrl, null); + initialize(); + } + + private void initialize() + { + JarFile jarfile = null; + try + { + jarfile = + ((JarURLConnection) baseJarURL.openConnection()).getJarFile(); + + Manifest manifest; + Attributes attributes; + String classPathString; + + IndexListParser parser = new IndexListParser(jarfile, baseJarURL, + baseURL); + LinkedHashMap<URL, Set<String>> indexMap = parser.getHeaders(); + if (indexMap != null) + { + // Note that the index also computes + // the resulting Class-Path -- there are jars out there + // where the index lists some required jars which do + // not appear in the Class-Path attribute in the manifest. + this.classPath = new ArrayList<URLLoader>(); + Iterator<Map.Entry<URL, Set<String>>> it = indexMap.entrySet().iterator(); + while (it.hasNext()) + { + Map.Entry<URL, Set<String>> entry = it.next(); + URL subURL = entry.getKey(); + Set<String> prefixes = entry.getValue(); + if (subURL.equals(baseURL)) + this.indexSet = prefixes; + else + { + JarURLLoader subLoader = new JarURLLoader(classloader, + cache, + factory, subURL, + subURL, + prefixes); + // Note that we don't care if the sub-loader itself has an + // index or a class-path -- only the top-level jar + // file gets this treatment; its index should cover + // everything. + this.classPath.add(subLoader); + } + } + } + else if ((manifest = jarfile.getManifest()) != null + && (attributes = manifest.getMainAttributes()) != null + && ((classPathString + = attributes.getValue(Attributes.Name.CLASS_PATH)) + != null)) + { + this.classPath = new ArrayList<URLLoader>(); + StringTokenizer st = new StringTokenizer(classPathString, " "); + while (st.hasMoreElements ()) + { + String e = st.nextToken (); + try + { + URL subURL = new URL(baseURL, e); + // We've seen at least one jar file whose Class-Path + // attribute includes the original jar. If we process + // that normally we end up with infinite recursion. + if (subURL.equals(baseURL)) + continue; + JarURLLoader subLoader = new JarURLLoader(classloader, + cache, factory, + subURL, subURL); + this.classPath.add(subLoader); + ArrayList<URLLoader> extra = subLoader.getClassPath(); + if (extra != null) + this.classPath.addAll(extra); + } + catch (java.net.MalformedURLException xx) + { + // Give up + } + } + } + } + catch (IOException ioe) + { + /* ignored */ + } + + this.jarfile = jarfile; + this.initialized = true; + } + + /** get resource with the name "name" in the jar url */ + public Resource getResource(String name) + { + if (name.startsWith("/")) + name = name.substring(1); + if (indexSet != null) + { + // Trust the index. + String basename = name; + int offset = basename.lastIndexOf('/'); + if (offset != -1) + basename = basename.substring(0, offset); + if (! indexSet.contains(basename)) + return null; + // FIXME: if the index claim to hold the resource, and jar file + // doesn't have it, we're supposed to throw an exception. However, + // in our model this is tricky to implement, as another URLLoader from + // the same top-level jar may have an overlapping index entry. + } + + if (! initialized) + initialize(); + if (jarfile == null) + return null; + + JarEntry je = jarfile.getJarEntry(name); + if (je != null) + return new JarURLResource(this, name, je); + else + return null; + } + + public Manifest getManifest() + { + try + { + return (jarfile == null) ? null : jarfile.getManifest(); + } + catch (IOException ioe) + { + return null; + } + } + + public ArrayList<URLLoader> getClassPath() + { + return classPath; + } +} diff --git a/libjava/classpath/gnu/java/net/loader/JarURLResource.java b/libjava/classpath/gnu/java/net/loader/JarURLResource.java new file mode 100644 index 000000000..1dec92fd3 --- /dev/null +++ b/libjava/classpath/gnu/java/net/loader/JarURLResource.java @@ -0,0 +1,94 @@ +/* JarURLResource.java -- a Resource for jar URLs + Copyright (C) 2006 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.loader; + + +import java.io.IOException; +import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URL; +import java.security.cert.Certificate; +import java.util.jar.JarEntry; + +public final class JarURLResource extends Resource +{ + private final JarEntry entry; + private final String name; + + public JarURLResource(JarURLLoader loader, String name, JarEntry entry) + { + super(loader); + this.entry = entry; + this.name = name; + } + + public InputStream getInputStream() throws IOException + { + return ((JarURLLoader) loader).jarfile.getInputStream(entry); + } + + public int getLength() + { + return (int) entry.getSize(); + } + + public Certificate[] getCertificates() + { + // We have to get the entry from the jar file again, because the + // certificates will not be available until the entire entry has + // been read. + return ((JarEntry) ((JarURLLoader) loader).jarfile.getEntry(name)) + .getCertificates(); + } + + public URL getURL() + { + try + { + return new URL(((JarURLLoader) loader).baseJarURL, name, + loader.cache.get(loader.factory, "jar")); + } + catch (MalformedURLException e) + { + InternalError ie = new InternalError(); + ie.initCause(e); + throw ie; + } + } +} diff --git a/libjava/classpath/gnu/java/net/loader/RemoteResource.java b/libjava/classpath/gnu/java/net/loader/RemoteResource.java new file mode 100644 index 000000000..dc33b6636 --- /dev/null +++ b/libjava/classpath/gnu/java/net/loader/RemoteResource.java @@ -0,0 +1,78 @@ +/* Resource.java -- a Resource for "remote" URLs + Copyright (C) 2006 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.loader; + + +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; + +/** + * A resource from some remote location. + */ +public final class RemoteResource extends Resource +{ + private final URL url; + private final InputStream stream; + final int length; + + public RemoteResource(RemoteURLLoader loader, String name, URL url, + InputStream stream, int length) + { + super(loader); + this.url = url; + this.stream = stream; + this.length = length; + } + + public InputStream getInputStream() throws IOException + { + return stream; + } + + public int getLength() + { + return length; + } + + public URL getURL() + { + return url; + } +} diff --git a/libjava/classpath/gnu/java/net/loader/RemoteURLLoader.java b/libjava/classpath/gnu/java/net/loader/RemoteURLLoader.java new file mode 100644 index 000000000..81ef34f9e --- /dev/null +++ b/libjava/classpath/gnu/java/net/loader/RemoteURLLoader.java @@ -0,0 +1,101 @@ +/* RemoteURLLoader.java -- a URLLoader for "remote" objects + Copyright (C) 2006 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.loader; + + +import java.io.IOException; +import java.io.InputStream; +import java.net.HttpURLConnection; +import java.net.URL; +import java.net.URLClassLoader; +import java.net.URLConnection; +import java.net.URLStreamHandlerFactory; + +/** + * Loader for remote directories. + */ +public final class RemoteURLLoader extends URLLoader +{ + private final String protocol; + + public RemoteURLLoader(URLClassLoader classloader, + URLStreamHandlerCache cache, + URLStreamHandlerFactory factory, + URL url) + { + super(classloader, cache, factory, url); + protocol = url.getProtocol(); + } + + /** + * Get a remote resource. + * Returns null if no such resource exists. + */ + public Resource getResource(String name) + { + try + { + URL url = new URL(baseURL, name, cache.get(factory, protocol)); + URLConnection connection = url.openConnection(); + + // Open the connection and check the stream + // just to be sure it exists. + int length = connection.getContentLength(); + InputStream stream = connection.getInputStream(); + + // We can do some extra checking if it is a http request + if (connection instanceof HttpURLConnection) + { + int response = + ((HttpURLConnection) connection).getResponseCode(); + if (response / 100 != 2) + return null; + } + + if (stream != null) + return new RemoteResource(this, name, url, stream, length); + else + return null; + } + catch (IOException ioe) + { + return null; + } + } +} diff --git a/libjava/classpath/gnu/java/net/loader/Resource.java b/libjava/classpath/gnu/java/net/loader/Resource.java new file mode 100644 index 000000000..53720b039 --- /dev/null +++ b/libjava/classpath/gnu/java/net/loader/Resource.java @@ -0,0 +1,110 @@ +/* Resource.java -- a resource for URLLoader + Copyright (C) 2006 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.loader; + + +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.security.CodeSource; +import java.security.cert.Certificate; + +/** + * A <code>Resource</code> represents a resource in some + * <code>URLLoader</code>. It also contains all information (e.g., + * <code>URL</code>, <code>CodeSource</code>, <code>Manifest</code> and + * <code>InputStream</code>) that is necessary for loading resources + * and creating classes from a <code>URL</code>. + */ +public abstract class Resource +{ + final URLLoader loader; + + public Resource(URLLoader loader) + { + this.loader = loader; + } + + /** + * Returns the non-null <code>CodeSource</code> associated with + * this resource. + */ + public CodeSource getCodeSource() + { + Certificate[] certs = getCertificates(); + if (certs == null) + return loader.noCertCodeSource; + else + return new CodeSource(loader.baseURL, certs); + } + + /** + * Returns <code>Certificates</code> associated with this + * resource, or null when there are none. + */ + public Certificate[] getCertificates() + { + return null; + } + + /** + * Return the URLLoader for this resource. + */ + public final URLLoader getLoader() + { + return loader; + } + + /** + * Return a <code>URL</code> that can be used to access this resource. + */ + public abstract URL getURL(); + + /** + * Returns the size of this <code>Resource</code> in bytes or + * <code>-1</code> when unknown. + */ + public abstract int getLength(); + + /** + * Returns the non-null <code>InputStream</code> through which + * this resource can be loaded. + */ + public abstract InputStream getInputStream() throws IOException; +} diff --git a/libjava/classpath/gnu/java/net/loader/URLLoader.java b/libjava/classpath/gnu/java/net/loader/URLLoader.java new file mode 100644 index 000000000..337564f6a --- /dev/null +++ b/libjava/classpath/gnu/java/net/loader/URLLoader.java @@ -0,0 +1,148 @@ +/* URLLoader.java -- base helper class for URLClassLoader + Copyright (C) 2006 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.loader; + +import java.net.URL; +import java.net.URLClassLoader; +import java.net.URLStreamHandlerFactory; +import java.security.CodeSource; +import java.security.cert.Certificate; +import java.util.ArrayList; +import java.util.jar.Manifest; + +/** + * A <code>URLLoader</code> contains all logic to load resources from a + * given base <code>URL</code>. + */ +public abstract class URLLoader +{ + /** + * Our classloader to get info from if needed. + */ + final URLClassLoader classloader; + + /** + * The base URL from which all resources are loaded. + */ + final URL baseURL; + + /** + * The stream handler factory. + */ + final URLStreamHandlerFactory factory; + + /** + * The source for stream handlers. + */ + final URLStreamHandlerCache cache; + + /** + * A <code>CodeSource</code> without any associated certificates. + * It is common for classes to not have certificates associated + * with them. If they come from the same <code>URLLoader</code> + * then it is safe to share the associated <code>CodeSource</code> + * between them since <code>CodeSource</code> is immutable. + */ + final CodeSource noCertCodeSource; + + public URLLoader(URLClassLoader classloader, URLStreamHandlerCache cache, + URLStreamHandlerFactory factory, + URL baseURL) + { + this(classloader, cache, factory, baseURL, baseURL); + } + + public URLLoader(URLClassLoader classloader, URLStreamHandlerCache cache, + URLStreamHandlerFactory factory, + URL baseURL, URL overrideURL) + { + this.classloader = classloader; + this.baseURL = baseURL; + this.factory = factory; + this.cache = cache; + this.noCertCodeSource = new CodeSource(overrideURL, (Certificate[]) null); + } + + /** + * Return the base URL of this loader. + */ + public final URL getBaseURL() + { + return baseURL; + } + + /** + * Returns a <code>Class</code> loaded by this + * <code>URLLoader</code>, or <code>null</code> when this loader + * either can't load the class or doesn't know how to load classes + * at all. Most subclasses do not need to override this; it is only + * useful in situations where the subclass has a more direct way of + * making <code>Class</code> objects. + */ + public Class getClass(String className) + { + return null; + } + + /** + * Returns a <code>Resource</code> loaded by this + * <code>URLLoader</code>, or <code>null</code> when no + * <code>Resource</code> with the given name exists. + */ + public abstract Resource getResource(String s); + + /** + * Returns the <code>Manifest</code> associated with the + * <code>Resource</code>s loaded by this <code>URLLoader</code> or + * <code>null</code> there is no such <code>Manifest</code>. + */ + public Manifest getManifest() + { + return null; + } + + /** + * Return a list of new URLLoader objects representing any + * class path entries added by this container. + */ + public ArrayList<URLLoader> getClassPath() + { + return null; + } +} diff --git a/libjava/classpath/gnu/java/net/loader/URLStreamHandlerCache.java b/libjava/classpath/gnu/java/net/loader/URLStreamHandlerCache.java new file mode 100644 index 000000000..b7b8bf12b --- /dev/null +++ b/libjava/classpath/gnu/java/net/loader/URLStreamHandlerCache.java @@ -0,0 +1,85 @@ +/* URLStreamHandlerCache.java -- a cache for URLStreamHandlers + Copyright (C) 2006 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.loader; + +import java.net.URLStreamHandler; +import java.net.URLStreamHandlerFactory; +import java.util.HashMap; + +/** + */ +public class URLStreamHandlerCache +{ + /** + * A cache to store mappings between handler factory and its + * private protocol handler cache (also a HashMap), so we can avoid + * creating handlers each time the same protocol comes. + */ + private HashMap<URLStreamHandlerFactory, HashMap<String, URLStreamHandler>> factoryCache + = new HashMap<URLStreamHandlerFactory, HashMap<String, URLStreamHandler>>(5); + + public URLStreamHandlerCache() + { + } + + public synchronized void add(URLStreamHandlerFactory factory) + { + // Since we only support three protocols so far, 5 is enough + // for cache initial size. + if (factory != null && factoryCache.get(factory) == null) + factoryCache.put(factory, new HashMap<String, URLStreamHandler>(5)); + } + + public synchronized URLStreamHandler get(URLStreamHandlerFactory factory, + String protocol) + { + if (factory == null) + return null; + // Check if there're handler for the same protocol in cache. + HashMap<String, URLStreamHandler> cache = factoryCache.get(factory); + URLStreamHandler handler = cache.get(protocol); + if (handler == null) + { + // Add it to cache. + handler = factory.createURLStreamHandler(protocol); + cache.put(protocol, handler); + } + return handler; + } +} diff --git a/libjava/classpath/gnu/java/net/local/LocalServerSocket.java b/libjava/classpath/gnu/java/net/local/LocalServerSocket.java new file mode 100644 index 000000000..9d8a6ccdf --- /dev/null +++ b/libjava/classpath/gnu/java/net/local/LocalServerSocket.java @@ -0,0 +1,171 @@ +/* LocalServerSocket.java -- a unix domain server socket. + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is a 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 of the License, 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; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, 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.local; + +import java.io.IOException; + +import java.net.InetAddress; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.SocketAddress; + +public final class LocalServerSocket extends ServerSocket +{ + + // Fields. + // ------------------------------------------------------------------------- + + private LocalSocketImpl myImpl; + private boolean closed; + + // Constructors. + // ------------------------------------------------------------------------- + + public LocalServerSocket () throws IOException + { + myImpl = new LocalSocketImpl (); + } + + public LocalServerSocket (SocketAddress bindPoint) throws IOException + { + this (); + bind (bindPoint); + } + + // Instance methods. + // ------------------------------------------------------------------------- + + public void bind (SocketAddress bindPoint) throws IOException + { + bind (bindPoint, 0); + } + + public void bind (SocketAddress bindPoint, int backlog) throws IOException + { + myImpl.doCreate (); + myImpl.bind (bindPoint); + myImpl.listen (backlog); + } + + public InetAddress getInetAddress () + { + return null; + } + + public int getLocalPort () + { + return -1; + } + + public SocketAddress getLocalSocketAddress () + { + return myImpl.getLocalAddress (); + } + + public Socket accept () throws IOException + { + LocalSocket s = new LocalSocket (true); + myImpl.accept (s.getLocalImpl()); + s.localConnected = true; + return s; + } + + public void close () throws IOException + { + myImpl.close (); + myImpl.unlink (); + closed = true; + } + + public boolean isBound () + { + return myImpl.getLocalAddress () != null; + } + + public boolean isClosed () + { + return closed; + } + + public void setSoTimeout (int timeout) + { + throw new UnsupportedOperationException ("local sockets do not support timeouts"); + } + + public int getSoTimeout () + { + throw new UnsupportedOperationException ("local sockets do not support timeouts"); + } + + public void setReuseAddress (boolean b) + { + throw new UnsupportedOperationException ("local sockets do not support reuse address"); + } + + public boolean getReuseAddress () + { + throw new UnsupportedOperationException ("local sockets do not support reuse address"); + } + + public String toString () + { + return LocalServerSocket.class.getName() + " [ address=" + + myImpl.getLocalAddress() + " ]"; + } + + public void setReceiveBufferSize (int size) + { + throw new UnsupportedOperationException ("local sockets do not support buffer size"); + } + + public int getReceiveBufferSize () + { + throw new UnsupportedOperationException ("local sockets do not support buffer size"); + } + + public void setSendBufferSize (int size) + { + throw new UnsupportedOperationException ("local sockets do not support buffer size"); + } + + public int getSendBufferSize () + { + throw new UnsupportedOperationException ("local sockets do not support buffer size"); + } +} diff --git a/libjava/classpath/gnu/java/net/local/LocalSocket.java b/libjava/classpath/gnu/java/net/local/LocalSocket.java new file mode 100644 index 000000000..92acc65bb --- /dev/null +++ b/libjava/classpath/gnu/java/net/local/LocalSocket.java @@ -0,0 +1,312 @@ +/* LocalSocket.java -- a unix domain client socket. + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is a 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 of the License, 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; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, 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.local; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import java.net.InetAddress; +import java.net.Socket; +import java.net.SocketAddress; +import java.net.SocketException; + +import java.nio.channels.IllegalBlockingModeException; +import java.nio.channels.SocketChannel; + +/** + * A local, or unix-domain socket. Unix domain sockets are connected on the + * local filesystem itself, rather than a remote address. + */ +public final class LocalSocket extends Socket +{ + + // Fields. + // ------------------------------------------------------------------------- + + private final LocalSocketImpl localimpl; + boolean localClosed; + boolean localConnected; + + // Constructors. + // ------------------------------------------------------------------------- + + public LocalSocket () throws SocketException + { + super (); + localimpl = new LocalSocketImpl (); + } + + public LocalSocket (LocalSocketAddress addr) throws SocketException + { + this (); + try + { + connect (addr); + } + catch (IOException ioe) + { + SocketException se = new SocketException (); + se.initCause (ioe); + throw se; + } + } + + LocalSocket (boolean nocreate) throws IOException + { + super (); + localimpl = new LocalSocketImpl (nocreate); + } + + // Instance methods. + // ------------------------------------------------------------------------- + + public void bind (SocketAddress bindpoint) throws IOException + { + throw new SocketException ("binding local client sockets is nonsensical"); + } + + public void connect (SocketAddress endpoint, int timeout) throws IOException + { + if (isClosed ()) + { + throw new SocketException ("socket is closed"); + } + if (! (endpoint instanceof LocalSocketAddress)) + { + throw new IllegalArgumentException ("socket address is not a local address"); + } + if (getChannel() != null && !getChannel().isBlocking()) + { + throw new IllegalBlockingModeException (); + } + + try + { + localimpl.doCreate (); + localimpl.localConnect ((LocalSocketAddress) endpoint); + } + catch (IOException ioe) + { + close (); + throw ioe; + } + localConnected = true; + } + + public InetAddress getInetAddress () + { + return null; + } + + public InetAddress getLocalAddress () + { + return null; + } + + public int getPort () + { + return -1; + } + + public int getLocalPort () + { + return -1; + } + + public SocketChannel getChannel () + { + return null; + } + + public SocketAddress getLocalSocketAddress () + { + return localimpl.getLocalAddress (); + } + + public SocketAddress getRemoteSocketAddress () + { + return localimpl.getRemoteAddress (); + } + + public InputStream getInputStream () throws IOException + { + return localimpl.getInputStream (); + } + + public OutputStream getOutputStream () throws IOException + { + return localimpl.getOutputStream (); + } + + public void sendUrgentData (int b) throws IOException + { + localimpl.sendUrgentData (b); + } + + public synchronized void close () throws IOException + { + localimpl.close (); + localClosed = true; + } + + public void shutdownInput () throws IOException + { + localimpl.shutdownInput (); + } + + public void shutdownOutput () throws IOException + { + localimpl.shutdownOutput (); + } + + public boolean isClosed () + { + return localClosed; + } + + public boolean isBound () + { + return false; + } + + public boolean isConnected () + { + return localConnected; + } + + // Unsupported methods. + // ------------------------------------------------------------------------- + + public void setTcpNoDelay (boolean b) throws SocketException + { + throw new SocketException ("local sockets do not support this option"); + } + + public boolean getTcpNoDelay() throws SocketException + { + throw new SocketException ("local sockets do not support this option"); + } + + public void setSoLinger (boolean b, int i) throws SocketException + { + throw new SocketException ("local sockets do not support this option"); + } + + public int getSoLinger () throws SocketException + { + throw new SocketException ("local sockets do not support this option"); + } + + public void setOOBInline (boolean b) throws SocketException + { + throw new SocketException ("local sockets do not support this option"); + } + + public boolean getOOBInline () throws SocketException + { + throw new SocketException ("local sockets do not support this option"); + } + + public void setSoTimeout (int i) throws SocketException + { + // Ignore. + } + + public int getSoTimeout () throws SocketException + { + // We don't support timeout, so we return 0. + return 0; + } + + public void setSendBufferSize (int i) throws SocketException + { + throw new SocketException ("local sockets do not support this option"); + } + + public int getSendBufferSize() throws SocketException + { + throw new SocketException ("local sockets do not support this option"); + } + + public void setReceiveBufferSize (int i) throws SocketException + { + throw new SocketException ("local sockets do not support this option"); + } + + public int getReceiveBufferSize () throws SocketException + { + throw new SocketException ("local sockets do not support this option"); + } + + public void setKeepAlive (boolean b) throws SocketException + { + throw new SocketException ("local sockets do not support this option"); + } + + public boolean getKeepAlive () throws SocketException + { + throw new SocketException ("local sockets do not support this option"); + } + + public void setTrafficClass (int i) throws SocketException + { + throw new SocketException ("local sockets do not support this option"); + } + + public int getTrafficClass () throws SocketException + { + throw new SocketException ("local sockets do not support this option"); + } + + public void setReuseAddress (boolean b) throws SocketException + { + throw new SocketException ("local sockets do not support this option"); + } + + public boolean getReuseAddress () throws SocketException + { + throw new SocketException ("local sockets do not support this option"); + } + + LocalSocketImpl getLocalImpl () + { + return localimpl; + } +} diff --git a/libjava/classpath/gnu/java/net/local/LocalSocketAddress.java b/libjava/classpath/gnu/java/net/local/LocalSocketAddress.java new file mode 100644 index 000000000..ac5c53db2 --- /dev/null +++ b/libjava/classpath/gnu/java/net/local/LocalSocketAddress.java @@ -0,0 +1,100 @@ +/* LocalSocketAddress.java -- unix-domain socket address. + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is a 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 of the License, 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; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, 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.local; + +import java.net.SocketAddress; + +public final class LocalSocketAddress extends SocketAddress +{ + + // Fields. + // ------------------------------------------------------------------------- + + private final String path; + + // Constructor. + // ------------------------------------------------------------------------- + + /** + * Creates a new unix domain socket address. + * + * @param path The path to the socket. + * @throws NullPointerException If <i>path</i> is <tt>null</tt>. + */ + public LocalSocketAddress (String path) + { + if (path == null) + { + throw new NullPointerException (); + } + this.path = path; + } + + // Instance methods. + // ------------------------------------------------------------------------- + + /** + * Returns the path of the socket. + * + * @return The path. + */ + public String getPath () + { + return path; + } + + public boolean equals (Object o) + { + if (!(o instanceof LocalSocketAddress)) + { + return false; + } + return getPath ().equals (((LocalSocketAddress) o).getPath ()); + } + + public int hashCode () + { + return path.hashCode(); + } + + public String toString () + { + return super.toString() + " [ " + path + " ]"; + } +} diff --git a/libjava/classpath/gnu/java/net/local/LocalSocketImpl.java b/libjava/classpath/gnu/java/net/local/LocalSocketImpl.java new file mode 100644 index 000000000..f49b79947 --- /dev/null +++ b/libjava/classpath/gnu/java/net/local/LocalSocketImpl.java @@ -0,0 +1,334 @@ +/* LocalSocketImpl.java -- a unix domain client socket implementation. + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is a 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 of the License, 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; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, 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.local; + +import gnu.classpath.Configuration; + +import java.io.FileDescriptor; +import java.io.InputStream; +import java.io.IOException; +import java.io.OutputStream; + +import java.net.InetAddress; +import java.net.SocketAddress; +import java.net.SocketException; +import java.net.SocketImpl; + +final class LocalSocketImpl extends SocketImpl +{ + + // Fields. + // ------------------------------------------------------------------------- + + private boolean created; + private InputStream in; + private OutputStream out; + // Package private to avoid synthetic accessor method. + int socket_fd; + private LocalSocketAddress local; + private LocalSocketAddress remote; + + static + { + try + { + if (Configuration.INIT_LOAD_LIBRARY) + { + System.loadLibrary ("javanet"); + } + } + catch (Exception x) + { + x.printStackTrace (); + } + } + + // Constructor. + // ------------------------------------------------------------------------- + + LocalSocketImpl () + { + this (false); + } + + LocalSocketImpl (boolean nocreate) + { + created = nocreate; + socket_fd = -1; + fd = new FileDescriptor (); + } + + // Instance methods. + // ------------------------------------------------------------------------- + + public void setOption (int opt, Object value) throws SocketException + { + throw new SocketException ("local sockets do not support options"); + } + + public Object getOption (int opt) throws SocketException + { + throw new SocketException ("local sockets do not support options"); + } + + protected native void create (boolean stream) throws IOException; + protected native void listen (int timeout) throws IOException; + protected native void accept (LocalSocketImpl socket) throws IOException; + protected native int available (int fd) throws IOException; + protected native void close () throws IOException; + protected native void sendUrgentData (int data) throws IOException; + protected native void shutdownInput () throws IOException; + protected native void shutdownOutput () throws IOException; + + native void unlink () throws IOException; + native void localBind (LocalSocketAddress addr) throws IOException; + native void localConnect (LocalSocketAddress addr) throws IOException; + native int read (int fd, byte[] buf, int off, int len) throws IOException; + native void write (int fd, byte[] buf, int off, int len) throws IOException; + + protected int available() + throws IOException + { + return available(socket_fd); + } + + void doCreate () throws IOException + { + if (!created) + { + create (true); + } + } + + LocalSocketAddress getLocalAddress () + { + return local; + } + + LocalSocketAddress getRemoteAddress () + { + return remote; + } + + protected InputStream getInputStream() + { + if (in == null) + { + in = new LocalInputStream (this); + } + + return in; + } + + protected OutputStream getOutputStream() + { + if (out == null) + { + out = new LocalOutputStream (this); + } + + return out; + } + + protected void accept (SocketImpl impl) throws IOException + { + if (! (impl instanceof LocalSocketImpl)) + { + throw new IllegalArgumentException ("not a local socket"); + } + accept ((LocalSocketImpl) impl); + } + + protected void connect (String host, int port) throws IOException + { + throw new SocketException ("this is a local socket"); + } + + protected void connect (InetAddress addr, int port) throws IOException + { + throw new SocketException ("this is a local socket"); + } + + protected void connect(SocketAddress addr, int timeout) throws IOException + { + if (! (addr instanceof LocalSocketAddress)) + { + throw new SocketException ("address is not local"); + } + localConnect ((LocalSocketAddress) addr); + } + + protected void bind (InetAddress addr, int port) throws IOException + { + throw new SocketException ("this is a local socket"); + } + + protected void bind (SocketAddress addr) throws IOException + { + if (! (addr instanceof LocalSocketAddress)) + { + throw new SocketException ("address is not local"); + } + localBind ((LocalSocketAddress) addr); + } + +// Inner classes. + // ------------------------------------------------------------------------- + + class LocalInputStream extends InputStream + { + + // Field. + // ----------------------------------------------------------------------- + + private final LocalSocketImpl impl; + + // Constructor. + // ----------------------------------------------------------------------- + + LocalInputStream (LocalSocketImpl impl) + { + this.impl = impl; + } + + // Instance methods. + // ----------------------------------------------------------------------- + + public int available () throws IOException + { + return impl.available(); + } + + public boolean markSupported () + { + return false; + } + + public void mark (int readLimit) + { + } + + public void reset () throws IOException + { + throw new IOException ("mark/reset not supported"); + } + + public void close () throws IOException + { + impl.close(); + } + + public int read () throws IOException + { + byte[] buf = new byte[1]; + int ret = read (buf); + if (ret != -1) + { + return buf[0] & 0xFF; + } + else + { + return -1; + } + } + + public int read (byte[] buf) throws IOException + { + return read (buf, 0, buf.length); + } + + public int read (byte[] buf, int off, int len) throws IOException + { + int ret = impl.read (socket_fd, buf, off, len); + + if (ret == 0) + { + return -1; + } + + return ret; + } + } + + class LocalOutputStream extends OutputStream + { + + // Field. + // ----------------------------------------------------------------------- + + private final LocalSocketImpl impl; + + // Constructor. + // ----------------------------------------------------------------------- + + LocalOutputStream (LocalSocketImpl impl) + { + this.impl = impl; + } + + // Instance methods. + // ----------------------------------------------------------------------- + + public void close () throws IOException + { + impl.close (); + } + + public void flush () throws IOException + { + } + + public void write (int b) throws IOException + { + byte[] buf = new byte [1]; + buf[0] = (byte) b; + write (buf); + } + + public void write (byte[] buf) throws IOException + { + write (buf, 0, buf.length); + } + + public void write (byte[] buf, int off, int len) throws IOException + { + impl.write (socket_fd, buf, off, len); + } + } +} diff --git a/libjava/classpath/gnu/java/net/package.html b/libjava/classpath/gnu/java/net/package.html new file mode 100644 index 000000000..5641fbd92 --- /dev/null +++ b/libjava/classpath/gnu/java/net/package.html @@ -0,0 +1,46 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<!-- package.html - describes classes in gnu.java.net package. + Copyright (C) 2005 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. --> + +<html> +<head><title>GNU Classpath - gnu.java.net</title></head> + +<body> +<p></p> + +</body> +</html> diff --git a/libjava/classpath/gnu/java/net/protocol/file/Connection.java b/libjava/classpath/gnu/java/net/protocol/file/Connection.java new file mode 100644 index 000000000..80155af0d --- /dev/null +++ b/libjava/classpath/gnu/java/net/protocol/file/Connection.java @@ -0,0 +1,376 @@ +/* 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; + } +} diff --git a/libjava/classpath/gnu/java/net/protocol/file/Handler.java b/libjava/classpath/gnu/java/net/protocol/file/Handler.java new file mode 100644 index 000000000..58ebe4c46 --- /dev/null +++ b/libjava/classpath/gnu/java/net/protocol/file/Handler.java @@ -0,0 +1,91 @@ +/* Handler.java -- "file" protocol handler for java.net + Copyright (C) 1998, 1999, 2000, 2002, 2003, 2004 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 java.io.IOException; +import java.net.URL; +import java.net.URLConnection; +import java.net.URLStreamHandler; + +/** + * This is the protocol handler for the "file" protocol. + * It implements the abstract openConnection() method from + * URLStreamHandler by returning a new FileURLConnection object (from + * this package). All other methods are inherited + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Warren Levy (warrenl@cygnus.com) + */ +public class Handler extends URLStreamHandler +{ + /** + * A do nothing constructor + */ + public Handler() + { + } + + /** + * This method returs a new FileURLConnection for the specified URL + * + * @param url The URL to return a connection for + * + * @return The URLConnection + * + * @exception IOException If an error occurs + */ + protected URLConnection openConnection(URL url) throws IOException + { + // If a hostname is set, then we need to switch protocols to ftp + // in order to transfer this from the remote host. + String host = url.getHost(); + if ((host != null) && (! host.equals(""))) + { + // Reset the protocol (and implicitly the handler) for this URL. + // Then have the URL attempt the connection again, as it will + // get the changed handler the next time around. + // If the ftp protocol handler is not installed, an + // exception will be thrown from the new openConnection() call. + setURL (url, "ftp", url.getHost(), url.getPort(), url.getFile(), + url.getRef()); + return url.openConnection(); + } + + return new Connection(url); + } +} // class Handler diff --git a/libjava/classpath/gnu/java/net/protocol/file/package.html b/libjava/classpath/gnu/java/net/protocol/file/package.html new file mode 100644 index 000000000..cbce7413f --- /dev/null +++ b/libjava/classpath/gnu/java/net/protocol/file/package.html @@ -0,0 +1,46 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<!-- package.html - describes classes in gnu.java.net.protocol.file package. + Copyright (C) 2005 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. --> + +<html> +<head><title>GNU Classpath - gnu.java.net.protocol.file</title></head> + +<body> +<p></p> + +</body> +</html> diff --git a/libjava/classpath/gnu/java/net/protocol/ftp/ActiveModeDTP.java b/libjava/classpath/gnu/java/net/protocol/ftp/ActiveModeDTP.java new file mode 100644 index 000000000..1ed31b830 --- /dev/null +++ b/libjava/classpath/gnu/java/net/protocol/ftp/ActiveModeDTP.java @@ -0,0 +1,251 @@ +/* ActiveModeDTP.java -- + Copyright (C) 2003, 2004 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.ftp; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.InetAddress; +import java.net.ServerSocket; +import java.net.Socket; + +/** + * An active mode FTP data transfer process. + * This starts a server on the specified port listening for a data + * connection. It converts the socket input into a file stream for reading. + * + * @author Chris Burdess (dog@gnu.org) + */ +final class ActiveModeDTP + implements DTP, Runnable +{ + + ServerSocket server; + Socket socket; + DTPInputStream in; + DTPOutputStream out; + boolean completed; + boolean inProgress; + int transferMode; + IOException exception; + Thread acceptThread; + int connectionTimeout; + + ActiveModeDTP(InetAddress localhost, int port, + int connectionTimeout, int timeout) + throws IOException + { + completed = false; + inProgress = false; + server = new ServerSocket(port, 1, localhost); + if (timeout > 0) + { + server.setSoTimeout(timeout); + } + if (connectionTimeout <= 0) + { + connectionTimeout = 20000; + } + this.connectionTimeout = connectionTimeout; + acceptThread = new Thread(this, "ActiveModeDTP"); + acceptThread.setDaemon(true); + acceptThread.start(); + } + + /** + * Start listening. + */ + public void run() + { + try + { + socket = server.accept(); + //System.err.println("Accepted connection from "+socket.getInetAddress()+":"+socket.getPort()); + } + catch (IOException e) + { + exception = e; + } + } + + /** + * Waits until a client has connected. + */ + public void waitFor() + throws IOException + { + try + { + acceptThread.join(connectionTimeout); + } + catch (InterruptedException e) + { + } + if (exception != null) + { + throw exception; + } + if (socket == null) + { + server.close(); + throw new IOException("client did not connect before timeout"); + } + acceptThread = null; + } + + /** + * Returns an input stream from which a remote file can be read. + */ + public InputStream getInputStream() + throws IOException + { + if (inProgress) + { + throw new IOException("Transfer in progress"); + } + if (acceptThread != null) + { + waitFor(); + } + switch (transferMode) + { + case FTPConnection.MODE_STREAM: + in = new StreamInputStream(this, socket.getInputStream()); + break; + case FTPConnection.MODE_BLOCK: + in = new BlockInputStream(this, socket.getInputStream()); + break; + case FTPConnection.MODE_COMPRESSED: + in = new CompressedInputStream(this, socket.getInputStream()); + break; + default: + throw new IllegalStateException("invalid transfer mode"); + } + in.setTransferComplete(false); + return in; + } + + /** + * Returns an output stream to which a local file can be written for + * upload. + */ + public OutputStream getOutputStream() throws IOException + { + if (inProgress) + { + throw new IOException("Transfer in progress"); + } + if (acceptThread != null) + { + waitFor(); + } + switch (transferMode) + { + case FTPConnection.MODE_STREAM: + out = new StreamOutputStream(this, socket.getOutputStream()); + break; + case FTPConnection.MODE_BLOCK: + out = new BlockOutputStream(this, socket.getOutputStream()); + break; + case FTPConnection.MODE_COMPRESSED: + out = new CompressedOutputStream(this, socket.getOutputStream()); + break; + default: + throw new IllegalStateException("invalid transfer mode"); + } + out.setTransferComplete(false); + return out; + } + + public void setTransferMode(int mode) + { + transferMode = mode; + } + + public void complete() + { + completed = true; + if (!inProgress) + { + transferComplete(); + } + } + + public boolean abort() + { + completed = true; + transferComplete(); + return inProgress; + } + + public void transferComplete() + { + if (socket == null) + { + return; + } + if (in != null) + { + in.setTransferComplete(true); + } + if (out != null) + { + out.setTransferComplete(true); + } + completed = completed || (transferMode == FTPConnection.MODE_STREAM); + if (completed && socket != null) + { + try + { + socket.close(); + } + catch (IOException e) + { + } + try + { + server.close(); + } + catch (IOException e) + { + } + } + } + +} diff --git a/libjava/classpath/gnu/java/net/protocol/ftp/BlockInputStream.java b/libjava/classpath/gnu/java/net/protocol/ftp/BlockInputStream.java new file mode 100644 index 000000000..09915e7ff --- /dev/null +++ b/libjava/classpath/gnu/java/net/protocol/ftp/BlockInputStream.java @@ -0,0 +1,149 @@ +/* BlockInputStream.java -- + Copyright (C) 2003, 2004 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.ftp; + +import java.io.IOException; +import java.io.InputStream; + +/** + * A DTP input stream that implements the FTP block transfer mode. + * + * @author Chris Burdess (dog@gnu.org) + */ +class BlockInputStream + extends DTPInputStream +{ + + static final int EOF = 64; + + int descriptor; + int max = -1; + int count = -1; + + BlockInputStream(DTP dtp, InputStream in) + { + super(dtp, in); + } + + public int read() + throws IOException + { + if (transferComplete) + { + return -1; + } + if (count == -1) + { + readHeader(); + } + if (max < 1) + { + close(); + return -1; + } + int c = in.read(); + if (c == -1) + { + dtp.transferComplete(); + } + count++; + if (count >= max) + { + count = -1; + if (descriptor == EOF) + { + close(); + } + } + return c; + } + + public int read(byte[] buf) + throws IOException + { + return read(buf, 0, buf.length); + } + + public int read(byte[] buf, int off, int len) + throws IOException + { + if (transferComplete) + { + return -1; + } + if (count == -1) + { + readHeader(); + } + if (max < 1) + { + close(); + return -1; + } + int l = in.read(buf, off, len); + if (l == -1) + { + dtp.transferComplete(); + } + count += l; + if (count >= max) + { + count = -1; + if (descriptor == EOF) + { + close(); + } + } + return l; + } + + /** + * Reads the block header. + */ + void readHeader() + throws IOException + { + descriptor = in.read(); + int max_hi = in.read(); + int max_lo = in.read(); + max = (max_hi << 8) | max_lo; + count = 0; + } + +} diff --git a/libjava/classpath/gnu/java/net/protocol/ftp/BlockOutputStream.java b/libjava/classpath/gnu/java/net/protocol/ftp/BlockOutputStream.java new file mode 100644 index 000000000..d181f9dec --- /dev/null +++ b/libjava/classpath/gnu/java/net/protocol/ftp/BlockOutputStream.java @@ -0,0 +1,110 @@ +/* BlockOutputStream.java -- + Copyright (C) 2003, 2004 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.ftp; + +import java.io.IOException; +import java.io.OutputStream; + +/** + * A DTP output stream that implements the FTP block transfer mode. + * + * @author Chris Burdess (dog@gnu.org) + */ +class BlockOutputStream + extends DTPOutputStream +{ + + static final byte RECORD = -128; // 0x80 + static final byte EOF = 64; // 0x40 + + BlockOutputStream(DTP dtp, OutputStream out) + { + super(dtp, out); + } + + public void write(int c) + throws IOException + { + if (transferComplete) + { + return; + } + byte[] buf = new byte[] + { + RECORD, /* record descriptor */ + 0x00, 0x01, /* one byte */ + (byte) c /* the byte */ + }; + out.write(buf, 0, 4); + } + + public void write(byte[] b) + throws IOException + { + write(b, 0, b.length); + } + + public void write(byte[] b, int off, int len) + throws IOException + { + if (transferComplete) + { + return; + } + byte[] buf = new byte[len + 3]; + buf[0] = RECORD; /* record descriptor */ + buf[1] = (byte) ((len & 0x00ff) >> 8); /* high byte of bytecount */ + buf[2] = (byte) (len & 0xff00); /* low byte of bytecount */ + System.arraycopy(b, off, buf, 3, len); + out.write(buf, 0, len); + } + + public void close() + throws IOException + { + byte[] buf = new byte[] + { + EOF, /* eof descriptor */ + 0x00, 0x00 /* no bytes */ + }; + out.write(buf, 0, 3); + super.close(); + } + +} diff --git a/libjava/classpath/gnu/java/net/protocol/ftp/CompressedInputStream.java b/libjava/classpath/gnu/java/net/protocol/ftp/CompressedInputStream.java new file mode 100644 index 000000000..638d780e6 --- /dev/null +++ b/libjava/classpath/gnu/java/net/protocol/ftp/CompressedInputStream.java @@ -0,0 +1,214 @@ +/* CompressedInputStream.java -- + Copyright (C) 2003, 2004 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.ftp; + +import java.io.IOException; +import java.io.InputStream; +import java.net.ProtocolException; + +/** + * A DTP input stream that implements the FTP compressed transfer mode. + * + * @author Chris Burdess (dog@gnu.org) + */ +class CompressedInputStream + extends DTPInputStream +{ + + static final int EOF = 64; + + static final int RAW = 0x00; + static final int COMPRESSED = 0x80; + static final int FILLER = 0xc0; + + int descriptor; + int max = -1; + int count = -1; + + int state = RAW; // RAW | STATE | FILLER + int rep; // the compressed byte + int n = 0; // the number of compressed or raw bytes + + CompressedInputStream(DTP dtp, InputStream in) + { + super(dtp, in); + } + + public int read() + throws IOException + { + if (transferComplete) + { + return -1; + } + if (count == -1) + { + readHeader(); + } + if (max < 1) + { + close(); + return -1; + } + if (n > 0 && (state == COMPRESSED || state == FILLER)) + { + n--; + return rep; + } + int c = in.read(); + if (c == -1) + { + close(); + } + count++; + if (count >= max) + { + count = -1; + if (descriptor == EOF) + { + close(); + } + } + if (c == -1) + { + return c; + } + while (n == 0) // read code header + { + state = (c & 0xc0); + n = (c & 0x3f); + c = in.read(); + if (c == -1) + { + return -1; + } + } + switch (state) + { + case RAW: + break; + case COMPRESSED: + case FILLER: + rep = c; + break; + default: + throw new ProtocolException("Illegal state: " + state); + } + n--; + return c; + } + + public int read(byte[] buf) + throws IOException + { + return read(buf, 0, buf.length); + } + + public int read(byte[] buf, int off, int len) + throws IOException + { + if (transferComplete) + { + return -1; + } + if (count == -1) + { + readHeader(); + } + if (max < 1) + { + close(); + return -1; + } + // TODO improve performance + for (int i = off; i < len; i++) + { + int c = read(); + if (c == -1) + { + close(); + return i; + } + buf[i] = (byte) c; + } + return len; + /* + int l = in.read (buf, off, len); + if (l==-1) + { + close (); + } + count += l; + if (count>=max) + { + count = -1; + if (descriptor==EOF) + { + close (); + } + } + return l; + */ + } + + /** + * Reads the block header. + */ + void readHeader() + throws IOException + { + descriptor = in.read(); + int max_hi = in.read(); + int max_lo = in.read(); + max = (max_hi << 8) | max_lo; + count = 0; + } + + /** + * Reads the code header. + */ + void readCodeHeader() + throws IOException + { + int code = in.read(); + state = (code & 0xc0); + n = (code & 0x3f); + } + +} diff --git a/libjava/classpath/gnu/java/net/protocol/ftp/CompressedOutputStream.java b/libjava/classpath/gnu/java/net/protocol/ftp/CompressedOutputStream.java new file mode 100644 index 000000000..ec3aef930 --- /dev/null +++ b/libjava/classpath/gnu/java/net/protocol/ftp/CompressedOutputStream.java @@ -0,0 +1,227 @@ +/* CompressedOutputStream.java -- + Copyright (C) 2003, 2004 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.ftp; + +import java.io.IOException; +import java.io.OutputStream; + +/** + * A DTP output stream that implements the FTP compressed transfer mode. + * + * @author Chris Burdess (dog@gnu.org) + */ +class CompressedOutputStream + extends DTPOutputStream +{ + + static final byte RECORD = -128; // 0x80 + static final byte EOF = 64; // 0x40 + + CompressedOutputStream(DTP dtp, OutputStream out) + { + super(dtp, out); + } + + /** + * Just one byte cannot be compressed. + * It takes 5 bytes to transmit - hardly very compressed! + */ + public void write(int c) + throws IOException + { + if (transferComplete) + { + return; + } + byte[] buf = new byte[] + { + RECORD, /* record descriptor */ + 0x00, 0x01, /* one byte */ + 0x01, /* one uncompressed byte */ + (byte) c /* the byte */ + }; + out.write(buf, 0, 5); + } + + public void write(byte[] b) + throws IOException + { + write(b, 0, b.length); + } + + /** + * The larger len is, the better. + */ + public void write(byte[] b, int off, int len) + throws IOException + { + if (transferComplete) + { + return; + } + byte[] buf = compress(b, off, len); + len = buf.length; + buf[0] = RECORD; /* record descriptor */ + buf[1] = (byte) ((len & 0x00ff) >> 8); /* high byte of bytecount */ + buf[2] = (byte) (len & 0xff00); /* low byte of bytecount */ + out.write(buf, 0, len); + } + + /** + * Returns the compressed form of the given byte array. + * The first 3 bytes are left free for header information. + */ + byte[] compress(byte[] b, int off, int len) + { + byte[] buf = new byte[len]; + byte last = 0; + int pos = 0, raw_count = 0, rep_count = 1; + for (int i = off; i < len; i++) + { + byte c = b[i]; + if (i > off && c == last) // compress + { + if (raw_count > 0) // flush raw bytes to buf + { + // need to add raw_count+1 bytes + if (pos + (raw_count + 1) > buf.length) + { + buf = realloc(buf, len); + } + pos = flush_raw(buf, pos, b, (i - raw_count) - 1, + raw_count); + raw_count = 0; + } + rep_count++; // keep looking for same byte + } + else + { + if (rep_count > 1) // flush compressed bytes to buf + { + // need to add 2 bytes + if (pos + 2 > buf.length) + { + buf = realloc(buf, len); + } + pos = flush_compressed(buf, pos, rep_count, last); + rep_count = 1; + } + raw_count++; // keep looking for raw bytes + } + if (rep_count == 127) // flush compressed bytes + { + // need to add 2 bytes + if (pos + 2 > buf.length) + { + buf = realloc(buf, len); + } + pos = flush_compressed(buf, pos, rep_count, last); + rep_count = 1; + } + if (raw_count == 127) // flush raw bytes + { + // need to add raw_count+1 bytes + if (pos + (raw_count + 1) > buf.length) + { + buf = realloc(buf, len); + } + pos = flush_raw(buf, pos, b, (i - raw_count), raw_count); + raw_count = 0; + } + last = c; + } + if (rep_count > 1) // flush compressed bytes + { + // need to add 2 bytes + if (pos + 2 > buf.length) + { + buf = realloc(buf, len); + } + pos = flush_compressed(buf, pos, rep_count, last); + rep_count = 1; + } + if (raw_count > 0) // flush raw bytes + { + // need to add raw_count+1 bytes + if (pos + (raw_count + 1) > buf.length) + { + buf = realloc(buf, len); + } + pos = flush_raw(buf, pos, b, (len - raw_count), raw_count); + raw_count = 0; + } + byte[] ret = new byte[pos + 3]; + System.arraycopy(buf, 0, ret, 3, pos); + return ret; + } + + int flush_compressed(byte[] buf, int pos, int count, byte c) + { + buf[pos++] = (byte) (0x80 | count); + buf[pos++] = c; + return pos; + } + + int flush_raw(byte[] buf, int pos, byte[] src, int off, int len) + { + buf[pos++] = (byte) len; + System.arraycopy(src, off, buf, pos, len); + return pos + len; + } + + byte[] realloc(byte[] buf, int len) + { + byte[] ret = new byte[buf.length + len]; + System.arraycopy(buf, 0, ret, 0, buf.length); + return ret; + } + + public void close() + throws IOException + { + byte[] buf = new byte[] + { + EOF, /* eof descriptor */ + 0x00, 0x00 /* no bytes */ + }; + out.write(buf, 0, 3); + out.close(); + } + +} diff --git a/libjava/classpath/gnu/java/net/protocol/ftp/DTP.java b/libjava/classpath/gnu/java/net/protocol/ftp/DTP.java new file mode 100644 index 000000000..9ba4b7c6c --- /dev/null +++ b/libjava/classpath/gnu/java/net/protocol/ftp/DTP.java @@ -0,0 +1,91 @@ +/* DTP.java -- + Copyright (C) 2003, 2004 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.ftp; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +/** + * An FTP data transfer process. + * + * @author Chris Burdess (dog@gnu.org) + */ +interface DTP +{ + + /** + * Returns an input stream from which a remote file can be read. + */ + InputStream getInputStream() + throws IOException; + + /** + * Returns an output stream to which a local file can be written for + * upload. + */ + OutputStream getOutputStream() + throws IOException; + + /** + * Sets the transfer mode to be used with this DTP. + */ + void setTransferMode(int mode); + + /** + * Marks this DTP completed. + * When the current transfer has finished, any resources will be released. + */ + void complete(); + + /** + * Aborts any current transfer and releases all resources held by this + * DTP. + * @return true if a transfer was interrupted, false otherwise + */ + boolean abort(); + + /** + * Used to notify the DTP that its current transfer is complete. + * This occurs either when end-of-stream is reached or a 226 response is + * received. + */ + void transferComplete(); + +} diff --git a/libjava/classpath/gnu/java/net/protocol/ftp/DTPInputStream.java b/libjava/classpath/gnu/java/net/protocol/ftp/DTPInputStream.java new file mode 100644 index 000000000..7280b0133 --- /dev/null +++ b/libjava/classpath/gnu/java/net/protocol/ftp/DTPInputStream.java @@ -0,0 +1,87 @@ +/* DTPInputStream.java -- + Copyright (C) 2003, 2004 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.ftp; + +import java.io.FilterInputStream; +import java.io.IOException; +import java.io.InputStream; + +/** + * An input stream that notifies a DTP on completion. + * + * @author Chris Burdess (dog@gnu.org) + */ +abstract class DTPInputStream + extends FilterInputStream +{ + + DTP dtp; + boolean transferComplete; + + /** + * Constructor. + * @param dtp the controlling data transfer process + * @param in the underlying socket stream + */ + DTPInputStream (DTP dtp, InputStream in) + { + super(in); + this.dtp = dtp; + transferComplete = false; + } + + /** + * Marks this input stream complete. + * This is called by the DTP. + */ + void setTransferComplete(boolean flag) + { + transferComplete = flag; + } + + /** + * Notifies the controlling DTP that this stream has completed transfer. + */ + public void close() + throws IOException + { + dtp.transferComplete(); + } + +} diff --git a/libjava/classpath/gnu/java/net/protocol/ftp/DTPOutputStream.java b/libjava/classpath/gnu/java/net/protocol/ftp/DTPOutputStream.java new file mode 100644 index 000000000..105c6f095 --- /dev/null +++ b/libjava/classpath/gnu/java/net/protocol/ftp/DTPOutputStream.java @@ -0,0 +1,85 @@ +/* DTPOutputStream.java -- + Copyright (C) 2003, 2004 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.ftp; + +import java.io.FilterOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +/** + * An output stream that notifies a DTP on end of stream. + * + * @author Chris Burdess (dog@gnu.org) + */ +abstract class DTPOutputStream extends FilterOutputStream +{ + + DTP dtp; + boolean transferComplete; + + /** + * Constructor. + * @param dtp the controlling data transfer process + * @param out the socket output stream + */ + DTPOutputStream (DTP dtp, OutputStream out) + { + super (out); + this.dtp = dtp; + transferComplete = false; + } + + /** + * Tells this stream whether transfer has completed or not. + * @param flag true if the process has completed, false otherwise + */ + void setTransferComplete (boolean flag) + { + transferComplete = flag; + } + + /** + * Notifies the controlling DTP that this stream has been terminated. + */ + public void close () throws IOException + { + dtp.transferComplete (); + } + +} diff --git a/libjava/classpath/gnu/java/net/protocol/ftp/FTPConnection.java b/libjava/classpath/gnu/java/net/protocol/ftp/FTPConnection.java new file mode 100644 index 000000000..4e253fcb9 --- /dev/null +++ b/libjava/classpath/gnu/java/net/protocol/ftp/FTPConnection.java @@ -0,0 +1,1352 @@ +/* FTPConnection.java -- + Copyright (C) 2003, 2004 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.ftp; + +import gnu.java.lang.CPStringBuilder; + +import gnu.java.net.CRLFInputStream; +import gnu.java.net.CRLFOutputStream; +import gnu.java.net.EmptyX509TrustManager; +import gnu.java.net.LineInputStream; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.BindException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.ProtocolException; +import java.net.Socket; +import java.net.UnknownHostException; +import java.security.GeneralSecurityException; +import java.util.ArrayList; +import java.util.List; + +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSocket; +import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.TrustManager; + +/** + * An FTP client connection, or PI. + * This implements RFC 959, with the following exceptions: + * <ul> + * <li>STAT, HELP, SITE, SMNT, and ACCT commands are not supported.</li> + * <li>the TYPE command does not allow alternatives to the default bytesize + * (Non-print), and local bytesize is not supported.</li> + * </ul> + * + * @author Chris Burdess (dog@gnu.org) + */ +public class FTPConnection +{ + + /** + * The default FTP transmission control port. + */ + public static final int FTP_PORT = 21; + + /** + * The FTP data port. + */ + public static final int FTP_DATA_PORT = 20; + + // -- FTP vocabulary -- + protected static final String USER = "USER"; + protected static final String PASS = "PASS"; + protected static final String ACCT = "ACCT"; + protected static final String CWD = "CWD"; + protected static final String CDUP = "CDUP"; + protected static final String SMNT = "SMNT"; + protected static final String REIN = "REIN"; + protected static final String QUIT = "QUIT"; + + protected static final String PORT = "PORT"; + protected static final String PASV = "PASV"; + protected static final String TYPE = "TYPE"; + protected static final String STRU = "STRU"; + protected static final String MODE = "MODE"; + + protected static final String RETR = "RETR"; + protected static final String STOR = "STOR"; + protected static final String STOU = "STOU"; + protected static final String APPE = "APPE"; + protected static final String ALLO = "ALLO"; + protected static final String REST = "REST"; + protected static final String RNFR = "RNFR"; + protected static final String RNTO = "RNTO"; + protected static final String ABOR = "ABOR"; + protected static final String DELE = "DELE"; + protected static final String RMD = "RMD"; + protected static final String MKD = "MKD"; + protected static final String PWD = "PWD"; + protected static final String LIST = "LIST"; + protected static final String NLST = "NLST"; + protected static final String SITE = "SITE"; + protected static final String SYST = "SYST"; + protected static final String STAT = "STAT"; + protected static final String HELP = "HELP"; + protected static final String NOOP = "NOOP"; + + protected static final String AUTH = "AUTH"; + protected static final String PBSZ = "PBSZ"; + protected static final String PROT = "PROT"; + protected static final String CCC = "CCC"; + protected static final String TLS = "TLS"; + + public static final int TYPE_ASCII = 1; + public static final int TYPE_EBCDIC = 2; + public static final int TYPE_BINARY = 3; + + public static final int STRUCTURE_FILE = 1; + public static final int STRUCTURE_RECORD = 2; + public static final int STRUCTURE_PAGE = 3; + + public static final int MODE_STREAM = 1; + public static final int MODE_BLOCK = 2; + public static final int MODE_COMPRESSED = 3; + + // -- Telnet constants -- + private static final String US_ASCII = "US-ASCII"; + + /** + * The socket used to communicate with the server. + */ + protected Socket socket; + + /** + * The socket input stream. + */ + protected LineInputStream in; + + /** + * The socket output stream. + */ + protected CRLFOutputStream out; + + /** + * The timeout when attempting to connect a socket. + */ + protected int connectionTimeout; + + /** + * The read timeout on sockets. + */ + protected int timeout; + + /** + * If true, print debugging information. + */ + protected boolean debug; + + /** + * The current data transfer process in use by this connection. + */ + protected DTP dtp; + + /** + * The current representation type. + */ + protected int representationType = TYPE_ASCII; + + /** + * The current file structure type. + */ + protected int fileStructure = STRUCTURE_FILE; + + /** + * The current transfer mode. + */ + protected int transferMode = MODE_STREAM; + + /** + * If true, use passive mode. + */ + protected boolean passive = false; + + /** + * Creates a new connection to the server using the default port. + * @param hostname the hostname of the server to connect to + */ + public FTPConnection(String hostname) + throws UnknownHostException, IOException + { + this(hostname, -1, 0, 0, false); + } + + /** + * Creates a new connection to the server. + * @param hostname the hostname of the server to connect to + * @param port the port to connect to(if <=0, use default port) + */ + public FTPConnection(String hostname, int port) + throws UnknownHostException, IOException + { + this(hostname, port, 0, 0, false); + } + + /** + * Creates a new connection to the server. + * @param hostname the hostname of the server to connect to + * @param port the port to connect to(if <=0, use default port) + * @param connectionTimeout the connection timeout, in milliseconds + * @param timeout the I/O timeout, in milliseconds + * @param debug print debugging information + */ + public FTPConnection(String hostname, int port, + int connectionTimeout, int timeout, boolean debug) + throws UnknownHostException, IOException + { + this.connectionTimeout = connectionTimeout; + this.timeout = timeout; + this.debug = debug; + if (port <= 0) + { + port = FTP_PORT; + } + + // Set up socket + socket = new Socket(); + InetSocketAddress address = new InetSocketAddress(hostname, port); + if (connectionTimeout > 0) + { + socket.connect(address, connectionTimeout); + } + else + { + socket.connect(address); + } + if (timeout > 0) + { + socket.setSoTimeout(timeout); + } + + InputStream in = socket.getInputStream(); + in = new BufferedInputStream(in); + in = new CRLFInputStream(in); + this.in = new LineInputStream(in); + OutputStream out = socket.getOutputStream(); + out = new BufferedOutputStream(out); + this.out = new CRLFOutputStream(out); + + // Read greeting + FTPResponse response = getResponse(); + switch (response.getCode()) + { + case 220: // hello + break; + default: + throw new FTPException(response); + } + } + + /** + * Authenticate using the specified username and password. + * If the username suffices for the server, the password will not be used + * and may be null. + * @param username the username + * @param password the optional password + * @return true on success, false otherwise + */ + public boolean authenticate(String username, String password) + throws IOException + { + String cmd = USER + ' ' + username; + send(cmd); + FTPResponse response = getResponse(); + switch (response.getCode()) + { + case 230: // User logged in + return true; + case 331: // User name okay, need password + break; + case 332: // Need account for login + case 530: // No such user + return false; + default: + throw new FTPException(response); + } + cmd = PASS + ' ' + password; + send(cmd); + response = getResponse(); + switch (response.getCode()) + { + case 230: // User logged in + case 202: // Superfluous + return true; + case 332: // Need account for login + case 530: // Bad password + return false; + default: + throw new FTPException(response); + } + } + + /** + * Negotiates TLS over the current connection. + * See IETF draft-murray-auth-ftp-ssl-15.txt for details. + * @param confidential whether to provide confidentiality for the + * connection + */ + public boolean starttls(boolean confidential) + throws IOException + { + return starttls(confidential, new EmptyX509TrustManager()); + } + + /** + * Negotiates TLS over the current connection. + * See IETF draft-murray-auth-ftp-ssl-15.txt for details. + * @param confidential whether to provide confidentiality for the + * connection + * @param tm the trust manager used to validate the server certificate. + */ + public boolean starttls(boolean confidential, TrustManager tm) + throws IOException + { + try + { + // Use SSLSocketFactory to negotiate a TLS session and wrap the + // current socket. + SSLContext context = SSLContext.getInstance("TLS"); + // We don't require strong validation of the server certificate + TrustManager[] trust = new TrustManager[] { tm }; + context.init(null, trust, null); + SSLSocketFactory factory = context.getSocketFactory(); + + send(AUTH + ' ' + TLS); + FTPResponse response = getResponse(); + switch (response.getCode()) + { + case 500: + case 502: + case 504: + case 534: + case 431: + return false; + case 234: + break; + default: + throw new FTPException(response); + } + + String hostname = socket.getInetAddress().getHostName(); + int port = socket.getPort(); + SSLSocket ss = + (SSLSocket) factory.createSocket(socket, hostname, port, true); + String[] protocols = { "TLSv1", "SSLv3" }; + ss.setEnabledProtocols(protocols); + ss.setUseClientMode(true); + ss.startHandshake(); + + // PBSZ:PROT sequence + send(PBSZ + ' ' + Integer.MAX_VALUE); + response = getResponse(); + switch (response.getCode()) + { + case 501: // syntax error + case 503: // not authenticated + return false; + case 200: + break; + default: + throw new FTPException(response); + } + send(PROT + ' ' +(confidential ? 'P' : 'C')); + response = getResponse(); + switch (response.getCode()) + { + case 503: // not authenticated + case 504: // invalid level + case 536: // level not supported + return false; + case 200: + break; + default: + throw new FTPException(response); + } + + if (confidential) + { + // Set up streams + InputStream in = ss.getInputStream(); + in = new BufferedInputStream(in); + in = new CRLFInputStream(in); + this.in = new LineInputStream(in); + OutputStream out = ss.getOutputStream(); + out = new BufferedOutputStream(out); + this.out = new CRLFOutputStream(out); + } + return true; + } + catch (GeneralSecurityException e) + { + return false; + } + } + + /** + * Changes directory to the specified path. + * @param path an absolute or relative pathname + * @return true on success, false if the specified path does not exist + */ + public boolean changeWorkingDirectory(String path) + throws IOException + { + // Do nothing if the path is empty. + if (path.length() == 0) + return true; + String cmd = CWD + ' ' + path; + send(cmd); + FTPResponse response = getResponse(); + switch (response.getCode()) + { + case 250: + return true; + case 550: + return false; + default: + throw new FTPException(response); + } + } + + /** + * Changes directory to the parent of the current working directory. + * @return true on success, false otherwise + */ + public boolean changeToParentDirectory() + throws IOException + { + send(CDUP); + FTPResponse response = getResponse(); + switch (response.getCode()) + { + case 250: + return true; + case 550: + return false; + default: + throw new FTPException(response); + } + } + + /** + * Terminates an authenticated login. + * If file transfer is in progress, it remains active for result response + * only. + */ + public void reinitialize() + throws IOException + { + send(REIN); + FTPResponse response = getResponse(); + switch (response.getCode()) + { + case 220: + if (dtp != null) + { + dtp.complete(); + dtp = null; + } + break; + default: + throw new FTPException(response); + } + } + + /** + * Terminates the control connection. + * The file transfer connection remains open for result response only. + * This connection is invalid and no further commands may be issued. + */ + public void logout() + throws IOException + { + send(QUIT); + try + { + getResponse(); // not required + } + catch (IOException e) + { + } + if (dtp != null) + { + dtp.complete(); + dtp = null; + } + try + { + socket.close(); + } + catch (IOException e) + { + } + } + + /** + * Initialise the data transfer process. + */ + protected void initialiseDTP() + throws IOException + { + if (dtp != null) + { + dtp.complete(); + dtp = null; + } + + InetAddress localhost = socket.getLocalAddress(); + if (passive) + { + send(PASV); + FTPResponse response = getResponse(); + switch (response.getCode()) + { + case 227: + String message = response.getMessage(); + try + { + int start = message.indexOf(','); + char c = message.charAt(start - 1); + while (c >= 0x30 && c <= 0x39) + { + c = message.charAt((--start) - 1); + } + int mid1 = start; + for (int i = 0; i < 4; i++) + { + mid1 = message.indexOf(',', mid1 + 1); + } + int mid2 = message.indexOf(',', mid1 + 1); + if (mid1 == -1 || mid2 < mid1) + { + throw new ProtocolException("Malformed 227: " + + message); + } + int end = mid2; + c = message.charAt(end + 1); + while (c >= 0x30 && c <= 0x39) + { + c = message.charAt((++end) + 1); + } + + String address = + message.substring(start, mid1).replace(',', '.'); + int port_hi = + Integer.parseInt(message.substring(mid1 + 1, mid2)); + int port_lo = + Integer.parseInt(message.substring(mid2 + 1, end + 1)); + int port = (port_hi << 8) | port_lo; + + /*System.out.println("Entering passive mode: " + address + + ":" + port);*/ + dtp = new PassiveModeDTP(address, port, localhost, + connectionTimeout, timeout); + break; + } + catch (ArrayIndexOutOfBoundsException e) + { + throw new ProtocolException(e.getMessage() + ": " + + message); + } + catch (NumberFormatException e) + { + throw new ProtocolException(e.getMessage() + ": " + + message); + } + default: + throw new FTPException(response); + } + } + else + { + // Get the local port + int port = socket.getLocalPort() + 1; + int tries = 0; + // Bind the active mode DTP + while (dtp == null) + { + try + { + dtp = new ActiveModeDTP(localhost, port, + connectionTimeout, timeout); + /*System.out.println("Listening on: " + port);*/ + } + catch (BindException e) + { + port++; + tries++; + if (tries > 9) + { + throw e; + } + } + } + + // Send PORT command + CPStringBuilder buf = new CPStringBuilder(PORT); + buf.append(' '); + // Construct the address/port string form + byte[] address = localhost.getAddress(); + for (int i = 0; i < address.length; i++) + { + int a =(int) address[i]; + if (a < 0) + { + a += 0x100; + } + buf.append(a); + buf.append(','); + } + int port_hi =(port & 0xff00) >> 8; + int port_lo =(port & 0x00ff); + buf.append(port_hi); + buf.append(','); + buf.append(port_lo); + send(buf.toString()); + // Get response + FTPResponse response = getResponse(); + switch (response.getCode()) + { + case 200: // OK + break; + default: + dtp.abort(); + dtp = null; + throw new FTPException(response); + } + } + dtp.setTransferMode(transferMode); + } + + /** + * Set passive mode. + * @param flag true if we should use passive mode, false otherwise + */ + public void setPassive(boolean flag) + throws IOException + { + if (passive != flag) + { + passive = flag; + initialiseDTP(); + } + } + + /** + * Returns the current representation type of the transfer data. + * @return TYPE_ASCII, TYPE_EBCDIC, or TYPE_BINARY + */ + public int getRepresentationType() + { + return representationType; + } + + /** + * Sets the desired representation type of the transfer data. + * @param type TYPE_ASCII, TYPE_EBCDIC, or TYPE_BINARY + */ + public void setRepresentationType(int type) + throws IOException + { + CPStringBuilder buf = new CPStringBuilder(TYPE); + buf.append(' '); + switch (type) + { + case TYPE_ASCII: + buf.append('A'); + break; + case TYPE_EBCDIC: + buf.append('E'); + break; + case TYPE_BINARY: + buf.append('I'); + break; + default: + throw new IllegalArgumentException(Integer.toString(type)); + } + //buf.append(' '); + //buf.append('N'); + send(buf.toString()); + FTPResponse response = getResponse(); + switch (response.getCode()) + { + case 200: + representationType = type; + break; + default: + throw new FTPException(response); + } + } + + /** + * Returns the current file structure type. + * @return STRUCTURE_FILE, STRUCTURE_RECORD, or STRUCTURE_PAGE + */ + public int getFileStructure() + { + return fileStructure; + } + + /** + * Sets the desired file structure type. + * @param structure STRUCTURE_FILE, STRUCTURE_RECORD, or STRUCTURE_PAGE + */ + public void setFileStructure(int structure) + throws IOException + { + CPStringBuilder buf = new CPStringBuilder(STRU); + buf.append(' '); + switch (structure) + { + case STRUCTURE_FILE: + buf.append('F'); + break; + case STRUCTURE_RECORD: + buf.append('R'); + break; + case STRUCTURE_PAGE: + buf.append('P'); + break; + default: + throw new IllegalArgumentException(Integer.toString(structure)); + } + send(buf.toString()); + FTPResponse response = getResponse(); + switch (response.getCode()) + { + case 200: + fileStructure = structure; + break; + default: + throw new FTPException(response); + } + } + + /** + * Returns the current transfer mode. + * @return MODE_STREAM, MODE_BLOCK, or MODE_COMPRESSED + */ + public int getTransferMode() + { + return transferMode; + } + + /** + * Sets the desired transfer mode. + * @param mode MODE_STREAM, MODE_BLOCK, or MODE_COMPRESSED + */ + public void setTransferMode(int mode) + throws IOException + { + CPStringBuilder buf = new CPStringBuilder(MODE); + buf.append(' '); + switch (mode) + { + case MODE_STREAM: + buf.append('S'); + break; + case MODE_BLOCK: + buf.append('B'); + break; + case MODE_COMPRESSED: + buf.append('C'); + break; + default: + throw new IllegalArgumentException(Integer.toString(mode)); + } + send(buf.toString()); + FTPResponse response = getResponse(); + switch (response.getCode()) + { + case 200: + transferMode = mode; + if (dtp != null) + { + dtp.setTransferMode(mode); + } + break; + default: + throw new FTPException(response); + } + } + + /** + * Retrieves the specified file. + * @param filename the filename of the file to retrieve + * @return an InputStream containing the file content + */ + public InputStream retrieve(String filename) + throws IOException + { + if (dtp == null || transferMode == MODE_STREAM) + { + initialiseDTP(); + } + /* + int size = -1; + String cmd = SIZE + ' ' + filename; + send(cmd); + FTPResponse response = getResponse(); + switch (response.getCode()) + { + case 213: + size = Integer.parseInt(response.getMessage()); + break; + case 550: // File not found + default: + throw new FTPException(response); + } + */ + String cmd = RETR + ' ' + filename; + send(cmd); + FTPResponse response = getResponse(); + switch (response.getCode()) + { + case 125: // Data connection already open; transfer starting + case 150: // File status okay; about to open data connection + return dtp.getInputStream(); + default: + throw new FTPException(response); + } + } + + /** + * Returns a stream for uploading a file. + * If a file with the same filename already exists on the server, it will + * be overwritten. + * @param filename the name of the file to save the content as + * @return an OutputStream to write the file data to + */ + public OutputStream store(String filename) + throws IOException + { + if (dtp == null || transferMode == MODE_STREAM) + { + initialiseDTP(); + } + String cmd = STOR + ' ' + filename; + send(cmd); + FTPResponse response = getResponse(); + switch (response.getCode()) + { + case 125: // Data connection already open; transfer starting + case 150: // File status okay; about to open data connection + return dtp.getOutputStream(); + default: + throw new FTPException(response); + } + } + + /** + * Returns a stream for uploading a file. + * If a file with the same filename already exists on the server, the + * content specified will be appended to the existing file. + * @param filename the name of the file to save the content as + * @return an OutputStream to write the file data to + */ + public OutputStream append(String filename) + throws IOException + { + if (dtp == null || transferMode == MODE_STREAM) + { + initialiseDTP(); + } + String cmd = APPE + ' ' + filename; + send(cmd); + FTPResponse response = getResponse(); + switch (response.getCode()) + { + case 125: // Data connection already open; transfer starting + case 150: // File status okay; about to open data connection + return dtp.getOutputStream(); + default: + throw new FTPException(response); + } + } + + /** + * This command may be required by some servers to reserve sufficient + * storage to accommodate the new file to be transferred. + * It should be immediately followed by a <code>store</code> or + * <code>append</code>. + * @param size the number of bytes of storage to allocate + */ + public void allocate(long size) + throws IOException + { + String cmd = ALLO + ' ' + size; + send(cmd); + FTPResponse response = getResponse(); + switch (response.getCode()) + { + case 200: // OK + case 202: // Superfluous + break; + default: + throw new FTPException(response); + } + } + + /** + * Renames a file. + * @param oldName the current name of the file + * @param newName the new name + * @return true if successful, false otherwise + */ + public boolean rename(String oldName, String newName) + throws IOException + { + String cmd = RNFR + ' ' + oldName; + send(cmd); + FTPResponse response = getResponse(); + switch (response.getCode()) + { + case 450: // File unavailable + case 550: // File not found + return false; + case 350: // Pending + break; + default: + throw new FTPException(response); + } + cmd = RNTO + ' ' + newName; + send(cmd); + response = getResponse(); + switch (response.getCode()) + { + case 250: // OK + return true; + case 450: + case 550: + return false; + default: + throw new FTPException(response); + } + } + + /** + * Aborts the transfer in progress. + * @return true if a transfer was in progress, false otherwise + */ + public boolean abort() + throws IOException + { + send(ABOR); + FTPResponse response = getResponse(); + // Abort client DTP + if (dtp != null) + { + dtp.abort(); + } + switch (response.getCode()) + { + case 226: // successful abort + return false; + case 426: // interrupted + response = getResponse(); + if (response.getCode() == 226) + { + return true; + } + // Otherwise fall through to throw exception + default: + throw new FTPException(response); + } + } + + /** + * Causes the file specified to be deleted at the server site. + * @param filename the file to delete + */ + public boolean delete(String filename) + throws IOException + { + String cmd = DELE + ' ' + filename; + send(cmd); + FTPResponse response = getResponse(); + switch (response.getCode()) + { + case 250: // OK + return true; + case 450: // File unavailable + case 550: // File not found + return false; + default: + throw new FTPException(response); + } + } + + /** + * Causes the directory specified to be deleted. + * This may be an absolute or relative pathname. + * @param pathname the directory to delete + */ + public boolean removeDirectory(String pathname) + throws IOException + { + String cmd = RMD + ' ' + pathname; + send(cmd); + FTPResponse response = getResponse(); + switch (response.getCode()) + { + case 250: // OK + return true; + case 550: // File not found + return false; + default: + throw new FTPException(response); + } + } + + /** + * Causes the directory specified to be created at the server site. + * This may be an absolute or relative pathname. + * @param pathname the directory to create + */ + public boolean makeDirectory(String pathname) + throws IOException + { + String cmd = MKD + ' ' + pathname; + send(cmd); + FTPResponse response = getResponse(); + switch (response.getCode()) + { + case 257: // Directory created + return true; + case 550: // File not found + return false; + default: + throw new FTPException(response); + } + } + + /** + * Returns the current working directory. + */ + public String getWorkingDirectory() + throws IOException + { + send(PWD); + FTPResponse response = getResponse(); + switch (response.getCode()) + { + case 257: + String message = response.getMessage(); + if (message.charAt(0) == '"') + { + int end = message.indexOf('"', 1); + if (end == -1) + { + throw new ProtocolException(message); + } + return message.substring(1, end); + } + else + { + int end = message.indexOf(' '); + if (end == -1) + { + return message; + } + else + { + return message.substring(0, end); + } + } + default: + throw new FTPException(response); + } + } + + /** + * Returns a listing of information about the specified pathname. + * If the pathname specifies a directory or other group of files, the + * server should transfer a list of files in the specified directory. + * If the pathname specifies a file then the server should send current + * information on the file. A null argument implies the user's + * current working or default directory. + * @param pathname the context pathname, or null + */ + public InputStream list(String pathname) + throws IOException + { + if (dtp == null || transferMode == MODE_STREAM) + { + initialiseDTP(); + } + if (pathname == null) + { + send(LIST); + } + else + { + String cmd = LIST + ' ' + pathname; + send(cmd); + } + FTPResponse response = getResponse(); + switch (response.getCode()) + { + case 125: // Data connection already open; transfer starting + case 150: // File status okay; about to open data connection + return dtp.getInputStream(); + default: + throw new FTPException(response); + } + } + + /** + * Returns a directory listing. The pathname should specify a + * directory or other system-specific file group descriptor; a null + * argument implies the user's current working or default directory. + * @param pathname the directory pathname, or null + * @return a list of filenames(strings) + */ + public List<String> nameList(String pathname) + throws IOException + { + if (dtp == null || transferMode == MODE_STREAM) + { + initialiseDTP(); + } + if (pathname == null) + { + send(NLST); + } + else + { + String cmd = NLST + ' ' + pathname; + send(cmd); + } + FTPResponse response = getResponse(); + switch (response.getCode()) + { + case 125: // Data connection already open; transfer starting + case 150: // File status okay; about to open data connection + InputStream in = dtp.getInputStream(); + in = new BufferedInputStream(in); + in = new CRLFInputStream(in); // TODO ensure that TYPE is correct + LineInputStream li = new LineInputStream(in); + ArrayList<String> ret = new ArrayList<String>(); + for (String line = li.readLine(); + line != null; + line = li.readLine()) + { + ret.add(line); + } + li.close(); + return ret; + default: + throw new FTPException(response); + } + } + + /** + * Returns the type of operating system at the server. + */ + public String system() + throws IOException + { + send(SYST); + FTPResponse response = getResponse(); + switch (response.getCode()) + { + case 215: + String message = response.getMessage(); + int end = message.indexOf(' '); + if (end == -1) + { + return message; + } + else + { + return message.substring(0, end); + } + default: + throw new FTPException(response); + } + } + + /** + * Does nothing. + * This method can be used to ensure that the connection does not time + * out. + */ + public void noop() + throws IOException + { + send(NOOP); + FTPResponse response = getResponse(); + switch (response.getCode()) + { + case 200: + break; + default: + throw new FTPException(response); + } + } + + // -- I/O -- + + /** + * Sends the specified command line to the server. + * The CRLF sequence is automatically appended. + * @param cmd the command line to send + */ + protected void send(String cmd) + throws IOException + { + byte[] data = cmd.getBytes(US_ASCII); + out.write(data); + out.writeln(); + out.flush(); + } + + /** + * Reads the next response from the server. + * If the server sends the "transfer complete" code, this is handled here, + * and the next response is passed to the caller. + */ + protected FTPResponse getResponse() + throws IOException + { + FTPResponse response = readResponse(); + if (response.getCode() == 226) + { + if (dtp != null) + { + dtp.transferComplete(); + } + response = readResponse(); + } + return response; + } + + /** + * Reads and parses the next response from the server. + */ + protected FTPResponse readResponse() + throws IOException + { + String line = in.readLine(); + if (line == null) + { + throw new ProtocolException( "EOF"); + } + if (line.length() < 4) + { + throw new ProtocolException(line); + } + int code = parseCode(line); + if (code == -1) + { + throw new ProtocolException(line); + } + char c = line.charAt(3); + if (c == ' ') + { + return new FTPResponse(code, line.substring(4)); + } + else if (c == '-') + { + CPStringBuilder buf = new CPStringBuilder(line.substring(4)); + buf.append('\n'); + while(true) + { + line = in.readLine(); + if (line == null) + { + throw new ProtocolException("EOF"); + } + if (line.length() >= 4 && + line.charAt(3) == ' ' && + parseCode(line) == code) + { + return new FTPResponse(code, line.substring(4), + buf.toString()); + } + else + { + buf.append(line); + buf.append('\n'); + } + } + } + else + { + throw new ProtocolException(line); + } + } + + /* + * Parses the 3-digit numeric code at the beginning of the given line. + * Returns -1 on failure. + */ + static final int parseCode(String line) + { + char[] c = { line.charAt(0), line.charAt(1), line.charAt(2) }; + int ret = 0; + for (int i = 0; i < 3; i++) + { + int digit =((int) c[i]) - 0x30; + if (digit < 0 || digit > 9) + { + return -1; + } + // Computing integer powers is way too expensive in Java! + switch (i) + { + case 0: + ret +=(100 * digit); + break; + case 1: + ret +=(10 * digit); + break; + case 2: + ret += digit; + break; + } + } + return ret; + } + +} diff --git a/libjava/classpath/gnu/java/net/protocol/ftp/FTPException.java b/libjava/classpath/gnu/java/net/protocol/ftp/FTPException.java new file mode 100644 index 000000000..1a7fcb85d --- /dev/null +++ b/libjava/classpath/gnu/java/net/protocol/ftp/FTPException.java @@ -0,0 +1,75 @@ +/* FTPException.java -- + Copyright (C) 2003. 2004 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.ftp; + +import java.io.IOException; + +/** + * An FTP control exception. + * + * @author Chris Burdess (dog@gnu.org) + */ +public class FTPException + extends IOException +{ + + /** + * The response that provoked this exception. + */ + protected final FTPResponse response; + + /** + * Constructs a new FTP exception. + * @param response the response that provoked this exception + */ + public FTPException(FTPResponse response) + { + super(response.getMessage()); + this.response = response; + } + + /** + * Returns the response that provoked this exception. + */ + public FTPResponse getResponse() + { + return response; + } + +} diff --git a/libjava/classpath/gnu/java/net/protocol/ftp/FTPResponse.java b/libjava/classpath/gnu/java/net/protocol/ftp/FTPResponse.java new file mode 100644 index 000000000..2620f0d70 --- /dev/null +++ b/libjava/classpath/gnu/java/net/protocol/ftp/FTPResponse.java @@ -0,0 +1,111 @@ +/* FTPResponse.java -- + Copyright (C) 2003, 2004 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.ftp; + +/** + * An FTP control response. + * + * @author Chris Burdess (dog@gnu.org) + */ +public final class FTPResponse +{ + + /** + * The 3-digit status code. + */ + protected final int code; + + /** + * The human-readable message. + */ + protected final String message; + + /** + * Multiline data, if present. + */ + protected final String data; + + /** + * Constructs a new FTP response. + * @param code the status code + * @param message the message + */ + public FTPResponse(int code, String message) + { + this(code, message, null); + } + + /** + * Constructs a new multiline FTP response. + * @param code the status code + * @param message the message + * @param data multiline data + */ + public FTPResponse(int code, String message, String data) + { + this.code = code; + this.message = message; + this.data = data; + } + + /** + * Returns the 3-digit status code. + */ + public int getCode() + { + return code; + } + + /** + * Returns the human-readable message. + */ + public String getMessage() + { + return message; + } + + /** + * Returns the multiline data, or null if there was no such data. + */ + public String getData() + { + return data; + } + +} diff --git a/libjava/classpath/gnu/java/net/protocol/ftp/FTPURLConnection.java b/libjava/classpath/gnu/java/net/protocol/ftp/FTPURLConnection.java new file mode 100644 index 000000000..8cc1fafd1 --- /dev/null +++ b/libjava/classpath/gnu/java/net/protocol/ftp/FTPURLConnection.java @@ -0,0 +1,375 @@ +/* FTPURLConnection.java -- + Copyright (C) 2003, 2004, 2006 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.ftp; + +import gnu.classpath.SystemProperties; +import gnu.java.net.GetLocalHostAction; + +import java.io.FilterInputStream; +import java.io.FilterOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.InetAddress; +import java.net.URL; +import java.net.URLConnection; +import java.security.AccessController; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * An FTP URL connection. + * + * @author Chris Burdess (dog@gnu.org) + */ +public class FTPURLConnection + extends URLConnection +{ + + /** + * The connection managing the protocol exchange. + */ + protected FTPConnection connection; + + protected boolean passive; + protected int representationType; + protected int fileStructure; + protected int transferMode; + + /** + * Constructs an FTP connection to the specified URL. + * @param url the URL + */ + public FTPURLConnection(URL url) + { + super(url); + passive = true; + representationType = FTPConnection.TYPE_BINARY; + fileStructure = -1; + transferMode = -1; + } + + /** + * Establishes the connection. + */ + public void connect() + throws IOException + { + if (connected) + { + return; + } + String host = url.getHost(); + int port = url.getPort(); + String username = url.getUserInfo(); + String password = null; + if (username != null) + { + int ci = username.indexOf(':'); + if (ci != -1) + { + password = username.substring(ci + 1); + username = username.substring(0, ci); + } + } + else + { + username = "anonymous"; + GetLocalHostAction a = new GetLocalHostAction(); + InetAddress localhost = AccessController.doPrivileged(a); + password = SystemProperties.getProperty("user.name") + "@" + + ((localhost == null) ? "localhost" : localhost.getHostName()); + } + connection = new FTPConnection(host, port); + if (!connection.authenticate(username, password)) + { + throw new SecurityException("Authentication failed"); + } + connection.setPassive(passive); + if (representationType != -1) + { + connection.setRepresentationType(representationType); + } + if (fileStructure != -1) + { + connection.setFileStructure(fileStructure); + } + if (transferMode != -1) + { + connection.setTransferMode(transferMode); + } + } + + /** + * This connection supports doInput. + */ + public void setDoInput(boolean doinput) + { + doInput = doinput; + } + + /** + * This connection supports doOutput. + */ + public void setDoOutput(boolean dooutput) + { + doOutput = dooutput; + } + + /** + * Returns an input stream that reads from this open connection. + */ + public InputStream getInputStream() + throws IOException + { + if (!connected) + { + connect(); + } + String path = url.getPath(); + if (connection.changeWorkingDirectory(path)) + { + return this.new ClosingInputStream(connection.list(null)); + } + else + { + return this.new ClosingInputStream(connection.retrieve(path)); + } + } + + /** + * Returns an output stream that writes to this connection. + */ + public OutputStream getOutputStream() + throws IOException + { + if (!connected) + { + connect(); + } + String path = url.getPath(); + return this.new ClosingOutputStream(connection.store(path)); + } + + public String getRequestProperty(String key) + { + if ("passive".equals(key)) + { + return Boolean.toString(passive); + } + else if ("representationType".equals(key)) + { + switch (representationType) + { + case FTPConnection.TYPE_ASCII: + return "ASCII"; + case FTPConnection.TYPE_EBCDIC: + return "EBCDIC"; + case FTPConnection.TYPE_BINARY: + return "BINARY"; + } + } + else if ("fileStructure".equals(key)) + { + switch (fileStructure) + { + case FTPConnection.STRUCTURE_FILE: + return "FILE"; + case FTPConnection.STRUCTURE_RECORD: + return "RECORD"; + case FTPConnection.STRUCTURE_PAGE: + return "PAGE"; + } + } + else if ("transferMode".equals(key)) + { + switch (transferMode) + { + case FTPConnection.MODE_STREAM: + return "STREAM"; + case FTPConnection.MODE_BLOCK: + return "BLOCK"; + case FTPConnection.MODE_COMPRESSED: + return "COMPRESSED"; + } + } + return null; + } + + public Map<String, List<String>> getRequestProperties() + { + Map<String, List<String>> map = new HashMap<String, List<String>>(); + addRequestPropertyValue(map, "passive"); + addRequestPropertyValue(map, "representationType"); + addRequestPropertyValue(map, "fileStructure"); + addRequestPropertyValue(map, "transferMode"); + return map; + } + + private void addRequestPropertyValue(Map<String, List<String>> map, + String key) + { + String value = getRequestProperty(key); + ArrayList<String> l = new ArrayList<String>(); + l.add(value); + map.put(key, l); + } + + public void setRequestProperty(String key, String value) + { + if (connected) + { + throw new IllegalStateException(); + } + if ("passive".equals(key)) + { + passive = Boolean.valueOf(value).booleanValue(); + } + else if ("representationType".equals(key)) + { + if ("A".equalsIgnoreCase(value) || + "ASCII".equalsIgnoreCase(value)) + { + representationType = FTPConnection.TYPE_ASCII; + } + else if ("E".equalsIgnoreCase(value) || + "EBCDIC".equalsIgnoreCase(value)) + { + representationType = FTPConnection.TYPE_EBCDIC; + } + else if ("I".equalsIgnoreCase(value) || + "BINARY".equalsIgnoreCase(value)) + { + representationType = FTPConnection.TYPE_BINARY; + } + else + { + throw new IllegalArgumentException(value); + } + } + else if ("fileStructure".equals(key)) + { + if ("F".equalsIgnoreCase(value) || + "FILE".equalsIgnoreCase(value)) + { + fileStructure = FTPConnection.STRUCTURE_FILE; + } + else if ("R".equalsIgnoreCase(value) || + "RECORD".equalsIgnoreCase(value)) + { + fileStructure = FTPConnection.STRUCTURE_RECORD; + } + else if ("P".equalsIgnoreCase(value) || + "PAGE".equalsIgnoreCase(value)) + { + fileStructure = FTPConnection.STRUCTURE_PAGE; + } + else + { + throw new IllegalArgumentException(value); + } + } + else if ("transferMode".equals(key)) + { + if ("S".equalsIgnoreCase(value) || + "STREAM".equalsIgnoreCase(value)) + { + transferMode = FTPConnection.MODE_STREAM; + } + else if ("B".equalsIgnoreCase(value) || + "BLOCK".equalsIgnoreCase(value)) + { + transferMode = FTPConnection.MODE_BLOCK; + } + else if ("C".equalsIgnoreCase(value) || + "COMPRESSED".equalsIgnoreCase(value)) + { + transferMode = FTPConnection.MODE_COMPRESSED; + } + else + { + throw new IllegalArgumentException(value); + } + } + } + + public void addRequestProperty(String key, String value) + { + setRequestProperty(key, value); + } + + class ClosingInputStream + extends FilterInputStream + { + + ClosingInputStream(InputStream in) + { + super(in); + } + + public void close() + throws IOException + { + super.close(); + connection.logout(); + } + + } + + class ClosingOutputStream + extends FilterOutputStream + { + + ClosingOutputStream(OutputStream out) + { + super(out); + } + + public void close() + throws IOException + { + super.close(); + connection.logout(); + } + + } + +} diff --git a/libjava/classpath/gnu/java/net/protocol/ftp/Handler.java b/libjava/classpath/gnu/java/net/protocol/ftp/Handler.java new file mode 100644 index 000000000..7638b6664 --- /dev/null +++ b/libjava/classpath/gnu/java/net/protocol/ftp/Handler.java @@ -0,0 +1,69 @@ +/* Handler.java -- + Copyright (C) 2003, 2004 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.ftp; + +import java.io.IOException; +import java.net.URL; +import java.net.URLConnection; +import java.net.URLStreamHandler; + +/** + * An FTP URL stream handler. + * + * @author Chris Burdess (dog@gnu.org) + */ +public class Handler + extends URLStreamHandler +{ + + protected int getDefaultPort() + { + return FTPConnection.FTP_PORT; + } + + /** + * Returns an FTPURLConnection for the given URL. + */ + public URLConnection openConnection(URL url) + throws IOException + { + return new FTPURLConnection(url); + } + +} diff --git a/libjava/classpath/gnu/java/net/protocol/ftp/PassiveModeDTP.java b/libjava/classpath/gnu/java/net/protocol/ftp/PassiveModeDTP.java new file mode 100644 index 000000000..a74346c74 --- /dev/null +++ b/libjava/classpath/gnu/java/net/protocol/ftp/PassiveModeDTP.java @@ -0,0 +1,200 @@ +/* PassiveModeDTP.java -- + Copyright (C) 2003, 2004 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.ftp; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.Socket; + +/** + * A passive mode FTP data transfer process. + * This connects to the host specified and proxies the resulting socket's + * input and output streams. + * + * @author Chris Burdess (dog@gnu.org) + */ +final class PassiveModeDTP + implements DTP +{ + + final String address; + final int port; + Socket socket; + DTPInputStream in; + DTPOutputStream out; + boolean completed; + boolean inProgress; + int transferMode; + + PassiveModeDTP(String address, int port, InetAddress localhost, + int connectionTimeout, int timeout) + throws IOException + { + this.address = address; + this.port = port; + completed = false; + inProgress = false; + socket = new Socket(); + InetSocketAddress remote = new InetSocketAddress(address, port); + InetSocketAddress local = new InetSocketAddress(localhost, port + 1); + socket.bind(local); + if (connectionTimeout > 0) + { + socket.connect(remote, connectionTimeout); + } + else + { + socket.connect(remote); + } + if (timeout > 0) + { + socket.setSoTimeout(timeout); + } + } + + /** + * Returns an input stream from which a remote file can be read. + */ + public InputStream getInputStream() + throws IOException + { + if (inProgress) + { + throw new IOException("Transfer in progress"); + } + switch (transferMode) + { + case FTPConnection.MODE_STREAM: + in = new StreamInputStream(this, socket.getInputStream()); + break; + case FTPConnection.MODE_BLOCK: + in = new BlockInputStream(this, socket.getInputStream()); + break; + case FTPConnection.MODE_COMPRESSED: + in = new CompressedInputStream(this, socket.getInputStream()); + break; + default: + throw new IllegalStateException("Invalid transfer mode"); + } + in.setTransferComplete(false); + return in; + } + + /** + * Returns an output stream to which a local file can be written for + * upload. + */ + public OutputStream getOutputStream() + throws IOException + { + if (inProgress) + { + throw new IOException("Transfer in progress"); + } + switch (transferMode) + { + case FTPConnection.MODE_STREAM: + out = new StreamOutputStream(this, socket.getOutputStream()); + break; + case FTPConnection.MODE_BLOCK: + out = new BlockOutputStream(this, socket.getOutputStream()); + break; + case FTPConnection.MODE_COMPRESSED: + out = new CompressedOutputStream(this, socket.getOutputStream()); + break; + default: + throw new IllegalStateException("Invalid transfer mode"); + } + out.setTransferComplete(false); + return out; + } + + public void setTransferMode(int mode) + { + transferMode = mode; + } + + public void complete() + { + completed = true; + if (!inProgress) + { + transferComplete(); + } + } + + public boolean abort() + { + completed = true; + transferComplete(); + return inProgress; + } + + /* + * Called by DTPInputStream or DTPOutputStream when end of + * stream is reached. + */ + public void transferComplete() + { + if (in != null) + { + in.setTransferComplete(true); + } + if (out != null) + { + out.setTransferComplete(true); + } + inProgress = false; + completed = completed ||(transferMode == FTPConnection.MODE_STREAM); + if (completed && socket != null) + { + try + { + socket.close(); + } + catch (IOException e) + { + } + } + } + +} diff --git a/libjava/classpath/gnu/java/net/protocol/ftp/StreamInputStream.java b/libjava/classpath/gnu/java/net/protocol/ftp/StreamInputStream.java new file mode 100644 index 000000000..beee14bcb --- /dev/null +++ b/libjava/classpath/gnu/java/net/protocol/ftp/StreamInputStream.java @@ -0,0 +1,94 @@ +/* StreamInputStream.java -- + Copyright (C) 2003, 2004 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.ftp; + +import java.io.IOException; +import java.io.InputStream; + +/** + * A DTP input stream that implements the FTP stream data transfer mode. + * + * @author Chris Burdess (dog@gnu.org) + */ +class StreamInputStream + extends DTPInputStream +{ + + StreamInputStream(DTP dtp, InputStream in) + { + super(dtp, in); + } + + public int read() + throws IOException + { + if (transferComplete) + { + return -1; + } + int c = in.read(); + if (c == -1) + { + close(); + } + return c; + } + + public int read(byte[] buf) + throws IOException + { + return read(buf, 0, buf.length); + } + + public int read(byte[] buf, int off, int len) + throws IOException + { + if (transferComplete) + { + return -1; + } + int l = in.read(buf, off, len); + if (l == -1) + { + close(); + } + return l; + } + +} diff --git a/libjava/classpath/gnu/java/net/protocol/ftp/StreamOutputStream.java b/libjava/classpath/gnu/java/net/protocol/ftp/StreamOutputStream.java new file mode 100644 index 000000000..2df1a87d8 --- /dev/null +++ b/libjava/classpath/gnu/java/net/protocol/ftp/StreamOutputStream.java @@ -0,0 +1,84 @@ +/* StreamOutputStream.java -- + Copyright (C) 2003, 2004 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.ftp; + +import java.io.IOException; +import java.io.OutputStream; + +/** + * A DTP output stream that implements the FTP stream transfer mode. + * + * @author Chris Burdess (dog@gnu.org) + */ +class StreamOutputStream + extends DTPOutputStream +{ + + StreamOutputStream(DTP dtp, OutputStream out) + { + super(dtp, out); + } + + public void write(int c) + throws IOException + { + if (transferComplete) + { + return; + } + out.write(c); + } + + public void write(byte[] b) + throws IOException + { + write(b, 0, b.length); + } + + public void write(byte[] b, int off, int len) + throws IOException + { + if (transferComplete) + { + return; + } + out.write(b, off, len); + } + +} diff --git a/libjava/classpath/gnu/java/net/protocol/ftp/package.html b/libjava/classpath/gnu/java/net/protocol/ftp/package.html new file mode 100644 index 000000000..fa3e34d74 --- /dev/null +++ b/libjava/classpath/gnu/java/net/protocol/ftp/package.html @@ -0,0 +1,60 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<!-- package.html - describes classes in gnu.java.net.protocol.ftp package. + Copyright (C) 2004 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. --> + +<html> +<head><title>GNU Classpath - gnu.java.net.protocol.ftp</title></head> + +<body> + +<p> +This package contains an FTP client. It can handle both active and passive +mode connections and the various transfer modes and representation types. +</p> + +<p> +Interaction with the server is via a simple stream interface. Only one +concurrent stream (input or output) is supported. +</p> + +<p> +The control connection to the server can be protected using TLS +(the starttls method). +</p> + +</body> +</html> diff --git a/libjava/classpath/gnu/java/net/protocol/http/Authenticator.java b/libjava/classpath/gnu/java/net/protocol/http/Authenticator.java new file mode 100644 index 000000000..b4ee41e11 --- /dev/null +++ b/libjava/classpath/gnu/java/net/protocol/http/Authenticator.java @@ -0,0 +1,58 @@ +/* Authenticator.java -- + Copyright (C) 2004 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.http; + +/** + * Callback interface for managing authentication. + * @see Request#setAuthenticator + * + * @author Chris Burdess (dog@gnu.org) + */ +public interface Authenticator +{ + + /** + * Returns the credentials to supply for the given realm. + * @param realm the authentication realm + * @param attempt zero on first authentication attempt, increments on each + * unsuccessful attempt + */ + Credentials getCredentials(String realm, int attempt); + +} diff --git a/libjava/classpath/gnu/java/net/protocol/http/ByteArrayRequestBodyWriter.java b/libjava/classpath/gnu/java/net/protocol/http/ByteArrayRequestBodyWriter.java new file mode 100644 index 000000000..22a33ccd3 --- /dev/null +++ b/libjava/classpath/gnu/java/net/protocol/http/ByteArrayRequestBodyWriter.java @@ -0,0 +1,106 @@ +/* ByteArrayRequestBodyWriter.java -- + Copyright (C) 2004 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.http; + +/** + * A simple request body writer using a byte array. + * + * @author Chris Burdess (dog@gnu.org) + */ +public class ByteArrayRequestBodyWriter + implements RequestBodyWriter +{ + + /** + * The content. + */ + protected byte[] content; + + /** + * The position within the content at which the next read will occur. + */ + protected int pos; + + /** + * Constructs a new byte array request body writer with the specified + * content. + * @param content the content buffer + */ + public ByteArrayRequestBodyWriter(byte[] content) + { + this.content = content; + pos = 0; + } + + /** + * Returns the total number of bytes that will be written in a single pass + * by this writer. + */ + public int getContentLength() + { + return content.length; + } + + /** + * Initialises the writer. + * This will be called before each pass. + */ + public void reset() + { + pos = 0; + } + + /** + * Writes body content to the supplied buffer. + * @param buffer the content buffer + * @return the number of bytes written + */ + public int write(byte[] buffer) + { + int len = content.length - pos; + len = (buffer.length < len) ? buffer.length : len; + if (len > -1) + { + System.arraycopy(content, pos, buffer, 0, len); + pos += len; + } + return len; + } + +} diff --git a/libjava/classpath/gnu/java/net/protocol/http/ChunkedInputStream.java b/libjava/classpath/gnu/java/net/protocol/http/ChunkedInputStream.java new file mode 100644 index 000000000..33df0df95 --- /dev/null +++ b/libjava/classpath/gnu/java/net/protocol/http/ChunkedInputStream.java @@ -0,0 +1,223 @@ +/* ChunkedInputStream.java -- + Copyright (C) 2004, 2006 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.http; + +import gnu.java.lang.CPStringBuilder; + +import java.io.IOException; +import java.io.InputStream; +import java.net.ProtocolException; + + +// +// Note that we rely on the implemtation of skip() in the super class +// (InputStream) calling our read methods to account for chunk headers +// while skipping. +// + + +/** + * Input stream wrapper for the "chunked" transfer-coding. + * + * @author Chris Burdess (dog@gnu.org) + */ +public class ChunkedInputStream + extends InputStream +{ + Headers headers; + + /** The underlying stream. */ + private InputStream in; + + /** Size of the chunk we're reading. */ + int size; + /** Number of bytes we've read in this chunk. */ + int count; + /** + * True when we should read meta-information, false when we should + * read data. + */ + boolean meta; + /** True when we've hit EOF. */ + boolean eof; + + /** + * Constructor. + * @param in the response socket input stream + * @param headers the headers to receive additional header lines + */ + public ChunkedInputStream(InputStream in, Headers headers) + { + this.in = in; + this.headers = headers; + size = -1; + count = 0; + meta = true; + } + + public int read() + throws IOException + { + byte[] buf = new byte[1]; + int len = read(buf, 0, 1); + if (len == -1) + { + return -1; + } + return 0xff & buf[0]; + } + + public synchronized int read(byte[] buffer, int offset, int length) + throws IOException + { + if (eof) + { + return -1; + } + if (meta) + { + // Read chunk header + int c, last = 0; + boolean seenSemi = false; + CPStringBuilder buf = new CPStringBuilder(); + do + { + c = in.read(); + if (c == 0x3b) // ; + { + seenSemi = true; + } + else if (c == 0x0a && last == 0x0d) // CRLF + { + try + { + size = Integer.parseInt(buf.toString(), 16); + } + catch (NumberFormatException nfe) + { + IOException ioe = new IOException("Bad chunk header"); + ioe.initCause(nfe); + // Unrecoverable. Don't try to read more. + in.close(); + throw ioe; + } + break; + } + else if (!seenSemi && c >= 0x30) + { + buf.append ((char) c); + } + last = c; + } + while(c != -1); + count = 0; + meta = false; + } + if (size == 0) + { + // Read trailer + headers.parse(in); + eof = true; + return -1; + } + else + { + int canRead = Math.min(size - count, length); + int len = in.read(buffer, offset, canRead); + if (len == -1) + { + // This is an error condition but it isn't clear what we + // should do with it. + eof = true; + return -1; + } + count += len; + if (count == size) + { + // Read CRLF + int c1 = in.read(); + int c2 = in.read(); + if (c1 == -1 || c2 == -1) + { + // EOF before CRLF: bad, but ignore + eof = true; + return -1; + } + if (c1 != 0x0d || c2 != 0x0a) + { + throw new ProtocolException("expecting CRLF: " + c1 + "," + c2); + } + meta = true; + } + return len; + } + } + + /** + * This method returns the number of bytes that can be read from + * this stream before a read might block. Even if the underlying + * InputStream has data available past the end of the current chunk, + * we have no way of knowing how large the next chunk header will + * be. So we cannot report available data past the current chunk. + * + * @return The number of bytes that can be read before a read might + * block + * + * @exception IOException If an error occurs + */ + public int available() throws IOException + { + if (meta) + return 0; + + return Math.min(in.available(), size - count); + } + + /** + * This method closes the ChunkedInputStream by closing the underlying + * InputStream. + * + * @exception IOException If an error occurs + */ + public void close() throws IOException + { + in.close(); + } + +} diff --git a/libjava/classpath/gnu/java/net/protocol/http/Cookie.java b/libjava/classpath/gnu/java/net/protocol/http/Cookie.java new file mode 100644 index 000000000..122a23f79 --- /dev/null +++ b/libjava/classpath/gnu/java/net/protocol/http/Cookie.java @@ -0,0 +1,161 @@ +/* Cookie.java -- + Copyright (C) 2004 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.http; + +import gnu.java.lang.CPStringBuilder; + +import java.util.Date; + +/** + * An HTTP cookie, as specified in RFC 2109. + * + * @author Chris Burdess (dog@gnu.org) + */ +public class Cookie +{ + + /** + * The name of the cookie. + */ + protected final String name; + + /** + * The value of the cookie. + */ + protected final String value; + + /** + * Optional documentation of the intended use of the cookie. + */ + protected final String comment; + + /** + * The domain for which the cookie is valid. + */ + protected final String domain; + + /** + * Optional subset of URL paths within the domain for which the cookie is + * valid. + */ + protected final String path; + + /** + * Indicates that the user-agent should only use secure means to transmit + * this cookie to the server. + */ + protected final boolean secure; + + /** + * The date at which this cookie expires. + */ + protected final Date expires; + + public Cookie(String name, String value, String comment, String domain, + String path, boolean secure, Date expires) + { + this.name = name; + this.value = value; + this.comment = comment; + this.domain = domain; + this.path = path; + this.secure = secure; + this.expires = expires; + } + + public String getName() + { + return name; + } + + public String getValue() + { + return value; + } + + public String getComment() + { + return comment; + } + + public String getDomain() + { + return domain; + } + + public String getPath() + { + return path; + } + + public boolean isSecure() + { + return secure; + } + + public Date getExpiryDate() + { + return expires; + } + + public String toString() + { + return toString(true, true); + } + + public String toString(boolean showPath, boolean showDomain) + { + CPStringBuilder buf = new CPStringBuilder(); + buf.append(name); + buf.append('='); + buf.append(value); + if (showPath) + { + buf.append("; $Path="); + buf.append(path); + } + if (showDomain) + { + buf.append("; $Domain="); + buf.append(domain); + } + return buf.toString(); + } + +} diff --git a/libjava/classpath/gnu/java/net/protocol/http/CookieManager.java b/libjava/classpath/gnu/java/net/protocol/http/CookieManager.java new file mode 100644 index 000000000..da3686689 --- /dev/null +++ b/libjava/classpath/gnu/java/net/protocol/http/CookieManager.java @@ -0,0 +1,65 @@ +/* CookieManager.java -- + Copyright (C) 2004 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.http; + +/** + * Cookie manager interface. + * If an application wants to handle cookies, they should implement this + * interface and register the instance with each HTTPConnection they use. + * + * @author Chris Burdess (dog@gnu.org) + */ +public interface CookieManager +{ + + /** + * Stores a cookie in the cookie manager. + * @param cookie the cookie to store + */ + void setCookie(Cookie cookie); + + /** + * Retrieves the cookies matching the specified criteria. + * @param host the host name + * @param secure whether the connection is secure + * @param path the path to access + */ + Cookie[] getCookies(String host, boolean secure, String path); + +} diff --git a/libjava/classpath/gnu/java/net/protocol/http/Credentials.java b/libjava/classpath/gnu/java/net/protocol/http/Credentials.java new file mode 100644 index 000000000..f95b4b53c --- /dev/null +++ b/libjava/classpath/gnu/java/net/protocol/http/Credentials.java @@ -0,0 +1,87 @@ +/* Credentials.java -- + Copyright (C) 2004 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.http; + +/** + * Represents a username/password combination that can be used to + * authenticate to an HTTP server. + * + * @author Chris Burdess (dog@gnu.org) + */ +public class Credentials +{ + + /** + * The username. + */ + private String username; + + /** + * The password. + */ + private String password; + + /** + * Constructor. + * @param username the username + * @param password the password + */ + public Credentials(String username, String password) + { + this.username = username; + this.password = password; + } + + /** + * Returns the username. + */ + public String getUsername() + { + return username; + } + + /** + * Returns the password. + */ + public String getPassword() + { + return password; + } + +} diff --git a/libjava/classpath/gnu/java/net/protocol/http/HTTPConnection.java b/libjava/classpath/gnu/java/net/protocol/http/HTTPConnection.java new file mode 100644 index 000000000..b96bf4c54 --- /dev/null +++ b/libjava/classpath/gnu/java/net/protocol/http/HTTPConnection.java @@ -0,0 +1,897 @@ +/* HTTPConnection.java -- + Copyright (C) 2004, 2005, 2006 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.http; + +import gnu.classpath.SystemProperties; + +import gnu.java.lang.CPStringBuilder; +import gnu.java.net.EmptyX509TrustManager; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.net.SocketException; +import java.security.GeneralSecurityException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.ListIterator; +import java.util.Map; + +import javax.net.ssl.HandshakeCompletedListener; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSocket; +import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.TrustManager; + +/** + * A connection to an HTTP server. + * + * @author Chris Burdess (dog@gnu.org) + */ +public class HTTPConnection +{ + + /** + * The default HTTP port. + */ + public static final int HTTP_PORT = 80; + + /** + * The default HTTPS port. + */ + public static final int HTTPS_PORT = 443; + + private static final String userAgent = SystemProperties.getProperty("http.agent"); + + /** + * The host name of the server to connect to. + */ + protected final String hostname; + + /** + * The port to connect to. + */ + protected final int port; + + /** + * Whether the connection should use transport level security (HTTPS). + */ + protected final boolean secure; + + /** + * The connection timeout for connecting the underlying socket. + */ + protected final int connectionTimeout; + + /** + * The read timeout for reads on the underlying socket. + */ + protected final int timeout; + + /** + * The host name of the proxy to connect to. + */ + protected String proxyHostname; + + /** + * The port on the proxy to connect to. + */ + protected int proxyPort; + + /** + * The major version of HTTP supported by this client. + */ + protected int majorVersion; + + /** + * The minor version of HTTP supported by this client. + */ + protected int minorVersion; + + private final List<HandshakeCompletedListener> handshakeCompletedListeners; + + /** + * The socket this connection communicates on. + */ + protected Socket socket; + + /** + * The SSL socket factory to use. + */ + private SSLSocketFactory sslSocketFactory; + + /** + * The socket input stream. + */ + protected InputStream in; + + /** + * The socket output stream. + */ + protected OutputStream out; + + /** + * Nonce values seen by this connection. + */ + private Map<String, Integer> nonceCounts; + + /** + * The cookie manager for this connection. + */ + protected CookieManager cookieManager; + + + /** + * The pool that this connection is a member of (if any). + */ + private Pool pool; + + /** + * Creates a new HTTP connection. + * @param hostname the name of the host to connect to + */ + public HTTPConnection(String hostname) + { + this(hostname, HTTP_PORT, false, 0, 0); + } + + /** + * Creates a new HTTP or HTTPS connection. + * @param hostname the name of the host to connect to + * @param secure whether to use a secure connection + */ + public HTTPConnection(String hostname, boolean secure) + { + this(hostname, secure ? HTTPS_PORT : HTTP_PORT, secure, 0, 0); + } + + /** + * Creates a new HTTP or HTTPS connection on the specified port. + * @param hostname the name of the host to connect to + * @param secure whether to use a secure connection + * @param connectionTimeout the connection timeout + * @param timeout the socket read timeout + */ + public HTTPConnection(String hostname, boolean secure, + int connectionTimeout, int timeout) + { + this(hostname, secure ? HTTPS_PORT : HTTP_PORT, secure, + connectionTimeout, timeout); + } + + /** + * Creates a new HTTP connection on the specified port. + * @param hostname the name of the host to connect to + * @param port the port on the host to connect to + */ + public HTTPConnection(String hostname, int port) + { + this(hostname, port, false, 0, 0); + } + + /** + * Creates a new HTTP or HTTPS connection on the specified port. + * @param hostname the name of the host to connect to + * @param port the port on the host to connect to + * @param secure whether to use a secure connection + */ + public HTTPConnection(String hostname, int port, boolean secure) + { + this(hostname, port, secure, 0, 0); + } + + /** + * Creates a new HTTP or HTTPS connection on the specified port. + * @param hostname the name of the host to connect to + * @param port the port on the host to connect to + * @param secure whether to use a secure connection + * @param connectionTimeout the connection timeout + * @param timeout the socket read timeout + * + * @throws IllegalArgumentException if either connectionTimeout or + * timeout less than zero. + */ + public HTTPConnection(String hostname, int port, boolean secure, + int connectionTimeout, int timeout) + { + if (connectionTimeout < 0 || timeout < 0) + throw new IllegalArgumentException(); + + this.hostname = hostname; + this.port = port; + this.secure = secure; + this.connectionTimeout = connectionTimeout; + this.timeout = timeout; + majorVersion = minorVersion = 1; + handshakeCompletedListeners + = new ArrayList<HandshakeCompletedListener>(2); + } + + /** + * Returns the name of the host to connect to. + */ + public String getHostName() + { + return hostname; + } + + /** + * Returns the port on the host to connect to. + */ + public int getPort() + { + return port; + } + + /** + * Indicates whether to use a secure connection or not. + */ + public boolean isSecure() + { + return secure; + } + + /** + * Returns the HTTP version string supported by this connection. + * @see #majorVersion + * @see #minorVersion + */ + public String getVersion() + { + return "HTTP/" + majorVersion + '.' + minorVersion; + } + + /** + * Sets the HTTP version supported by this connection. + * @param majorVersion the major version + * @param minorVersion the minor version + */ + public void setVersion(int majorVersion, int minorVersion) + { + if (majorVersion != 1) + { + throw new IllegalArgumentException("major version not supported: " + + majorVersion); + } + if (minorVersion < 0 || minorVersion > 1) + { + throw new IllegalArgumentException("minor version not supported: " + + minorVersion); + } + this.majorVersion = majorVersion; + this.minorVersion = minorVersion; + } + + /** + * Directs this connection to use the specified proxy. + * @param hostname the proxy host name + * @param port the port on the proxy to connect to + */ + public void setProxy(String hostname, int port) + { + proxyHostname = hostname; + proxyPort = port; + } + + /** + * Indicates whether this connection is using an HTTP proxy. + */ + public boolean isUsingProxy() + { + return (proxyHostname != null && proxyPort > 0); + } + + /** + * Sets the cookie manager to use for this connection. + * @param cookieManager the cookie manager + */ + public void setCookieManager(CookieManager cookieManager) + { + this.cookieManager = cookieManager; + } + + /** + * Returns the cookie manager in use for this connection. + */ + public CookieManager getCookieManager() + { + return cookieManager; + } + + /** + * Manages a pool of HTTPConections. The pool will have a maximum + * size determined by the value of the maxConn parameter passed to + * the {@link #get} method. This value inevitably comes from the + * http.maxConnections system property. If the + * classpath.net.http.keepAliveTTL system property is set, that will + * be the maximum time (in seconds) that an idle connection will be + * maintained. + */ + static class Pool + { + /** + * Singleton instance of the pool. + */ + static Pool instance = new Pool(); + + /** + * The pool + */ + final LinkedList<HTTPConnection> connectionPool + = new LinkedList<HTTPConnection>(); + + /** + * Maximum size of the pool. + */ + int maxConnections; + + /** + * If greater than zero, the maximum time a connection will remain + * int the pool. + */ + int connectionTTL; + + /** + * A thread that removes connections older than connectionTTL. + */ + class Reaper + implements Runnable + { + public void run() + { + synchronized (Pool.this) + { + try + { + do + { + while (connectionPool.size() > 0) + { + long currentTime = System.currentTimeMillis(); + + HTTPConnection c = + (HTTPConnection)connectionPool.getFirst(); + + long waitTime = c.timeLastUsed + + connectionTTL - currentTime; + + if (waitTime <= 0) + removeOldest(); + else + try + { + Pool.this.wait(waitTime); + } + catch (InterruptedException _) + { + // Ignore the interrupt. + } + } + // After the pool is empty, wait TTL to see if it + // is used again. This is because in the + // situation where a single thread is making HTTP + // requests to the same server it can remove the + // connection from the pool before the Reaper has + // a chance to start. This would cause the Reaper + // to exit if it were not for this extra delay. + // The result would be starting a Reaper thread + // for each HTTP request. With the delay we get + // at most one Reaper created each TTL. + try + { + Pool.this.wait(connectionTTL); + } + catch (InterruptedException _) + { + // Ignore the interrupt. + } + } + while (connectionPool.size() > 0); + } + finally + { + reaper = null; + } + } + } + } + + Reaper reaper; + + /** + * Private constructor to ensure singleton. + */ + private Pool() + { + } + + /** + * Tests for a matching connection. + * + * @param c connection to match. + * @param h the host name. + * @param p the port. + * @param sec true if using https. + * + * @return true if c matches h, p, and sec. + */ + private static boolean matches(HTTPConnection c, + String h, int p, boolean sec) + { + return h.equals(c.hostname) && (p == c.port) && (sec == c.secure); + } + + /** + * Get a pooled HTTPConnection. If there is an existing idle + * connection to the requested server it is returned. Otherwise a + * new connection is created. + * + * @param host the name of the host to connect to + * @param port the port on the host to connect to + * @param secure whether to use a secure connection + * + * @return the HTTPConnection. + */ + synchronized HTTPConnection get(String host, + int port, + boolean secure, + int connectionTimeout, int timeout) + { + String ttl = + SystemProperties.getProperty("classpath.net.http.keepAliveTTL"); + connectionTTL = 10000; + if (ttl != null && ttl.length() > 0) + try + { + int v = 1000 * Integer.parseInt(ttl); + if (v >= 0) + connectionTTL = v; + } + catch (NumberFormatException _) + { + // Ignore. + } + + String mc = SystemProperties.getProperty("http.maxConnections"); + maxConnections = 5; + if (mc != null && mc.length() > 0) + try + { + int v = Integer.parseInt(mc); + if (v > 0) + maxConnections = v; + } + catch (NumberFormatException _) + { + // Ignore. + } + + HTTPConnection c = null; + + ListIterator it = connectionPool.listIterator(0); + while (it.hasNext()) + { + HTTPConnection cc = (HTTPConnection)it.next(); + if (matches(cc, host, port, secure)) + { + c = cc; + it.remove(); + // Update the timeout. + if (c.socket != null) + try + { + c.socket.setSoTimeout(timeout); + } + catch (SocketException _) + { + // Ignore. + } + break; + } + } + if (c == null) + { + c = new HTTPConnection(host, port, secure, + connectionTimeout, timeout); + c.setPool(this); + } + return c; + } + + /** + * Put an idle HTTPConnection back into the pool. If this causes + * the pool to be come too large, the oldest connection is removed + * and closed. + * + */ + synchronized void put(HTTPConnection c) + { + c.timeLastUsed = System.currentTimeMillis(); + connectionPool.addLast(c); + + // maxConnections must always be >= 1 + while (connectionPool.size() >= maxConnections) + removeOldest(); + + if (connectionTTL > 0 && null == reaper) { + // If there is a connectionTTL, then the reaper has removed + // any stale connections, so we don't have to check for stale + // now. We do have to start a reaper though, as there is not + // one running now. + reaper = new Reaper(); + Thread t = new Thread(reaper, "HTTPConnection.Reaper"); + t.setDaemon(true); + t.start(); + } + } + + /** + * Remove the oldest connection from the pool and close it. + */ + void removeOldest() + { + HTTPConnection cx = (HTTPConnection)connectionPool.removeFirst(); + try + { + cx.closeConnection(); + } + catch (IOException ioe) + { + // Ignore it. We are just cleaning up. + } + } + } + + /** + * The number of times this HTTPConnection has be used via keep-alive. + */ + int useCount; + + /** + * If this HTTPConnection is in the pool, the time it was put there. + */ + long timeLastUsed; + + /** + * Set the connection pool that this HTTPConnection is a member of. + * If left unset or set to null, it will not be a member of any pool + * and will not be a candidate for reuse. + * + * @param p the pool. + */ + void setPool(Pool p) + { + pool = p; + } + + /** + * Signal that this HTTPConnection is no longer needed and can be + * returned to the connection pool. + * + */ + void release() + { + if (pool != null) + { + useCount++; + pool.put(this); + + } + else + { + // If there is no pool, just close. + try + { + closeConnection(); + } + catch (IOException ioe) + { + // Ignore it. We are just cleaning up. + } + } + } + + /** + * Creates a new request using this connection. + * @param method the HTTP method to invoke + * @param path the URI-escaped RFC2396 <code>abs_path</code> with + * optional query part + */ + public Request newRequest(String method, String path) + { + if (method == null || method.length() == 0) + { + throw new IllegalArgumentException("method must have non-zero length"); + } + if (path == null || path.length() == 0) + { + path = "/"; + } + Request ret = new Request(this, method, path); + if ((secure && port != HTTPS_PORT) || + (!secure && port != HTTP_PORT)) + { + ret.setHeader("Host", hostname + ":" + port); + } + else + { + ret.setHeader("Host", hostname); + } + ret.setHeader("User-Agent", userAgent); + ret.setHeader("Connection", "keep-alive"); + ret.setHeader("Accept-Encoding", + "chunked;q=1.0, gzip;q=0.9, deflate;q=0.8, " + + "identity;q=0.6, *;q=0"); + if (cookieManager != null) + { + Cookie[] cookies = cookieManager.getCookies(hostname, secure, path); + if (cookies != null && cookies.length > 0) + { + CPStringBuilder buf = new CPStringBuilder(); + buf.append("$Version=1"); + for (int i = 0; i < cookies.length; i++) + { + buf.append(','); + buf.append(' '); + buf.append(cookies[i].toString()); + } + ret.setHeader("Cookie", buf.toString()); + } + } + return ret; + } + + /** + * Closes this connection. + */ + public void close() + throws IOException + { + closeConnection(); + } + + /** + * Retrieves the socket associated with this connection. + * This creates the socket if necessary. + */ + protected synchronized Socket getSocket() + throws IOException + { + if (socket == null) + { + String connectHostname = hostname; + int connectPort = port; + if (isUsingProxy()) + { + connectHostname = proxyHostname; + connectPort = proxyPort; + } + socket = new Socket(); + InetSocketAddress address = + new InetSocketAddress(connectHostname, connectPort); + if (connectionTimeout > 0) + { + socket.connect(address, connectionTimeout); + } + else + { + socket.connect(address); + } + if (timeout > 0) + { + socket.setSoTimeout(timeout); + } + if (secure) + { + try + { + SSLSocketFactory factory = getSSLSocketFactory(); + SSLSocket ss = + (SSLSocket) factory.createSocket(socket, connectHostname, + connectPort, true); + String[] protocols = { "TLSv1", "SSLv3" }; + ss.setEnabledProtocols(protocols); + ss.setUseClientMode(true); + synchronized (handshakeCompletedListeners) + { + if (!handshakeCompletedListeners.isEmpty()) + { + for (Iterator i = + handshakeCompletedListeners.iterator(); + i.hasNext(); ) + { + HandshakeCompletedListener l = + (HandshakeCompletedListener) i.next(); + ss.addHandshakeCompletedListener(l); + } + } + } + ss.startHandshake(); + socket = ss; + } + catch (GeneralSecurityException e) + { + throw new IOException(e.getMessage()); + } + } + in = socket.getInputStream(); + in = new BufferedInputStream(in); + out = socket.getOutputStream(); + out = new BufferedOutputStream(out); + } + return socket; + } + + SSLSocketFactory getSSLSocketFactory() + throws GeneralSecurityException + { + if (sslSocketFactory == null) + { + TrustManager tm = new EmptyX509TrustManager(); + SSLContext context = SSLContext.getInstance("SSL"); + TrustManager[] trust = new TrustManager[] { tm }; + context.init(null, trust, null); + sslSocketFactory = context.getSocketFactory(); + } + return sslSocketFactory; + } + + void setSSLSocketFactory(SSLSocketFactory factory) + { + sslSocketFactory = factory; + } + + protected synchronized InputStream getInputStream() + throws IOException + { + if (socket == null) + { + getSocket(); + } + return in; + } + + protected synchronized OutputStream getOutputStream() + throws IOException + { + if (socket == null) + { + getSocket(); + } + return out; + } + + /** + * Closes the underlying socket, if any. + */ + protected synchronized void closeConnection() + throws IOException + { + if (socket != null) + { + try + { + socket.close(); + } + finally + { + socket = null; + } + } + } + + /** + * Returns a URI representing the connection. + * This does not include any request path component. + */ + protected String getURI() + { + CPStringBuilder buf = new CPStringBuilder(); + buf.append(secure ? "https://" : "http://"); + buf.append(hostname); + if (secure) + { + if (port != HTTPConnection.HTTPS_PORT) + { + buf.append(':'); + buf.append(port); + } + } + else + { + if (port != HTTPConnection.HTTP_PORT) + { + buf.append(':'); + buf.append(port); + } + } + return buf.toString(); + } + + /** + * Get the number of times the specified nonce has been seen by this + * connection. + */ + int getNonceCount(String nonce) + { + if (nonceCounts == null) + { + return 0; + } + return nonceCounts.get(nonce).intValue(); + } + + /** + * Increment the number of times the specified nonce has been seen. + */ + void incrementNonce(String nonce) + { + int current = getNonceCount(nonce); + if (nonceCounts == null) + { + nonceCounts = new HashMap<String, Integer>(); + } + nonceCounts.put(nonce, new Integer(current + 1)); + } + + // -- Events -- + + void addHandshakeCompletedListener(HandshakeCompletedListener l) + { + synchronized (handshakeCompletedListeners) + { + handshakeCompletedListeners.add(l); + } + } + void removeHandshakeCompletedListener(HandshakeCompletedListener l) + { + synchronized (handshakeCompletedListeners) + { + handshakeCompletedListeners.remove(l); + } + } + +} diff --git a/libjava/classpath/gnu/java/net/protocol/http/HTTPDateFormat.java b/libjava/classpath/gnu/java/net/protocol/http/HTTPDateFormat.java new file mode 100644 index 000000000..743f8e8e2 --- /dev/null +++ b/libjava/classpath/gnu/java/net/protocol/http/HTTPDateFormat.java @@ -0,0 +1,440 @@ +/* HTTPDateFormat.java -- + Copyright (C) 2004 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.http; + +import java.text.DateFormat; +import java.text.DecimalFormat; +import java.text.FieldPosition; +import java.text.NumberFormat; +import java.text.ParsePosition; +import java.util.Calendar; +import java.util.Date; +import java.util.GregorianCalendar; +import java.util.TimeZone; + +/** + * HTTP date formatter and parser. + * Formats dates according to RFC 822 (updated by RFC 1123). + * Parses dates according to the above, <i>or</i> RFC 1036, <i>or</i> the + * ANSI C <code>asctime()</code> format. + * + * @author Chris Burdess (dog@gnu.org) + */ +public class HTTPDateFormat + extends DateFormat +{ + + static final String[] DAYS_OF_WEEK = { + null, "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" + }; + + static final String[] MONTHS = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" + }; + + public HTTPDateFormat() + { + calendar = new GregorianCalendar(TimeZone.getTimeZone ("GMT")); + numberFormat = new DecimalFormat(); + } + + /** + * Appends the textual value for the specified field to the given string + * buffer. This method should be avoided, use <code>format(Date)</code> + * instead. + * @param date the Date object + * @param buf the buffer to append to + * @param field the current field position + * @return the modified buffer + */ + public StringBuffer format(Date date, StringBuffer buf, + FieldPosition field) + { + calendar.clear(); + calendar.setTime(date); + buf.setLength(0); + + // Day of week + buf.append(DAYS_OF_WEEK[calendar.get(Calendar.DAY_OF_WEEK)]); + buf.append(','); + buf.append(' '); + + // Day of month + int day = calendar.get(Calendar.DAY_OF_MONTH); + buf.append(Character.forDigit(day / 10, 10)); + buf.append(Character.forDigit(day % 10, 10)); + buf.append(' '); + + // Month + buf.append(MONTHS[calendar.get(Calendar.MONTH)]); + buf.append(' '); + + // Year + int year = calendar.get(Calendar.YEAR); + if (year < 1000) + { + buf.append('0'); + if (year < 100) + { + buf.append('0'); + if (year < 10) + { + buf.append('0'); + } + } + } + buf.append(Integer.toString(year)); + buf.append(' '); + + // Hour + int hour = calendar.get(Calendar.HOUR_OF_DAY); + buf.append(Character.forDigit(hour / 10, 10)); + buf.append(Character.forDigit(hour % 10, 10)); + buf.append(':'); + + // Minute + int minute = calendar.get(Calendar.MINUTE); + buf.append(Character.forDigit(minute / 10, 10)); + buf.append(Character.forDigit(minute % 10, 10)); + buf.append(':'); + + // Second + int second = calendar.get(Calendar.SECOND); + buf.append(Character.forDigit(second / 10, 10)); + buf.append(Character.forDigit(second % 10, 10)); + buf.append(' '); + + // Timezone + // Get time offset in minutes + int zoneOffset =(calendar.get(Calendar.ZONE_OFFSET) + + calendar.get(Calendar.DST_OFFSET)) / 60000; + + // Apply + or - appropriately + if (zoneOffset < 0) + { + zoneOffset = -zoneOffset; + buf.append('-'); + } + else + { + buf.append('+'); + } + + // Set the 2 2-char fields as specified above + int tzhours = zoneOffset / 60; + buf.append(Character.forDigit(tzhours / 10, 10)); + buf.append(Character.forDigit(tzhours % 10, 10)); + int tzminutes = zoneOffset % 60; + buf.append(Character.forDigit(tzminutes / 10, 10)); + buf.append(Character.forDigit(tzminutes % 10, 10)); + + field.setBeginIndex(0); + field.setEndIndex(buf.length()); + return buf; + } + + /** + * Parses the given date in the current TimeZone. + * @param text the formatted date to be parsed + * @param pos the current parse position + */ + public Date parse(String text, ParsePosition pos) + { + int date, month, year, hour, minute, second; + String monthText; + int start = 0, end = -1; + int len = text.length(); + calendar.clear(); + pos.setIndex(start); + try + { + // Advance to date + if (Character.isLetter(text.charAt(start))) + { + start = skipNonWhitespace(text, start); + } + // Determine mode + switch(start) + { + case 3: + // asctime + start = skipWhitespace(text, start); + pos.setIndex(start); + end = skipNonWhitespace(text, start + 1); + monthText = text.substring(start, end); + month = -1; + for (int i = 0; i < 12; i++) + { + if (MONTHS[i].equals(monthText)) + { + month = i; + break; + } + } + if (month == -1) + { + pos.setErrorIndex(end); + return null; + } + // Advance to date + start = skipWhitespace(text, end + 1); + pos.setIndex(start); + end = skipNonWhitespace(text, start + 1); + date = Integer.parseInt(text.substring(start, end)); + // Advance to hour + start = skipWhitespace(text, end + 1); + pos.setIndex(start); + end = skipTo(text, start + 1, ':'); + hour = Integer.parseInt(text.substring(start, end)); + // Advance to minute + start = end + 1; + pos.setIndex(start); + end = skipTo(text, start + 1, ':'); + minute = Integer.parseInt(text.substring(start, end)); + // Advance to second + start = end + 1; + pos.setIndex(start); + end = skipNonWhitespace(text, start + 1); + second = Integer.parseInt(text.substring(start, end)); + // Advance to year + start = skipWhitespace(text, end + 1); + pos.setIndex(start); + end = skipNonWhitespace(text, start + 1); + year = Integer.parseInt(text.substring(start, end)); + break; + case 0: + case 4: + // rfc822 + start = skipWhitespace(text, start); + pos.setIndex(start); + end = skipNonWhitespace(text, start + 1); + date = Integer.parseInt(text.substring(start, end)); + // Advance to month + start = skipWhitespace(text, end + 1); + pos.setIndex(start); + end = skipNonWhitespace(text, start + 1); + monthText = text.substring(start, end); + month = -1; + for (int i = 0; i < 12; i++) + { + if (MONTHS[i].equals(monthText)) + { + month = i; + break; + } + } + if (month == -1) + { + pos.setErrorIndex(end); + return null; + } + // Advance to year + start = skipWhitespace(text, end + 1); + pos.setIndex(start); + end = skipNonWhitespace(text, start + 1); + year = Integer.parseInt(text.substring(start, end)); + // Advance to hour + start = skipWhitespace(text, end + 1); + pos.setIndex(start); + end = skipTo(text, start + 1, ':'); + hour = Integer.parseInt(text.substring(start, end)); + // Advance to minute + start = end + 1; + pos.setIndex(start); + end = skipTo(text, start + 1, ':'); + minute = Integer.parseInt(text.substring(start, end)); + // Advance to second + start = end + 1; + pos.setIndex(start); + end = start + 1; + while (end < len && !Character.isWhitespace(text.charAt(end))) + { + end++; + } + second = Integer.parseInt(text.substring(start, end)); + break; + default: + // rfc850(obsolete) + start = skipWhitespace(text, start); + pos.setIndex(start); + end = skipTo(text, start + 1, '-'); + date = Integer.parseInt(text.substring(start, end)); + // Advance to month + start = end + 1; + pos.setIndex(start); + end = skipTo(text, start + 1, '-'); + monthText = text.substring(start, end); + month = -1; + for (int i = 0; i < 12; i++) + { + if (MONTHS[i].equals(monthText)) + { + month = i; + break; + } + } + if (month == -1) + { + pos.setErrorIndex(end); + return null; + } + // Advance to year + start = end + 1; + pos.setIndex(start); + end = skipNonWhitespace(text, start + 1); + year = 1900 + Integer.parseInt(text.substring(start, end)); + // Advance to hour + start = skipWhitespace(text, end + 1); + pos.setIndex(start); + end = skipTo(text, start + 1, ':'); + hour = Integer.parseInt(text.substring(start, end)); + // Advance to minute + start = end + 1; + pos.setIndex(start); + end = skipTo(text, start + 1, ':'); + minute = Integer.parseInt(text.substring(start, end)); + // Advance to second + start = end + 1; + pos.setIndex(start); + end = start + 1; + while (end < len && !Character.isWhitespace(text.charAt(end))) + { + end++; + } + second = Integer.parseInt(text.substring(start, end)); + } + + calendar.set(Calendar.YEAR, year); + calendar.set(Calendar.MONTH, month); + calendar.set(Calendar.DAY_OF_MONTH, date); + calendar.set(Calendar.HOUR, hour); + calendar.set(Calendar.MINUTE, minute); + calendar.set(Calendar.SECOND, second); + + if (end != len) + { + // Timezone + start = skipWhitespace(text, end + 1); + end = start + 1; + while (end < len && !Character.isWhitespace(text.charAt(end))) + { + end++; + } + char pm = text.charAt(start); + if (Character.isLetter(pm)) + { + TimeZone tz = + TimeZone.getTimeZone(text.substring(start, end)); + calendar.set(Calendar.ZONE_OFFSET, tz.getRawOffset()); + } + else + { + int zoneOffset = 0; + zoneOffset += 600 * Character.digit(text.charAt(++start), 10); + zoneOffset += 60 * Character.digit(text.charAt(++start), 10); + zoneOffset += 10 * Character.digit(text.charAt(++start), 10); + zoneOffset += Character.digit(text.charAt(++start), 10); + zoneOffset *= 60000; // minutes -> ms + if ('-' == pm) + { + zoneOffset = -zoneOffset; + } + calendar.set(Calendar.ZONE_OFFSET, zoneOffset); + } + } + pos.setIndex(end); + + return calendar.getTime(); + } + catch (NumberFormatException e) + { + pos.setErrorIndex(Math.max(start, end)); + } + catch (StringIndexOutOfBoundsException e) + { + pos.setErrorIndex(Math.max(start, end)); + } + return null; + } + + private int skipWhitespace(String text, int pos) + { + while(Character.isWhitespace(text.charAt(pos))) + { + pos++; + } + return pos; + } + + private int skipNonWhitespace(String text, int pos) + { + while(!Character.isWhitespace(text.charAt(pos))) + { + pos++; + } + return pos; + } + + private int skipTo(String text, int pos, char c) + { + while(text.charAt(pos) != c) + { + pos++; + } + return pos; + } + + /** + * Don't allow setting the calendar. + */ + public void setCalendar(Calendar newCalendar) + { + throw new UnsupportedOperationException(); + } + + /** + * Don't allow setting the NumberFormat. + */ + public void setNumberFormat(NumberFormat newNumberFormat) + { + throw new UnsupportedOperationException(); + } + +} diff --git a/libjava/classpath/gnu/java/net/protocol/http/HTTPURLConnection.java b/libjava/classpath/gnu/java/net/protocol/http/HTTPURLConnection.java new file mode 100644 index 000000000..9ba5c4793 --- /dev/null +++ b/libjava/classpath/gnu/java/net/protocol/http/HTTPURLConnection.java @@ -0,0 +1,693 @@ +/* HTTPURLConnection.java -- + Copyright (C) 2004, 2005, 2006 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.http; + +import gnu.classpath.SystemProperties; + +import java.io.ByteArrayOutputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.ProtocolException; +import java.net.URL; +import java.security.cert.Certificate; +import java.util.Collections; +import java.util.Date; +import java.util.List; +import java.util.Map; + +import javax.net.ssl.HandshakeCompletedEvent; +import javax.net.ssl.HandshakeCompletedListener; +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.SSLPeerUnverifiedException; +import javax.net.ssl.SSLSocketFactory; + +/** + * A URLConnection that uses the HTTPConnection class. + * + * @author Chris Burdess (dog@gnu.org) + */ +public class HTTPURLConnection + extends HttpsURLConnection + implements HandshakeCompletedListener +{ + /* + * The underlying connection. + */ + private HTTPConnection connection; + + // These are package private for use in anonymous inner classes. + String proxyHostname; + int proxyPort = -1; + String agent; + boolean keepAlive; + + private Request request; + private Headers requestHeaders; + private ByteArrayOutputStream requestSink; + private boolean requestMethodSetExplicitly; + + private Response response; + private InputStream responseSink; + private InputStream errorSink; + + private HandshakeCompletedEvent handshakeEvent; + + /** + * Constructor. + * @param url the URL + */ + public HTTPURLConnection(URL url) + throws IOException + { + super(url); + requestHeaders = new Headers(); + String proxy = SystemProperties.getProperty("http.proxyHost"); + if (proxy != null && proxy.length() > 0) + { + String port = SystemProperties.getProperty("http.proxyPort"); + if (port != null && port.length() > 0) + { + try + { + proxyPort = Integer.parseInt(port); + proxyHostname = proxy; + } + catch (NumberFormatException _) + { + // Ignore. + } + } + } + agent = SystemProperties.getProperty("http.agent"); + String ka = SystemProperties.getProperty("http.keepAlive"); + keepAlive = !(ka != null && "false".equals(ka)); + } + + public void connect() + throws IOException + { + if (connected) + { + return; + } + String protocol = url.getProtocol(); + boolean secure = "https".equals(protocol); + String host = url.getHost(); + int port = url.getPort(); + if (port < 0) + { + port = secure ? HTTPConnection.HTTPS_PORT : + HTTPConnection.HTTP_PORT; + } + String file = url.getFile(); + String username = url.getUserInfo(); + String password = null; + if (username != null) + { + int ci = username.indexOf(':'); + if (ci != -1) + { + password = username.substring(ci + 1); + username = username.substring(0, ci); + } + } + final Credentials creds = (username == null) ? null : + new Credentials (username, password); + + if ("POST".equals(method)) + { + String contentType = requestHeaders.getValue("Content-Type"); + if (null == contentType) + requestHeaders.addValue("Content-Type", + "application/x-www-form-urlencoded"); + } + + boolean retry; + do + { + retry = false; + if (connection == null) + { + connection = getConnection(host, port, secure); + if (secure) + { + SSLSocketFactory factory = getSSLSocketFactory(); + // FIXME: use the verifier + // HostnameVerifier verifier = getHostnameVerifier(); + if (factory != null) + { + connection.setSSLSocketFactory(factory); + } + connection.addHandshakeCompletedListener(this); + // TODO verifier + } + } + if (proxyHostname != null) + { + if (proxyPort < 0) + { + proxyPort = secure ? HTTPConnection.HTTPS_PORT : + HTTPConnection.HTTP_PORT; + } + connection.setProxy(proxyHostname, proxyPort); + } + try + { + request = connection.newRequest(method, file); + if (!keepAlive) + { + request.setHeader("Connection", "close"); + } + if (agent != null) + { + request.setHeader("User-Agent", agent); + } + request.getHeaders().putAll(requestHeaders); + if (requestSink != null) + { + byte[] content = requestSink.toByteArray(); + RequestBodyWriter writer = new ByteArrayRequestBodyWriter(content); + request.setRequestBodyWriter(writer); + } + if (creds != null) + { + request.setAuthenticator(new Authenticator() { + public Credentials getCredentials(String realm, int attempts) + { + return (attempts < 2) ? creds : null; + } + }); + } + response = request.dispatch(); + } + catch (IOException ioe) + { + if (connection.useCount > 0) + { + // Connection re-use failed: Try a new connection. + try + { + connection.close(); + } + catch (IOException _) + { + // Ignore. + } + connection = null; + retry = true; + continue; + } + else + { + // First time the connection was used: Hard failure. + throw ioe; + } + } + + if (response.isRedirect() && getInstanceFollowRedirects()) + { + // Read the response body, if there is one. If the + // redirect points us back at the same server, we will use + // the cached connection, so we must make sure there is no + // pending data in it. + InputStream body = response.getBody(); + if (body != null) + { + byte[] ignore = new byte[1024]; + while (true) + { + int n = body.read(ignore, 0, ignore.length); + if (n == -1) + break; + } + } + + // Follow redirect + String location = response.getHeader("Location"); + if (location != null) + { + String connectionUri = connection.getURI(); + int start = connectionUri.length(); + if (location.startsWith(connectionUri) && + location.charAt(start) == '/') + { + file = location.substring(start); + retry = true; + } + else if (location.startsWith("http:")) + { + connection.close(); + connection = null; + secure = false; + start = 7; + int end = location.indexOf('/', start); + if (end == -1) + end = location.length(); + host = location.substring(start, end); + int ci = host.lastIndexOf(':'); + if (ci != -1) + { + port = Integer.parseInt(host.substring (ci + 1)); + host = host.substring(0, ci); + } + else + { + port = HTTPConnection.HTTP_PORT; + } + file = location.substring(end); + retry = true; + } + else if (location.startsWith("https:")) + { + connection.close(); + connection = null; + secure = true; + start = 8; + int end = location.indexOf('/', start); + if (end == -1) + end = location.length(); + host = location.substring(start, end); + int ci = host.lastIndexOf(':'); + if (ci != -1) + { + port = Integer.parseInt(host.substring (ci + 1)); + host = host.substring(0, ci); + } + else + { + port = HTTPConnection.HTTPS_PORT; + } + file = location.substring(end); + retry = true; + } + else if (location.length() > 0) + { + // Malformed absolute URI, treat as file part of URI + if (location.charAt(0) == '/') + { + // Absolute path + file = location; + } + else + { + // Relative path + int lsi = file.lastIndexOf('/'); + file = (lsi == -1) ? "/" : file.substring(0, lsi + 1); + file += location; + } + retry = true; + } + } + } + else + { + responseSink = response.getBody(); + + if (response.isError()) + errorSink = responseSink; + } + } + while (retry); + connected = true; + } + + /** + * Returns a connection, from the pool if necessary. + */ + HTTPConnection getConnection(String host, int port, boolean secure) + throws IOException + { + HTTPConnection connection; + if (keepAlive) + { + connection = HTTPConnection.Pool.instance.get(host, port, secure, + getConnectTimeout(), + getReadTimeout()); + } + else + { + connection = new HTTPConnection(host, port, secure, + getConnectTimeout(), getReadTimeout()); + } + return connection; + } + + public void disconnect() + { + if (connection != null) + { + try + { + connection.close(); + } + catch (IOException e) + { + } + } + } + + public boolean usingProxy() + { + return (proxyHostname != null); + } + + /** + * Overrides the corresponding method in HttpURLConnection to permit + * arbitrary methods, as long as they're valid ASCII alphabetic + * characters. This is to permit WebDAV and other HTTP extensions to + * function. + * @param method the method + */ + public void setRequestMethod(String method) + throws ProtocolException + { + if (connected) + { + throw new ProtocolException("Already connected"); + } + // Validate + method = method.toUpperCase(); + int len = method.length(); + if (len == 0) + { + throw new ProtocolException("Empty method name"); + } + for (int i = 0; i < len; i++) + { + char c = method.charAt(i); + if (c < 0x41 || c > 0x5a) + { + throw new ProtocolException("Illegal character '" + c + + "' at index " + i); + } + } + // OK + this.method = method; + requestMethodSetExplicitly = true; + } + + public String getRequestProperty(String key) + { + return requestHeaders.getValue(key); + } + + public Map<String, List<String>> getRequestProperties() + { + if (connected) + throw new IllegalStateException("Already connected"); + + Map<String, List<String>> m = requestHeaders.getAsMap(); + return Collections.unmodifiableMap(m); + } + + public void setRequestProperty(String key, String value) + { + super.setRequestProperty(key, value); + + requestHeaders.put(key, value); + } + + public void addRequestProperty(String key, String value) + { + super.addRequestProperty(key, value); + requestHeaders.addValue(key, value); + } + + public OutputStream getOutputStream() + throws IOException + { + if (connected) + { + throw new ProtocolException("Already connected"); + } + if (!doOutput) + { + throw new ProtocolException("doOutput is false"); + } + else if (!requestMethodSetExplicitly) + { + /* + * Silently change the method to POST if no method was set + * explicitly. This is due to broken applications depending on this + * behaviour (Apache XMLRPC for one). + */ + method = "POST"; + } + if (requestSink == null) + { + requestSink = new ByteArrayOutputStream(); + } + return requestSink; + } + + // -- Response -- + + public InputStream getInputStream() + throws IOException + { + if (!connected) + { + connect(); + } + if (!doInput) + { + throw new ProtocolException("doInput is false"); + } + + if (response.isError()) + { + int code = response.getCode(); + if (code == 404 || code == 410) + throw new FileNotFoundException(url.toString()); + + throw new IOException("Server returned HTTP response code " + code + + " for URL " + url.toString()); + } + + return responseSink; + } + + public InputStream getErrorStream() + { + return errorSink; + } + + public Map<String,List<String>> getHeaderFields() + { + if (!connected) + { + try + { + connect(); + } + catch (IOException e) + { + return null; + } + } + Map<String,List<String>> m = response.getHeaders().getAsMap(); + m.put(null, Collections.singletonList(getStatusLine(response))); + return Collections.unmodifiableMap(m); + } + + String getStatusLine(Response response) + { + return "HTTP/" + response.getMajorVersion() + + "." + response.getMinorVersion() + + " " + response.getCode() + + " " + response.getMessage(); + } + + public String getHeaderField(int index) + { + if (!connected) + { + try + { + connect(); + } + catch (IOException e) + { + return null; + } + } + if (index == 0) + { + return getStatusLine(response); + } + return response.getHeaders().getHeaderValue(index - 1); + } + + public String getHeaderFieldKey(int index) + { + if (!connected) + { + try + { + connect(); + } + catch (IOException e) + { + return null; + } + } + // index of zero is the status line. + return response.getHeaders().getHeaderName(index - 1); + } + + public String getHeaderField(String name) + { + if (!connected) + { + try + { + connect(); + } + catch (IOException e) + { + return null; + } + } + return response.getHeader(name); + } + + public long getHeaderFieldDate(String name, long def) + { + if (!connected) + { + try + { + connect(); + } + catch (IOException e) + { + return def; + } + } + Date date = response.getDateHeader(name); + return (date == null) ? def : date.getTime(); + } + + public String getContentType() + { + return getHeaderField("Content-Type"); + } + + public int getResponseCode() + throws IOException + { + if (!connected) + { + connect(); + } + return response.getCode(); + } + + public String getResponseMessage() + throws IOException + { + if (!connected) + { + connect(); + } + return response.getMessage(); + } + + // -- HTTPS specific -- + + public String getCipherSuite() + { + if (!connected) + { + throw new IllegalStateException("not connected"); + } + return handshakeEvent.getCipherSuite(); + } + + public Certificate[] getLocalCertificates() + { + if (!connected) + { + throw new IllegalStateException("not connected"); + } + return handshakeEvent.getLocalCertificates(); + } + + public Certificate[] getServerCertificates() + throws SSLPeerUnverifiedException + { + if (!connected) + { + throw new IllegalStateException("not connected"); + } + return handshakeEvent.getPeerCertificates(); + } + + // HandshakeCompletedListener + + public void handshakeCompleted(HandshakeCompletedEvent event) + { + handshakeEvent = event; + } + + /** + * Set the read timeout, in milliseconds, or zero if the timeout + * is to be considered infinite. + * + * Overloaded. + * + */ + public void setReadTimeout(int timeout) + throws IllegalArgumentException + { + super.setReadTimeout(timeout); + if (connection == null) + return; + try + { + connection.getSocket().setSoTimeout(timeout); + } + catch (IOException se) + { + // Ignore socket exceptions. + } + } +} diff --git a/libjava/classpath/gnu/java/net/protocol/http/Handler.java b/libjava/classpath/gnu/java/net/protocol/http/Handler.java new file mode 100644 index 000000000..30810321d --- /dev/null +++ b/libjava/classpath/gnu/java/net/protocol/http/Handler.java @@ -0,0 +1,72 @@ +/* Handler.java -- + Copyright (C) 2004 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.http; + +import java.io.IOException; +import java.net.URL; +import java.net.URLConnection; +import java.net.URLStreamHandler; + +/** + * An HTTP URL stream handler. + * + * @author Chris Burdess (dog@gnu.org) + */ +public class Handler + extends URLStreamHandler +{ + + /** + * Returns the default HTTP port (80). + */ + protected int getDefaultPort() + { + return HTTPConnection.HTTP_PORT; + } + + /** + * Returns an HTTPURLConnection for the given URL. + */ + public URLConnection openConnection(URL url) + throws IOException + { + return new HTTPURLConnection(url); + } + +} diff --git a/libjava/classpath/gnu/java/net/protocol/http/Headers.java b/libjava/classpath/gnu/java/net/protocol/http/Headers.java new file mode 100644 index 000000000..faf5eb195 --- /dev/null +++ b/libjava/classpath/gnu/java/net/protocol/http/Headers.java @@ -0,0 +1,424 @@ +/* Headers.java -- + Copyright (C) 2004, 2006 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.http; + +import gnu.java.lang.CPStringBuilder; + +import gnu.java.net.LineInputStream; + +import java.io.IOException; +import java.io.InputStream; +import java.lang.Iterable; +import java.text.DateFormat; +import java.text.ParseException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +/** + * A collection of HTTP header names and associated values. The + * values are {@link ArrayList ArrayLists} of Strings. Retrieval of + * values is case insensitive. An iteration over the collection + * returns the header names in the order they were received. + * + * @author Chris Burdess (dog@gnu.org) + * @author David Daney (ddaney@avtrex.com) + */ +class Headers implements Iterable<Headers.HeaderElement> +{ + /** + * A list of HeaderElements + */ + private final ArrayList<HeaderElement> headers + = new ArrayList<HeaderElement>(); + + /** + * The HTTP dateformat used to parse date header fields. + */ + private static final DateFormat dateFormat = new HTTPDateFormat(); + + /** + * Class for a Header element consisting of + * a name and value String. + */ + static class HeaderElement + { + String name; + String value; + + HeaderElement(String name, String value) + { + this.name = name; + this.value = value; + } + } + + /** + * Default constructor. + */ + public Headers() + { + // nothing to do + } + + /** + * Return an Iterator over this collection of headers. + * Iterator.getNext() returns objects of type {@link HeaderElement}. + * + * @return the Iterator. + */ + public Iterator<HeaderElement> iterator() + { + return headers.iterator(); + } + + /** + * Returns the value of the specified header as a string. If + * multiple values are present, the last one is returned. + * + * @param header the header name (case insensitive search) + * @return The header value or <code>null</code> if not found. + */ + public String getValue(String header) + { + for (int i = headers.size() - 1; i >= 0; i--) + { + HeaderElement e = headers.get(i); + if (e.name.equalsIgnoreCase(header)) + { + return e.value; + } + } + return null; + } + + /** + * Returns the value of the specified header as an integer. If + * multiple values are present, the last one is returned. + * + * @param header the header name (case insensitive search) + * @return The header value or <code>-1</code> if not present or + * not an integer value. + */ + public int getIntValue(String header) + { + String val = getValue(header); + if (val == null) + { + return -1; + } + try + { + return Integer.parseInt(val); + } + catch (NumberFormatException e) + { + // fall through + } + return -1; + } + + /** + * Returns the value of the specified header as a long. If + * multiple values are present, the last one is returned. + * + * @param header the header name (case insensitive search) + * @return The header value or <code>-1</code> if not present or + * not a long value. + */ + public long getLongValue(String header) + { + String val = getValue(header); + if (val == null) + { + return -1; + } + try + { + return Long.parseLong(val); + } + catch (NumberFormatException e) + { + // fall through + } + return -1; + } + + /** + * Returns the value of the specified header as a date. If + * multiple values are present, the last one is returned. + * + * @param header the header name (case insensitive search) + * @return The header value or <code>null</code> if not present or + * not a date value. + */ + public Date getDateValue(String header) + { + String val = getValue(header); + if (val == null) + { + return null; + } + try + { + return dateFormat.parse(val); + } + catch (ParseException e) + { + return null; + } + } + + /** + * Add a header to this set of headers. If there is an existing + * header with the same name it's value is replaced with the new value. + * If multiple headers of the same name exist only the last one's value + * is replaced. + * + * @param name the header name + * @param value the header value + * + * @see #addValue(String, String) + */ + public void put(String name, String value) + { + for (int i = headers.size() - 1; i >= 0; i--) + { + HeaderElement e = headers.get(i); + if (e.name.equalsIgnoreCase(name)) + { + e.value = value; + return; + } + } + + // nothing was replaced so add it as new HeaderElement + addValue(name, value); + } + + /** + * Add all headers from a set of headers to this set. Any existing header + * with the same (case insensitive) name as one of the new headers will + * be overridden. + * + * @param o the headers to be added + */ + public void putAll(Headers o) + { + for (Iterator<HeaderElement> it = o.iterator(); it.hasNext(); ) + { + HeaderElement e = it.next(); + remove(e.name); + addValue(e.name, e.value); + } + } + + /** + * Remove a header from this set of headers. If there is more than + * one instance of a header of the given name, they are all removed. + * + * @param name the header name + */ + public void remove(String name) + { + for (Iterator<HeaderElement> it = headers.iterator(); it.hasNext(); ) + { + HeaderElement e = it.next(); + if (e.name.equalsIgnoreCase(name)) + it.remove(); + } + } + + /** + * Parse the specified InputStream, adding headers to this collection. + * + * @param in the InputStream. + * @throws IOException if I/O error occured. + */ + public void parse(InputStream in) + throws IOException + { + LineInputStream lin = (in instanceof LineInputStream) ? + (LineInputStream) in : new LineInputStream(in); + + String name = null; + CPStringBuilder value = new CPStringBuilder(); + while (true) + { + String line = lin.readLine(); + if (line == null) + { + if (name != null) + { + addValue(name, value.toString()); + } + break; + } + int len = line.length(); + if (len < 2) + { + if (name != null) + { + addValue(name, value.toString()); + } + break; + } + char c1 = line.charAt(0); + if (c1 == ' ' || c1 == '\t') + { + // Continuation + int last = len - 1; + if (line.charAt(last) != '\r') + ++last; + value.append(line.substring(0, last)); + } + else + { + if (name != null) + { + addValue(name, value.toString()); + } + + int di = line.indexOf(':'); + name = line.substring(0, di); + value.setLength(0); + do + { + di++; + } + while (di < len && line.charAt(di) == ' '); + int last = len - 1; + if (line.charAt(last) != '\r') + ++last; + value.append(line.substring(di, last)); + } + } + } + + + /** + * Add a header to this set of headers. If there is an existing + * header with the same name, it is not effected. + * + * @param name the header name + * @param value the header value + * + * @see #put(String, String) + */ + public void addValue(String name, String value) + { + headers.add(headers.size(), new HeaderElement(name, value)); + } + + /** + * Get a new Map containing all the headers. The keys of the Map + * are Strings (the header names). The headers will be included + * case-sensitive in the map so that querying must be done with the + * correct case of the needed header name. The values of the Map are + * unmodifiable Lists containing Strings (the header values). + * + * <p> + * The returned map is modifiable. Changing it will not effect this + * collection of Headers in any way.</p> + * + * @return a Map containing all the headers. + */ + public Map<String,List<String>> getAsMap() + { + LinkedHashMap<String,List<String>> m = new LinkedHashMap<String,List<String>>(); + for (Iterator<HeaderElement> it = headers.iterator(); it.hasNext(); ) + { + HeaderElement e = it.next(); + ArrayList<String> l = (ArrayList<String>)m.get(e.name); + if (l == null) + { + l = new ArrayList<String>(1); + l.add(e.value); + m.put(e.name, l); + } + else + l.add(0, e.value); + } + for (Iterator<Map.Entry<String,List<String>>> it = m.entrySet().iterator(); it.hasNext(); ) + { + Map.Entry<String,List<String>> me = it.next(); + List<String> l = me.getValue(); + me.setValue(Collections.unmodifiableList(l)); + } + return m; + } + + /** + * Get the name of the Nth header. + * + * @param i the header index. + * + * @return The header name, or <code>null</code> if index outside of range. + * + * @see #getHeaderValue(int) + */ + public String getHeaderName(int i) + { + if (i >= headers.size() || i < 0) + return null; + + return headers.get(i).name; + } + + /** + * Get the value of the Nth header. + * + * @param i the header index. + * + * @return the header value, or <code>null</code> if index outside of range. + * + * @see #getHeaderName(int) + */ + public String getHeaderValue(int i) + { + if (i >= headers.size() || i < 0) + return null; + + return headers.get(i).value; + } +} diff --git a/libjava/classpath/gnu/java/net/protocol/http/LimitedLengthInputStream.java b/libjava/classpath/gnu/java/net/protocol/http/LimitedLengthInputStream.java new file mode 100644 index 000000000..568f830fd --- /dev/null +++ b/libjava/classpath/gnu/java/net/protocol/http/LimitedLengthInputStream.java @@ -0,0 +1,216 @@ +/* LimitedLengthInputStream.java -- + Copyright (C) 2005, 2008 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.http; + +import java.io.IOException; +import java.io.InputStream; + +/** + * InputStream that limits the total number of bytes that can be read + * from an underlying stream. In addition to limiting the number of + * bytes read, close() is not propagated to the underlying stream. + * + * @author David Daney (ddaney@avtrex.com) + */ +class LimitedLengthInputStream + extends InputStream +{ + private long remainingLen; + private boolean restrictLen; + private HTTPConnection connection; + private boolean eof; + private InputStream in; + private boolean doClose; + + private void handleClose() + throws IOException + { + eof = true; + + if (doClose) + in.close(); + else + connection.release(); + + in = null; + connection = null; + } + + /** + * Constructor. + * + * @param in the underlying stream + * + * @param maxLen the maximum number of bytes to read + * + * @param restrictLen if true the number of bytes that can be read + * from this stream will be limited to maxLen, otherwise the number + * of bytes is not restricted. + * + * @param con the HTTPConnection associated with this stream + * + * @param doClose if true con will be closed when finished reading, + * else it will be placed back in the connection pool. + * + */ + LimitedLengthInputStream(InputStream in, + long maxLen, + boolean restrictLen, + HTTPConnection con, + boolean doClose) + throws IOException + { + this.in = in; + this.remainingLen = maxLen; + this.restrictLen = restrictLen; + this.connection = con; + this.doClose = doClose; + + if (restrictLen) + { + if (maxLen < 0) + throw new IllegalArgumentException(); + else if (maxLen == 0) + handleClose(); // Nothing to do, release the connection. + } + } + + public synchronized int read() + throws IOException + { + if (eof) + return -1; // EOF + + int r; + + if (restrictLen) + { + r = in.read(); + if (-1 != r) + remainingLen--; + + if (0 == remainingLen) + handleClose(); + } + else + { + r = in.read(); + if (r == -1) + handleClose(); + } + + return r; + } + + public int read(byte[] buffer) + throws IOException + { + return read(buffer, 0, buffer.length); + } + + public synchronized int read(byte[] buffer, int offset, int length) + throws IOException + { + if (eof) + return -1; // EOF + + if (restrictLen && length > remainingLen) + length = (int) remainingLen; + + int r = in.read(buffer, offset, length); + + if (-1 == r) + handleClose(); + + if (restrictLen && r > 0) + { + remainingLen -= r; + if (0 == remainingLen) + handleClose(); + } + return r; + } + + public synchronized long skip(long n) + throws IOException + { + + if (eof) + return 0; + + if (restrictLen && n > remainingLen) + n = remainingLen; + + long r = in.skip(n); + + if (restrictLen) + { + remainingLen -= r; + if (0 == remainingLen) + handleClose(); + } + return r; + } + + public synchronized int available() + throws IOException + { + if (eof) + return 0; + + int a = in.available(); + if (restrictLen && a > remainingLen) + a = (int)remainingLen; + return a; + } + + public synchronized void close() + throws IOException + { + if (eof) + return; + + // If we get to here, the stream was not fully read. Just throw + // it away. + + doClose = true; + + handleClose(); + } +} diff --git a/libjava/classpath/gnu/java/net/protocol/http/Request.java b/libjava/classpath/gnu/java/net/protocol/http/Request.java new file mode 100644 index 000000000..534213eed --- /dev/null +++ b/libjava/classpath/gnu/java/net/protocol/http/Request.java @@ -0,0 +1,857 @@ +/* Request.java -- + Copyright (C) 2004, 2005, 2006, 2007 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.http; + +import gnu.java.lang.CPStringBuilder; +import gnu.java.net.LineInputStream; +import gnu.java.util.Base64; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.ProtocolException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.text.DateFormat; +import java.text.ParseException; +import java.util.Calendar; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; +import java.util.zip.GZIPInputStream; +import java.util.zip.InflaterInputStream; + +/** + * A single HTTP request. + * + * @author Chris Burdess (dog@gnu.org) + */ +public class Request +{ + + /** + * The connection context in which this request is invoked. + */ + protected final HTTPConnection connection; + + /** + * The HTTP method to invoke. + */ + protected final String method; + + /** + * The path identifying the resource. + * This string must conform to the abs_path definition given in RFC2396, + * with an optional "?query" part, and must be URI-escaped by the caller. + */ + protected final String path; + + /** + * The headers in this request. + */ + protected final Headers requestHeaders; + + /** + * The request body provider. + */ + protected RequestBodyWriter requestBodyWriter; + + /** + * Map of response header handlers. + */ + protected Map<String, ResponseHeaderHandler> responseHeaderHandlers; + + /** + * The authenticator. + */ + protected Authenticator authenticator; + + /** + * Whether this request has been dispatched yet. + */ + private boolean dispatched; + + /** + * Constructor for a new request. + * @param connection the connection context + * @param method the HTTP method + * @param path the resource path including query part + */ + protected Request(HTTPConnection connection, String method, + String path) + { + this.connection = connection; + this.method = method; + this.path = path; + requestHeaders = new Headers(); + responseHeaderHandlers = new HashMap<String, ResponseHeaderHandler>(); + } + + /** + * Returns the connection associated with this request. + * @see #connection + */ + public HTTPConnection getConnection() + { + return connection; + } + + /** + * Returns the HTTP method to invoke. + * @see #method + */ + public String getMethod() + { + return method; + } + + /** + * Returns the resource path. + * @see #path + */ + public String getPath() + { + return path; + } + + /** + * Returns the full request-URI represented by this request, as specified + * by HTTP/1.1. + */ + public String getRequestURI() + { + return connection.getURI() + path; + } + + /** + * Returns the headers in this request. + */ + public Headers getHeaders() + { + return requestHeaders; + } + + /** + * Returns the value of the specified header in this request. + * @param name the header name + */ + public String getHeader(String name) + { + return requestHeaders.getValue(name); + } + + /** + * Returns the value of the specified header in this request as an integer. + * @param name the header name + */ + public int getIntHeader(String name) + { + return requestHeaders.getIntValue(name); + } + + /** + * Returns the value of the specified header in this request as a date. + * @param name the header name + */ + public Date getDateHeader(String name) + { + return requestHeaders.getDateValue(name); + } + + /** + * Sets the specified header in this request. + * @param name the header name + * @param value the header value + */ + public void setHeader(String name, String value) + { + requestHeaders.put(name, value); + } + + /** + * Convenience method to set the entire request body. + * @param requestBody the request body content + */ + public void setRequestBody(byte[] requestBody) + { + setRequestBodyWriter(new ByteArrayRequestBodyWriter(requestBody)); + } + + /** + * Sets the request body provider. + * @param requestBodyWriter the handler used to obtain the request body + */ + public void setRequestBodyWriter(RequestBodyWriter requestBodyWriter) + { + this.requestBodyWriter = requestBodyWriter; + } + + /** + * Sets a callback handler to be invoked for the specified header name. + * @param name the header name + * @param handler the handler to receive the value for the header + */ + public void setResponseHeaderHandler(String name, + ResponseHeaderHandler handler) + { + responseHeaderHandlers.put(name, handler); + } + + /** + * Sets an authenticator that can be used to handle authentication + * automatically. + * @param authenticator the authenticator + */ + public void setAuthenticator(Authenticator authenticator) + { + this.authenticator = authenticator; + } + + /** + * Dispatches this request. + * A request can only be dispatched once; calling this method a second + * time results in a protocol exception. + * @exception IOException if an I/O error occurred + * @return an HTTP response object representing the result of the operation + */ + public Response dispatch() + throws IOException + { + if (dispatched) + { + throw new ProtocolException("request already dispatched"); + } + final String CRLF = "\r\n"; + final String HEADER_SEP = ": "; + final String US_ASCII = "US-ASCII"; + final String version = connection.getVersion(); + Response response; + int contentLength = -1; + boolean retry = false; + int attempts = 0; + boolean expectingContinue = false; + if (requestBodyWriter != null) + { + contentLength = requestBodyWriter.getContentLength(); + String expect = getHeader("Expect"); + if (expect != null && expect.equals("100-continue")) + { + expectingContinue = true; + } + else + { + setHeader("Content-Length", Integer.toString(contentLength)); + } + } + + try + { + // Loop while authentication fails or continue + do + { + retry = false; + + // Get socket output and input streams + OutputStream out = connection.getOutputStream(); + + // Request line + String requestUri = path; + if (connection.isUsingProxy() && + !"*".equals(requestUri) && + !"CONNECT".equals(method)) + { + requestUri = getRequestURI(); + } + String line = method + ' ' + requestUri + ' ' + version + CRLF; + out.write(line.getBytes(US_ASCII)); + // Request headers + for (Headers.HeaderElement elt : requestHeaders) + { + line = elt.name + HEADER_SEP + elt.value + CRLF; + out.write(line.getBytes(US_ASCII)); + } + out.write(CRLF.getBytes(US_ASCII)); + // Request body + if (requestBodyWriter != null && !expectingContinue) + { + byte[] buffer = new byte[4096]; + int len; + int count = 0; + + requestBodyWriter.reset(); + do + { + len = requestBodyWriter.write(buffer); + if (len > 0) + { + out.write(buffer, 0, len); + } + count += len; + } + while (len > -1 && count < contentLength); + } + out.flush(); + // Get response + while(true) + { + response = readResponse(connection.getInputStream()); + int sc = response.getCode(); + if (sc == 401 && authenticator != null) + { + if (authenticate(response, attempts++)) + { + retry = true; + } + } + else if (sc == 100) + { + if (expectingContinue) + { + requestHeaders.remove("Expect"); + setHeader("Content-Length", + Integer.toString(contentLength)); + expectingContinue = false; + retry = true; + } + else + { + // A conforming server can send an unsoliceted + // Continue response but *should* not (RFC 2616 + // sec 8.2.3). Ignore the bogus Continue + // response and get the real response that + // should follow + continue; + } + } + break; + } + } + while (retry); + } + catch (IOException e) + { + connection.close(); + throw e; + } + return response; + } + + Response readResponse(InputStream in) + throws IOException + { + String line; + int len; + + // Read response status line + LineInputStream lis = new LineInputStream(in); + + line = lis.readLine(); + if (line == null) + { + throw new ProtocolException("Peer closed connection"); + } + if (!line.startsWith("HTTP/")) + { + throw new ProtocolException(line); + } + len = line.length(); + int start = 5, end = 6; + while (line.charAt(end) != '.') + { + end++; + } + int majorVersion = Integer.parseInt(line.substring(start, end)); + start = end + 1; + end = start + 1; + while (line.charAt(end) != ' ') + { + end++; + } + int minorVersion = Integer.parseInt(line.substring(start, end)); + start = end + 1; + end = start + 3; + int code = Integer.parseInt(line.substring(start, end)); + String message = line.substring(end + 1, len - 1); + // Read response headers + Headers responseHeaders = new Headers(); + responseHeaders.parse(lis); + notifyHeaderHandlers(responseHeaders); + InputStream body = null; + + switch (code) + { + case 100: + break; + case 204: + case 205: + case 304: + body = createResponseBodyStream(responseHeaders, majorVersion, + minorVersion, in, false); + break; + default: + body = createResponseBodyStream(responseHeaders, majorVersion, + minorVersion, in, true); + } + + // Construct response + Response ret = new Response(majorVersion, minorVersion, code, + message, responseHeaders, body); + return ret; + } + + void notifyHeaderHandlers(Headers headers) + { + for (Headers.HeaderElement entry : headers) + { + // Handle Set-Cookie + if ("Set-Cookie".equalsIgnoreCase(entry.name)) + handleSetCookie(entry.value); + + ResponseHeaderHandler handler = + (ResponseHeaderHandler) responseHeaderHandlers.get(entry.name); + if (handler != null) + handler.setValue(entry.value); + } + } + + private InputStream createResponseBodyStream(Headers responseHeaders, + int majorVersion, + int minorVersion, + InputStream in, + boolean mayHaveBody) + throws IOException + { + long contentLength = -1; + + // Persistent connections are the default in HTTP/1.1 + boolean doClose = "close".equalsIgnoreCase(getHeader("Connection")) || + "close".equalsIgnoreCase(responseHeaders.getValue("Connection")) || + (connection.majorVersion == 1 && connection.minorVersion == 0) || + (majorVersion == 1 && minorVersion == 0); + + String transferCoding = responseHeaders.getValue("Transfer-Encoding"); + if ("HEAD".equals(method) || !mayHaveBody) + { + // Special case no body. + in = new LimitedLengthInputStream(in, 0, true, connection, doClose); + } + else if ("chunked".equalsIgnoreCase(transferCoding)) + { + in = new LimitedLengthInputStream(in, -1, false, connection, doClose); + + in = new ChunkedInputStream(in, responseHeaders); + } + else + { + contentLength = responseHeaders.getLongValue("Content-Length"); + + if (contentLength < 0) + doClose = true; // No Content-Length, must close. + + in = new LimitedLengthInputStream(in, contentLength, + contentLength >= 0, + connection, doClose); + } + String contentCoding = responseHeaders.getValue("Content-Encoding"); + if (contentCoding != null && !"identity".equals(contentCoding)) + { + if ("gzip".equals(contentCoding)) + { + in = new GZIPInputStream(in); + } + else if ("deflate".equals(contentCoding)) + { + in = new InflaterInputStream(in); + } + else + { + throw new ProtocolException("Unsupported Content-Encoding: " + + contentCoding); + } + // Remove the Content-Encoding header because the content is + // no longer compressed. + responseHeaders.remove("Content-Encoding"); + } + return in; + } + + boolean authenticate(Response response, int attempts) + throws IOException + { + String challenge = response.getHeader("WWW-Authenticate"); + if (challenge == null) + { + challenge = response.getHeader("Proxy-Authenticate"); + } + int si = challenge.indexOf(' '); + String scheme = (si == -1) ? challenge : challenge.substring(0, si); + if ("Basic".equalsIgnoreCase(scheme)) + { + Properties params = parseAuthParams(challenge.substring(si + 1)); + String realm = params.getProperty("realm"); + Credentials creds = authenticator.getCredentials(realm, attempts); + String userPass = creds.getUsername() + ':' + creds.getPassword(); + byte[] b_userPass = userPass.getBytes("US-ASCII"); + byte[] b_encoded = Base64.encode(b_userPass).getBytes("US-ASCII"); + String authorization = + scheme + " " + new String(b_encoded, "US-ASCII"); + setHeader("Authorization", authorization); + return true; + } + else if ("Digest".equalsIgnoreCase(scheme)) + { + Properties params = parseAuthParams(challenge.substring(si + 1)); + String realm = params.getProperty("realm"); + String nonce = params.getProperty("nonce"); + String qop = params.getProperty("qop"); + String algorithm = params.getProperty("algorithm"); + String digestUri = getRequestURI(); + Credentials creds = authenticator.getCredentials(realm, attempts); + String username = creds.getUsername(); + String password = creds.getPassword(); + connection.incrementNonce(nonce); + try + { + MessageDigest md5 = MessageDigest.getInstance("MD5"); + final byte[] COLON = { 0x3a }; + + // Calculate H(A1) + md5.reset(); + md5.update(username.getBytes("US-ASCII")); + md5.update(COLON); + md5.update(realm.getBytes("US-ASCII")); + md5.update(COLON); + md5.update(password.getBytes("US-ASCII")); + byte[] ha1 = md5.digest(); + if ("md5-sess".equals(algorithm)) + { + byte[] cnonce = generateNonce(); + md5.reset(); + md5.update(ha1); + md5.update(COLON); + md5.update(nonce.getBytes("US-ASCII")); + md5.update(COLON); + md5.update(cnonce); + ha1 = md5.digest(); + } + String ha1Hex = toHexString(ha1); + + // Calculate H(A2) + md5.reset(); + md5.update(method.getBytes("US-ASCII")); + md5.update(COLON); + md5.update(digestUri.getBytes("US-ASCII")); + if ("auth-int".equals(qop)) + { + byte[] hEntity = null; // TODO hash of entity body + md5.update(COLON); + md5.update(hEntity); + } + byte[] ha2 = md5.digest(); + String ha2Hex = toHexString(ha2); + + // Calculate response + md5.reset(); + md5.update(ha1Hex.getBytes("US-ASCII")); + md5.update(COLON); + md5.update(nonce.getBytes("US-ASCII")); + if ("auth".equals(qop) || "auth-int".equals(qop)) + { + String nc = getNonceCount(nonce); + byte[] cnonce = generateNonce(); + md5.update(COLON); + md5.update(nc.getBytes("US-ASCII")); + md5.update(COLON); + md5.update(cnonce); + md5.update(COLON); + md5.update(qop.getBytes("US-ASCII")); + } + md5.update(COLON); + md5.update(ha2Hex.getBytes("US-ASCII")); + String digestResponse = toHexString(md5.digest()); + + String authorization = scheme + + " username=\"" + username + "\"" + + " realm=\"" + realm + "\"" + + " nonce=\"" + nonce + "\"" + + " uri=\"" + digestUri + "\"" + + " response=\"" + digestResponse + "\""; + setHeader("Authorization", authorization); + return true; + } + catch (NoSuchAlgorithmException e) + { + return false; + } + } + // Scheme not recognised + return false; + } + + Properties parseAuthParams(String text) + { + int len = text.length(); + String key = null; + CPStringBuilder buf = new CPStringBuilder(); + Properties ret = new Properties(); + boolean inQuote = false; + for (int i = 0; i < len; i++) + { + char c = text.charAt(i); + if (c == '"') + { + inQuote = !inQuote; + } + else if (c == '=' && key == null) + { + key = buf.toString().trim(); + buf.setLength(0); + } + else if (c == ' ' && !inQuote) + { + String value = unquote(buf.toString().trim()); + ret.put(key, value); + key = null; + buf.setLength(0); + } + else if (c != ',' || (i <(len - 1) && text.charAt(i + 1) != ' ')) + { + buf.append(c); + } + } + if (key != null) + { + String value = unquote(buf.toString().trim()); + ret.put(key, value); + } + return ret; + } + + String unquote(String text) + { + int len = text.length(); + if (len > 0 && text.charAt(0) == '"' && text.charAt(len - 1) == '"') + { + return text.substring(1, len - 1); + } + return text; + } + + /** + * Returns the number of times the specified nonce value has been seen. + * This always returns an 8-byte 0-padded hexadecimal string. + */ + String getNonceCount(String nonce) + { + int nc = connection.getNonceCount(nonce); + String hex = Integer.toHexString(nc); + CPStringBuilder buf = new CPStringBuilder(); + for (int i = 8 - hex.length(); i > 0; i--) + { + buf.append('0'); + } + buf.append(hex); + return buf.toString(); + } + + /** + * Client nonce value. + */ + byte[] nonce; + + /** + * Generates a new client nonce value. + */ + byte[] generateNonce() + throws IOException, NoSuchAlgorithmException + { + if (nonce == null) + { + long time = System.currentTimeMillis(); + MessageDigest md5 = MessageDigest.getInstance("MD5"); + md5.update(Long.toString(time).getBytes("US-ASCII")); + nonce = md5.digest(); + } + return nonce; + } + + String toHexString(byte[] bytes) + { + char[] ret = new char[bytes.length * 2]; + for (int i = 0, j = 0; i < bytes.length; i++) + { + int c =(int) bytes[i]; + if (c < 0) + { + c += 0x100; + } + ret[j++] = Character.forDigit(c / 0x10, 0x10); + ret[j++] = Character.forDigit(c % 0x10, 0x10); + } + return new String(ret); + } + + /** + * Parse the specified cookie list and notify the cookie manager. + */ + void handleSetCookie(String text) + { + CookieManager cookieManager = connection.getCookieManager(); + if (cookieManager == null) + { + return; + } + String name = null; + String value = null; + String comment = null; + String domain = connection.getHostName(); + String path = this.path; + int lsi = path.lastIndexOf('/'); + if (lsi != -1) + { + path = path.substring(0, lsi); + } + boolean secure = false; + Date expires = null; + + int len = text.length(); + String attr = null; + CPStringBuilder buf = new CPStringBuilder(); + boolean inQuote = false; + for (int i = 0; i <= len; i++) + { + char c =(i == len) ? '\u0000' : text.charAt(i); + if (c == '"') + { + inQuote = !inQuote; + } + else if (!inQuote) + { + if (c == '=' && attr == null) + { + attr = buf.toString().trim(); + buf.setLength(0); + } + else if (c == ';' || i == len || c == ',') + { + String val = unquote(buf.toString().trim()); + if (name == null) + { + name = attr; + value = val; + } + else if ("Comment".equalsIgnoreCase(attr)) + { + comment = val; + } + else if ("Domain".equalsIgnoreCase(attr)) + { + domain = val; + } + else if ("Path".equalsIgnoreCase(attr)) + { + path = val; + } + else if ("Secure".equalsIgnoreCase(val)) + { + secure = true; + } + else if ("Max-Age".equalsIgnoreCase(attr)) + { + int delta = Integer.parseInt(val); + Calendar cal = Calendar.getInstance(); + cal.setTimeInMillis(System.currentTimeMillis()); + cal.add(Calendar.SECOND, delta); + expires = cal.getTime(); + } + else if ("Expires".equalsIgnoreCase(attr)) + { + DateFormat dateFormat = new HTTPDateFormat(); + try + { + expires = dateFormat.parse(val); + } + catch (ParseException e) + { + // if this isn't a valid date, it may be that + // the value was returned unquoted; in that case, we + // want to continue buffering the value + buf.append(c); + continue; + } + } + attr = null; + buf.setLength(0); + // case EOL + if (i == len || c == ',') + { + Cookie cookie = new Cookie(name, value, comment, domain, + path, secure, expires); + cookieManager.setCookie(cookie); + } + if (c == ',') + { + // Reset cookie fields + name = null; + value = null; + comment = null; + domain = connection.getHostName(); + path = this.path; + if (lsi != -1) + { + path = path.substring(0, lsi); + } + secure = false; + expires = null; + } + } + else + { + buf.append(c); + } + } + else + { + buf.append(c); + } + } + } + +} diff --git a/libjava/classpath/gnu/java/net/protocol/http/RequestBodyWriter.java b/libjava/classpath/gnu/java/net/protocol/http/RequestBodyWriter.java new file mode 100644 index 000000000..aa5b78a54 --- /dev/null +++ b/libjava/classpath/gnu/java/net/protocol/http/RequestBodyWriter.java @@ -0,0 +1,68 @@ +/* RequestBodyWriter.java -- + Copyright (C) 2004 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.http; + +/** + * Callback interface for writing request body content. + * + * @author Chris Burdess (dog@gnu.org) + */ +public interface RequestBodyWriter +{ + + /** + * Returns the total number of bytes that will be written in a single pass + * by this writer. + */ + int getContentLength(); + + /** + * Initialises the writer. + * This will be called before each pass. + */ + void reset(); + + /** + * Writes body content to the supplied buffer. + * @param buffer the content buffer + * @return the number of bytes written + */ + int write(byte[] buffer); + +} diff --git a/libjava/classpath/gnu/java/net/protocol/http/Response.java b/libjava/classpath/gnu/java/net/protocol/http/Response.java new file mode 100644 index 000000000..084cf75fa --- /dev/null +++ b/libjava/classpath/gnu/java/net/protocol/http/Response.java @@ -0,0 +1,223 @@ +/* Response.java -- + Copyright (C) 2004, 2006 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.http; + +import java.io.InputStream; +import java.util.Date; + +/** + * An HTTP response. + * + * @author Chris Burdess (dog@gnu.org) + */ +public class Response +{ + + /** + * The HTTP major version of the server issuing the response. + */ + protected final int majorVersion; + + /** + * The HTTP minor version of the server issuing the response. + */ + protected final int minorVersion; + + /** + * The HTTP status code of the response. + */ + protected final int code; + + /** + * Human-readable text of the response. + */ + protected final String message; + + /** + * The response headers. + */ + protected final Headers headers; + + /** + * An InputStream that returns the body of the response. + */ + protected final InputStream body; + + /** + * Constructs a new response with the specified parameters. + */ + protected Response(int majorVersion, int minorVersion, int code, + String message, Headers headers, InputStream body) + { + this.majorVersion = majorVersion; + this.minorVersion = minorVersion; + this.code = code; + this.message = message; + this.headers = headers; + this.body = body; + } + + /** + * Returns the HTTP major version of the server issuing the response. + * @see #majorVersion + */ + public int getMajorVersion() + { + return majorVersion; + } + + /** + * Returns the HTTP minor version of the server issuing the response. + * @see #minorVersion + */ + public int getMinorVersion() + { + return minorVersion; + } + + /** + * Returns the HTTP status code of the response. + * @see #code + */ + public int getCode() + { + return code; + } + + /** + * Returns the class of the response. This is the most significant + * digit of the status code. + * <dl> + * <dt><code>1xx</code></dt> <dd>Informational response</dd> + * <dt><code>2xx</code></dt> <dd>Success</dd> + * <dt><code>3xx</code></dt> <dd>Redirection</dd> + * <dt><code>4xx</code></dt> <dd>Client error</dd> + * <dt><code>5xx</code></dt> <dd>Server error</dd> + * </dl> + */ + public int getCodeClass() + { + return code / 100; + } + + /** + * Returns the human-readable text of the response. + * @see #message + */ + public String getMessage() + { + return message; + } + + /** + * Returns the headers in the response. + */ + public Headers getHeaders() + { + return headers; + } + + /** + * Returns the header value for the specified name. + * @param name the header name + */ + public String getHeader(String name) + { + return headers.getValue(name); + } + + /** + * Returns the header value for the specified name as an integer. + * @param name the header name + */ + public int getIntHeader(String name) + { + return headers.getIntValue(name); + } + + /** + * Returns the header value for the specified name as a long. + * @param name the header name + */ + public long getLongHeader(String name) + { + return headers.getLongValue(name); + } + + /** + * Returns the header value for the specified name as a date. + * @param name the header name + */ + public Date getDateHeader(String name) + { + return headers.getDateValue(name); + } + + /** + * Tests whether this response indicates a redirection. + * + * @return <code>true</code> if, <code>false</code> otherwise. + */ + public boolean isRedirect() + { + return (code != 304 && getCodeClass() == 3); + } + + /** + * Tests whether this response indicates an error. + * Errors are the response codes <code>4xx</code> - Client error and + * <code>5xx</code> - Server error. + * + * @return <code>true</code> if, <code>false</code> otherwise. + */ + public boolean isError() + { + return (getCodeClass() == 4 || getCodeClass() == 5); + } + + /** + * Returns an InputStream that returns the body of the response. + * + * @return the body of the response + */ + public InputStream getBody() + { + return body; + } +} diff --git a/libjava/classpath/gnu/java/net/protocol/http/ResponseHeaderHandler.java b/libjava/classpath/gnu/java/net/protocol/http/ResponseHeaderHandler.java new file mode 100644 index 000000000..ca863440e --- /dev/null +++ b/libjava/classpath/gnu/java/net/protocol/http/ResponseHeaderHandler.java @@ -0,0 +1,56 @@ +/* ResponseHeaderHandler.java -- + Copyright (C) 2004 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.http; + +/** + * Callback interface for objects that wish to be notified of response + * header values. + * @see Request#setResponseHeaderHandler(String, ResponseHeaderHandler) + * + * @author Chris Burdess (dog@gnu.org) + */ +public interface ResponseHeaderHandler +{ + + /** + * Sets the value for the header associated with this handler. + */ + void setValue(String value); + +} diff --git a/libjava/classpath/gnu/java/net/protocol/http/SimpleCookieManager.java b/libjava/classpath/gnu/java/net/protocol/http/SimpleCookieManager.java new file mode 100644 index 000000000..f08204769 --- /dev/null +++ b/libjava/classpath/gnu/java/net/protocol/http/SimpleCookieManager.java @@ -0,0 +1,137 @@ +/* CookieManager.java -- + Copyright (C) 2004, 2006 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.http; + +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +/** + * A simple non-persistent cookie manager. This class can be extended to + * provide cookie persistence. + * + * @author Chris Burdess (dog@gnu.org) + */ +public class SimpleCookieManager + implements CookieManager +{ + + /** + * The cookie cache. + * This is a dictionary mapping domains to maps of cookies by name. + */ + protected Map<String, Map<String, Cookie>> cookies; + + /** + * Constructor. + */ + public SimpleCookieManager() + { + cookies = new HashMap<String, Map<String, Cookie>>(); + } + + public void setCookie(Cookie cookie) + { + String domain = cookie.getDomain(); + Map<String, Cookie> map = cookies.get(domain); + if (map == null) + { + map = new HashMap<String, Cookie>(); + cookies.put(domain, map); + } + String name = cookie.getName(); + map.put(name, cookie); // will replace a cookie of the same name + } + + public Cookie[] getCookies(String host, boolean secure, String path) + { + ArrayList<Cookie> matches = new ArrayList<Cookie>(); + Date now = new Date(); + if (Character.isLetter(host.charAt(0))) + { + int di = host.indexOf('.'); + while (di != -1) + { + addCookies(matches, host, secure, path, now); + host = host.substring(di); + di = host.indexOf('.', 1); + } + } + addCookies(matches, host, secure, path, now); + Cookie[] ret = new Cookie[matches.size()]; + matches.toArray(ret); + return ret; + } + + private void addCookies(ArrayList<Cookie> matches, String domain, + boolean secure, String path, Date now) + { + Map<String, Cookie> map = cookies.get(domain); + if (map != null) + { + ArrayList<String> expired = new ArrayList<String>(); + for (Map.Entry<String, Cookie> entry : map.entrySet()) + { + Cookie cookie = entry.getValue(); + Date expires = cookie.getExpiryDate(); + if (expires != null && expires.before(now)) + { + expired.add(entry.getKey()); + continue; + } + if (secure && !cookie.isSecure()) + { + continue; + } + if (path.startsWith(cookie.getPath())) + { + matches.add(cookie); + } + } + // Good housekeeping + for (Iterator<String> i = expired.iterator(); i.hasNext(); ) + { + map.remove(i.next()); + } + } + } + +} diff --git a/libjava/classpath/gnu/java/net/protocol/http/package.html b/libjava/classpath/gnu/java/net/protocol/http/package.html new file mode 100644 index 000000000..8cf7c1e16 --- /dev/null +++ b/libjava/classpath/gnu/java/net/protocol/http/package.html @@ -0,0 +1,76 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<!-- package.html - describes classes in gnu.java.net.protocol.http package. + Copyright (C) 2004 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. --> + +<html> +<head><title>GNU Classpath - gnu.java.net.protocol.http</title></head> + +<body> + +<p> +This package contains an HTTP/1.1 client, as described in RFC 2616. +It supports the following features: +<ul> +<li>Persistent connections</li> +<li>Basic and Digest authentication (RFC 2617)</li> +<li>HTTPS</li> +<li>HTTP proxies</li> +<li>HTTP/1.0 compatibility</li> +<li>Support for WebDAV methods and other HTTP extensions</li> +<li>Automatic decoding of the chunked transfer-coding</li> +<li>Parsing of HTTP date headers</li> +<li>Support for the 100-continue expectation</li> +</ul> +</p> + +<p> +The API is similar to the <a href='http://www.webdav.org/neon/'>neon</a> +WebDAV/HTTP library. A logical connection to the server is instantiated, +and multiple requests can be issued for this connection. Each request +has an atomic <code>dispatch</code> method which returns the response. +All I/O, authentication, etc is handled by registering callback objects +with the request prior to dispatch, which are notified during the dispatch +procedure as necessary. Simple byte-array content callbacks are supplied +which can manage any request/response content that fits in available memory. +</p> + +<p> +An URL stream handler is provided, supporting the full HttpURLConnection +specification. +</p> + +</body> diff --git a/libjava/classpath/gnu/java/net/protocol/https/Handler.java b/libjava/classpath/gnu/java/net/protocol/https/Handler.java new file mode 100644 index 000000000..dbb619905 --- /dev/null +++ b/libjava/classpath/gnu/java/net/protocol/https/Handler.java @@ -0,0 +1,75 @@ +/* Handler.java -- + Copyright (C) 2004, 2005 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.https; + +import gnu.java.net.protocol.http.HTTPConnection; +import gnu.java.net.protocol.http.HTTPURLConnection; + +import java.io.IOException; +import java.net.URL; +import java.net.URLConnection; +import java.net.URLStreamHandler; + +/** + * An HTTPS URL stream handler. + * + * @author Chris Burdess (dog@gnu.org) + */ +public class Handler + extends URLStreamHandler +{ + + /** + * Returns the default HTTPS port (443). + */ + protected int getDefaultPort() + { + return HTTPConnection.HTTPS_PORT; + } + + /** + * Returns an HTTPURLConnection for the given URL. + */ + public URLConnection openConnection(URL url) + throws IOException + { + return new HTTPURLConnection(url); + } + +} diff --git a/libjava/classpath/gnu/java/net/protocol/jar/Connection.java b/libjava/classpath/gnu/java/net/protocol/jar/Connection.java new file mode 100644 index 000000000..85d27bfc9 --- /dev/null +++ b/libjava/classpath/gnu/java/net/protocol/jar/Connection.java @@ -0,0 +1,232 @@ +/* Connection - jar url connection for java.net + Copyright (C) 1999, 2002, 2003, 2005, 2006 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.jar; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.JarURLConnection; +import java.net.MalformedURLException; +import java.net.ProtocolException; +import java.net.URL; +import java.net.URLConnection; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Hashtable; +import java.util.Locale; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.zip.ZipFile; + +/** + * This subclass of java.net.JarURLConnection models a URLConnection via + * the "jar" protocol. + * + * @author Kresten Krab Thorup (krab@gnu.org) + */ +public final class Connection extends JarURLConnection +{ + /** + * HTTP-style DateFormat, used to format the last-modified header. + * Lazy initialized since jar files are used during bootstrapping. + */ + private static SimpleDateFormat dateFormat; + + private JarFile jar_file; + private JarEntry jar_entry; + private URL jar_url; + + public static class JarFileCache + { + private static Hashtable<URL, JarFile> cache + = new Hashtable<URL, JarFile>(); + private static final int READBUFSIZE = 4*1024; + + public static synchronized JarFile get (URL url, boolean useCaches) + throws IOException + { + JarFile jf; + if (useCaches) + { + jf = cache.get (url); + if (jf != null) + return jf; + } + + if ("file".equals (url.getProtocol())) + { + String fn = url.getFile(); + fn = gnu.java.net.protocol.file.Connection.unquote(fn); + File f = new File (fn); + jf = new JarFile (f, true, ZipFile.OPEN_READ); + } + else + { + URLConnection urlconn = url.openConnection(); + InputStream is = urlconn.getInputStream(); + byte[] buf = new byte [READBUFSIZE]; + File f = File.createTempFile ("cache", "jar"); + FileOutputStream fos = new FileOutputStream (f); + int len = 0; + + while ((len = is.read (buf)) != -1) + { + fos.write (buf, 0, len); + } + + fos.close(); + // Always verify the Manifest, open read only and delete when done. + jf = new JarFile (f, true, + ZipFile.OPEN_READ | ZipFile.OPEN_DELETE); + } + + if (useCaches) + cache.put (url, jf); + + return jf; + } + } + + protected Connection(URL url) + throws MalformedURLException + { + super(url); + } + + public synchronized void connect() throws IOException + { + // Call is ignored if already connected. + if (connected) + return; + + jar_url = getJarFileURL(); + jar_file = JarFileCache.get (jar_url, useCaches); + String entry_name = getEntryName(); + + if (entry_name != null + && !entry_name.equals ("")) + { + jar_entry = (JarEntry) jar_file.getEntry (entry_name); + + if(jar_entry == null) + throw new FileNotFoundException("No entry for " + entry_name + " exists."); + } + + connected = true; + } + + public InputStream getInputStream() throws IOException + { + if (!connected) + connect(); + + if (! doInput) + throw new ProtocolException("Can't open InputStream if doInput is false"); + + return jar_file.getInputStream (jar_entry); + } + + public synchronized JarFile getJarFile() throws IOException + { + if (!connected) + connect(); + + if (! doInput) + throw new ProtocolException("Can't open JarFile if doInput is false"); + + return jar_file; + } + + public String getHeaderField(String field) + { + try + { + if (!connected) + connect(); + + if (field.equals("content-type")) + return guessContentTypeFromName(getJarEntry().getName()); + else if (field.equals("content-length")) + return Long.toString(getJarEntry().getSize()); + else if (field.equals("last-modified")) + { + // Both creating and manipulating dateFormat need synchronization. + synchronized (Connection.class) + { + if (dateFormat == null) + dateFormat = new SimpleDateFormat + ("EEE, dd MMM yyyy hh:mm:ss 'GMT'", + new Locale ("En", "Us", "Unix")); + + return dateFormat.format(new Date(getJarEntry().getTime())); + } + } + } + catch (IOException e) + { + // Fall through. + } + return null; + } + + public int getContentLength() + { + if (!connected) + return -1; + + return (int) jar_entry.getSize(); + } + + public long getLastModified() + { + if (!connected) + return -1; + + try + { + return getJarEntry().getTime(); + } + catch (IOException e) + { + return -1; + } + } +} diff --git a/libjava/classpath/gnu/java/net/protocol/jar/Handler.java b/libjava/classpath/gnu/java/net/protocol/jar/Handler.java new file mode 100644 index 000000000..7d6103e93 --- /dev/null +++ b/libjava/classpath/gnu/java/net/protocol/jar/Handler.java @@ -0,0 +1,217 @@ +/* gnu.java.net.protocol.jar.Handler - jar protocol handler for java.net + Copyright (C) 1999, 2002, 2003, 2005, 2006 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.jar; + +import gnu.java.lang.CPStringBuilder; + +import gnu.java.net.URLParseError; + +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLConnection; +import java.net.URLStreamHandler; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.StringTokenizer; + +/** + * @author Kresten Krab Thorup (krab@gnu.org) + */ +public class Handler extends URLStreamHandler +{ + /** + * A do nothing constructor + */ + public Handler() + { + } + + /** + * This method returs a new JarURLConnection for the specified URL + * + * @param url The URL to return a connection for + * + * @return The URLConnection + * + * @exception IOException If an error occurs + */ + protected URLConnection openConnection(URL url) throws IOException + { + return new Connection(url); + } + + /** + * This method overrides URLStreamHandler's for parsing url of protocol "jar" + * + * @param url The URL object in which to store the results + * @param url_string The String-ized URL to parse + * @param start The position in the string to start scanning from + * @param end The position in the string to stop scanning + */ + protected void parseURL (URL url, String url_string, int start, int end) + { + // This method does not throw an exception or return a value. Thus our + // strategy when we encounter an error in parsing is to return without + // doing anything. + String file = url.getFile(); + + if (!file.equals("")) + { //has context url + url_string = url_string.substring (start, end); + if (url_string.startsWith("/")) + { //url string is an absolute path + int idx = file.lastIndexOf ("!/"); + + if (idx < 0) + throw new URLParseError("no !/ in spec"); + + file = file.substring (0, idx + 1) + url_string; + } + else if (url_string.length() > 0) + { + int idx = file.lastIndexOf ("/"); + if (idx == -1) //context path is weird + file = "/" + url_string; + else if (idx == (file.length() - 1)) + //just concatenate two parts + file = file + url_string; + else + // according to Java API Documentation, here is a little different + // with URLStreamHandler.parseURL + // but JDK seems doesn't handle it well + file = file.substring(0, idx + 1) + url_string; + } + + setURL (url, "jar", url.getHost(), url.getPort(), flat(file), null); + return; + } + + // Bunches of things should be true. Make sure. + if (end < start) + return; + if (end - start < 2) + return; + if (start > url_string.length()) + return; + + // Skip remains of protocol + url_string = url_string.substring (start, end); + + int jar_stop; + if ((jar_stop = url_string.indexOf("!/")) < 0) + throw new URLParseError("no !/ in spec"); + + try + { + new URL(url_string.substring (0, jar_stop)); + } + catch (MalformedURLException e) + { + throw new URLParseError("invalid inner URL: " + e.getMessage()); + } + + if (!url.getProtocol().equals ("jar") ) + throw new URLParseError("unexpected protocol " + url.getProtocol()); + + setURL (url, "jar", url.getHost(), url.getPort(), url_string, null); + } + + /** + * Makes the given jar url string 'flat' by removing any . and .. from + * jar file path because ZipFile entries can only handle flat paths. + * Inside jar files '/' is always the path separator. + */ + private static String flat(String url_string) + { + int jar_stop = url_string.indexOf("!/"); + String jar_path = url_string.substring(jar_stop + 1, url_string.length()); + + if (jar_path.indexOf("/.") < 0) + return url_string; + + ArrayList<String> tokens = new ArrayList<String>(); + StringTokenizer st = new StringTokenizer(jar_path, "/"); + while (st.hasMoreTokens()) + { + String token = st.nextToken(); + if (token.equals(".")) + continue; + else if (token.equals("..")) + { + if (! tokens.isEmpty()) + tokens.remove(tokens.size() - 1); + } + else + tokens.add(token); + } + + CPStringBuilder path = new CPStringBuilder(url_string.length()); + path.append(url_string.substring(0, jar_stop + 1)); + + Iterator<String> it = tokens.iterator(); + while (it.hasNext()) + path.append('/').append(it.next()); + + return path.toString(); + } + + /** + * This method converts a Jar URL object into a String. + * + * @param url The URL object to convert + */ + protected String toExternalForm (URL url) + { + String file = url.getFile(); + String ref = url.getRef(); + + // return "jar:" + file; + // Performance!!: + // Do the concatenation manually to avoid resize StringBuffer's + // internal buffer. The length of ref is not taken into consideration + // as it's a rare path. + CPStringBuilder sb = new CPStringBuilder (file.length() + 5); + sb.append ("jar:"); + sb.append (file); + if (ref != null) + sb.append('#').append(ref); + return sb.toString(); + } +} diff --git a/libjava/classpath/gnu/java/net/protocol/jar/package.html b/libjava/classpath/gnu/java/net/protocol/jar/package.html new file mode 100644 index 000000000..dcd263d59 --- /dev/null +++ b/libjava/classpath/gnu/java/net/protocol/jar/package.html @@ -0,0 +1,46 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<!-- package.html - describes classes in gnu.java.net.protocol.jar package. + Copyright (C) 2005 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. --> + +<html> +<head><title>GNU Classpath - gnu.java.net.protocol.jar</title></head> + +<body> +<p></p> + +</body> +</html> |