643 lines · 21.0 KB · Java
RawDownload
/* * Copyright (c) 1994, 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation.  Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code 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 * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ /** * FTP stream opener. */ package sun.net.www.protocol.ftp; import java.io.IOException;import java.io.InputStream;import java.io.OutputStream;import java.io.BufferedInputStream;import java.io.FilterInputStream;import java.io.FilterOutputStream;import java.io.FileNotFoundException;import java.net.URL;import java.net.SocketPermission;import java.net.UnknownHostException;import java.net.InetSocketAddress;import java.net.URI;import java.net.Proxy;import java.net.ProxySelector;import java.util.StringTokenizer;import java.util.Iterator;import java.security.Permission;import sun.net.NetworkClient;import sun.net.www.MessageHeader;import sun.net.www.MeteredStream;import sun.net.www.URLConnection;import sun.net.www.protocol.http.HttpURLConnection;import sun.net.ftp.FtpClient;import sun.net.ftp.FtpProtocolException;import sun.net.ProgressSource;import sun.net.ProgressMonitor;import sun.net.www.ParseUtil;import sun.security.action.GetPropertyAction;  /** * This class Opens an FTP input (or output) stream given a URL. * It works as a one shot FTP transfer : * <UL> * <LI>Login</LI> * <LI>Get (or Put) the file</LI> * <LI>Disconnect</LI> * </UL> * You should not have to use it directly in most cases because all will be handled * in a abstract layer. Here is an example of how to use the class : * <P> * <code>URL url = new URL("ftp://ftp.sun.com/pub/test.txt");<p> * UrlConnection con = url.openConnection();<p> * InputStream is = con.getInputStream();<p> * ...<p> * is.close();</code> * * @see sun.net.ftp.FtpClient */public class FtpURLConnection extends URLConnection {     // In case we have to use proxies, we use HttpURLConnection    HttpURLConnection http = null;    private Proxy instProxy;     InputStream is = null;    OutputStream os = null;     FtpClient ftp = null;    Permission permission;     String password;    String user;     String host;    String pathname;    String filename;    String fullpath;    int port;    static final int NONE = 0;    static final int ASCII = 1;    static final int BIN = 2;    static final int DIR = 3;    int type = NONE;    /* Redefine timeouts from java.net.URLConnection as we need -1 to mean     * not set. This is to ensure backward compatibility.     */    private int connectTimeout = NetworkClient.DEFAULT_CONNECT_TIMEOUT;;    private int readTimeout = NetworkClient.DEFAULT_READ_TIMEOUT;;     /**     * For FTP URLs we need to have a special InputStream because we     * need to close 2 sockets after we're done with it :     *  - The Data socket (for the file).     *   - The command socket (FtpClient).     * Since that's the only class that needs to see that, it is an inner class.     */    protected class FtpInputStream extends FilterInputStream {        FtpClient ftp;        FtpInputStream(FtpClient cl, InputStream fd) {            super(new BufferedInputStream(fd));            ftp = cl;        }         @Override        public void close() throws IOException {            super.close();            if (ftp != null) {                ftp.close();            }        }    }     /**     * For FTP URLs we need to have a special OutputStream because we     * need to close 2 sockets after we're done with it :     *  - The Data socket (for the file).     *   - The command socket (FtpClient).     * Since that's the only class that needs to see that, it is an inner class.     */    protected class FtpOutputStream extends FilterOutputStream {        FtpClient ftp;        FtpOutputStream(FtpClient cl, OutputStream fd) {            super(fd);            ftp = cl;        }         @Override        public void close() throws IOException {            super.close();            if (ftp != null) {                ftp.close();            }        }    }     /**     * Creates an FtpURLConnection from a URL.     *     * @param   url     The <code>URL</code> to retrieve or store.     */    public FtpURLConnection(URL url) {        this(url, null);    }     /**     * Same as FtpURLconnection(URL) with a per connection proxy specified     */    FtpURLConnection(URL url, Proxy p) {        super(url);        instProxy = p;        host = url.getHost();        port = url.getPort();        String userInfo = url.getUserInfo();         if (userInfo != null) { // get the user and password            int delimiter = userInfo.indexOf(':');            if (delimiter == -1) {                user = ParseUtil.decode(userInfo);                password = null;            } else {                user = ParseUtil.decode(userInfo.substring(0, delimiter++));                password = ParseUtil.decode(userInfo.substring(delimiter));            }        }    }     private void setTimeouts() {        if (ftp != null) {            if (connectTimeout >= 0) {                ftp.setConnectTimeout(connectTimeout);            }            if (readTimeout >= 0) {                ftp.setReadTimeout(readTimeout);            }        }    }     /**     * Connects to the FTP server and logs in.     *     * @throws  FtpLoginException if the login is unsuccessful     * @throws  FtpProtocolException if an error occurs     * @throws  UnknownHostException if trying to connect to an unknown host     */     public synchronized void connect() throws IOException {        if (connected) {            return;        }         Proxy p = null;        if (instProxy == null) { // no per connection proxy specified            /**             * Do we have to use a proxy?             */            ProxySelector sel = java.security.AccessController.doPrivileged(                    new java.security.PrivilegedAction<ProxySelector>() {                        public ProxySelector run() {                            return ProxySelector.getDefault();                        }                    });            if (sel != null) {                URI uri = sun.net.www.ParseUtil.toURI(url);                Iterator<Proxy> it = sel.select(uri).iterator();                while (it.hasNext()) {                    p = it.next();                    if (p == null || p == Proxy.NO_PROXY ||                        p.type() == Proxy.Type.SOCKS) {                        break;                    }                    if (p.type() != Proxy.Type.HTTP ||                            !(p.address() instanceof InetSocketAddress)) {                        sel.connectFailed(uri, p.address(), new IOException("Wrong proxy type"));                        continue;                    }                    // OK, we have an http proxy                    InetSocketAddress paddr = (InetSocketAddress) p.address();                    try {                        http = new HttpURLConnection(url, p);                        http.setDoInput(getDoInput());                        http.setDoOutput(getDoOutput());                        if (connectTimeout >= 0) {                            http.setConnectTimeout(connectTimeout);                        }                        if (readTimeout >= 0) {                            http.setReadTimeout(readTimeout);                        }                        http.connect();                        connected = true;                        return;                    } catch (IOException ioe) {                        sel.connectFailed(uri, paddr, ioe);                        http = null;                    }                }            }        } else { // per connection proxy specified            p = instProxy;            if (p.type() == Proxy.Type.HTTP) {                http = new HttpURLConnection(url, instProxy);                http.setDoInput(getDoInput());                http.setDoOutput(getDoOutput());                if (connectTimeout >= 0) {                    http.setConnectTimeout(connectTimeout);                }                if (readTimeout >= 0) {                    http.setReadTimeout(readTimeout);                }                http.connect();                connected = true;                return;            }        }         if (user == null) {            user = "anonymous";            String vers = java.security.AccessController.doPrivileged(                    new GetPropertyAction("java.version"));            password = java.security.AccessController.doPrivileged(                    new GetPropertyAction("ftp.protocol.user",                                          "Java" + vers + "@"));        }        try {            ftp = FtpClient.create();            if (p != null) {                ftp.setProxy(p);            }            setTimeouts();            if (port != -1) {                ftp.connect(new InetSocketAddress(host, port));            } else {                ftp.connect(new InetSocketAddress(host, FtpClient.defaultPort()));            }        } catch (UnknownHostException e) {            // Maybe do something smart here, like use a proxy like iftp.            // Just keep throwing for now.            throw e;        } catch (FtpProtocolException fe) {            throw new IOException(fe);        }        try {            ftp.login(user, password.toCharArray());        } catch (sun.net.ftp.FtpProtocolException e) {            ftp.close();            // Backward compatibility            throw new sun.net.ftp.FtpLoginException("Invalid username/password");        }        connected = true;    }      /*     * Decodes the path as per the RFC-1738 specifications.     */    private void decodePath(String path) {        int i = path.indexOf(";type=");        if (i >= 0) {            String s1 = path.substring(i + 6, path.length());            if ("i".equalsIgnoreCase(s1)) {                type = BIN;            }            if ("a".equalsIgnoreCase(s1)) {                type = ASCII;            }            if ("d".equalsIgnoreCase(s1)) {                type = DIR;            }            path = path.substring(0, i);        }        if (path != null && path.length() > 1 &&                path.charAt(0) == '/') {            path = path.substring(1);        }        if (path == null || path.length() == 0) {            path = "./";        }        if (!path.endsWith("/")) {            i = path.lastIndexOf('/');            if (i > 0) {                filename = path.substring(i + 1, path.length());                filename = ParseUtil.decode(filename);                pathname = path.substring(0, i);            } else {                filename = ParseUtil.decode(path);                pathname = null;            }        } else {            pathname = path.substring(0, path.length() - 1);            filename = null;        }        if (pathname != null) {            fullpath = pathname + "/" + (filename != null ? filename : "");        } else {            fullpath = filename;        }    }     /*     * As part of RFC-1738 it is specified that the path should be     * interpreted as a series of FTP CWD commands.     * This is because, '/' is not necessarly the directory delimiter     * on every systems.     */    private void cd(String path) throws FtpProtocolException, IOException {        if (path == null || path.isEmpty()) {            return;        }        if (path.indexOf('/') == -1) {            ftp.changeDirectory(ParseUtil.decode(path));            return;        }         StringTokenizer token = new StringTokenizer(path, "/");        while (token.hasMoreTokens()) {            ftp.changeDirectory(ParseUtil.decode(token.nextToken()));        }    }     /**     * Get the InputStream to retreive the remote file. It will issue the     * "get" (or "dir") command to the ftp server.     *     * @return  the <code>InputStream</code> to the connection.     *     * @throws  IOException if already opened for output     * @throws  FtpProtocolException if errors occur during the transfert.     */    @Override    public InputStream getInputStream() throws IOException {        if (!connected) {            connect();        }         if (http != null) {            return http.getInputStream();        }         if (os != null) {            throw new IOException("Already opened for output");        }         if (is != null) {            return is;        }         MessageHeader msgh = new MessageHeader();         boolean isAdir = false;        try {            decodePath(url.getPath());            if (filename == null || type == DIR) {                ftp.setAsciiType();                cd(pathname);                if (filename == null) {                    is = new FtpInputStream(ftp, ftp.list(null));                } else {                    is = new FtpInputStream(ftp, ftp.nameList(filename));                }            } else {                if (type == ASCII) {                    ftp.setAsciiType();                } else {                    ftp.setBinaryType();                }                cd(pathname);                is = new FtpInputStream(ftp, ftp.getFileStream(filename));            }             /* Try to get the size of the file in bytes.  If that is            successful, then create a MeteredStream. */            try {                long l = ftp.getLastTransferSize();                msgh.add("content-length", Long.toString(l));                if (l > 0) {                     // Wrap input stream with MeteredStream to ensure read() will always return -1                    // at expected length.                     // Check if URL should be metered                    boolean meteredInput = ProgressMonitor.getDefault().shouldMeterInput(url, "GET");                    ProgressSource pi = null;                     if (meteredInput) {                        pi = new ProgressSource(url, "GET", l);                        pi.beginTracking();                    }                     is = new MeteredStream(is, pi, l);                }            } catch (Exception e) {                e.printStackTrace();            /* do nothing, since all we were doing was trying to            get the size in bytes of the file */            }             if (isAdir) {                msgh.add("content-type", "text/plain");                msgh.add("access-type", "directory");            } else {                msgh.add("access-type", "file");                String ftype = guessContentTypeFromName(fullpath);                if (ftype == null && is.markSupported()) {                    ftype = guessContentTypeFromStream(is);                }                if (ftype != null) {                    msgh.add("content-type", ftype);                }            }        } catch (FileNotFoundException e) {            try {                cd(fullpath);                /* if that worked, then make a directory listing                and build an html stream with all the files in                the directory */                ftp.setAsciiType();                 is = new FtpInputStream(ftp, ftp.list(null));                msgh.add("content-type", "text/plain");                msgh.add("access-type", "directory");            } catch (IOException ex) {                throw new FileNotFoundException(fullpath);            } catch (FtpProtocolException ex2) {                throw new FileNotFoundException(fullpath);            }        } catch (FtpProtocolException ftpe) {            throw new IOException(ftpe);        }        setProperties(msgh);        return is;    }     /**     * Get the OutputStream to store the remote file. It will issue the     * "put" command to the ftp server.     *     * @return  the <code>OutputStream</code> to the connection.     *     * @throws  IOException if already opened for input or the URL

Lines 1–500 of 643.