/* * 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. Load more
/* * 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()) {