/*
* JFtp.java
*
* Created on 9. Dezember 2006, 09:20
*
* Copyright (C) 9. Dezember 2006 <reiner>
* This program 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.
*
* This program 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.
*P
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
package shared.ftp;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.RandomAccessFile;
import java.net.BindException;
import java.net.ConnectException;
import java.net.InetSocketAddress;
import java.net.NoRouteToHostException;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.regex.Pattern;
import java.util.regex.Matcher;
import shared.proglistener.ProgListener;
import shared.proglistener.ProgNotify;
import shared.files.JPathHelper;
import shared.misc.JSleepHelper;
//http://java.sun.com/j2se/javadoc/writingdoccomments/index.html#@see
/**
*
* main class for FTP
* @author reiner
* @see <a href="http://www.faqs.org/rfcs/rfc959.html">http://www.faqs.org/rfcs/rfc959.html</a><br>
* @see <a href="http://cr.yp.to/ftpparse/ftpparse.c">http://cr.yp.to/ftpparse/ftpparse.c</a> for passing the <code>LIST</code> results<br>
* @see <a href="http://www.ftp-sites.org">List with anonymous FTP sites</a><br>
* Tested with<br>
* <code>ftp.suse.com /</code><br>
* <code>ftp.openbsd.org /pub</code><br>
* <code>ftp.fastech.com</code> WarFTP server<br>
* <code>ftp.fica.com</code> Windows 5.0<br>
* <code>ftp.microsoft.com</code> Windows 5.0<br>
* <code>ftp.globalvillage.com</code> NcFTPd server<br>
* <code>ftp.genicom.com></code> WS_FTP Server<br>
* <code>ftp.lrz-muenchen.de</code><br>
* <code>ftp.adfa.edu.au</code> Icarus FTP server<br>
* <code>ftp.agso.gov.au</code> ProFTPD 1.2.6 Server<br>
* <code>ftp.kruemel.org</code> OS/2 system<br>
* <code>ftp.sun.com</code> Icarus FTP server<br>
* <code>ftp.linux-ipv6.org</code><br>
* <code>ftp.linuxfocus.org</code> freespirit FTP server<br>
* <code>ftp.mklinux.org</code> no password needed<br>
* <code>ftp.modulabbs.org</code><br>
* <code>ftp.mozilla.org</code><br>
* <code>ftp.apple.com</code><br>
* <code>ftp.april.se</code> MSDOS LIST mode<br>
* <code>ftp.cnam.fr</code><br>
*
*/
public class JFtp implements ProgListener
{
public static final int REPLY_125 = 125; // Data connection already open; transfer starting.
public static final int REPLY_150 = 150; // File status okay; about to open data connection.
public static final int REPLY_200 = 200; // command ok
public static final int REPLY_214 = 214; // Help message. On how to use the server or the meaning of a particular non-standard command. This reply is useful only to the human user.
public static final int REPLY_215 = 215; // 215 NAME system type. Where NAME is an official system name from the list in the Assigned Numbers document.
public static final int REPLY_220 = 220; // Service ready for new user
public static final int REPLY_221 = 221; // Service closing control connection. Logged out if appropriate.
public static final int REPLY_226 = 226; // Closing data connection.
public static final int REPLY_227 = 227; // Entering Passive Mode (h1,h2,h3,h4,p1,p2).
public static final int REPLY_230 = 230; // User logged in, proceed.
public static final int REPLY_250 = 250; // Requested file action okay, completed
public static final int REPLY_257 = 257; // "PATHNAME" created
public static final int REPLY_331 = 331; // User name okay, need password
public static final int REPLY_332 = 332; // Need account for login
public static final int REPLY_350 = 350; // Requested file action pending further information.
public static final char TYPE_ASCII = 'A';
public static final char TYPE_IMAGE = 'I';
public static final char MODE_STREAM = 'S';
public static final char MODE_BLOCK = 'B';
public static final char MODE_COMPRESSED = 'C';
protected static boolean m_isDebug = false;
protected boolean m_bOpen = false;
protected int m_dataPort = 0;
protected boolean m_bPassive = true;
protected Socket m_comSocket;
protected int m_waitTimeout = 1000;
protected int m_comConTimeOut = 30000;
protected int m_comTimeOut = 10000;
protected int m_dataConTimeout = 5000;
protected int m_dataTimeout = 25000;
protected int m_activeTimeOut = 5000;
// protected int m_bufSize = 512;
protected int m_bufSize = 1396;
// Used for opening the data port in active mode
protected int m_socketUsedReties = 10;
protected int m_socketUsedWait = 1000;
// true for command line programs, false for gui applications, which can use isReady()
protected boolean m_bWaitForReady = true;
protected BufferedReader m_comBufReader;
protected BufferedWriter m_comBuffWriter;
protected String[] m_tempFileNames = null;
protected ArrayList<String> m_messages = new ArrayList<String>();
protected ArrayList<String> m_lastListOutput = null;
protected boolean m_bHasLastListOutputHardlinks;
protected int m_lastReplyCode;
protected Thread m_dataThread = null;
protected final Object m_lock = new Object();
/** Creates a new instance of JFtp */
public JFtp()
{
}
public void setDebug(boolean bDebug)
{
m_isDebug = bDebug;
}
public ArrayList<String> getLastListOutput()
{
return m_lastListOutput;
}
public boolean getHasLastListOutputHardlinks()
{
return m_bHasLastListOutputHardlinks;
}
public ArrayList<String> getMessages()
{
return m_messages;
}
public boolean getWaitForReady()
{
return m_bWaitForReady;
}
public void setWaitForReady(boolean bWaitForReady)
{
m_bWaitForReady = bWaitForReady;
}
public boolean isOpen()
{
return (m_bOpen && m_comSocket.isConnected());
}
/**
* waits until the next command can be executed
*/
public void waitForReady()
{
boolean flag = true;
while(flag)
{
synchronized (m_lock)
{
if (m_dataThread == null)
flag = false;
else
{
if (m_isDebug)
System.err.println("Waiting ....");
}
}
if (flag)
JSleepHelper.sleep(300);
else break;
}
}
/**
* checks if the next command can be executed
* @return <code>true</code> if ready and <code>false</code> if not
*/
public boolean isReady()
{
Thread.yield();
boolean flag = false;
if (isOpen())
{
Thread.yield();
if (m_dataThread == null)
flag = true;
}
return flag;
}
public void setActive(int dataPort)
{
m_dataPort = dataPort;
m_bPassive = false;
}
public void setPassive()
{
m_bPassive = true;
}
// passive
public boolean open(String server, String user, String pass)
{
return open(server, 21, user, pass, null, TYPE_IMAGE, true, 0);
}
public boolean open(String server, String user, String pass, String account)
{
return open(server, 21, user, pass, account, TYPE_IMAGE, true, 0);
}
public boolean open(String server, int port, String user, String pass)
{
return open(server, port, user, pass, null, TYPE_IMAGE, true, 0);
}
public boolean open(String server, int port, String user, String pass, String account)
{
return open(server, port, user, pass, account, TYPE_IMAGE, true, 0);
}
// active
public boolean open(String server, String user, String pass, boolean bPassive)
{
return open(server, 21, user, pass, null, TYPE_IMAGE, bPassive, 0);
}
public boolean open(String server, String user, String pass, String account, boolean bPassive)
{
return open(server, 21, user, pass, account, TYPE_IMAGE, bPassive, 0);
}
public boolean open(String server, String user, String pass, boolean bPassive, int dataPort)
{
return open(server, 21, user, pass, null, TYPE_IMAGE, bPassive, dataPort);
}
public boolean open(String server, String user, String pass, String account, boolean bPassive, int dataPort)
{
return open(server, 21, user, pass, account, TYPE_IMAGE, bPassive, dataPort);
}
public boolean open(String server, int port, String user, String pass, boolean bPassive)
{
return open(server, port, user, pass, null, TYPE_IMAGE, bPassive, 0);
}
public boolean open(String server, int port, String user, String pass, String account, boolean bPassive)
{
return open(server, port, user, pass, account, TYPE_IMAGE, bPassive, 0);
}
public boolean open(String server, int port, String user, String pass, boolean bPassive, int dataPort)
{
return open(server, port, user, pass, null, TYPE_IMAGE, bPassive, dataPort);
}
public boolean open(String server, int port, String user, String pass, String account, boolean bPassive, int dataPort)
{
return open(server, port, user, pass, account, TYPE_IMAGE, bPassive, dataPort);
}
/**
* full featured open function
* @param server the server name or IP
* @param port the port number for the communication, default is <code>21</code>
* @param user the user name for login
* @param pass the password for login
* @param account the account name for login, if it is required
* @param dataType the data type, default is binary<br>
* <code><table>
* <tr><td> </td><td> </td><td> </td><td>TYPE_ASCII</td></tr>
* <tr><td> </td><td> </td><td> </td><td>TYPE_IMAGE</td></tr>
* </table></code>
* @param bPassive <code>true</code> for passive FTP else <code>false</code>
* @param dataPort if bPassive is false port number for data channel, default is <code>20</code>
*/
public boolean open(String server, int port, String user, String pass, String account, char dataType, boolean bPassive, int dataPort)
{
boolean flag = false;
if (!isOpen())
{
try
{
m_comSocket = new Socket();
m_comSocket.connect(new InetSocketAddress(server, port), m_comConTimeOut);
m_comBufReader = new BufferedReader(new InputStreamReader(m_comSocket.getInputStream()));
m_comBuffWriter = new BufferedWriter(new OutputStreamWriter(m_comSocket.getOutputStream()));
getMessage(m_comTimeOut);
if (m_lastReplyCode == REPLY_220 && doLogin(user, pass, account))
{
m_bOpen = true;
flag = setDataType(dataType);
if (bPassive) setPassive();
else setActive(dataPort);
}
}
catch (UnknownHostException ex)
{
if (m_isDebug)
System.err.println("Unable to connect to '" + server + "'");
}
catch (NoRouteToHostException ex)
{
if (m_isDebug)
System.err.println("Unable to connect to '" + server + "'; route error");
}
catch (ConnectException ex)
{
if (m_isDebug)
System.err.println("Unable to find '" + server + "'");
}
catch (SocketTimeoutException ex)
{
if (m_isDebug)
System.err.println("Timeout during connecting to '" + server + "'");
}
catch (IOException ex)
{
if (m_isDebug)
System.err.println("Error during connect to '" + server + "'");
}
}
return flag;
}
/**
* close the FTP connection, all data transfers must be completed or aborted before
* @return <code>true</code> if successful or <code>false</code> if an error occured
*/
public boolean close()
{
boolean flag = false;
synchronized (m_lock)
{
if (isReady())
{
try
{
m_comBuffWriter.write("QUIT\r\n");
m_comBuffWriter.flush();
getMessage(m_comTimeOut);
if (m_lastReplyCode == REPLY_221)
{
m_comSocket.close();
m_bOpen = false;
flag = true;
}
}
catch(IOException ex)
{}
}
}
return flag;
}
public boolean ping()
{
return noop();
}
public boolean noop()
{
boolean flag = false;
synchronized (m_lock)
{
if (isReady())
{
try
{
m_comBuffWriter.write("NOOP\r\n");
m_comBuffWriter.flush();
getMessage(m_comTimeOut);
if (m_lastReplyCode == REPLY_200)
flag = true;
}
catch(IOException ex)
{}
}
}
return flag;
}
public String getCurrentDirectory()
{
String str = null;
synchronized (m_lock)
{
if (isReady())
{
try
{
String hstr;
m_comBuffWriter.write("PWD\r\n");
m_comBuffWriter.flush();
hstr = getMessage(m_comTimeOut);
if (m_lastReplyCode == REPLY_257 && hstr != null)
{
Pattern pat = Pattern.compile("^\\d{3}[ \t]+\"(.+)\".*");
Matcher m = pat.matcher(hstr);
if (m.find())
{
str = m.group(1);
if (m_isDebug)
System.err.println(str);
}
}
}
catch(IOException ex)
{}
}
}
return str;
}
/**
* implements the <code>CWD</code> command
* @param str the directory
* @return true or false
*/
public boolean setCurrentDirectory(String str)
{
boolean flag = false;
synchronized (m_lock)
{
if (isReady())
{
try
{
String hstr;
m_comBuffWriter.write("CWD " + str + "\r\n");
m_comBuffWriter.flush();
hstr = getMessage(m_comTimeOut);
if (m_lastReplyCode == REPLY_250)
flag = true;
}
catch(IOException ex)
{}
}
}
return flag;
}
public boolean changeToParentDirectory()
{
boolean flag = false;
synchronized (m_lock)
{
if (isReady())
{
try
{
String hstr;
m_comBuffWriter.write("CDUP\r\n");
m_comBuffWriter.flush();
hstr = getMessage(m_comTimeOut, 1);
if (m_lastReplyCode == REPLY_250)
flag = true;
}
catch(IOException ex)
{}
}
}
return flag;
}
public boolean getStatus(String str)
{
boolean flag = false;
synchronized (m_lock)
{
if (isReady())
{
try
{
m_comBuffWriter.write("STAT " + str + "\r\n");
m_comBuffWriter.flush();
getMessage(m_comTimeOut);
if (m_lastReplyCode == REPLY_250)
flag = true;
}
catch(IOException ex)
{}
}
}
return flag;
}
public boolean system()
{
boolean flag = false;
synchronized (m_lock)
{
if (isReady())
{
try
{
m_comBuffWriter.write("SYST\r\n");
m_comBuffWriter.flush();
getMessage(m_comTimeOut);
if (m_lastReplyCode == REPLY_215)
flag = true;
}
catch(IOException ex)
{}
}
}
return flag;
}
public boolean site(String str)
{
boolean flag = false;
synchronized (m_lock)
{
if (isReady())
{
try
{
m_comBuffWriter.write("SITE " + str + "\r\n");
m_comBuffWriter.flush();
getMessage(m_comTimeOut);
if (m_lastReplyCode == REPLY_200)
flag = true;
}
catch(IOException ex)
{}
}
}
return flag;
}
public boolean help(String str)
{
boolean flag = false;
synchronized (m_lock)
{
if (isReady())
{
try
{
m_comBuffWriter.write("HELP " + str + "\r\n");
m_comBuffWriter.flush();
getMessage(m_comTimeOut);
if (m_lastReplyCode == REPLY_214)
flag = true;
}
catch(IOException ex)
{}
}
}
return flag;
}
protected boolean abortInternal()
{
boolean flag = false;
try
{
m_comBuffWriter.write("ABOR\r\n");
m_comBuffWriter.flush();
getMessage(m_comTimeOut);
if (m_lastReplyCode == REPLY_226)
flag = true;
}
catch(IOException ex)
{}
return flag;
}
public boolean abort()
{
boolean flag = false;
synchronized (m_lock)
{
if (isReady())
flag = abortInternal();
}
return flag;
}
protected boolean renameInternal(String source, String target)
{
boolean flag = false;
try
{
m_comBuffWriter.write("RNFR " + source + "\r\n");
m_comBuffWriter.flush();
getMessage(m_comTimeOut);
if (m_lastReplyCode == REPLY_350)
{
m_comBuffWriter.write("RNTO " + target + "\r\n");
m_comBuffWriter.flush();
getMessage(m_comTimeOut);
if (m_lastReplyCode == REPLY_250)
flag = true;
}
}
catch(IOException ex)
{}
return flag;
}
public boolean rename(String source, String target)
{
boolean flag = false;
synchronized (m_lock)
{
if (isReady())
flag = renameInternal(source, target);
}
return flag;
}
public boolean deleteFile(String str)
{
boolean flag = false;
synchronized (m_lock)
{
if (isReady())
{
try
{
m_comBuffWriter.write("DELE " + str + "\r\n");
m_comBuffWriter.flush();
getMessage(m_comTimeOut);
if (m_lastReplyCode == REPLY_250)
flag = true;
}
catch(IOException ex)
{}
}
}
return flag;
}
public boolean makeDirectory(String str)
{
boolean flag = false;
synchronized (m_lock)
{
if (isReady())
{
try
{
m_comBuffWriter.write("MKD " + str + "\r\n");
m_comBuffWriter.flush();
getMessage(m_comTimeOut);
if (m_lastReplyCode == REPLY_257)
flag = true;
}
catch(IOException ex)
{}
}
}
return flag;
}
public boolean deleteDirectory(String str)
{
boolean flag = false;
synchronized (m_lock)
{
if (isReady())
{
try
{
m_comBuffWriter.write("RMD " + str + "\r\n");
m_comBuffWriter.flush();
getMessage(m_comTimeOut);
if (m_lastReplyCode == REPLY_250)
flag = true;
}
catch(IOException ex)
{}
}
}
return flag;
}
public boolean listDirectory(String str)
{
synchronized (m_lock)
{}
boolean flag = false;
if (isReady())
{
Socket socket = null;
ServerSocket serverSocket = null;
if (m_bPassive)
socket = doPassive();
else serverSocket = doActive();
if ((m_bPassive && socket != null) || (!m_bPassive && serverSocket != null))
{
final Socket _socket = socket;
final ServerSocket _serverSocket = serverSocket;
final JFtp ftp = this;
final int waitTimeout = m_waitTimeout;
final boolean [] bServerSocketWaits = new boolean[1];
bServerSocketWaits[0] = false;
m_dataThread = new Thread()
{
public void run()
{
synchronized (ftp.m_lock)
{
ArrayList<String> results = new ArrayList<String>();
boolean bAbort = false;
Socket socket = _socket;
boolean flag = false;
try
{
if (_serverSocket != null)
{
bServerSocketWaits[0] = true;
socket = activeWaitForConnection(_serverSocket);
}
ftp.notify(new FtpNotify(FtpNotify.TYPE_LIST, ProgNotify.START));
BufferedReader bufReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String str;
while (!isInterrupted() && !bAbort && (str = bufReader.readLine()) != null)
{
results.add(str);
if (!ftp.notify(new FtpNotify<String>(FtpNotify.TYPE_LIST, ProgNotify.RUN, str)))
{
ftp.abort();
bAbort = true;
}
}
flag = true;
}
catch(IOException ex)
{}
closeSocket(socket);
if (flag)
ftp.notify(new FtpNotify<ArrayList<String> >(FtpNotify.TYPE_LIST, bAbort ? ProgNotify.ABORT : ProgNotify.END, results));
else
ftp.notify(new FtpNotify(FtpNotify.TYPE_LIST, ProgNotify.ERROR));
}
}
};
if (!m_bPassive)
{
m_dataThread.start();
int count = 100;
while (!bServerSocketWaits[0] && count-- > 0)
JSleepHelper.sleep(50);
Thread.yield();
}
try
{
flag = false;
m_comBuffWriter.write("LIST -al " + (str != null ? str : ".") + "\r\n");
m_comBuffWriter.flush();
getMessage(m_comTimeOut);
if (m_lastReplyCode == REPLY_250 || m_lastReplyCode == REPLY_125 || m_lastReplyCode == REPLY_150)
{
if (m_bPassive)
m_dataThread.start();
flag = true;
}
if (!flag)
{
if (!m_bPassive)
m_dataThread.interrupt();
closeSocket(socket);
closeSocket(serverSocket);
}
}
catch(IOException ex)
{
if (!m_bPassive)
m_dataThread.interrupt();
closeSocket(socket);
closeSocket(serverSocket);
}
}
}
if (!flag)
m_dataThread = null;
else if (m_bWaitForReady)
waitForReady();
return flag;
}
public boolean getFile(String source, String target)
{
return getFile(source, target, false, 0);
}
public boolean getFile(String source, String target, boolean bTemp)
{
return getFile(source, target, bTemp, 0);
}
public boolean getFile(String source, String target, long startPos)
{
return getFile(source, target, false, startPos);
}
public boolean getFile(String source, final String target, final boolean bTemp, final long startPos)
{
synchronized (m_lock)
{}
boolean flag = false;
if (isReady())
{
Socket socket = null;
ServerSocket serverSocket = null;
if (m_bPassive)
socket = doPassive();
else serverSocket = doActive();
if ((m_bPassive && socket != null) || (!m_bPassive && serverSocket != null))
{
final Socket _socket = socket;
final ServerSocket _serverSocket = serverSocket;
final JFtp ftp = this;
final int waitTimeout = m_waitTimeout;
final boolean [] bServerSocketWaits = new boolean[1];
bServerSocketWaits[0] = false;
m_dataThread = new Thread()
{
public void run()
{
synchronized (ftp.m_lock)
{
FileOutputStream fileOutput = null;
long size = 0;
boolean bAbort = false;
File tempFile = null;
Socket socket = _socket;
boolean flag = false;
try
{
if (_serverSocket != null)
{
bServerSocketWaits[0] = true;
socket = activeWaitForConnection(_serverSocket);
}
if (bTemp)
{
tempFile = File.createTempFile("ftp", "", new File(JPathHelper.getFolder(target)));
fileOutput = new FileOutputStream(tempFile);
}
else
{
File file = new File(target);
if (file.exists())
{
if (startPos > 0)
{
RandomAccessFile rf = new RandomAccessFile(file, "rw");
if (rf.getFilePointer() > startPos)
rf.setLength(startPos);
rf.close();
fileOutput = new FileOutputStream(target, true);
}
}
if (fileOutput == null)
fileOutput = new FileOutputStream(target);
}
ftp.notify(new FtpNotify<String>(FtpNotify.TYPE_RETR, ProgNotify.START, bTemp ? tempFile.getPath() : target));
InputStream inputStream = socket.getInputStream();
int readed;
byte[] buf = new byte[m_bufSize];
while (!isInterrupted() && !bAbort && (readed = inputStream.read(buf)) > 0)
{
fileOutput.write(buf, 0, readed);
size += readed;
if (!ftp.notify(new FtpNotify<Long>(FtpNotify.TYPE_RETR, ProgNotify.RUN, new Long(readed))))
{
ftp.abortInternal();
bAbort = true;
}
}
flag = true;
}
catch(IOException ex)
{}
closeSocket(socket);
if (fileOutput != null)
{
try
{fileOutput.close();}
catch (IOException ex)
{}
}
if (flag && !bAbort && tempFile != null)
tempFile.renameTo(new File(target));
if (flag)
ftp.notify(new FtpNotify<Long>(FtpNotify.TYPE_RETR, bAbort ? ProgNotify.ABORT : ProgNotify.END, new Long(size)));
else
ftp.notify(new FtpNotify<String>(FtpNotify.TYPE_RETR, ProgNotify.ERROR, bTemp ? tempFile.getPath() : null));
}
}
};
try
{
flag = false;
if (startPos > 0)
{
m_comBuffWriter.write("REST " + Long.toString(startPos) + "\r\n");
m_comBuffWriter.flush();
getMessage(m_comTimeOut);
if (m_lastReplyCode == REPLY_350)
flag = true;
}
else flag = true;
if (flag)
{
if (!m_bPassive)
{
m_dataThread.start();
// wait to ensure that the accept command from the server socket is called
int count = 100;
while (!bServerSocketWaits[0] && count-- > 0)
JSleepHelper.sleep(50);
Thread.yield();
}
flag = false;
m_comBuffWriter.write("RETR " + source + "\r\n");
m_comBuffWriter.flush();
getMessage(m_comTimeOut);
if (m_lastReplyCode == REPLY_150 || m_lastReplyCode == REPLY_125)
{
if (m_bPassive)
m_dataThread.start();
flag = true;
}
}
if (!flag)
{
if (!m_bPassive)
m_dataThread.interrupt();
closeSocket(socket);
closeSocket(serverSocket);
}
}
catch(IOException ex)
{
if (!m_bPassive)
m_dataThread.interrupt();
closeSocket(socket);
closeSocket(serverSocket);
}
}
}
if (!flag)
m_dataThread = null;
else if (m_bWaitForReady)
waitForReady();
return flag;
}
public boolean sendFile(final String source, String target)
{
return sendFile(source, target, false, 0);
}
public boolean sendFile(final String source, String target, boolean bTemp)
{
return sendFile(source, target, bTemp, 0);
}
public boolean sendFile(final String source, String target, long startPos)
{
return sendFile(source, target, false, startPos);
}
public boolean sendFile(final String source, String target, final boolean bTemp, final long startPos)
{
synchronized (m_lock)
{}
m_tempFileNames = null;
boolean flag = false;
if (isReady())
{
Socket socket = null;
ServerSocket serverSocket = null;
if (m_bPassive)
socket = doPassive();
else serverSocket = doActive();
final String[] tempFileNameArray = new String[1];
if ((m_bPassive && socket != null) || (!m_bPassive && serverSocket != null))
{
final Socket _socket = socket;
final ServerSocket _serverSocket = serverSocket;
final JFtp ftp = this;
final int waitTimeout = m_waitTimeout;
final String _target = target;
final boolean [] bServerSocketWaits = new boolean[1];
bServerSocketWaits[0] = false;
m_dataThread = new Thread()
{
public void run()
{
synchronized (ftp.m_lock)
{
boolean flag = false;
long size = 0;
boolean bAbort = false;
FileInputStream fileInput = null;
Socket socket = _socket;
try
{
if (_serverSocket != null)
{
bServerSocketWaits[0] = true;
socket = activeWaitForConnection(_serverSocket);
}
fileInput = new FileInputStream(source);
if (startPos > 0)
fileInput.skip(startPos);
ftp.notify(new FtpNotify<String>(FtpNotify.TYPE_STOR, ProgNotify.START, bTemp ? m_tempFileNames[0] : _target));
OutputStream outputStream = socket.getOutputStream();
int readed;
byte[] buf = new byte[m_bufSize];
while (!bAbort && (readed = fileInput.read(buf)) > 0)
{
outputStream.write(buf, 0, readed);
size += readed;
if (!ftp.notify(new FtpNotify<Long>(FtpNotify.TYPE_STOR, ProgNotify.RUN, new Long(readed))))
{
ftp.abort();
bAbort = true;
}
}
flag = true;
}
catch(IOException ex)
{}
try
{fileInput.close();}
catch (IOException ex)
{}
closeSocket(socket);
if (flag)
ftp.notify(new FtpNotify<Long>(FtpNotify.TYPE_STOR, bAbort ? ProgNotify.ABORT : ProgNotify.END, new Long(size)));
else
ftp.notify(new FtpNotify<String>(FtpNotify.TYPE_STOR, ProgNotify.ERROR, bTemp ? m_tempFileNames[0] : null));
}
}
};
try
{
if (startPos > 0)
{
m_comBuffWriter.write("REST " + Long.toString(startPos) + "\r\n");
m_comBuffWriter.flush();
getMessage(m_comTimeOut);
if (m_lastReplyCode == REPLY_350)
flag = true;
}
else flag = true;
if (flag)
{
flag = false;
if (!m_bPassive)
{
m_dataThread.start();
// wait to ensure that the accept command from the server socket is called
int count = 100;
while (!bServerSocketWaits[0] && count-- > 0)
JSleepHelper.sleep(50);
Thread.yield();
}
if (bTemp)
m_comBuffWriter.write("STOU \r\n");
else
m_comBuffWriter.write("STOR " + target + "\r\n");
m_comBuffWriter.flush();
String str = getMessage(m_comTimeOut);
if (m_lastReplyCode == REPLY_150)
{
if (bTemp)
{
Pattern pat = Pattern.compile("150[- ]FILE: (.+)");
Matcher m = pat.matcher(str);
if (m.find())
{
m_tempFileNames = new String[2];
m_tempFileNames[0] = m.group(1);
m_tempFileNames[1] = target;
flag = true;
}
}
else flag = true;
if (m_bPassive && flag)
m_dataThread.start();
}
}
if (!flag)
{
if (m_bPassive)
m_dataThread.interrupt();
closeSocket(socket);
closeSocket(serverSocket);
}
}
catch(IOException ex)
{
if (m_bPassive)
m_dataThread.interrupt();
closeSocket(socket);
closeSocket(serverSocket);
}
}
}
if (!flag)
m_dataThread = null;
else if (m_bWaitForReady)
waitForReady();
return flag;
}
public boolean transferFile(final String source, String target, JFtp ftp2, final boolean bTemp)
{
boolean flag = false;
if ((!m_bPassive && ftp2.m_bPassive) || (m_bPassive && !ftp2.m_bPassive))
{
synchronized (m_lock)
{}
synchronized (ftp2.m_lock)
{}
try
{
if (isReady() && ftp2.isReady())
{
if (ftp2.m_bPassive)
{
InetSocketAddress addr = ftp2.doPassiveSimple();
flag = doActiveSimple(addr);
}
else
{
InetSocketAddress addr = doPassiveSimple();
flag = ftp2.doActiveSimple(addr);
}
if (flag)
{
flag = false;
if (bTemp)
ftp2.m_comBuffWriter.write("STOU \r\n");
else
ftp2.m_comBuffWriter.write("STOR " + target + "\r\n");
ftp2.m_comBuffWriter.flush();
String str = ftp2.getMessage(m_comTimeOut), str2;
if (ftp2.m_lastReplyCode == REPLY_150)
{
if (bTemp)
{
Pattern pat = Pattern.compile("150[- ]FILE: (.+)");
Matcher m = pat.matcher(str);
if (m.find())
flag = true;
}
else flag = true;
if (flag)
{
flag = false;
m_comBuffWriter.write("RETR " + source + "\r\n");
m_comBuffWriter.flush();
getMessage(m_comTimeOut);
if (m_lastReplyCode == REPLY_150 || m_lastReplyCode == REPLY_125)
flag = true;
}
if (flag)
{
flag = false;
notify(new FtpNotify<Long>(FtpNotify.TYPE_TRANSFER, ProgNotify.START));
str = getMessage(0, 1);
str2 = ftp2.getMessage(0, 1);
if ((m_lastReplyCode == REPLY_150 || m_lastReplyCode == REPLY_125) && ftp2.m_lastReplyCode == REPLY_150)
{
if (bTemp)
{
}
else flag = true;
}
notify(new FtpNotify<Long>(FtpNotify.TYPE_TRANSFER, flag ? ProgNotify.END : ProgNotify.ERROR));
}
}
}
}
}
catch(IOException ex)
{}
}
return flag;
}
protected Socket doPassive()
{
Socket socket = null;
if (isReady())
{
try
{
InetSocketAddress addr = doPassiveSimple();
if (addr != null)
{
socket = new Socket();
socket.connect(addr, m_dataConTimeout);
socket.setSoTimeout(m_dataTimeout);
}
}
catch(IOException ex)
{}
}
return socket;
}
protected InetSocketAddress doPassiveSimple()
{
InetSocketAddress addr = null;
if (isReady())
{
try
{
m_comBuffWriter.write("PASV\r\n");
m_comBuffWriter.flush();
String str = getMessage(m_comTimeOut);
if (m_lastReplyCode == REPLY_227)
{
Pattern pat = Pattern.compile("\\d{3}.*\\((\\d{1,3}),(\\d{1,3}),(\\d{1,3}),(\\d{1,3}),(\\d{1,3}),(\\d{1,3})\\)");
Matcher m = pat.matcher(str);
if (m.find())
addr = new InetSocketAddress(m.group(1) + "." + m.group(2) + "." + m.group(3) + "." + m.group(4), Integer.parseInt(m.group(5))*256 + Integer.parseInt(m.group(6)));
}
}
catch(IOException ex)
{}
}
return addr;
}
protected Socket activeWaitForConnection(ServerSocket serverSocket) throws IOException
{
Socket socket = null;
if (serverSocket != null)
{
socket = serverSocket.accept();
socket.setSoTimeout(m_dataTimeout);
closeSocket(serverSocket);
}
return socket;
}
/**
* prepare active FTP data transfer, so we listen on the data port and wait that the server connect to us
*/
protected ServerSocket doActive()
{
ServerSocket socket = null;
int count = 0;
try
{
InetSocketAddress addr = new InetSocketAddress("0.0.0.0", m_dataPort);
while(socket == null && count++ < m_socketUsedReties)
{
try
{
socket = doInternalActive(addr);
}
catch(BindException ex)
{
JSleepHelper.sleep(m_socketUsedWait);
}
}
}
catch(IOException ex)
{}
return socket;
}
protected ServerSocket doInternalActive(InetSocketAddress addr) throws IOException
{
ServerSocket socket = null;
if (isReady())
{
String str = addr.getAddress().getHostAddress();
int port = addr.getPort();
if (str.equals("0.0.0.0"))
str = m_comSocket.getLocalAddress().getHostAddress();
socket = new ServerSocket();
socket.setReuseAddress(true);
if (port > 0)
socket.bind(new InetSocketAddress("0.0.0.0", port));
else
socket.bind(null);
socket.setSoTimeout(m_activeTimeOut);
port = socket.getLocalPort();
str = str.replace('.', ',');
str += ',';
str += Integer.toString(port/256);
str += ',';
str += Integer.toString(port%256);
m_comBuffWriter.write("PORT " + str + "\r\n");
m_comBuffWriter.flush();
getMessage(m_comTimeOut);
if (m_lastReplyCode != REPLY_200)
{
socket.close();
socket = null;
}
}
return socket;
}
protected boolean doActiveSimple(InetSocketAddress addr)
{
boolean flag = false;
if (addr != null && isReady())
{
try
{
String str = addr.getAddress().getHostAddress();
int port = addr.getPort();
str = str.replace('.', ',');
str += ',';
str += Integer.toString(port/256);
str += ',';
str += Integer.toString(port%256);
m_comBuffWriter.write("PORT " + str + "\r\n");
m_comBuffWriter.flush();
getMessage(m_comTimeOut);
flag = true;
if (m_lastReplyCode != REPLY_200)
flag = false;
}
catch(IOException ex)
{}
}
return flag;
}
protected boolean doLogin(String user, String pass, String account)
{
boolean flag = false;
try
{
m_comBuffWriter.write("USER "+ user + "\r\n");
m_comBuffWriter.flush();
getMessage(m_comTimeOut);
if (m_lastReplyCode == REPLY_331)
{
m_comBuffWriter.write("PASS "+ pass + "\r\n");
m_comBuffWriter.flush();
getMessage(m_comTimeOut);
if (m_lastReplyCode == REPLY_230)
flag = true;
else if (m_lastReplyCode == REPLY_332)
{
m_comBuffWriter.write("ACCT "+ account + "\r\n");
m_comBuffWriter.flush();
getMessage(m_comTimeOut);
if (m_lastReplyCode == REPLY_230)
flag = true;
}
}
else if (m_lastReplyCode == REPLY_230)
flag = true;
}
catch(IOException ex)
{}
return flag;
}
public boolean setDataType(char type)
{
boolean flag = false;
try
{
m_comBuffWriter.write("TYPE "+ type + "\r\n");
m_comBuffWriter.flush();
getMessage(m_comTimeOut);
if (m_lastReplyCode == REPLY_200)
flag = true;
}
catch(IOException ex)
{}
return flag;
}
public boolean setTransferMode(char mode)
{
boolean flag = false;
try
{
m_comBuffWriter.write("MODE "+ mode + "\r\n");
m_comBuffWriter.flush();
getMessage(m_comTimeOut);
if (m_lastReplyCode == REPLY_200)
flag = true;
}
catch(IOException ex)
{}
return flag;
}
protected String getMessage(int timeout)
{
return getMessage(timeout, 1);
}
protected String getMessage(int timeout, int lines)
{
String str = null;
String hstr = null;
try
{
m_comSocket.setSoTimeout(timeout);
int count = lines > 0 ? lines : -1;
Boolean bMultiLine = false;
while (bMultiLine || count < 0 || count-- > 0)
{
if ((hstr = m_comBufReader.readLine()) == null)
break;
else
{
if (str == null) str = hstr;
int status = getReplyCode(hstr, bMultiLine);
if (status > 0)
{
if (bMultiLine && status == m_lastReplyCode)
{
bMultiLine = false;
count = 0;
}
else m_lastReplyCode = status;
}
else if (status < 0)
{
m_lastReplyCode = -status;
bMultiLine = true;
}
synchronized(m_messages)
{
m_messages.add(hstr);
if (m_isDebug)
System.err.println(hstr);
}
}
}
}
catch (SocketException ex)
{}
catch (IOException ex)
{}
return str;
}
protected int getReplyCode(String str, Boolean bMultiline)
{
int status = 0;
if (str != null)
{
Pattern pat = Pattern.compile("^(\\d{3})(-|\\s+).*");
Matcher m = pat.matcher(str);
if (m.find())
{
status = Integer.parseInt(m.group(1));
if (m.group(2).equals("-"))
status = -status;
}
}
return status;
}
@SuppressWarnings("unchecked")
public boolean notify(ProgNotify progNotify)
{
boolean flag = true;
boolean bTransferOk = false;
FtpNotify ftpNotify = (FtpNotify)progNotify;
if (ftpNotify.getReason() == ProgNotify.END)
{
getMessage(m_comTimeOut);
if (m_lastReplyCode == REPLY_226)
bTransferOk = true;
m_dataThread = null;
}
else if (ftpNotify.getReason() == ProgNotify.ABORT || ftpNotify.getReason() == ProgNotify.ERROR)
{
m_dataThread = null;
}
if (ftpNotify.getType () == FtpNotify.TYPE_LIST)
{
if (ftpNotify.getReason() == ProgNotify.START)
{
if (m_isDebug)
System.err.println("List start");
}
else if (ftpNotify.getReason() == ProgNotify.RUN)
{
FtpNotify<String> ftpNotify2 = (FtpNotify<String>)ftpNotify;
String fileName = ftpNotify2.getData();
if (m_isDebug)
System.err.println("List :" + fileName);
}
else if (ftpNotify.getReason() == ProgNotify.END)
{
FtpNotify<ArrayList<String> > ftpNotify2 = (FtpNotify<ArrayList<String> >)ftpNotify;
ArrayList<String> list = ftpNotify2.getData();
if (m_isDebug)
{
System.err.println("List results:");
for (String str : list)
{
System.err.println(str);
parseListLine(str);
}
}
m_lastListOutput = list;
}
else if (ftpNotify.getReason() == ProgNotify.ERROR)
{
if (m_isDebug)
System.err.println("List error");
}
else if (ftpNotify.getReason() == ProgNotify.ABORT)
{
FtpNotify<ArrayList<String> > ftpNotify2 = (FtpNotify<ArrayList<String> >)ftpNotify;
ArrayList<String> list = ftpNotify2.getData();
if (m_isDebug)
{
System.err.println("List abort");
for (String str : list)
System.err.println(str);
}
}
}
else if (ftpNotify.getType () == FtpNotify.TYPE_RETR)
{
if (ftpNotify.getReason() == ProgNotify.START)
{
FtpNotify<String> ftpNotify2 = (FtpNotify<String>)ftpNotify;
String fileName = ftpNotify2.getData();
if (m_isDebug)
System.err.println("Retr start of '" + fileName + "'");
}
else if (ftpNotify.getReason() == ProgNotify.RUN)
{
if (m_isDebug)
System.err.println("Retr :" + ftpNotify.getData().toString());
}
else if (ftpNotify.getReason() == ProgNotify.END)
{
FtpNotify<Long> ftpNotify2 = (FtpNotify<Long>)ftpNotify;
Long fileSize = ftpNotify2.getData();
if (m_isDebug)
System.err.println("Retr end fileSize = " + fileSize);
}
else if (ftpNotify.getReason() == ProgNotify.ERROR)
{
FtpNotify<String> ftpNotify2 = (FtpNotify<String>)ftpNotify;
String tempFileName = ftpNotify2.getData();
if (m_isDebug)
{
if (tempFileName != null)
System.err.println("Retr error tempfile = '" + tempFileName + "'");
else
System.err.println("Retr error");
}
}
else if (ftpNotify.getReason() == ProgNotify.ABORT)
{
FtpNotify<Long> ftpNotify2 = (FtpNotify<Long>)ftpNotify;
Long fileSize = ftpNotify2.getData();
if (m_isDebug)
System.err.println("Retr abort fileSize = " + fileSize);
}
}
else if (ftpNotify.getType () == FtpNotify.TYPE_STOR)
{
if (ftpNotify.getReason() == ProgNotify.START)
{
FtpNotify<String> ftpNotify2 = (FtpNotify<String>)ftpNotify;
String fileName = ftpNotify2.getData();
if (m_isDebug)
System.err.println("Stor start of '" + fileName + "'");
}
else if (ftpNotify.getReason() == ProgNotify.RUN)
{
if (m_isDebug)
System.err.println("Stor :" + ftpNotify.getData().toString());
// return false;
}
else if (ftpNotify.getReason() == ProgNotify.END)
{
if (m_tempFileNames != null)
renameInternal(m_tempFileNames[0], m_tempFileNames[1]);
FtpNotify<Long> ftpNotify2 = (FtpNotify<Long>)ftpNotify;
Long fileSize = ftpNotify2.getData();
if (m_isDebug)
System.err.println("Stor end fileSize = " + fileSize);
}
else if (ftpNotify.getReason() == ProgNotify.ERROR)
{
FtpNotify<String> ftpNotify2 = (FtpNotify<String>)ftpNotify;
String tempFileName = ftpNotify2.getData();
if (m_isDebug)
{
if (tempFileName != null)
System.err.println("Stor error tempfile = '" + tempFileName + "'");
else
System.err.println("Stor error");
}
}
else if (ftpNotify.getReason() == ProgNotify.ABORT)
{
FtpNotify<Long> ftpNotify2 = (FtpNotify<Long>)ftpNotify;
Long fileSize = ftpNotify2.getData();
if (m_isDebug)
System.err.println("Stor abort fileSize = " + fileSize);
}
}
return flag;
}
/**
* @param str line to parse
* @return <code>JFtpFile</code> object if format was recognized, else <code>null</code>
*/
public JFtpFile parseListLine(String str)
{
JFtpFile ftpFile = null;
// Linux style
/* ftp.suse.com
drwxr-xr-x 2 root root 4096 May 26 2004 bin
drwxr-xr-x 2 root root 4096 May 26 2004 dev
drwxr-xr-x 2 root root 4096 Sep 10 2004 etc
drwxr-xr-x 2 root root 4096 May 26 2004 lib
drwxr-xr-x 2 root root 4096 Feb 13 2006 msgs
drwxr-xr-x 8 ftp root 4096 Dec 18 00:13 pub
drwxr-xr-x 3 root root 4096 May 26 2004 usr
*/
Calendar cal = Calendar.getInstance();
Calendar now = Calendar.getInstance();
cal.set(Calendar.MILLISECOND, 0);
cal.set(Calendar.SECOND, 0);
Pattern pat = Pattern.compile("([dl-])((?:[r-][w-][xst-]){3})\\s+(\\d+)\\s+([^\\s]+)\\s+([^\\s]+)\\s+(\\d+)\\s+([a-zA-Z]{3})\\s+(\\d{1,2})\\s+(\\d{4}|\\d{2}:\\d{2})\\s+(.+)");
Matcher m = pat.matcher(str);
if (m.matches())
{
ftpFile = new JFtpFile();
if (m.group(1).equals("-"))
ftpFile.setType(JFtpFile.TYPE_FILE);
else if (m.group(1).equals("d"))
ftpFile.setType(JFtpFile.TYPE_DIRECTORY);
else if (m.group(1).equals("l"))
ftpFile.setType(JFtpFile.TYPE_LINK);
ftpFile.setRights(m.group(2));
ftpFile.setNumberOfHardLinks(Integer.parseInt(m.group(3)));
ftpFile.setUser(m.group(4));
ftpFile.setGroup(m.group(5));
ftpFile.setSize(Long.parseLong(m.group(6)));
String month = m.group(7);
if (month.equalsIgnoreCase("jan"))
cal.set(Calendar.MONTH, Calendar.JANUARY);
else if (month.equalsIgnoreCase("feb"))
cal.set(Calendar.MONTH, Calendar.FEBRUARY);
else if (month.equalsIgnoreCase("mar"))
cal.set(Calendar.MONTH, Calendar.MARCH);
else if (month.equalsIgnoreCase("apr"))
cal.set(Calendar.MONTH, Calendar.APRIL);
else if (month.equalsIgnoreCase("may"))
cal.set(Calendar.MONTH, Calendar.MAY);
else if (month.equalsIgnoreCase("jun"))
cal.set(Calendar.MONTH, Calendar.JUNE);
else if (month.equalsIgnoreCase("jul"))
cal.set(Calendar.MONTH, Calendar.JULY);
else if (month.equalsIgnoreCase("aug"))
cal.set(Calendar.MONTH, Calendar.AUGUST);
else if (month.equalsIgnoreCase("sep"))
cal.set(Calendar.MONTH, Calendar.SEPTEMBER);
else if (month.equalsIgnoreCase("oct"))
cal.set(Calendar.MONTH, Calendar.OCTOBER);
else if (month.equalsIgnoreCase("nov"))
cal.set(Calendar.MONTH, Calendar.NOVEMBER);
else if (month.equalsIgnoreCase("dec"))
cal.set(Calendar.MONTH, Calendar.DECEMBER);
cal.set(Calendar.DATE, Integer.parseInt(m.group(8)));
ftpFile.setTimeStamp(cal);
pat = Pattern.compile("(\\d{2}):(\\d{2})");
Matcher m2 = pat.matcher(m.group(9));
if (m2.matches())
{
cal.set(Calendar.HOUR_OF_DAY, Integer.parseInt(m2.group(1)));
cal.set(Calendar.MINUTE, Integer.parseInt(m2.group(2)));
// guess the year
if (cal.get(Calendar.MONTH) > now.get(Calendar.MONTH))
cal.set(Calendar.YEAR, cal.get(Calendar.YEAR) - 1);
}
else
{
cal.set(Calendar.HOUR_OF_DAY, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.YEAR, Integer.parseInt(m.group(9)));
}
ftpFile.setName(m.group(10));
m_bHasLastListOutputHardlinks = true;
}
if (ftpFile == null)
{
// Linux style icarus FTP server (Version wu-2.6.2(3)
/* ftp.adfa.edu.au, ftp.sun.com, ftp.cnam.fr
drwxr-xr-x 8 12 512 Mar 27 2002 .
drwxr-xr-x 8 12 512 Mar 27 2002 ..
drwxr-xr-x 4 1 512 Sep 26 1995 EPub
-rw-r--r-- 1 1 57574 Apr 3 2002 Index
drwxr-xr-x 2 1 512 Jul 14 2000 NSS
lrwxrwxrwx 1 1 7 Apr 3 2002 bin -> usr/bin
dr-xr-xr-x 2 1 512 Mar 27 2002 dev
dr-xr-xr-x 2 1 512 Apr 18 2002 etc
-rw-r--r-- 1 1 14797 Apr 3 2002 ls-lRt.Z
drwxrwxrwx 5 1 512 Sep 26 2003 pub
drwxr-xr-x 5 1 512 Mar 27 2002 usr
-rw-r--r-- 1 1 750 Aug 30 1999 welcome.msg
*/
pat = Pattern.compile("([dl-])((?:[r-][w-][xst-]){3})\\s+([^\\s]+)\\s+(\\d+)\\s+(\\d+)\\s+([a-zA-Z]{3})\\s+(\\d{1,2})\\s+(\\d{4}|\\d{2}:\\d{2})\\s+(.+)");
m = pat.matcher(str);
if (m.matches())
{
ftpFile = new JFtpFile();
if (m.group(1).equals("-"))
ftpFile.setType(JFtpFile.TYPE_FILE);
else if (m.group(1).equals("d"))
ftpFile.setType(JFtpFile.TYPE_DIRECTORY);
else if (m.group(1).equals("l"))
ftpFile.setType(JFtpFile.TYPE_LINK);
ftpFile.setRights(m.group(2));
ftpFile.setNumberOfHardLinks(Integer.parseInt(m.group(3)));
ftpFile.setUser(m.group(4));
ftpFile.setSize(Long.parseLong(m.group(5)));
String month = m.group(6);
if (month.equalsIgnoreCase("jan"))
cal.set(Calendar.MONTH, Calendar.JANUARY);
else if (month.equalsIgnoreCase("feb"))
cal.set(Calendar.MONTH, Calendar.FEBRUARY);
else if (month.equalsIgnoreCase("mar"))
cal.set(Calendar.MONTH, Calendar.MARCH);
else if (month.equalsIgnoreCase("apr"))
cal.set(Calendar.MONTH, Calendar.APRIL);
else if (month.equalsIgnoreCase("may"))
cal.set(Calendar.MONTH, Calendar.MAY);
else if (month.equalsIgnoreCase("jun"))
cal.set(Calendar.MONTH, Calendar.JUNE);
else if (month.equalsIgnoreCase("jul"))
cal.set(Calendar.MONTH, Calendar.JULY);
else if (month.equalsIgnoreCase("aug"))
cal.set(Calendar.MONTH, Calendar.AUGUST);
else if (month.equalsIgnoreCase("sep"))
cal.set(Calendar.MONTH, Calendar.SEPTEMBER);
else if (month.equalsIgnoreCase("oct"))
cal.set(Calendar.MONTH, Calendar.OCTOBER);
else if (month.equalsIgnoreCase("nov"))
cal.set(Calendar.MONTH, Calendar.NOVEMBER);
else if (month.equalsIgnoreCase("dec"))
cal.set(Calendar.MONTH, Calendar.DECEMBER);
cal.set(Calendar.DATE, Integer.parseInt(m.group(7)));
ftpFile.setTimeStamp(cal);
pat = Pattern.compile("(\\d{2}):(\\d{2})");
Matcher m2 = pat.matcher(m.group(8));
if (m2.matches())
{
cal.set(Calendar.HOUR_OF_DAY, Integer.parseInt(m2.group(1)));
cal.set(Calendar.MINUTE, Integer.parseInt(m2.group(2)));
// guess the year
if (cal.get(Calendar.MONTH) > now.get(Calendar.MONTH))
cal.set(Calendar.YEAR, cal.get(Calendar.YEAR) - 1);
}
else
{
cal.set(Calendar.HOUR_OF_DAY, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.YEAR, Integer.parseInt(m.group(8)));
}
ftpFile.setName(m.group(9));
m_bHasLastListOutputHardlinks = true;
}
}
if (ftpFile == null)
{
// Linux style yshome PROXY-FTP server (DeleGate/9.4.2-pre1)
/* ftp.delegate.org, ftp.sunfreeware.com
drwxrwxr-x 3 delegate 4096 Jan 25 2004 .
drwxrwxr-x 6 delegate 4096 Feb 2 2005 ..
drwxrwxr-x 13 delegate 4096 Aug 12 2005 pub
rw-rw-r-- 1 delegate 272 Jan 25 2004 rsa-pubkey.pem
*/
// I'am not sure if the first digit is the number of links!
pat = Pattern.compile("([dl-])((?:[r-][w-][xst-]){3})\\s+(\\d+)\\s+([^\\s]+)\\s+(\\d+)\\s+([a-zA-Z]{3})\\s+(\\d{1,2})\\s+(\\d{4}|\\d{2}:\\d{2})\\s+(.+)");
m = pat.matcher(str);
if (m.matches())
{
ftpFile = new JFtpFile();
if (m.group(1).equals("-"))
ftpFile.setType(JFtpFile.TYPE_FILE);
else if (m.group(1).equals("d"))
ftpFile.setType(JFtpFile.TYPE_DIRECTORY);
else if (m.group(1).equals("l"))
ftpFile.setType(JFtpFile.TYPE_LINK);
ftpFile.setRights(m.group(2));
ftpFile.setNumberOfHardLinks(Integer.parseInt(m.group(3)));
ftpFile.setUser(m.group(4));
ftpFile.setSize(Long.parseLong(m.group(5)));
String month = m.group(6);
if (month.equalsIgnoreCase("jan"))
cal.set(Calendar.MONTH, Calendar.JANUARY);
else if (month.equalsIgnoreCase("feb"))
cal.set(Calendar.MONTH, Calendar.FEBRUARY);
else if (month.equalsIgnoreCase("mar"))
cal.set(Calendar.MONTH, Calendar.MARCH);
else if (month.equalsIgnoreCase("apr"))
cal.set(Calendar.MONTH, Calendar.APRIL);
else if (month.equalsIgnoreCase("may"))
cal.set(Calendar.MONTH, Calendar.MAY);
else if (month.equalsIgnoreCase("jun"))
cal.set(Calendar.MONTH, Calendar.JUNE);
else if (month.equalsIgnoreCase("jul"))
cal.set(Calendar.MONTH, Calendar.JULY);
else if (month.equalsIgnoreCase("aug"))
cal.set(Calendar.MONTH, Calendar.AUGUST);
else if (month.equalsIgnoreCase("sep"))
cal.set(Calendar.MONTH, Calendar.SEPTEMBER);
else if (month.equalsIgnoreCase("oct"))
cal.set(Calendar.MONTH, Calendar.OCTOBER);
else if (month.equalsIgnoreCase("nov"))
cal.set(Calendar.MONTH, Calendar.NOVEMBER);
else if (month.equalsIgnoreCase("dec"))
cal.set(Calendar.MONTH, Calendar.DECEMBER);
cal.set(Calendar.DATE, Integer.parseInt(m.group(7)));
ftpFile.setTimeStamp(cal);
pat = Pattern.compile("(\\d{2}):(\\d{2})");
Matcher m2 = pat.matcher(m.group(8));
if (m2.matches())
{
cal.set(Calendar.HOUR_OF_DAY, Integer.parseInt(m2.group(1)));
cal.set(Calendar.MINUTE, Integer.parseInt(m2.group(2)));
// guess the year
if (cal.get(Calendar.MONTH) > now.get(Calendar.MONTH))
cal.set(Calendar.YEAR, cal.get(Calendar.YEAR) - 1);
}
else
{
cal.set(Calendar.HOUR_OF_DAY, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.YEAR, Integer.parseInt(m.group(8)));
}
ftpFile.setName(m.group(9));
m_bHasLastListOutputHardlinks = true;
}
}
if (ftpFile == null)
{
// Linux style freespirit FTP server (Version wu-2.6.2-gus050408)
/* ftp.linuxfocus.org
drwxr-xr-x 6 root @ root @ 4096 May 5 2003 .
drwxr-xr-x 6 root @ root @ 4096 May 5 2003 ..
drwxr-x--x 2 root @ root @ 4096 May 5 2003 bin
drwxr-x--x 2 root @ root @ 4096 May 5 2003 etc
drwxr-x--x 2 root @ root @ 4096 May 5 2003 lib
drwxr-xr-x 4 root @ 50 @ 4096 May 5 2003 pub
-rw-r--r-- 1 root @ root @ 313 Jul 11 2001 welcome.msg */
pat = Pattern.compile("([dl-])((?:[r-][w-][xst-]){3})\\s+(\\d+)\\s+([^\\s]+)\\s+@\\s+([^\\s]+)\\s+@\\s+(\\d+)\\s+([a-zA-Z]{3})\\s+(\\d{1,2})\\s+(\\d{4}|\\d{2}:\\d{2})\\s+(.+)");
m = pat.matcher(str);
if (m.matches())
{
ftpFile = new JFtpFile();
if (m.group(1).equals("-"))
ftpFile.setType(JFtpFile.TYPE_FILE);
else if (m.group(1).equals("d"))
ftpFile.setType(JFtpFile.TYPE_DIRECTORY);
else if (m.group(1).equals("l"))
ftpFile.setType(JFtpFile.TYPE_LINK);
ftpFile.setRights(m.group(2));
ftpFile.setNumberOfHardLinks(Integer.parseInt(m.group(3)));
ftpFile.setUser(m.group(4));
ftpFile.setGroup(m.group(5));
ftpFile.setSize(Long.parseLong(m.group(6)));
String month = m.group(7);
if (month.equalsIgnoreCase("jan"))
cal.set(Calendar.MONTH, Calendar.JANUARY);
else if (month.equalsIgnoreCase("feb"))
cal.set(Calendar.MONTH, Calendar.FEBRUARY);
else if (month.equalsIgnoreCase("mar"))
cal.set(Calendar.MONTH, Calendar.MARCH);
else if (month.equalsIgnoreCase("apr"))
cal.set(Calendar.MONTH, Calendar.APRIL);
else if (month.equalsIgnoreCase("may"))
cal.set(Calendar.MONTH, Calendar.MAY);
else if (month.equalsIgnoreCase("jun"))
cal.set(Calendar.MONTH, Calendar.JUNE);
else if (month.equalsIgnoreCase("jul"))
cal.set(Calendar.MONTH, Calendar.JULY);
else if (month.equalsIgnoreCase("aug"))
cal.set(Calendar.MONTH, Calendar.AUGUST);
else if (month.equalsIgnoreCase("sep"))
cal.set(Calendar.MONTH, Calendar.SEPTEMBER);
else if (month.equalsIgnoreCase("oct"))
cal.set(Calendar.MONTH, Calendar.OCTOBER);
else if (month.equalsIgnoreCase("nov"))
cal.set(Calendar.MONTH, Calendar.NOVEMBER);
else if (month.equalsIgnoreCase("dec"))
cal.set(Calendar.MONTH, Calendar.DECEMBER);
cal.set(Calendar.DATE, Integer.parseInt(m.group(8)));
ftpFile.setTimeStamp(cal);
pat = Pattern.compile("(\\d{2}):(\\d{2})");
Matcher m2 = pat.matcher(m.group(9));
if (m2.matches())
{
cal.set(Calendar.HOUR_OF_DAY, Integer.parseInt(m2.group(1)));
cal.set(Calendar.MINUTE, Integer.parseInt(m2.group(2)));
// guess the year
if (cal.get(Calendar.MONTH) > now.get(Calendar.MONTH))
cal.set(Calendar.YEAR, cal.get(Calendar.YEAR) - 1);
}
else
{
cal.set(Calendar.HOUR_OF_DAY, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.YEAR, Integer.parseInt(m.group(9)));
}
ftpFile.setName(m.group(10));
m_bHasLastListOutputHardlinks = true;
}
}
if (ftpFile == null)
{
/*
MSDOS style
05-29-03 11:57AM <DIR> Control
02-21-03 11:39AM <DIR> emergency
05-13-03 02:20PM <DIR> MediaSync
11-14-06 07:44PM <DIR> Reiner
02-28-03 12:01PM <DIR> Router
03-23-05 04:15PM <DIR> Sound
11-12-04 08:58AM <DIR> tempStreams
02-21-03 01:27PM <DIR> wma
12-16-04 02:19PM <DIR> WUTemp
11-18-03 02:48PM <DIR> wwwRoot
*/
pat = Pattern.compile("(\\d{2})-(\\d{2})-(\\d{2})\\s+(\\d{2}):(\\d{2})([A|P]M)\\s+(<DIR>|\\d+)\\s+(.+)");
m = pat.matcher(str);
if (m.matches())
{
ftpFile = new JFtpFile();
cal.set(Calendar.MONTH, Integer.parseInt(m.group(1)) - 1);
cal.set(Calendar.DAY_OF_MONTH, Integer.parseInt(m.group(2)));
cal.set(Calendar.YEAR, Integer.parseInt(m.group(3)));
cal.set(Calendar.HOUR, Integer.parseInt(m.group(4)));
cal.set(Calendar.MINUTE, Integer.parseInt(m.group(5)));
cal.set(Calendar.AM_PM, m.group(6).equalsIgnoreCase("pm") ? Calendar.PM : Calendar.AM);
ftpFile.setTimeStamp(cal);
try
{
ftpFile.setSize(Long.parseLong(m.group(7)));
ftpFile.setType(JFtpFile.TYPE_FILE);
}
catch(NumberFormatException ex)
{
ftpFile.setType(JFtpFile.TYPE_DIRECTORY);
}
ftpFile.setName(m.group(8));
m_bHasLastListOutputHardlinks = false;
}
}
if (m_isDebug)
{
if (ftpFile == null)
System.err.println("NO MATCH FOUND FOR '"+str + "'");
}
return ftpFile;
}
private void closeSocket(Socket socket)
{
if (socket != null)
{
try
{socket.close();}
catch(Exception ex)
{}
}
}
private void closeSocket(ServerSocket socket)
{
if (socket != null)
{
try
{socket.close();}
catch(Exception ex)
{}
}
}
}