2204 lines · 81.3 KB · Java
RawDownload
/* * Copyright (c) 2009, 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. */package sun.net.ftp.impl; import java.net.*;import java.io.*;import java.security.AccessController;import java.security.PrivilegedAction;import java.text.DateFormat;import java.text.ParseException;import java.text.SimpleDateFormat;import java.util.ArrayList;import java.util.Calendar;import java.util.Date;import java.util.Iterator;import java.util.List;import java.util.TimeZone;import java.util.Vector;import java.util.regex.Matcher;import java.util.regex.Pattern;import javax.net.ssl.SSLSocket;import javax.net.ssl.SSLSocketFactory;import sun.misc.BASE64Decoder;import sun.misc.BASE64Encoder;import sun.net.ftp.*;import sun.util.logging.PlatformLogger;  public class FtpClient extends sun.net.ftp.FtpClient {     private static int defaultSoTimeout;    private static int defaultConnectTimeout;    private static final PlatformLogger logger =             PlatformLogger.getLogger("sun.net.ftp.FtpClient");    private Proxy proxy;    private Socket server;    private PrintStream out;    private InputStream in;    private int readTimeout = -1;    private int connectTimeout = -1;     /* Name of encoding to use for output */    private static String encoding = "ISO8859_1";    /** remember the ftp server name because we may need it */    private InetSocketAddress serverAddr;    private boolean replyPending = false;    private boolean loggedIn = false;    private boolean useCrypto = false;    private SSLSocketFactory sslFact;    private Socket oldSocket;    /** Array of strings (usually 1 entry) for the last reply from the server. */    private Vector<String> serverResponse = new Vector<String>(1);    /** The last reply code from the ftp daemon. */    private FtpReplyCode lastReplyCode = null;    /** Welcome message from the server, if any. */    private String welcomeMsg;    private boolean passiveMode = true;    private TransferType type = TransferType.BINARY;    private long restartOffset = 0;    private long lastTransSize = -1; // -1 means 'unknown size'    private String lastFileName;    /**     * Static members used by the parser     */    private static String[] patStrings = {        // drwxr-xr-x  1 user01        ftp   512 Jan 29 23:32 prog        "([\\-ld](?:[r\\-][w\\-][x\\-]){3})\\s*\\d+ (\\w+)\\s*(\\w+)\\s*(\\d+)\\s*([A-Z][a-z][a-z]\\s*\\d+)\\s*(\\d\\d:\\d\\d)\\s*(\\p{Print}*)",        // drwxr-xr-x  1 user01        ftp   512 Jan 29 1997 prog        "([\\-ld](?:[r\\-][w\\-][x\\-]){3})\\s*\\d+ (\\w+)\\s*(\\w+)\\s*(\\d+)\\s*([A-Z][a-z][a-z]\\s*\\d+)\\s*(\\d{4})\\s*(\\p{Print}*)",        // 04/28/2006  09:12a               3,563 genBuffer.sh        "(\\d{2}/\\d{2}/\\d{4})\\s*(\\d{2}:\\d{2}[ap])\\s*((?:[0-9,]+)|(?:<DIR>))\\s*(\\p{Graph}*)",        // 01-29-97    11:32PM <DIR> prog        "(\\d{2}-\\d{2}-\\d{2})\\s*(\\d{2}:\\d{2}[AP]M)\\s*((?:[0-9,]+)|(?:<DIR>))\\s*(\\p{Graph}*)"    };    private static int[][] patternGroups = {        // 0 - file, 1 - size, 2 - date, 3 - time, 4 - year, 5 - permissions,        // 6 - user, 7 - group        {7, 4, 5, 6, 0, 1, 2, 3},        {7, 4, 5, 0, 6, 1, 2, 3},        {4, 3, 1, 2, 0, 0, 0, 0},        {4, 3, 1, 2, 0, 0, 0, 0}};    private static Pattern[] patterns;    private static Pattern linkp = Pattern.compile("(\\p{Print}+) \\-\\> (\\p{Print}+)$");    private DateFormat df = DateFormat.getDateInstance(DateFormat.MEDIUM, java.util.Locale.US);     static {        final int vals[] = {0, 0};        final String encs[] = {null};         AccessController.doPrivileged(                new PrivilegedAction<Object>() {                     public Object run() {                        vals[0] = Integer.getInteger("sun.net.client.defaultReadTimeout", 0).intValue();                        vals[1] = Integer.getInteger("sun.net.client.defaultConnectTimeout", 0).intValue();                        encs[0] = System.getProperty("file.encoding", "ISO8859_1");                        return null;                    }                });        if (vals[0] == 0) {            defaultSoTimeout = -1;        } else {            defaultSoTimeout = vals[0];        }         if (vals[1] == 0) {            defaultConnectTimeout = -1;        } else {            defaultConnectTimeout = vals[1];        }         encoding = encs[0];        try {            if (!isASCIISuperset(encoding)) {                encoding = "ISO8859_1";            }        } catch (Exception e) {            encoding = "ISO8859_1";        }         patterns = new Pattern[patStrings.length];        for (int i = 0; i < patStrings.length; i++) {            patterns[i] = Pattern.compile(patStrings[i]);        }    }     /**     * Test the named character encoding to verify that it converts ASCII     * characters correctly. We have to use an ASCII based encoding, or else     * the NetworkClients will not work correctly in EBCDIC based systems.     * However, we cannot just use ASCII or ISO8859_1 universally, because in     * Asian locales, non-ASCII characters may be embedded in otherwise     * ASCII based protocols (eg. HTTP). The specifications (RFC2616, 2398)     * are a little ambiguous in this matter. For instance, RFC2398 [part 2.1]     * says that the HTTP request URI should be escaped using a defined     * mechanism, but there is no way to specify in the escaped string what     * the original character set is. It is not correct to assume that     * UTF-8 is always used (as in URLs in HTML 4.0).  For this reason,     * until the specifications are updated to deal with this issue more     * comprehensively, and more importantly, HTTP servers are known to     * support these mechanisms, we will maintain the current behavior     * where it is possible to send non-ASCII characters in their original     * unescaped form.     */    private static boolean isASCIISuperset(String encoding) throws Exception {        String chkS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" +                "abcdefghijklmnopqrstuvwxyz-_.!~*'();/?:@&=+$,";         // Expected byte sequence for string above        byte[] chkB = {48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 65, 66, 67, 68, 69, 70, 71, 72,            73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 97, 98, 99,            100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114,            115, 116, 117, 118, 119, 120, 121, 122, 45, 95, 46, 33, 126, 42, 39, 40, 41, 59,            47, 63, 58, 64, 38, 61, 43, 36, 44};         byte[] b = chkS.getBytes(encoding);        return java.util.Arrays.equals(b, chkB);    }     private class DefaultParser implements FtpDirParser {         /**         * Possible patterns:         *         *  drwxr-xr-x  1 user01        ftp   512 Jan 29 23:32 prog         *  drwxr-xr-x  1 user01        ftp   512 Jan 29 1997 prog         *  drwxr-xr-x  1 1             1     512 Jan 29 23:32 prog         *  lrwxr-xr-x  1 user01        ftp   512 Jan 29 23:32 prog -> prog2000         *  drwxr-xr-x  1 username      ftp   512 Jan 29 23:32 prog         *  -rw-r--r--  1 jcc      staff     105009 Feb  3 15:05 test.1         *         *  01-29-97    11:32PM <DIR> prog         *  04/28/2006  09:12a               3,563 genBuffer.sh         *         *  drwxr-xr-x  folder   0       Jan 29 23:32 prog         *         *  0 DIR 01-29-97 23:32 PROG         */        private DefaultParser() {        }         public FtpDirEntry parseLine(String line) {            String fdate = null;            String fsize = null;            String time = null;            String filename = null;            String permstring = null;            String username = null;            String groupname = null;            boolean dir = false;            Calendar now = Calendar.getInstance();            int year = now.get(Calendar.YEAR);             Matcher m = null;            for (int j = 0; j < patterns.length; j++) {                m = patterns[j].matcher(line);                if (m.find()) {                    // 0 - file, 1 - size, 2 - date, 3 - time, 4 - year,                    // 5 - permissions, 6 - user, 7 - group                    filename = m.group(patternGroups[j][0]);                    fsize = m.group(patternGroups[j][1]);                    fdate = m.group(patternGroups[j][2]);                    if (patternGroups[j][4] > 0) {                        fdate += (", " + m.group(patternGroups[j][4]));                    } else if (patternGroups[j][3] > 0) {                        fdate += (", " + String.valueOf(year));                    }                    if (patternGroups[j][3] > 0) {                        time = m.group(patternGroups[j][3]);                    }                    if (patternGroups[j][5] > 0) {                        permstring = m.group(patternGroups[j][5]);                        dir = permstring.startsWith("d");                    }                    if (patternGroups[j][6] > 0) {                        username = m.group(patternGroups[j][6]);                    }                    if (patternGroups[j][7] > 0) {                        groupname = m.group(patternGroups[j][7]);                    }                    // Old DOS format                    if ("<DIR>".equals(fsize)) {                        dir = true;                        fsize = null;                    }                }            }             if (filename != null) {                Date d;                try {                    d = df.parse(fdate);                } catch (Exception e) {                    d = null;                }                if (d != null && time != null) {                    int c = time.indexOf(":");                    now.setTime(d);                    now.set(Calendar.HOUR, Integer.parseInt(time.substring(0, c)));                    now.set(Calendar.MINUTE, Integer.parseInt(time.substring(c + 1)));                    d = now.getTime();                }                // see if it's a symbolic link, i.e. the name if followed                // by a -> and a path                Matcher m2 = linkp.matcher(filename);                if (m2.find()) {                    // Keep only the name then                    filename = m2.group(1);                }                boolean[][] perms = new boolean[3][3];                for (int i = 0; i < 3; i++) {                    for (int j = 0; j < 3; j++) {                        perms[i][j] = (permstring.charAt((i * 3) + j) != '-');                    }                }                FtpDirEntry file = new FtpDirEntry(filename);                file.setUser(username).setGroup(groupname);                file.setSize(Long.parseLong(fsize)).setLastModified(d);                file.setPermissions(perms);                file.setType(dir ? FtpDirEntry.Type.DIR : (line.charAt(0) == 'l' ? FtpDirEntry.Type.LINK : FtpDirEntry.Type.FILE));                return file;            }            return null;        }    }     private class MLSxParser implements FtpDirParser {         private SimpleDateFormat df = new SimpleDateFormat("yyyyMMddhhmmss");         public FtpDirEntry parseLine(String line) {            String name = null;            int i = line.lastIndexOf(";");            if (i > 0) {                name = line.substring(i + 1).trim();                line = line.substring(0, i);            } else {                name = line.trim();                line = "";            }            FtpDirEntry file = new FtpDirEntry(name);            while (!line.isEmpty()) {                String s;                i = line.indexOf(";");                if (i > 0) {                    s = line.substring(0, i);                    line = line.substring(i + 1);                } else {                    s = line;                    line = "";                }                i = s.indexOf("=");                if (i > 0) {                    String fact = s.substring(0, i);                    String value = s.substring(i + 1);                    file.addFact(fact, value);                }            }            String s = file.getFact("Size");            if (s != null) {                file.setSize(Long.parseLong(s));            }            s = file.getFact("Modify");            if (s != null) {                Date d = null;                try {                    d = df.parse(s);                } catch (ParseException ex) {                }                if (d != null) {                    file.setLastModified(d);                }            }            s = file.getFact("Create");            if (s != null) {                Date d = null;                try {                    d = df.parse(s);                } catch (ParseException ex) {                }                if (d != null) {                    file.setCreated(d);                }            }            s = file.getFact("Type");            if (s != null) {                if (s.equalsIgnoreCase("file")) {                    file.setType(FtpDirEntry.Type.FILE);                }                if (s.equalsIgnoreCase("dir")) {                    file.setType(FtpDirEntry.Type.DIR);                }                if (s.equalsIgnoreCase("cdir")) {                    file.setType(FtpDirEntry.Type.CDIR);                }                if (s.equalsIgnoreCase("pdir")) {                    file.setType(FtpDirEntry.Type.PDIR);                }            }            return file;        }    };    private FtpDirParser parser = new DefaultParser();    private FtpDirParser mlsxParser = new MLSxParser();    private static Pattern transPat = null;     private void getTransferSize() {        lastTransSize = -1;        /**         * If it's a start of data transfer response, let's try to extract         * the size from the response string. Usually it looks like that:         *         * 150 Opening BINARY mode data connection for foo (6701 bytes).         */        String response = getLastResponseString();        if (transPat == null) {            transPat = Pattern.compile("150 Opening .*\\((\\d+) bytes\\).");        }        Matcher m = transPat.matcher(response);        if (m.find()) {            String s = m.group(1);            lastTransSize = Long.parseLong(s);        }    }     /**     * extract the created file name from the response string:     * 226 Transfer complete (unique file name:toto.txt.1).     * Usually happens when a STOU (store unique) command had been issued.     */    private void getTransferName() {        lastFileName = null;        String response = getLastResponseString();        int i = response.indexOf("unique file name:");        int e = response.lastIndexOf(')');        if (i >= 0) {            i += 17; // Length of "unique file name:"            lastFileName = response.substring(i, e);        }    }     /**     * Pulls the response from the server and returns the code as a     * number. Returns -1 on failure.     */    private int readServerResponse() throws IOException {        StringBuffer replyBuf = new StringBuffer(32);        int c;        int continuingCode = -1;        int code;        String response;         serverResponse.setSize(0);        while (true) {            while ((c = in.read()) != -1) {                if (c == '\r') {                    if ((c = in.read()) != '\n') {                        replyBuf.append('\r');                    }                }                replyBuf.append((char) c);                if (c == '\n') {                    break;                }            }            response = replyBuf.toString();            replyBuf.setLength(0);            if (logger.isLoggable(PlatformLogger.FINEST)) {                logger.finest("Server [" + serverAddr + "] --> " + response);            }             if (response.length() == 0) {                code = -1;            } else {                try {                    code = Integer.parseInt(response.substring(0, 3));                } catch (NumberFormatException e) {                    code = -1;                } catch (StringIndexOutOfBoundsException e) {                    /* this line doesn't contain a response code, so                    we just completely ignore it */                    continue;                }            }            serverResponse.addElement(response);            if (continuingCode != -1) {                /* we've seen a ###- sequence */                if (code != continuingCode ||                        (response.length() >= 4 && response.charAt(3) == '-')) {                    continue;                } else {                    /* seen the end of code sequence */                    continuingCode = -1;                    break;                }            } else if (response.length() >= 4 && response.charAt(3) == '-') {                continuingCode = code;                continue;            } else {                break;            }        }         return code;    }     /** Sends command <i>cmd</i> to the server. */    private void sendServer(String cmd) {        out.print(cmd);        if (logger.isLoggable(PlatformLogger.FINEST)) {            logger.finest("Server [" + serverAddr + "] <-- " + cmd);        }    }     /** converts the server response into a string. */    private String getResponseString() {        return serverResponse.elementAt(0);    }     /** Returns all server response strings. */    private Vector<String> getResponseStrings() {        return serverResponse;    }     /**     * Read the reply from the FTP server.     *     * @return <code>true</code> if the command was successful     * @throws IOException if an error occured     */    private boolean readReply() throws IOException {        lastReplyCode = FtpReplyCode.find(readServerResponse());         if (lastReplyCode.isPositivePreliminary()) {            replyPending = true;            return true;        }        if (lastReplyCode.isPositiveCompletion() || lastReplyCode.isPositiveIntermediate()) {

Lines 1–500 of 2204.