package javaforce;
import java.io.*;
import java.net.*;
import java.util.*;
import javax.net.ssl.*;
/**
* FTP client class. Supports Passive and Active mode.
*/
public class FTP {
public static interface ProgressListener {
public void setProgress(int value);
}
private Socket s;
private Socket ds;
private InputStream is;
private OutputStream os;
private BufferedReader br;
private boolean passive = true;
private String host; //passive host
private int pasvport; //passive port
private ServerSocket ss; //active socket
private boolean log = false;
private ProgressListener progress;
private boolean aborted = false;
private boolean active = true;
private Reader reader;
public boolean debug = false;
/**
* Holds the repsonse strings from the last executed command
*/
public ArrayList<String> response = new ArrayList<String>();
public int getResponseLength() {
synchronized(response) {
return response.size();
}
}
public void addResponse(String r) {
synchronized(response) {
response.add(r);
}
}
public String getNextResponse() {
synchronized(response) {
if (response.size() == 0) return null;
return response.remove(0);
}
}
public String getLastResponse() {
while (!aborted) {
synchronized(response) {
if (response.size() == 0) return null;
String res = response.remove(0);
if (res.charAt(3) == ' ') {
return res;
}
}
}
return null;
}
public boolean connect(String host, int port) throws Exception {
active = true;
s = new Socket(host, port);
is = s.getInputStream();
br = new BufferedReader(new InputStreamReader(is));
os = s.getOutputStream();
this.host = host;
reader = new Reader();
reader.start();
wait4Response();
if (getLastResponse().startsWith("220")) {
return true;
}
disconnect(); //not valid FTP site
return false;
}
public boolean connectSSL(String host, int port) throws Exception {
active = true;
TrustManager[] trustAllCerts = new TrustManager[]{
new X509TrustManager() {
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return null;
}
public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType) {
}
public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType) {
}
}
};
// Let us create the factory where we can set some parameters for the connection
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, trustAllCerts, new java.security.SecureRandom());
// SSLSocketFactory sslsocketfactory = (SSLSocketFactory) SSLSocketFactory.getDefault(); //this method will only work with trusted certs
SSLSocketFactory sslsocketfactory = (SSLSocketFactory) sc.getSocketFactory(); //this method will work with untrusted certs
SSLSocket ssl = (SSLSocket) sslsocketfactory.createSocket(host, port);
s = (Socket) ssl;
is = s.getInputStream();
br = new BufferedReader(new InputStreamReader(is));
os = s.getOutputStream();
this.host = host;
reader = new Reader();
reader.start();
wait4Response();
if (getLastResponse().startsWith("220")) {
return true;
}
disconnect(); //not valid FTP site
return false;
}
public void abort() {
aborted = true;
}
public void disconnect() throws Exception {
active = false;
if (s != null) {
s.close();
}
s = null;
is = null;
os = null;
reader = null;
}
public void addProgressListener(ProgressListener progress) {
this.progress = progress;
}
public void setLogging(boolean state) {
log = state;
}
public void setPassiveMode(boolean mode) {
passive = mode;
}
public boolean setBinary() throws Exception {
cmd("type i");
wait4Response();
if (!getLastResponse().startsWith("200")) {
return false;
}
return true;
}
public boolean setAscii() throws Exception {
cmd("type a");
wait4Response();
if (!getLastResponse().startsWith("200")) {
return false;
}
return true;
}
public boolean login(String user, String pass) throws Exception {
cmd("user " + user);
wait4Response();
if (!getLastResponse().startsWith("331")) {
return false;
}
cmd("pass " + pass);
wait4Response();
if (!getLastResponse().startsWith("230")) {
return false;
}
return true;
}
public void logout() throws Exception {
cmd("quit");
wait4Response(); //should be "221" but ignored
getLastResponse();
}
public void cmd(String cmd) throws Exception {
if ((s == null) || (s.isClosed())) {
throw new Exception("not connected");
}
aborted = false;
if (log) {
if (cmd.startsWith("pass ")) {
JFLog.log("pass ****");
} else {
JFLog.log(cmd);
}
}
synchronized(response) {
response.clear();
}
cmd += "\r\n";
os.write(cmd.getBytes());
}
public void get(String filename, String out) throws Exception {
getPort();
cmd("retr " + filename);
FileOutputStream fos = new FileOutputStream(out);
getData(fos);
fos.close();
wait4Response();
getLastResponse();
}
public void get(String filename, OutputStream os) throws Exception {
getPort();
cmd("retr " + filename);
getData(os);
wait4Response();
getLastResponse();
}
public InputStream getStart(String filename) throws Exception {
getPort();
cmd("retr " + filename);
return getData();
}
public void get(File remote, File local) throws Exception {
get(remote.getAbsolutePath(), local.getAbsolutePath());
}
public void getFinish() throws Exception {
wait4Response();
getLastResponse();
}
public void put(InputStream is, String filename) throws Exception {
getPort();
cmd("stor " + filename);
putData(is);
wait4Response();
getLastResponse();
}
public void put(String in, String filename) throws Exception {
getPort();
cmd("stor " + filename);
FileInputStream fis = new FileInputStream(in);
putData(fis);
fis.close();
wait4Response();
getLastResponse();
}
public void put(File local, File remote) throws Exception {
put(local.getAbsolutePath(), remote.getAbsolutePath());
}
public OutputStream putStart(String filename) throws Exception {
getPort();
cmd("stor " + filename);
return putData();
}
public void putFinish() throws Exception {
wait4Response();
getLastResponse();
}
public void cd(String path) throws Exception {
cmd("cwd " + path);
wait4Response();
getLastResponse();
}
public void chmod(int mode, String path) throws Exception {
cmd("site chmod " + Integer.toString(mode, 8) + " " + path);
wait4Response();
getLastResponse();
}
public void mkdir(String path) throws Exception {
cmd("mkd " + path);
wait4Response();
getLastResponse();
}
public void rename(String oldpath, String newpath) throws Exception {
cmd("rnfr " + oldpath);
wait4Response();
getLastResponse();
cmd("rnto " + newpath);
wait4Response();
getLastResponse();
}
public void rm(String path) throws Exception {
cmd("dele " + path);
wait4Response();
getLastResponse();
}
public void rmdir(String path) throws Exception {
cmd("rmd " + path);
wait4Response();
getLastResponse();
}
public String ls(String path) throws Exception {
getPort();
cmd("list " + path);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
getData(baos);
wait4Response();
if (!getLastResponse().startsWith("226")) {
throw new Exception("bad listing");
}
return baos.toString();
}
public String pwd() throws Exception {
cmd("pwd");
wait4Response();
String str = getLastResponse();
if (!str.startsWith("257")) {
throw new Exception("pwd failed");
}
if ((str.charAt(4) == '\"') && (str.charAt(str.length() - 1) == '\"')) {
return str.substring(5, str.length() - 1);
} else {
return str.substring(4);
}
}
private final int BUFSIZ = 64 * 1024;
private void getPort() throws Exception {
if (passive) {
cmd("pasv");
wait4Response();
String str = getLastResponse();
if (!str.startsWith("227")) {
throw new Exception("pasv failed");
}
//227 Entering Passive Mode (ip,ip,ip,ip,PORTHI,PORTLO).
String strs[] = str.split(",");
if (strs.length != 6) {
throw new Exception("pasv failed - bad response");
}
int hi = Integer.valueOf(strs[4]);
int lo = Integer.valueOf(strs[5].substring(0, strs[5].indexOf(")")));
pasvport = (hi << 8) + lo;
} else {
ss = new ServerSocket();
int hi = ss.getLocalPort() >> 8;
int lo = ss.getLocalPort() & 0xff;
cmd(s.getLocalAddress().getHostAddress().replaceAll("[.]", ",") + "," + hi + "," + lo);
wait4Response();
String str = getLastResponse();
if (!str.startsWith("200")) {
throw new Exception("port failed");
}
}
}
private InputStream getData() throws Exception {
aborted = false;
if (passive) {
JFLog.log("FTP:connect:" + host + ":" + pasvport);
ds = new Socket(host, pasvport);
} else {
ds = ss.accept();
}
InputStream dis = ds.getInputStream();
wait4Response();
if (!getLastResponse().startsWith("150")) {
throw new Exception("bad get");
}
return dis;
}
private void getData(OutputStream os) throws Exception {
aborted = false;
if (passive) {
JFLog.log("FTP:connect:" + host + ":" + pasvport);
ds = new Socket(host, pasvport);
} else {
ds = ss.accept();
}
byte data[] = new byte[BUFSIZ];
InputStream dis = ds.getInputStream();
wait4Response();
if (!getLastResponse().startsWith("150")) {
throw new Exception("bad get");
}
int read, total = 0;
startTimeoutThread();
while (!ds.isClosed() && !aborted) {
timeoutCounter = 0;
read = dis.read(data);
if (read == -1) {
break;
}
if (read > 0) {
os.write(data, 0, read);
total += read;
if (progress != null) {
progress.setProgress(total);
}
}
}
//read any remaining data left in buffers
if (aborted) {
return;
}
do {
timeoutCounter = 0;
read = dis.read(data);
if (read > 0) {
os.write(data, 0, read);
total += read;
if (progress != null) {
progress.setProgress(total);
}
}
} while ((read > 0) && (!aborted));
stopTimeoutThread();
}
private OutputStream putData() throws Exception {
aborted = false;
if (passive) {
JFLog.log("FTP:connect:" + host + ":" + pasvport);
ds = new Socket(host, pasvport);
} else {
ds = ss.accept();
}
OutputStream dos = ds.getOutputStream();
wait4Response();
if (!getLastResponse().startsWith("150")) {
throw new Exception("bad put");
}
return dos;
}
private void putData(InputStream is) throws Exception {
aborted = false;
if (passive) {
JFLog.log("FTP:connect:" + host + ":" + pasvport);
ds = new Socket(host, pasvport);
} else {
ds = ss.accept();
}
byte data[] = new byte[BUFSIZ];
OutputStream dos = ds.getOutputStream();
wait4Response();
if (!getLastResponse().startsWith("150")) {
throw new Exception("bad put");
}
int total = 0;
startTimeoutThread();
while ((!ds.isClosed()) && (is.available() > 0) && (!aborted)) {
timeoutCounter = 0;
int read = is.read(data);
if (read > 0) {
dos.write(data, 0, read);
total += read;
if (progress != null) {
progress.setProgress(total);
}
}
}
stopTimeoutThread();
dos.flush();
ds.close();
ds = null;
}
private void wait4Response() throws Exception {
while (!aborted) {
synchronized(response) {
int size = response.size();
if (size > 0) {
String last = response.get(size-1);
if (last.charAt(3) == ' ') return;
}
response.wait();
}
}
}
private class Reader extends Thread {
public void run() {
while (active) {
try {
String res = br.readLine();
JFLog.log(res);
addResponse(res);
if (res.charAt(3) == ' ') {
synchronized(response) {
response.notify();
}
}
if (res.startsWith("5")) {
JFLog.log("Aborted");
aborted = true;
if (ds != null) {
ds.close();
ds = null;
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
private Timer timer;
private int timeoutCounter;
private void startTimeoutThread() {
timeoutCounter = 0;
if (timer != null) {
timer.cancel();
timer = null;
}
timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask() {
public void run() {
timeoutCounter++;
if (timeoutCounter == 20) {
JFLog.log("Error:Transfer Timeout");
aborted = true;
if (ds != null) {
try {ds.close();} catch (Exception e) {}
ds = null;
}
timer.cancel();
timer = null;
}
}
}, 1000, 1000);
}
private void stopTimeoutThread() {
timer.cancel();
timer = null;
}
}