package org.fnppl.opensdx.ftp_bridge;
/*
* Copyright (C) 2010-2015
* fine people e.V. <opensdx@fnppl.org>
* Henning Thieß <ht@fnppl.org>
*
* http://fnppl.org
*/
/*
* Software license
*
* As far as this file or parts of this file is/are software, rather than documentation, this software-license applies / shall be applied.
*
* This file is part of openSDX
* openSDX is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* openSDX 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.
*
* You should have received a copy of the GNU Lesser General Public License
* and GNU General Public License along with openSDX.
* If not, see <http://www.gnu.org/licenses/>.
*
*/
/*
* Documentation license
*
* As far as this file or parts of this file is/are documentation, rather than software, this documentation-license applies / shall be applied.
*
* This file is part of openSDX.
* Permission is granted to copy, distribute and/or modify this document
* under the terms of the GNU Free Documentation License, Version 1.3
* or any later version published by the Free Software Foundation;
* with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts.
* A copy of the license is included in the section entitled "GNU
* Free Documentation License" resp. in the file called "FDL.txt".
*
*/
import java.awt.BorderLayout;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Vector;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.table.DefaultTableModel;
import org.fnppl.opensdx.file_transfer.CommandResponseListener;
import org.fnppl.opensdx.file_transfer.OSDXFileTransferClient;
import org.fnppl.opensdx.file_transfer.commands.OSDXFileTransferCommand;
import org.fnppl.opensdx.file_transfer.commands.OSDXFileTransferDeleteCommand;
import org.fnppl.opensdx.file_transfer.commands.OSDXFileTransferListCommand;
import org.fnppl.opensdx.file_transfer.commands.OSDXFileTransferMkDirCommand;
import org.fnppl.opensdx.file_transfer.commands.OSDXFileTransferRenameCommand;
import org.fnppl.opensdx.file_transfer.commands.OSDXFileTransferUploadOldStyleCommand;
import org.fnppl.opensdx.file_transfer.model.RemoteFile;
import org.fnppl.opensdx.file_transfer.model.Transfer;
import org.fnppl.opensdx.helper.QueueWaiting;
import org.fnppl.opensdx.keyserver.helper.IdGenerator;
public class FTP_OSDX_BridgeThread extends Thread {
private FTP_OSDX_Bridge control = null;
private String host;
private boolean running;
private Socket ftpSocket;
private static final int MODE_NOT_SET = 0;
private static final int MODE_ACTIVE = 1;
private static final int MODE_PASSIVE = 2;
private int connectionMode = MODE_NOT_SET;
private int next_port = -1;
private ServerSocket ftpPassiveDataServerSocket = null;
private Socket ftpPassiveDataSocket = null;
public PrintWriter out;
public FTP_OSDX_BridgeThread(Socket ftpsocket, FTP_OSDX_Bridge control) {
System.out.println("NEW FTP_OSDX_BridgeThread");
System.out.println("SOCKET: "+ftpsocket.getPort()+", "+ftpsocket.getLocalPort()+", "+ftpsocket.getInetAddress());
this.ftpSocket = ftpsocket;
this.control = control;
}
public void run() {
InetAddress inet;
try {
inet = ftpSocket.getInetAddress();
host = inet.getHostName();
System.out.println("host: "+host);
//init connection
BufferedReader in = new BufferedReader(new InputStreamReader(ftpSocket.getInputStream()));
out = new PrintWriter(ftpSocket.getOutputStream(), true);
out.println("220 FTP Server ready.\r");
running = true;
while (running) {
try {
String str = in.readLine();
if (str==null) {
running = false;
} else {
String command = str;
String param = null;
int ind = str.indexOf(' ');
if (ind>0) {
command = str.substring(0,ind);
if (str.length()>ind+1) {
param = str.substring(ind+1);
}
}
command = command.toUpperCase();
try {
Method commandHandler = getClass().getMethod("handle_"+command, String.class);
commandHandler.invoke(this, param);
} catch (NoSuchMethodException ex) {
handle_command_not_implemented(str);
} catch (InvocationTargetException ex) {
handle_command_not_implemented(str);
} catch (IllegalAccessException ex) {
handle_command_not_implemented(str);
}
}
} catch (Exception ex) {
ex.printStackTrace();
running = false;
}
}
ftpSocket.close();
//TODO osdxclient.closeConnection();
} catch (Exception e) { // System.out.println(e);
e.printStackTrace();
}
}
// public boolean ensureConnection() {
// boolean connected = osdxclient.isConnected();
// if (!connected) {
// try {
// connected = osdxclient.connect(user.host, user.port, user.prepath, user.signingKey, user.username);
//
// } catch (Exception ex) {
// ex.printStackTrace();
// try {
// System.out.println("Closing connection to osdx server ...");
// connected = false;
// osdxclient.closeConnection();
// } catch (Exception ex2) {
// ex2.printStackTrace();
// }
// }
// }
// if (!connected) {
// System.out.println("ensureConnection -> false");
// out.println("426 Connection closed; transfer aborted.");
// }
// return connected;
// }
public void handle_RETR(final String param) {
Thread t = new Thread() {
public void run() {
try {
// if (ensureConnection()) {
//loading file data to tmp file
String filename = param;
if (!filename.startsWith("/")) {
if (control.pwd.equals("/")) {
filename = "/"+param;
} else {
filename = control.pwd+"/"+param;
}
}
//download with stream
try {
System.out.println("downloading file: "+filename);
out.println("150 Binary data connection");
Socket t = getDataSocket();
if (t==null) {
return;
}
BufferedOutputStream outData = new BufferedOutputStream(t.getOutputStream());
long id = control.osdxclient.download(filename, outData);
control.commandThread.put(id, FTP_OSDX_BridgeThread.this);
} catch (Exception ex) {
out.println("550 Requested action not taken. File unavailable (e.g., file not found, no access).");
ex.printStackTrace();
}
//download with temp file
// System.out.println("downloading file: "+filename);
// final File tmpFile = File.createTempFile("osdx"+System.currentTimeMillis(), ".tmp");
// tmpFile.delete();
//
// long id = control.osdxclient.download(filename, tmpFile);
// Transfer t = new Transfer();
// t.type = "download";
// t.file = tmpFile;
//
// control.transfersInProgress.put(id,t);
// control.commandThread.put(id,FTP_OSDX_BridgeThread.this);
// final FileTransferProgress progress = new FileTransferProgress(FileTransferProgress.END_UNKNOWN) {
// public void onUpdate() {
// super.onUpdate();
// if (hasFinished()) {
// try {
// if (tmpFile.exists() && tmpFile.length()==getProgressEnd()) {
// out.println("150 Binary data connection");
//
// //transfer downloaded file to ftp client
// FileInputStream fin = new FileInputStream(tmpFile);
//
// Socket t = new Socket(host, next_port);
// OutputStream out2 = t.getOutputStream();
// byte buffer[] = new byte[1024];
// int read;
// try {
// while ((read = fin.read(buffer)) != -1) {
// out2.write(buffer, 0, read);
// }
// out2.close();
// out.println("226 transfer complete");
// fin.close();
// tmpFile.delete();
// t.close();
// } catch (IOException e) {
// e.printStackTrace();
// }
// } else {
// out.println("550 Requested action not taken. File unavailable (e.g., file not found, no access).");
// }
// } catch (Exception ex) {
// ex.printStackTrace();
// }
// }
// };
// };
// osdxclient.downloadFile(filename, tmpFile, progress);
//
// }
} catch (Exception ex) {
ex.printStackTrace();
}
}
};
t.start();
}
public void handle_STOR_ORIG(final String param) { //upload file to osdx server
Thread t = new Thread() {
public void run() {
try {
//if (ensureConnection()) {
String filename = removeDoubleSlashes(param);
out.println("150 Binary data connection");
System.out.println("HANDLE_STOR :: "+filename);
Socket t = getDataSocket();
if (t==null) return;
InputStream inFromFTP = t.getInputStream();
File tmpFile = File.createTempFile("osdx"+System.currentTimeMillis(), ".tmp");
tmpFile.deleteOnExit();
FileOutputStream outFileBuffer = new FileOutputStream(tmpFile);
//String filename = param;
if (!filename.startsWith("/")) {
if (control.pwd.equals("/")) {
filename = "/"+filename;
} else {
filename = control.pwd+"/"+filename;
}
}
byte buffer[] = new byte[1024];
int read;
try {
//write input stream from FTP to buffer file
while ((read = inFromFTP.read(buffer)) != -1) {
outFileBuffer.write(buffer, 0, read);
}
inFromFTP.close();
outFileBuffer.close();
t.close();
//send ACK (caution: ack before transfer to OSDX Server ready
out.println("226 transfer complete");
System.out.println("Queueing upload to OSDX Server of "+filename);
Transfer transfer = new Transfer();
transfer.type = "upload";
transfer.file = tmpFile;
transfer.fileLen = tmpFile.length();
transfer.originalFilename = filename;
control.waitingUploads.put(transfer);
} catch (IOException e) {
e.printStackTrace();
}
//}
} catch (Exception ex) {
ex.printStackTrace();
}
}
};
t.start();
}
public void handle_STOR(final String param) { //upload file to osdx server
try {
//if (ensureConnection()) {
String filename = removeDoubleSlashes(param);
out.println("150 Binary data connection");
System.out.println("HANDLE_STOR :: "+filename);
final Socket t = getDataSocket();
if (t==null) return;
InputStream inFromFTP = t.getInputStream();
//File tmpFile = File.createTempFile("osdx"+System.currentTimeMillis(), ".tmp");
//tmpFile.deleteOnExit();
//FileOutputStream outFileBuffer = new FileOutputStream(tmpFile);
//String filename = param;
if (!filename.startsWith("/")) {
if (control.pwd.equals("/")) {
filename = "/"+filename;
} else {
filename = control.pwd+"/"+filename;
}
}
System.out.println("handle STOR :: "+filename);
final String tFilename = filename;
try {
long id = control.osdxclient.uploadStream(new BufferedInputStream(inFromFTP), filename, new CommandResponseListener() {
private long lastProgress = 0;
public void onSuccess(OSDXFileTransferCommand command) {
//block[0] = false;
try {
t.close();
} catch (IOException e) {
e.printStackTrace();
}
out.println("226 transfer complete");
System.out.println("Transfer succeeded: "+tFilename);
}
public void onStatusUpdate(OSDXFileTransferCommand command, long progress, long maxProgress, String msg) {
//
if (progress-lastProgress>1024*1024-1) {
lastProgress = progress;
System.out.println("transferred: "+progress/1024+" kB");
}
}
public void onError(OSDXFileTransferCommand command, String msg) {
try {
t.close();
} catch (IOException e) {
e.printStackTrace();
}
//out.println("226 transfer complete");
System.out.println("Transfer failed: "+tFilename+" :: "+msg);
}
});
} catch (Exception ex) {
ex.printStackTrace();
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
public void handle_TYPE(String str) {
out.println("200 type set");
}
public void handle_SYST(String str) {
out.println("215 UNIX Type: L8");
}
private String lastRenameFilename = null;
public void handle_RNFR(String str) {
try {
String filename = str;
if (!filename.startsWith("/")) {
if (control.pwd.equals("/")) {
filename = "/"+str;
} else {
filename = control.pwd+"/"+str;
}
}
lastRenameFilename = filename;
out.println("350 Ready for destination name");
} catch (Exception ex) {
ex.printStackTrace();
lastRenameFilename = null;
out.println("500 Error in RNFR command");
}
}
public void handle_RNTO(String str) {
try {
if (lastRenameFilename!=null) {
String filename = str;
if (filename.contains("/")) {
filename = filename.substring(filename.lastIndexOf('/')+1);
}
System.out.println("Rename: "+lastRenameFilename+" -> "+filename);
long id = control.osdxclient.rename(lastRenameFilename, filename);
control.commandThread.put(id,this);
}
} catch (Exception ex) {
ex.printStackTrace();
out.println("500 Error in RNTO command");
}
}
public void handle_DELE(String str) {
try {
String filename = str;
if (!filename.startsWith("/")) {
if (control.pwd.equals("/")) {
filename = "/"+str;
} else {
filename = control.pwd+"/"+str;
}
}
System.out.println("Remove file: "+filename);
long id = control.osdxclient.delete(filename);
control.commandThread.put(id,this);
} catch (Exception ex) {
ex.printStackTrace();
}
}
public long lastRMDCommandID = -1L;
public void handle_RMD(String str) {
try {
String filename = str;
if (!filename.startsWith("/")) {
if (control.pwd.equals("/")) {
filename = "/"+str;
} else {
filename = control.pwd+"/"+str;
}
}
lastRMDCommandID = control.osdxclient.delete(filename);
control.commandThread.put(lastRMDCommandID,this);
System.out.println("Remove dir: "+filename);
} catch (Exception ex) {
ex.printStackTrace();
}
}
public void handle_CDUP(String str) {
try {
if (!control.pwd.equals("/")) {
int ind = control.pwd.lastIndexOf('/');
if (ind>0) {
control.pwd = control.pwd.substring(0,ind);
} else {
control.pwd="/";
}
}
System.out.println("PWD: "+control.pwd);
out.println("250 CWD command succesful");
} catch (Exception ex) {
ex.printStackTrace();
}
}
public void handle_CWD(String param) {
try {
param = removeDoubleSlashes(param);
if (param.startsWith("/")) {
control.pwd = param;
} else {
if (control.pwd.equals("/")) {
control.pwd = "/"+param;
} else {
control.pwd = control.pwd+"/"+param;
}
}
if (control.pwd.endsWith("/") && control.pwd.length()>1) {
control.pwd = control.pwd.substring(0,control.pwd.length()-1);
}
System.out.println("PWD after \"CWD "+param+"\" :: "+control.pwd);
out.println("250 CWD command succesful");
} catch (Exception ex) {
ex.printStackTrace();
}
}
public void handle_QUIT(String param) {
//control.osdxclient.closeConnection();
//closePassiveSocket();
//running = false;
System.out.println("got QUIT command");
out.println("221 Goodbye");
}
public void handle_USER(String param) {
control.user = control.getUser(param);
out.println("331 Password");
}
public void handle_PASS(String param) {
if (control.user!=null && control.user.ftppassword.equals(param)) { //&& ensureConnection()) {
//try {
out.println("230 User " + control.user.ftpusername + " logged in.");
System.out.println("User "+control.user.ftpusername + " logged in.");
//} catch (Exception e) {
// e.printStackTrace();
// out.println("430 Error: cannot connect to given account");
// System.out.println("Error: cannot connect to given account");
//}
} else {
out.println("430 Invalid username or password");
System.out.println("Invalid username or password");
}
}
public void handle_PWD(String str) {
try {
out.println("257 \""+control.pwd+"\" is current directory");
System.out.println("PWD: "+control.pwd);
} catch (Exception ex) {
ex.printStackTrace();
}
}
public void handle_MKD(String param) {
try {
if (param.startsWith("/")) {
long id = control.osdxclient.mkdir(param);
control.commandThread.put(id,this);
System.out.println("MKDIR: "+param);
} else {
if (control.pwd.equals("/")) {
long id = control.osdxclient.mkdir("/"+param);
control.commandThread.put(id,this);
System.out.println("MKDIR: /"+param);
} else {
long id = control.osdxclient.mkdir(control.pwd+"/"+param);
control.commandThread.put(id,this);
System.out.println("MKDIR: "+control.pwd+"/"+param);
}
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
public void handle_SYS(String str) {
out.println("500 SYS not understood");
}
public void handle_PORT(String str) {
try {
out.println("200 PORT command successful");
int lng, lng2, lng1;
String a1="", a2="";
lng = str.length() - 1;
lng2 = str.lastIndexOf(",");
lng1 = str.lastIndexOf(",", lng2 - 1);
for (int i = lng1 + 1; i < lng2; i++) {
a1 = a1 + str.charAt(i);
}
for (int i = lng2 + 1; i <= lng; i++) {
a2 = a2 + str.charAt(i);
}
int ip1 = Integer.parseInt(a1);
int ip2 = Integer.parseInt(a2);
next_port = ip1 * 16 * 16 + ip2;
connectionMode = MODE_ACTIVE;
closePassiveSocket();
//System.out.println("next port = "+next_port);
} catch (Exception ex) {
connectionMode = MODE_NOT_SET;
ex.printStackTrace();
}
}
// eg: EPRT |2|::1|36299|
public void handle_EPRT(String str) {
String[] part = null;
try {
String delimiter = str.substring(0,1);
part = str.split("["+delimiter+"]");
//part[1] = net-protocoll: 1 = IPv4, 2 = IPv6
//part[2] = net-address
//part[3] = tcp-port
next_port = Integer.parseInt(part[3]);
connectionMode = MODE_ACTIVE;
closePassiveSocket();
out.println("200 EPRT command successful");
} catch (Exception ex) {
connectionMode = MODE_NOT_SET;
ex.printStackTrace();
if (part!=null) {
System.out.println("EPRT parsing error: "+Arrays.toString(part));
}
out.println("500 EPRT command error");
}
}
//eg: EPSV |||6446|
public void handle_EPSV(String str) {
if (str==null) {
out.println("500 EPSV missing command parameter");
return;
}
String[] part = null;
try {
String delimiter = str.substring(0,1);
part = str.split("["+delimiter+"]");
//part[3] = tcp-port
next_port = Integer.parseInt(part[3]);
connectionMode = MODE_ACTIVE;
closePassiveSocket();
out.println("200 EPSV command successful");
} catch (Exception ex) {
connectionMode = MODE_NOT_SET;
ex.printStackTrace();
if (part!=null) {
System.out.println("EPSV parsing error: "+Arrays.toString(part));
} else {
System.out.println("EPSV parsing error");
}
out.println("500 EPSV command error");
}
}
public void handle_LIST(String param) {
try {
if (param==null || param.length()==0) {
long id = control.osdxclient.list(control.pwd,null);
control.commandThread.put(id,this);
}
else if (param.startsWith("/")) {
long id = control.osdxclient.list(param,null);
control.commandThread.put(id,this);
} else {
if (control.pwd.equals("/")) {
long id = control.osdxclient.list("/"+param,null);
control.commandThread.put(id,this);
} else {
long id = control.osdxclient.list(control.pwd+"/"+param,null);
control.commandThread.put(id,this);
}
}
} catch (Exception ex) {
ex.printStackTrace();
// try {
// System.out.println("Closing connection to osdx server ...");
// osdxclient.closeConnection();
// } catch (Exception ex2) {
// ex2.printStackTrace();
// }
// out.println("426 Connection closed; transfer aborted.");
}
}
public void handle_FEAT(String str) {
out.println("211-Features supported\n211 End");
}
public void handle_NOOP(String str) {
out.println("200 NOOP command successful");
}
public void handle_PASV(String str) {
//out.println("502 passive mode not implemented");
if (ftpPassiveDataServerSocket==null) {
try {
ftpPassiveDataServerSocket = new ServerSocket(0); //0 = any free port
} catch (IOException e) {
ftpPassiveDataServerSocket = null;
e.printStackTrace();
}
}
if (ftpPassiveDataServerSocket==null) {
out.println("451 Requested action aborted: local error in processing");
return;
}
try {
int port = ftpPassiveDataServerSocket.getLocalPort();
int p1 = port/256;
int p2 = port%256;
//next_port = p1 * 16 * 16 + p2;
connectionMode = MODE_PASSIVE;
out.println("227 Entering Passive Mode (127,0,0,1,"+p1+","+p2+")");
if (ftpPassiveDataSocket!=null && ftpPassiveDataSocket.isConnected()) {
try {
ftpPassiveDataSocket.close();
} catch (IOException ex) {
ex.printStackTrace();
}
}
new Thread() {
public void run() {
try {
ftpPassiveDataSocket = ftpPassiveDataServerSocket.accept();
} catch (IOException ex) {
ftpPassiveDataServerSocket = null;
ftpPassiveDataSocket = null;
ex.printStackTrace();
}
}
}.start();
} catch (Exception e) {
e.printStackTrace();
}
}
public void handle_command_not_implemented(String str) {
out.println("502 "+str+" not understood");
System.out.println("command: "+str+" not implemented");
}
public Socket getDataSocket() throws UnknownHostException, IOException {
if (connectionMode==MODE_ACTIVE && next_port>0) {
return new Socket(host, next_port);
}
else if (connectionMode == MODE_PASSIVE && ftpPassiveDataSocket != null && ftpPassiveDataSocket.isConnected()) {
return ftpPassiveDataSocket;
}
out.println("425 Error: Can't open data connection");
return null;
}
private void closePassiveSocket() {
if (ftpPassiveDataSocket !=null) {
try {
ftpPassiveDataSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
ftpPassiveDataSocket = null;
}
if (ftpPassiveDataServerSocket != null) {
try {
ftpPassiveDataServerSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
ftpPassiveDataServerSocket = null;
}
}
private String removeDoubleSlashes(String text) {
while (text.contains("//")) {
text = text.replace("//", "/");
}
return text;
}
}