package org.fnppl.opensdx.file_transfer;
/*
* 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.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.Socket;
import java.util.Arrays;
import java.util.HashMap;
import org.fnppl.opensdx.common.Util;
import org.fnppl.opensdx.file_transfer.helper.ClientSettings;
import org.fnppl.opensdx.file_transfer.helper.FileTransferLog;
import org.fnppl.opensdx.file_transfer.helper.RightsAndDuties;
import org.fnppl.opensdx.file_transfer.model.RemoteFile;
import org.fnppl.opensdx.file_transfer.errors.OSDXErrorCode;
import org.fnppl.opensdx.file_transfer.trigger.Trigger;
import org.fnppl.opensdx.file_transfer.trigger.TriggerContext;
import org.fnppl.opensdx.security.AsymmetricKeyPair;
import org.fnppl.opensdx.security.MD5;
import org.fnppl.opensdx.security.OSDXKey;
import org.fnppl.opensdx.security.SecurityHelper;
import org.fnppl.opensdx.security.SymmetricKey;
import org.fnppl.opensdx.xml.Document;
public class OSDXFileTransferServerThread extends Thread {
public final static int DEBUG_LEVEL = 50; //0=off, 50=some, 100=all
public final static int DEBUG_MSGVISIBILITY_HIGH = 1;
public final static int DEBUG_MSGVISIBILITY_MID = 51;
public final static int DEBUG_MSGVISIBILITY_LOW = 101;
public final static int DEBUG_MSGTYPE_INFO = 1;
public final static int DEBUG_MSGTYPE_ERROR = 2;
public final static int DEBUG_MSGTYPE_WARNING = 3;
private OSDXFileTransferServer server;
private Socket socket;
private BufferedInputStream in;
private BufferedOutputStream out;
private SecureConnection data = null;
private ClientSettings cs = null;
private HashMap<Long, FileUploadInfo> uploads = new HashMap<Long, FileUploadInfo>();
private OSDXKey mySigningKey;
private String clientID = "";
private String addr = "unknown";
private SymmetricKey key= null;
private String client_keyid = null;
private byte[] client_nonce = null;
private byte[] server_nonce = null;
private long maxFilelengthForMD5 = 100*1024*1024L; //100 MB
protected int maxPacketSize = 50*1024; //50kB
public OSDXFileTransferServerThread(Socket socket, OSDXKey mySigningKey, OSDXFileTransferServer server) throws Exception {
this.socket = socket;
this.mySigningKey = mySigningKey;
this.server = server;
in = new BufferedInputStream(socket.getInputStream());
out = new BufferedOutputStream(socket.getOutputStream());
data = new SecureConnection(null, in,out);
clientID = socket.getInetAddress().getHostAddress()+":"+socket.getPort();
addr = socket.getInetAddress().getHostAddress();
// debugMSG(DEBUG_MSGVISIBILITY_LOW, DEBUG_MSGTYPE_INFO,"connected to client: "+clientID+ " at "+FileTransferLog.getDateString());
debugMSG(DEBUG_MSGVISIBILITY_HIGH, DEBUG_MSGTYPE_INFO, "connected to client: "+clientID+ " at "+FileTransferLog.getDateString());
if (server!=null) server.log.logIncomingConnection(clientID, addr,"");
}
public void run() {
boolean run = initSecureConnection();
//just wait and listen
while (run && socket.isConnected()) {
try {
run = data.receiveNextPackage();
if (run) {
onRequestReceived(data.id, data.num, data.type, data.content);
//String content = new String(data.content, "UTF-8");
//System.out.println("SERVER RECEIVED :: "+content);
}
} catch (Exception ex) {
run = false;
server.log.logError(clientID, addr, ex.getMessage());
ex.printStackTrace();
}
}
debugMSG(DEBUG_MSGVISIBILITY_HIGH, DEBUG_MSGTYPE_INFO, "closing socket to client: "+clientID+ " at "+FileTransferLog.getDateString());
server.log.logConnectionClose(clientID, socket.getInetAddress().getHostAddress(),"");
}
public HashMap<String, Object> buildContextParams(ClientSettings cs) {
HashMap<String, Object> context = new HashMap<String, Object>();
context.put(TriggerContext.USERNAME, cs.getUsername());
context.put(TriggerContext.KEYID, cs.getKeyID());
return context;
}
public HashMap<String, Object> buildContextParamsWithDate(ClientSettings cs) {
HashMap<String, Object> context = buildContextParams(cs);
long datetime = System.currentTimeMillis();
context.put(TriggerContext.RELATED_START_DATE, datetime);
context.put(TriggerContext.RELATED_END_DATE, datetime);
return context;
}
public void onRequestReceived(long commandid, int num, byte code, byte[] content) {
try {
String msg = "RECEIVED REQUEST at "+FileTransferLog.getDateString()+" : id="+commandid+"\tCLIENTID["+clientID+"]";
if (code == SecureConnection.TYPE_DATA && content !=null) {
debugMSG(DEBUG_MSGVISIBILITY_MID, DEBUG_MSGTYPE_INFO, msg+ ", DATA len="+content.length);
//handle data
FileUploadInfo upload = uploads.get(commandid);
if (upload!=null) {
if (upload.out==null) {
upload.out = new FileOutputStream(upload.file, true);
}
debugMSG(DEBUG_MSGVISIBILITY_LOW, DEBUG_MSGTYPE_INFO, "appending "+content.length+" bytes to "+upload.file.getAbsolutePath());
upload.out.write(content);
upload.loaded += content.length;
upload.md5.update(content);
if (upload.fromStream) {
}
else {
if (upload.loaded>=upload.length) {
//ready with upload
debugMSG(DEBUG_MSGVISIBILITY_MID, DEBUG_MSGTYPE_INFO, "upload ready: "+upload.file.getAbsolutePath());
upload.out.close();
if (upload.loaded == upload.length) {
boolean ok = true;
if (upload.md5String!=null) {
//check md5
try {
byte[] my_md5 = upload.md5.getMD5bytes();
byte[] your_md5 = SecurityHelper.HexDecoder.decode(upload.md5String);
if (!Arrays.equals(my_md5,your_md5)) {
ok = false;
debugMSG(DEBUG_MSGVISIBILITY_HIGH, DEBUG_MSGTYPE_INFO, "MD5 check failed for "+upload.file.getAbsolutePath());
} else {
debugMSG(DEBUG_MSGVISIBILITY_MID, DEBUG_MSGTYPE_INFO, "MD5 check ok for "+upload.file.getAbsolutePath());
}
} catch (Exception ex) {
ok = false;
}
if (!ok) {
data.setError(commandid, num, "md5 check failed", OSDXErrorCode.ERROR_MD5_CHECK);
//OLD: data.setError(commandid, num, "md5 check failed");
data.sendPackage();
}
}
if (ok) {
data.setAckComplete(commandid, num);
data.sendPackage();
}
} else {
data.setError(commandid, num, "wrong filesize", OSDXErrorCode.ERROR_WRONG_FILESIZE);
//OLD: data.setError(commandid, num, "wrong filesize");
data.sendPackage();
}
//trigger upload_end event
HashMap<String, Object> context = buildContextParams(cs);
context.put(TriggerContext.RELATED_FILE,upload.file);
context.put(TriggerContext.RELATED_FILENAME,upload.file.getAbsolutePath());
context.put(TriggerContext.RELATED_START_DATE,upload.uploadStartDatetime);
context.put(TriggerContext.RELATED_END_DATE,System.currentTimeMillis());
cs.triggerEvent(Trigger.TRIGGER_UPLOAD_END, context);
} else {
//dont ack every package ?
}
}
}
else {
debugMSG(DEBUG_MSGVISIBILITY_HIGH, DEBUG_MSGTYPE_INFO, "No UploadInfo found for id:"+commandid);
Exception ex = new Exception("No UploadInfo found for id:"+commandid);
ex.printStackTrace();
}
}
else if (content != null) { //if (code == SecureConnection.TYPE_TEXT) {
String text = new String(content,"UTF-8");
debugMSG(DEBUG_MSGVISIBILITY_LOW, DEBUG_MSGTYPE_INFO, msg+", "+SecurityHelper.HexDecoder.encode(new byte[]{(byte)code})+" :: MSG: "+text);
//parse command
String command = text;
String param = null;
int ind = text.indexOf(' ');
if (ind>0) {
command = text.substring(0,ind);
if (text.length()>ind+1) {
param = text.substring(ind+1);
}
}
//find right handler method
command = command.toLowerCase();
if(command.equals("put_eof")) {
handle_put_eof(commandid, num, code, param);
}
else if(command.equals("put")) {
handle_put(commandid, num, code, param);
}
else if(command.equals("delete")) {
handle_delete(commandid, num, code, param);
}
else if(command.equals("put_resume")) {
handle_put_resume(commandid, num, code, param);
}
else if(command.equals("file")) {
handle_file(commandid, num, code, param);
}
else if(command.equals("get")) {
handle_get(commandid, num, code, param);
}
else if(command.equals("list")) {
handle_list(commandid, num, code, param);
}
else if(command.equals("login")) {
handle_login(commandid, num, code, param);
}
else if(command.equals("mkdir")) {
handle_mkdir(commandid, num, code, param);
}
else if(command.equals("put_break")) {
handle_put_break(commandid, num, code, param);
}
else if(command.equals("put_cancel")) {
handle_put_cancel(commandid, num, code, param);
}
else if(command.equals("quit")) {
handle_quit(commandid, num, code, param);
}
else if(command.equals("rename")) {
handle_rename(commandid, num, code, param);
}
else if(command.equals("resumeget")) {
handle_resumeget(commandid, num, code, param);
}
else if(command.equals("resumeput")) {
handle_resumeput(commandid, num, code, param);
}
else if(command.equals("userpasslogin")) {
handle_userpasslogin(commandid, num, code, param);
}
else {
try {
Method commandHandler = OSDXFileTransferServerThread.class.getMethod("handle_"+command, Long.TYPE, Integer.TYPE, Byte.TYPE, String.class);
commandHandler.invoke(this, commandid, num, code, param);
// public void handle_put_eof(long commandid, int num, byte code, String param) throws Exception {
} catch (NoSuchMethodException ex) {
debugMSG(DEBUG_MSGVISIBILITY_HIGH, DEBUG_MSGTYPE_ERROR, "NoSuchMethodException for \"handle_"+command+"\"@commandid="+commandid+",num="+num+",code="+code+",param="+param);
handle_command_not_implemented(commandid, num, code, command, param);
} catch (InvocationTargetException ex) {
debugMSG(DEBUG_MSGVISIBILITY_HIGH, DEBUG_MSGTYPE_ERROR, "InvocationTargetException for \"handle_"+command+"\"@commandid="+commandid+",num="+num+",code="+code+",param="+param);
handle_command_not_implemented(commandid, num, code, command, param);
} catch (IllegalAccessException ex) {
debugMSG(DEBUG_MSGVISIBILITY_HIGH, DEBUG_MSGTYPE_ERROR, "IllegalAccessException for \"handle_"+command+"\"@commandid="+commandid+",num="+num+",code="+code+",param="+param);
handle_command_not_implemented(commandid, num, code, command, param);
}
}
}
} catch (Exception e) {
server.log.logError(clientID, addr, "onRequestReceived :: Exception :: "+e.getMessage());
e.printStackTrace();
}
}
public boolean initSecureConnection() {
try {
byte[] clientRequest = data.receiveRawBytesPackage();
String[] lines = new String(clientRequest,"UTF-8").split("\n");
if (lines!=null) {
boolean ok = true;
String clientVersion = lines[0];
String host = lines[1];
client_nonce = SecurityHelper.HexDecoder.decode(lines[2]);
client_keyid = lines[3];
byte[] client_mod = SecurityHelper.HexDecoder.decode(lines[4]);
byte[] client_exp = SecurityHelper.HexDecoder.decode(lines[5]);
byte[] client_signature = SecurityHelper.HexDecoder.decode(lines[6]);
AsymmetricKeyPair client_pubkey = new AsymmetricKeyPair(client_mod, client_exp, null);
byte[][] checks = SecurityHelper.getMD5SHA1SHA256(client_nonce);
boolean verifySig = client_pubkey.verify(client_signature, checks[1],checks[2],checks[3],0L);
if (verifySig) {
server.log.logDebug(clientID, addr, "initSecureConnection :: signature of client_nonce verified.");
//generate response
server_nonce = SecurityHelper.getRandomBytes(32);
StringBuffer msg = new StringBuffer();
msg.append(" 200 OSDXFileTransferServer "+OSDXFileTransferServer.VERSION+"\n");
msg.append(host+"\n");
msg.append(mySigningKey.getKeyID()+"\n");
msg.append(SecurityHelper.HexDecoder.encode(mySigningKey.getPublicModulusBytes(),':',-1)+"\n");
msg.append(SecurityHelper.HexDecoder.encode(mySigningKey.getPublicExponentBytes(),':',-1)+"\n");
//encrypted part
StringBuffer encmsg = new StringBuffer();
encmsg.append(SecurityHelper.HexDecoder.encode(client_nonce,':',-1)+"\n");
encmsg.append(SecurityHelper.HexDecoder.encode(server_nonce,':',-1)+"\n");
encmsg.append(lines[3]+"\n");
encmsg.append(lines[4]+"\n");
encmsg.append(lines[5]+"\n");
byte[] enc = client_pubkey.encryptBlocks(encmsg.toString().getBytes("UTF-8"));
//sign enc part
checks = SecurityHelper.getMD5SHA1SHA256(enc);
byte[] sigOfEnc = mySigningKey.sign(checks[1],checks[2],checks[3],0L);
//add signature bytes and enc part
msg.append(SecurityHelper.HexDecoder.encode(sigOfEnc,':',-1)+"\n");
msg.append("ENC "+enc.length+"\n");
//build sym key of client_nonce and server nonce
byte[] concat_nonce = SecurityHelper.concat(client_nonce, server_nonce);
byte[] key_bytes = SecurityHelper.getSHA256(concat_nonce); //32 bytes = 256 bit
byte[] iv = Arrays.copyOf(SecurityHelper.getMD5(concat_nonce),16); //16 bytes = 128 bit
key = new SymmetricKey(key_bytes, iv);
data.key = key;
//send packet
data.sendRawBytes(msg.toString().getBytes("UTF-8"));
data.sendRawBytes(enc);
// data.setAck(0, 0);
} else {
server.log.logError(clientID, addr, "initSecureConnection :: ERROR: verification of client_nonce signature faild!");
debugMSG(DEBUG_MSGVISIBILITY_HIGH, DEBUG_MSGTYPE_ERROR, "ERROR: verification of client_nonce signature failed!");
String debugMsg = "";
for (int i=0;i<lines.length;i++) {
debugMsg += lines[i]+"\n";
}
debugMSG(DEBUG_MSGVISIBILITY_LOW, DEBUG_MSGTYPE_ERROR, debugMsg);
server.log.logDebug(clientID, addr, debugMsg);
ok = false;
// String msg = version+" 421 You are not authorized to make the connection\n";
// msg += host+"\n";
socket.close();
}
return ok;
}
} catch (Exception e1) {
server.log.logError(clientID, addr, "initSecureConnection :: Exception :: "+e1.getMessage());
debugMSG(DEBUG_MSGVISIBILITY_HIGH, DEBUG_MSGTYPE_ERROR, clientID+" "+addr+" initSecureConnection :: Exception :: "+e1.getMessage());
e1.printStackTrace();
}
return false;
}
// COMMAND IMPLEMENTATIONS
public void handle_command_not_implemented(long commandid, int num, byte code, String command, String param) throws Exception {
String s = clientID+" "+addr+" COMMAND NOT UNDERSTOOD: "+command+" PARAM="+param;
Exception ex = new Exception(s);
ex.printStackTrace();
debugMSG(DEBUG_MSGVISIBILITY_HIGH, DEBUG_MSGTYPE_ERROR,s);
data.setErrorCommandNotUnderstood(commandid, num);
data.sendPackage();
server.log.logError(clientID, addr, "COMMAND NOT UNDERSTOOD :: "+command);
}
public void handle_login(long commandid, int num, byte code, String username) throws Exception {
debugMSG(DEBUG_MSGVISIBILITY_HIGH, DEBUG_MSGTYPE_INFO,"handle_login :: "+username);
if (username!=null) {
String userid = username+"::"+client_keyid;
debugMSG(DEBUG_MSGVISIBILITY_LOW, DEBUG_MSGTYPE_INFO,"userid: "+userid);
cs = server.getClientSetting(userid);
if (cs!=null) {
//login ok -> ACK with rights and duties
String param = Util.makeParamsString(new String[]{client_keyid, Document.buildDocument(cs.getRightsAndDuties().toElement(true)).toStringCompact()});
debugMSG(DEBUG_MSGVISIBILITY_LOW, DEBUG_MSGTYPE_INFO,"SENDING: ACK :: "+param);
data.setAck(commandid, num, param);
data.sendPackage();
server.log.logCommand(clientID, addr, "LOGIN", username, param);
//trigger login event
HashMap<String, Object> context = buildContextParamsWithDate(cs);
cs.triggerEvent(Trigger.TRIGGER_LOGIN, context);
}
else {
//login failed
data.setError(commandid, num, "ERROR IN LOGIN :: ACCESS DENIED", OSDXErrorCode.ERROR_LOGIN_ACCESS_DENIED);
//OLD: data.setError(commandid, num, "ERROR IN LOGIN :: ACCESS DENIED");
data.sendPackage();
server.log.logCommand(clientID, addr, "LOGIN", username, "ERROR IN LOGIN :: ACCESS DENIED");
debugMSG(DEBUG_MSGVISIBILITY_HIGH, DEBUG_MSGTYPE_ERROR, clientID+" "+ addr+ " LOGIN "+ username+" ERROR IN LOGIN :: ACCESS DENIED");
}
} else {
//login failed
data.setError(commandid, num, "ERROR IN LOGIN :: MISSING USERNAME", OSDXErrorCode.ERROR_LOGIN_USERNAME_MISSING);
//OLD: data.setError(commandid, num, "ERROR IN LOGIN :: MISSING USERNAME");
data.sendPackage();
server.log.logCommand(clientID, addr, "LOGIN", username, "ERROR IN LOGIN :: MISSING USERNAME");
debugMSG(DEBUG_MSGVISIBILITY_HIGH, DEBUG_MSGTYPE_ERROR, clientID+" "+ addr+ " LOGIN "+ username+" ERROR IN LOGIN :: MISSING USERNAME");
}
}
public void handle_userpasslogin(long commandid, int num, byte code, String userauth) throws Exception {
debugMSG(DEBUG_MSGVISIBILITY_HIGH, DEBUG_MSGTYPE_INFO,"handle_userpasslogin :: "+userauth);
String username = userauth.substring(0, userauth.indexOf("\t"));
String auth = userauth.substring(userauth.indexOf("\t")+1);
if (username != null && auth != null) {
String userid = username+"::"+auth;
debugMSG(DEBUG_MSGVISIBILITY_LOW, DEBUG_MSGTYPE_INFO,"userid: "+userid);
cs = server.getClientSetting(userid);
if(cs != null) {
//login ok -> ACK with rights and duties
String param = Util.makeParamsString(new String[]{client_keyid, Document.buildDocument(cs.getRightsAndDuties().toElement(true)).toStringCompact()});
debugMSG(DEBUG_MSGVISIBILITY_LOW, DEBUG_MSGTYPE_INFO,"SENDING: ACK :: "+param);
data.setAck(commandid, num, param);
data.sendPackage();
server.log.logCommand(clientID, addr, "LOGIN", username, param);
//trigger login event
HashMap<String, Object> context = buildContextParams(cs);
cs.triggerEvent(Trigger.TRIGGER_LOGIN, context);
}
else {
//login failed
data.setError(commandid, num, "ERROR IN LOGIN :: ACCESS DENIED", OSDXErrorCode.ERROR_LOGIN_ACCESS_DENIED);
//OLD: data.setError(commandid, num, "ERROR IN LOGIN :: ACCESS DENIED");
data.sendPackage();
server.log.logCommand(clientID, addr, "LOGIN", username, "ERROR IN LOGIN :: ACCESS DENIED");
}
} else {
//login failed
data.setError(commandid, num, "ERROR IN LOGIN :: MISSING USERNAME", OSDXErrorCode.ERROR_LOGIN_USERNAME_MISSING);
//OLD: data.setError(commandid, num, "ERROR IN LOGIN :: MISSING USERNAME");
data.sendPackage();
server.log.logCommand(clientID, addr, "LOGIN", username, "ERROR IN LOGIN :: MISSING USERNAME");
}
}
public void handle_mkdir(long commandid, int num, byte code, String param) throws Exception {
ensureAllowed(RightsAndDuties.ALLOW_MKDIR, commandid, num, "Sorry, no right to make directories.");
if (param!=null) {
File path = null;
if (param.startsWith("/")) {
path = new File(cs.getLocalRootPath()+param);
if (!cs.isAllowed(path)) {
data.setError(commandid, num, "restricted path", OSDXErrorCode.ERROR_PATH_IS_RESTRICTED);
//OLD: data.setError(commandid, num, "restricted path");
data.sendPackage();
server.log.logCommand(clientID, addr, "MKDIR", param, "restricted path");
}
else if (path.exists()) {
data.setError(commandid, num, "path already exists", OSDXErrorCode.ERROR_PATH_ALREADY_EXISTS);
//OLD: data.setError(commandid, num, "path already exists");
data.sendPackage();
server.log.logCommand(clientID, addr, "MKDIR", param, "path already exists");
}
else if (!cs.isAllowedDepthDir(path)) {
String msg = "max directory depth = "+cs.getRightsAndDuties().getMaxDirectoryDepth();
data.setError(commandid, num, msg, OSDXErrorCode.ERROR_DIRECTORY_DEPTH);
//OLD: data.setError(commandid, num, msg);
data.sendPackage();
server.log.logCommand(clientID, addr, "MKDIR", param, msg);
}
else {
boolean ok = path.mkdirs();
if (ok) {
data.setAck(commandid, num);
data.sendPackage();
server.log.logCommand(clientID, addr, "MKDIR", param, "ACK");
//trigger mkdir event
HashMap<String, Object> context = buildContextParams(cs);
context.put(TriggerContext.RELATED_FILE,path);
context.put(TriggerContext.RELATED_FILENAME,path.getAbsolutePath());
cs.triggerEvent(Trigger.TRIGGER_MKDIR,context);
} else {
data.setError(commandid, num, "error in mkdir", OSDXErrorCode.ERROR_MKDIR);
//OLD: data.setError(commandid, num, "error in mkdir");
data.sendPackage();
server.log.logCommand(clientID, addr, "MKDIR", param, "error in mkdir");
}
}
} else {
data.setError(commandid, num, "path must be absolute", OSDXErrorCode.ERROR_PATH_IS_NOT_ABSOLUTE);
//OLD: data.setError(commandid, num, "path must be absolute");
data.sendPackage();
server.log.logCommand(clientID, addr, "MKDIR", param, "path must be absolute");
}
} else {
data.setError(commandid, num, "missing path parameter", OSDXErrorCode.ERROR_PATH_IS_MISSING);
//OLD: data.setError(commandid, num, "missing path parameter");
data.sendPackage();
server.log.logCommand(clientID, addr, "MKDIR", param, "missing path parameter");
}
}
public void handle_delete(long commandid, int num, byte code, String param) throws Exception {
ensureAllowed(RightsAndDuties.ALLOW_DELETE, commandid, num, "Sorry, no right to delete files or directories.");
if (param!=null) {
File file = null;
if (param.startsWith("/")) {
file = new File(cs.getLocalRootPath()+param);
if (!cs.isAllowed(file)) {
data.setError(commandid, num, "restricted file", OSDXErrorCode.ERROR_FILE_RESTRICTED);
//OLD: data.setError(commandid, num, "restricted file");
data.sendPackage();
server.log.logCommand(clientID, addr, "DELETE", param, "restricted file");
}
else if (!file.exists()) {
data.setError(commandid, num, "file does not exist", OSDXErrorCode.ERROR_FILE_NOT_EXISTS);
//OLD: data.setError(commandid, num, "file does not exist");
data.sendPackage();
server.log.logCommand(clientID, addr, "DELETE", param, "file does not exist");
} else {
//delete
debugMSG(DEBUG_MSGVISIBILITY_LOW, DEBUG_MSGTYPE_INFO,"DELETING: "+file.getAbsolutePath());
if (file.isDirectory()) {
boolean ok = deleteDirectory(file);
if (ok) {
data.setAck(commandid, num);
data.sendPackage();
server.log.logCommand(clientID, addr, "DELETE", param,"ACK");
//trigger delete event
HashMap<String, Object> context = buildContextParams(cs);
context.put(TriggerContext.RELATED_FILE,file);
context.put(TriggerContext.RELATED_FILENAME,file.getAbsolutePath());
cs.triggerEvent(Trigger.TRIGGER_DELETE, context);
} else {
data.setError(commandid, num, "directory \""+param+"\" could not be deleted", OSDXErrorCode.ERROR_CANNOT_DELETE_DIR);
//OLD: data.setError(commandid, num, "directory \""+param+"\" could not be deleted");
data.sendPackage();
server.log.logCommand(clientID, addr, "DELETE", param, "directory \""+param+"\" could not be deleted");
}
} else {
boolean ok = file.delete();
if (ok) {
data.setAck(commandid, num);
data.sendPackage();
server.log.logCommand(clientID, addr, "DELETE", param, "ACK");
//trigger delete event
HashMap<String, Object> context = buildContextParams(cs);
context.put(TriggerContext.RELATED_FILE,file);
context.put(TriggerContext.RELATED_FILENAME,file.getAbsolutePath());
cs.triggerEvent(Trigger.TRIGGER_DELETE, context);
} else {
data.setError(commandid, num, "file \""+param+"\" could not be deleted", OSDXErrorCode.ERROR_CANNOT_DELETE_FILE);
//OLD: data.setError(commandid, num, "file \""+param+"\" could not be deleted");
data.sendPackage();
server.log.logCommand(clientID, addr, "DELETE", param,"file \""+param+"\" could not be deleted");
}
}
}
} else {
data.setError(commandid, num, "path must be absolute", OSDXErrorCode.ERROR_PATH_IS_NOT_ABSOLUTE);
//OLD: data.setError(commandid, num, "path must be absolute");
data.sendPackage();
server.log.logCommand(clientID, addr, "DELETE", param, "path must be absolute");
}
} else {
data.setError(commandid, num, "missing path/file parameter", OSDXErrorCode.ERROR_PATH_IS_MISSING);
//OLD: data.setError(commandid, num, "missing path/file parameter");
data.sendPackage();
server.log.logCommand(clientID, addr, "DELETE", param, "missing path/file parameter");
}
}
private boolean deleteDirectory(File path) {
if( path.exists() ) {
File[] list = path.listFiles();
for(int i=0; i<list.length; i++) {
if(list[i].isDirectory()) {
deleteDirectory(list[i]);
}
else {
list[i].delete();
}
}
}
if (!path.equals(cs.getLocalRootPath())) {
return(path.delete());
} else {
return true;
}
}
public void handle_rename(long commandid, int num, byte code, String param) throws Exception {
ensureAllowed(RightsAndDuties.ALLOW_RENAME, commandid, num, "Sorry, no right to rename files or directories.");
if (param!=null && param.length()>0) {
String[] p = Util.getParams(param);
if (p.length==2) {
if (p[1].contains("/") || p[1].contains("\\")) {
data.setError(commandid, num, "wrong destination filename", OSDXErrorCode.ERROR_WRONG_DESTINATION);
//OLD: data.setError(commandid, num, "wrong destination filename");
data.sendPackage();
server.log.logCommand(clientID, addr, "RENAME", param, "wrong destination filename" );
} else {
File file = null;
if (p[0].startsWith("/")) {
file = new File(cs.getLocalRootPath()+p[0]);
if (!cs.isAllowed(file)) {
data.setError(commandid, num, "restricted file", OSDXErrorCode.ERROR_FILE_RESTRICTED);
//OLD: data.setError(commandid, num, "restricted file");
data.sendPackage();
server.log.logCommand(clientID, addr, "RENAME", param, "restricted file");
}
else if (!file.exists()) {
data.setError(commandid, num, "file does not exist", OSDXErrorCode.ERROR_FILE_NOT_EXISTS);
//OLD: data.setError(commandid, num, "file does not exist");
data.sendPackage();
server.log.logCommand(clientID, addr, "RENAME", param, "file does not exist");
} else {
//init destination file
File dest = new File(file.getParentFile(),p[1]);
if (!cs.isAllowed(file)) {
data.setError(commandid, num, "restricted destination file", OSDXErrorCode.ERROR_FILE_RESTRICTED);
//OLD: data.setError(commandid, num, "restricted destination file");
data.sendPackage();
server.log.logCommand(clientID, addr, "RENAME", param, "restricted destination file");
}
else if (dest.exists()) {
data.setError(commandid, num, "destination file already exists", OSDXErrorCode.ERROR_FILE_ALREADY_EXISTS);
//OLD: data.setError(commandid, num, "destination file already exists");
data.sendPackage();
server.log.logCommand(clientID, addr, "RENAME", param, "destination file already exists");
}
else {
//rename
debugMSG(DEBUG_MSGVISIBILITY_LOW, DEBUG_MSGTYPE_INFO,"RENAMEING: "+file.getAbsolutePath()+" -> "+dest.getAbsolutePath());
boolean ok = file.renameTo(dest);
if (ok) {
data.setAck(commandid, num);
data.sendPackage();
server.log.logCommand(clientID, addr, "RENAME", param, "ACK");
//trigger rename event
HashMap<String, Object> context = buildContextParams(cs);
context.put(TriggerContext.RELATED_FILE,file);
context.put(TriggerContext.RELATED_FILENAME,file.getAbsolutePath());
cs.triggerEvent(Trigger.TRIGGER_RENAME, context);
} else {
data.setError(commandid, num, "file \""+p[0]+"\" could not be renamed to "+p[1], OSDXErrorCode.ERROR_CANNOT_RENAME);
//OLD: data.setError(commandid, num, "file \""+p[0]+"\" could not be renamed to "+p[1]);
data.sendPackage();
server.log.logCommand(clientID, addr, "RENAME", param, "file \""+p[0]+"\" could not be renamed to "+p[1]);
}
}
}
} else {
data.setError(commandid, num, "path must be absolute", OSDXErrorCode.ERROR_PATH_IS_NOT_ABSOLUTE);
//OLD: data.setError(commandid, num, "path must be absolute");
data.sendPackage();
server.log.logCommand(clientID, addr, "RENAME", param, "path must be absolute");
}
}
} else {
data.setError(commandid, num, "missing destination filename parameter", OSDXErrorCode.ERROR_FILENAME_IS_MISSING);
//OLD: data.setError(commandid, num, "missing destination filename parameter");
data.sendPackage();
server.log.logCommand(clientID, addr, "RENAME", param, "missing destination filename parameter");
}
} else {
data.setError(commandid, num, "missing path/file parameter", OSDXErrorCode.ERROR_PATH_IS_MISSING);
//OLD: data.setError(commandid, num, "missing path/file parameter");
data.sendPackage();
server.log.logCommand(clientID, addr, "RENAME", param,"missing path/file parameter" );
}
}
public void handle_file(long commandid, int num, byte code, String param) throws Exception {
ensureAllowed(RightsAndDuties.ALLOW_LIST, commandid, num, "Sorry, no right to read fileinfo.");
if (param!=null) {
File file = null;
if (param.startsWith("/")) {
file = new File(cs.getLocalRootPath()+param);
if (!cs.isAllowed(file)) {
data.setError(commandid, num, "restricted file", OSDXErrorCode.ERROR_FILE_RESTRICTED);
//OLD: data.setError(commandid, num, "restricted file");
data.sendPackage();
server.log.logCommand(clientID, addr, "FILE", param, "restricted file");
}
else if (!file.exists()) {
data.setError(commandid, num, "file does not exist", OSDXErrorCode.ERROR_FILE_NOT_EXISTS);
//OLD: data.setError(commandid, num, "file does not exist");
data.sendPackage();
server.log.logCommand(clientID, addr, "FILE", param, "file does not exist" );
} else {
//ack -> send fileinfo
RemoteFile rf = cs.getAsRemoteFile(file);
if (rf!=null) {
data.setAck(commandid, num, rf.toParamString());
data.sendPackage();
server.log.logCommand(clientID, addr, "FILE", param, "ACK + file info");
} else {
data.setError(commandid, num, "error retrieving file information", OSDXErrorCode.ERROR_RETRIEVING_FILE_INFO);
//OLD: data.setError(commandid, num, "error retrieving file information");
data.sendPackage();
server.log.logCommand(clientID, addr, "FILE", param, "error retrieving file information" );
}
}
} else {
data.setError(commandid, num, "path must be absolute", OSDXErrorCode.ERROR_PATH_IS_NOT_ABSOLUTE);
//OLD: data.setError(commandid, num, "path must be absolute");
data.sendPackage();
server.log.logCommand(clientID, addr, "FILE", param, "path must be absolute" );
}
} else {
data.setError(commandid, num, "missing path/file parameter", OSDXErrorCode.ERROR_PATH_IS_MISSING);
//OLD: data.setError(commandid, num, "missing path/file parameter");
data.sendPackage();
server.log.logCommand(clientID, addr, "FILE", param, "missing path/file parameter" );
}
}
public void handle_list(long commandid, int num, byte code, String param) throws Exception {
ensureAllowed(RightsAndDuties.ALLOW_LIST, commandid, num, "Sorry, no right to read directory info.");
if (param!=null) {
File file = null;
if (param.startsWith("/")) {
file = new File(cs.getLocalRootPath()+param);
if (!cs.isAllowed(file)) {
data.setError(commandid, num, "restricted file", OSDXErrorCode.ERROR_FILE_RESTRICTED);
//OLD: data.setError(commandid, num, "restricted file");
data.sendPackage();
server.log.logCommand(clientID, addr, "LIST", param, "restricted file");
}
else if (!file.exists()) {
data.setError(commandid, num, "directory does not exist", OSDXErrorCode.ERROR_DIRECTORY_NOT_EXISTS);
//OLD: data.setError(commandid, num, "directory does not exist");
data.sendPackage();
server.log.logCommand(clientID, addr, "LIST", param, "directory does not exist");
}
else if (!file.isDirectory()) {
data.setError(commandid, num, "this is not a directory", OSDXErrorCode.ERROR_NOT_A_DIRECTORY);
//OLD: data.setError(commandid, num, "this is not a directory");
data.sendPackage();
server.log.logCommand(clientID, addr, "LIST", param, "this is not a directory");
}
else {
//ack -> send list
StringBuffer b = new StringBuffer();
File[] list = file.listFiles();
for (int i=0;i<list.length;i++) {
RemoteFile rf = cs.getAsRemoteFile(list[i]);
if (rf!=null) {
b.append(rf.toParamString());
}
}
data.setAck(commandid, num, b.toString());
data.sendPackage();
server.log.logCommand(clientID, addr, "LIST", param, "ACK + list of files");
}
} else {
data.setError(commandid, num, "path must be absolute", OSDXErrorCode.ERROR_PATH_IS_NOT_ABSOLUTE);
//OLD: data.setError(commandid, num, "path must be absolute");
data.sendPackage();
server.log.logCommand(clientID, addr, "LIST", param, "path must be absolute");
}
} else {
data.setError(commandid, num, "missing path parameter", OSDXErrorCode.ERROR_PATH_IS_MISSING);
//OLD: data.setError(commandid, num, "missing path parameter");
data.sendPackage();
server.log.logCommand(clientID, addr, "LIST", param, "missing path parameter");
}
}
// public void handle_put_stream(long commandid, int num, byte code, String param) throws Exception {
// ensureAllowed(RightsAndDuties.ALLOW_UPLOAD, commandid, num, "Sorry, no right to upload files.");
// if (param!=null) {
// String[] p = Util.getParams(param);
// File file = null;
//
// if (p[0].startsWith("/")) {
// file = new File(cs.getLocalRootPath()+p[0]);
// if (!cs.isAllowed(file)) {
// data.setError(commandid, num, "restricted file");
// data.sendPackage();
// server.log.logCommand(clientID, addr, "PUT_STREAM", param, "restricted file");
// }
// else if (file.exists()) {
// data.setError(commandid, num, "file already exists");
// data.sendPackage();
// server.log.logCommand(clientID, addr, "PUT_STREAM", param, "file already exists");
// }
// else if (!cs.isAllowedDepthFile(file)) {
// String msg = "max directory depth = "+cs.getRightsAndDuties().getMaxDirectoryDepth();
// data.setError(commandid, num, msg);
// data.sendPackage();
// server.log.logCommand(clientID, addr, "PUT_STREAM", param, msg);
// }
// else {
// //ensure directories exists
// file.getParentFile().mkdirs();
//
// //ack -> ready for upload
// data.setAck(commandid, num);
// data.sendPackage();
// server.log.logCommand(clientID, addr, "PUT_STREAM", param, "ACK");
//
// //trigger upload_start event
//
// HashMap<String, Object> context = buildContextParams(cs);
// context.put(TriggerContext.RELATED_FILE,file);
// context.put(TriggerContext.RELATED_FILENAME,file.getAbsolutePath());
// context.put(TriggerContext.RELATED_START_DATE,System.currentTimeMillis());
// cs.triggerEvent(Trigger.TRIGGER_UPLOAD_START, context);
//
//
// FileUploadInfo info = new FileUploadInfo();
// info.file = file;
// info.length = Long.MAX_VALUE;
// info.out = null;
// info.loaded = 0L;
// info.md5 = null;
// info.uploadStartDatetime = System.currentTimeMillis();
// info.fromStream = true;
// uploads.put(commandid,info);
//
// }
// } else {
// data.setError(commandid, num, "path must be absolute");
// data.sendPackage();
// server.log.logCommand(clientID, addr, "PUT_STREAM", param, "path must be absolute");
// }
// } else {
// data.setError(commandid, num, "missing path/file parameter");
// data.sendPackage();
// server.log.logCommand(clientID, addr, "PUT_STREAM", param, "missing path/file parameter");
// }
// }
/**
* Command: PUT for file uploads
*
* PUT [filename], [length], [MD5] <- OLD_STYLE_PUT
*
* PUT [filename]
* ..stream data packages..
* PUT_EOF
*
* Other control commands (use same commandid):
* PUT_CANCEL -> remove already uploaded part
* PUT_BREAK -> keep uploaded part (can be resumed)
*
*
* PUT_RESUME [filename] -> resume previous upload
*
*/
public void handle_put(long commandid, int num, byte code, String param) throws Exception {
ensureAllowed(RightsAndDuties.ALLOW_UPLOAD, commandid, num, "Sorry, no right to upload files.");
if (param!=null) {
String[] p = Util.getParams(param);
if (p.length<=0) {
data.setError(commandid, num, "missing path/file parameter", OSDXErrorCode.ERROR_PATH_IS_MISSING);
//OLD: data.setError(commandid, num, "missing path/file parameter");
data.sendPackage();
server.log.logCommand(clientID, addr, "PUT", param, "missing path/file parameter");
return;
}
boolean oldstylePUT = p.length>1;
File file = null;
long length = -1L;
if (oldstylePUT) {
try {
length = Long.parseLong(p[1]);
} catch (Exception ex) {}
if (length<0) {
data.setError(commandid, num, "missing or wrong file length parameter", OSDXErrorCode.ERROR_FILE_LENGTH_PARAM);
//OLDdata.setError(commandid, num, "missing or wrong file length parameter");
data.sendPackage();
server.log.logCommand(clientID, addr, "PUT", param, "missing or wrong file length parameter");
return;
}
}
if (p[0].startsWith("/")) {
file = new File(cs.getLocalRootPath()+p[0]);
if (!cs.isAllowed(file)) {
data.setError(commandid, num, "restricted file", OSDXErrorCode.ERROR_FILE_RESTRICTED);
//OLD: data.setError(commandid, num, "restricted file");
data.sendPackage();
server.log.logCommand(clientID, addr, "PUT", param, "restricted file");
}
else if (file.exists()) {
data.setError(commandid, num, "file already exists", OSDXErrorCode.ERROR_FILE_ALREADY_EXISTS);
//OLD: data.setError(commandid, num, "file already exists");
data.sendPackage();
server.log.logCommand(clientID, addr, "PUT", param, "file already exists");
}
else if (!cs.isAllowedDepthFile(file)) {
String msg = "max directory depth = "+cs.getRightsAndDuties().getMaxDirectoryDepth();
data.setError(commandid, num, msg, OSDXErrorCode.ERROR_DIRECTORY_DEPTH);
//OLD: data.setError(commandid, num, msg);
data.sendPackage();
server.log.logCommand(clientID, addr, "PUT", param, msg);
}
else {
//ensure directories exists
file.getParentFile().mkdirs();
//ack -> ready for upload
data.setAck(commandid, num);
data.sendPackage();
server.log.logCommand(clientID, addr, "PUT", param, "ACK");
//trigger upload_start event
long now = System.currentTimeMillis();
HashMap<String, Object> context = buildContextParams(cs);
context.put(TriggerContext.RELATED_FILE,file);
context.put(TriggerContext.RELATED_FILENAME,file.getAbsolutePath());
context.put(TriggerContext.RELATED_START_DATE,now);
cs.triggerEvent(Trigger.TRIGGER_UPLOAD_START, context);
if (length==0) {
file.createNewFile();
context = buildContextParams(cs);
//this can only be triggered in oldstyle PUT method with given file.length
cs.triggerEvent(Trigger.TRIGGER_UPLOAD_END, context);
} else {
FileUploadInfo info = new FileUploadInfo();
info.file = file;
info.out = null;
info.loaded = 0L;
info.md5 = new MD5();
info.canCancel = true;
if (oldstylePUT) {
info.length = length;
if (p.length>2) {
info.md5String = p[2];
} else {
info.md5String = null;
}
} else {
info.length = Long.MAX_VALUE;
info.md5String = null;
info.fromStream = true;
}
info.uploadStartDatetime = now;
uploads.put(commandid,info);
}
}
} else {
data.setError(commandid, num, "path must be absolute", OSDXErrorCode.ERROR_PATH_IS_NOT_ABSOLUTE);
//OLD: data.setError(commandid, num, "path must be absolute");
data.sendPackage();
server.log.logCommand(clientID, addr, "PUT", param, "path must be absolute");
}
}
}
public void handle_put_eof(long commandid, int num, byte code, String param) throws Exception {
FileUploadInfo upload = uploads.get(commandid);
if (upload!=null) {
upload.canCancel = false;
//file length
String[] p = Util.getParams(param); //0 = lenght, 1 = MD5
long length = 0L;
try {
length = Long.parseLong(p[0]);
} catch (Exception ex) {
data.setError(commandid, num, "missing or wrong file length parameter", OSDXErrorCode.ERROR_FILE_LENGTH_PARAM);
//OLDdata.setError(commandid, num, "missing or wrong file length parameter");
server.log.logCommand(clientID, addr, "PUT_EOF", param, "missing or wrong file length parameter");
data.sendPackage();
triggerUploadEndEvent(upload);
return;
}
if (upload.out!=null) {
upload.out.close();
}
if (length==0) {
//empty file (0 bytes)
upload.file.createNewFile();
data.setAckComplete(commandid, num);
data.sendPackage();
triggerUploadEndEvent(upload);
return;
}
if (length>0) {
if (upload.file.length()!=length) {
data.setError(commandid, num, "wrong filesize :: is="+upload.file.length()+", given="+length, OSDXErrorCode.ERROR_WRONG_FILESIZE);
//OLD: data.setError(commandid, num, "wrong filesize :: is="+upload.file.length()+", given="+length);
server.log.logCommand(clientID, addr, "PUT_EOF", param, "wrong filesize :: is="+upload.file.length()+", given="+length);
data.sendPackage();
triggerUploadEndEvent(upload);
return;
}
//md5
upload.md5String = null;
if (p.length>=2) {
upload.md5String = p[1];
}
boolean ok = true;
if (upload.md5String!=null) {
//check md5
try {
byte[] my_md5 = upload.md5.getMD5bytes();
byte[] your_md5 = SecurityHelper.HexDecoder.decode(upload.md5String);
if (!Arrays.equals(my_md5,your_md5)) {
ok = false;
System.out.println("MD5 check failed for "+upload.file.getAbsolutePath());
server.log.logCommand(clientID, addr, "PUT_EOF", param, "MD5 check failed for "+upload.file.getAbsolutePath());
}
// else {
// System.out.println("MD5 check ok for "+upload.file.getAbsolutePath()+" :: "+upload.md5String);
// }
} catch (Exception ex) {
ok = false;
}
if (!ok) {
data.setError(commandid, num, "md5 check failed", OSDXErrorCode.ERROR_MD5_CHECK);
//OLD: data.setError(commandid, num, "md5 check failed");
data.sendPackage();
}
}
if (ok) {
data.setAckComplete(commandid, num);
data.sendPackage();
}
triggerUploadEndEvent(upload);
return;
}
} else {
data.setError(commandid, num, "PUT_EOF :: no matching open stream id = "+commandid, OSDXErrorCode.ERROR_UPLOAD_IS_NULL);
//OLD: data.setError(commandid, num, "PUT_EOF :: no matching open stream id = "+commandid);
server.log.logCommand(clientID, addr, "PUT_EOF", param, "no matching open stream id = "+commandid);
data.sendPackage();
}
}
private void triggerUploadEndEvent(FileUploadInfo upload) {
//trigger upload_end event
HashMap<String, Object> context = buildContextParams(cs);
context.put(TriggerContext.RELATED_FILE,upload.file);
context.put(TriggerContext.RELATED_FILENAME,upload.file.getAbsolutePath());
context.put(TriggerContext.RELATED_START_DATE,upload.uploadStartDatetime);
context.put(TriggerContext.RELATED_END_DATE,System.currentTimeMillis());
cs.triggerEvent(Trigger.TRIGGER_UPLOAD_END, context);
}
public void handle_put_cancel(long commandid, int num, byte code, String param) throws Exception {
FileUploadInfo upload = uploads.get(commandid);
if (upload!=null) {
if (upload.canCancel) {
if (upload.out!=null) {
upload.out.close();
upload.out = null;
}
if (upload.file.exists()) {
upload.file.delete();
}
data.setAck(commandid, num);
data.sendPackage();
} else {
data.setError(commandid, num, "PUT_CANCEL :: canceling upload not possible", OSDXErrorCode.ERROR_UPLOAD_CANCEL);
//OLD: data.setError(commandid, num, "PUT_CANCEL :: canceling upload not possible");
data.sendPackage();
}
} else {
data.setError(commandid, num, "PUT_CANCEL :: no matching open stream id = "+commandid, OSDXErrorCode.ERROR_UPLOAD_IS_NULL);
//OLD: data.setError(commandid, num, "PUT_CANCEL :: no matching open stream id = "+commandid);
data.sendPackage();
}
}
public void handle_put_break(long commandid, int num, byte code, String param) throws Exception {
FileUploadInfo upload = uploads.get(commandid);
if (upload!=null) {
if (upload.canCancel) {
if (upload.out!=null) {
upload.out.close();
upload.out = null;
}
data.setAck(commandid, num);
data.sendPackage();
} else {
data.setError(commandid, num, "PUT_BREAK :: halting upload not possible", OSDXErrorCode.ERROR_UPLOAD_HALT);
//OLD: data.setError(commandid, num, "PUT_BREAK :: halting upload not possible");
data.sendPackage();
}
} else {
data.setError(commandid, num, "PUT_BREAK :: no matching open stream id = "+commandid, OSDXErrorCode.ERROR_UPLOAD_IS_NULL);
//OLD: data.setError(commandid, num, "PUT_BREAK :: no matching open stream id = "+commandid);
data.sendPackage();
}
}
//TODO
public void handle_put_resume(long commandid, int num, byte code, String param) throws Exception {
//if file exists && length > 0
// -> ACK file.length and MD5 so far
//else
// -> ACK 0
ensureAllowed(RightsAndDuties.ALLOW_UPLOAD, commandid, num, "Sorry, no right to upload files.");
if (param!=null) {
String[] p = Util.getParams(param);
if (p.length<=0) {
data.setError(commandid, num, "missing path/file parameter", OSDXErrorCode.ERROR_PATH_IS_MISSING);
//OLD: data.setError(commandid, num, "missing path/file parameter");
data.sendPackage();
server.log.logCommand(clientID, addr, "PUT_RESUME", param, "missing path/file parameter");
return;
}
File file = null;
if (p[0].startsWith("/")) {
file = new File(cs.getLocalRootPath()+p[0]);
if (!cs.isAllowed(file)) {
data.setError(commandid, num, "restricted file", OSDXErrorCode.ERROR_FILE_RESTRICTED);
//OLD: data.setError(commandid, num, "restricted file");
data.sendPackage();
server.log.logCommand(clientID, addr, "PUT_RESUME", param, "restricted file");
}
else if (!cs.isAllowedDepthFile(file)) {
String msg = "max directory depth = "+cs.getRightsAndDuties().getMaxDirectoryDepth();
data.setError(commandid, num, msg, OSDXErrorCode.ERROR_DIRECTORY_DEPTH);
//OLD: data.setError(commandid, num, msg);
data.sendPackage();
server.log.logCommand(clientID, addr, "PUT_RESUME", param, msg);
}
else {
//ensure directories exists
file.getParentFile().mkdirs();
MD5 md5Local = new MD5(); //can only read md5 sum once!!! -> local copy
MD5 md5 = new MD5();
//ack -> ready for resume upload at length
if (file.exists()) {
long length = file.length();
FileInputStream in = new FileInputStream(file);
int read = 0;
byte[] buffer = new byte[16*1024];
while ((read = in.read(buffer))>=0) {
if (read>0) {
md5Local.update(buffer, read);
md5.update(buffer, read);
}
}
data.setAck(commandid, num, Util.makeParamsString(new String[] {""+length,md5Local.getMD5HexString()}));
in.close();
}
else {
data.setAck(commandid, num, Util.makeParamsString(new String[] {"0"}));
}
data.sendPackage();
server.log.logCommand(clientID, addr, "PUT", param, "ACK");
//trigger upload_start event
long now = System.currentTimeMillis();
HashMap<String, Object> context = buildContextParams(cs);
context.put(TriggerContext.RELATED_FILE,file);
context.put(TriggerContext.RELATED_FILENAME,file.getAbsolutePath());
context.put(TriggerContext.RELATED_START_DATE,now);
cs.triggerEvent(Trigger.TRIGGER_UPLOAD_START, context);
FileUploadInfo info = new FileUploadInfo();
info.file = file;
info.out = null;
info.loaded = file.length();
info.md5 = md5;
info.canCancel = true;
info.length = Long.MAX_VALUE;
info.md5String = null;
info.fromStream = true;
info.uploadStartDatetime = now;
uploads.put(commandid,info);
}
} else {
data.setError(commandid, num, "path must be absolute", OSDXErrorCode.ERROR_PATH_IS_NOT_ABSOLUTE);
//OLD: data.setError(commandid, num, "path must be absolute");
data.sendPackage();
server.log.logCommand(clientID, addr, "PUT", param, "path must be absolute");
}
}
}
// public void handle_putpart(long commandid, int num, byte code, String param) throws Exception {
// ensureAllowed(RightsAndDuties.ALLOW_UPLOAD, commandid, num, "Sorry, no right to upload files.");
// if (param!=null) {
// String[] params = Util.getParams(param);
// String filename = null;
// long start = -1;
// int length = -1;
//
// try {
// filename = params[0];
// start = Long.parseLong(params[1]);
// length = Integer.parseInt(params[2]);
// } catch (Exception ex) {
// String resp = "ERROR IN PUTPART :: WRONG FORMAT";
// //log.logCommand(sender.getID(), sender.getRemoteIP(), "PUTPART", param, resp);
// //sender.sendEncryptedText(resp);
// data.setError(commandid, num, "wrong format");
// data.sendPackage();
// }
// File f = state.getWriteFile();
// if (f==null || !filename.equals(f.getName())) {
// String resp = "ERROR IN PUTPART :: PLEASE SEND PUT OR RESUMEPUT COMMAND FIRST";
// log.logCommand(sender.getID(), sender.getRemoteIP(), "PUTPART", param, resp);
// sender.sendEncryptedText(resp);
// }
//
// if (f.exists()) {
// if (f.length()==start) {
// //append
// state.setNextFilePartStart(start);
// state.setNextFilePartLength(length);
// String resp = "ACK PUTPART :: WAITING FOR DATA";
// log.logCommand(sender.getID(), sender.getRemoteIP(), "PUTPART", param, resp);
// sender.sendEncryptedText(resp);
// } else {
// String resp = "ERROR IN PUTPART :: WRONG START POSITION, EXPECTED POS="+f.length();
// log.logCommand(sender.getID(), sender.getRemoteIP(), "PUTPART", param, resp);
// sender.sendEncryptedText(resp);
// }
// } else {
// state.setNextFilePartStart(start);
// state.setNextFilePartLength(length);
// String resp = "ACK PUTPART :: WAITING FOR DATA";
// log.logCommand(sender.getID(), sender.getRemoteIP(), "PUTPART", param, resp);
// sender.sendEncryptedText(resp);
// }
// }
// }
public void handle_resumeput(long commandid, int num, byte code, String param) throws Exception {
ensureAllowed(RightsAndDuties.ALLOW_UPLOAD, commandid, num, "Sorry, no right to upload files.");
if (param!=null) {
//RESUMEPUT "filename",length
String[] p = Util.getParams(param);
File file = null;
long length = -1L;
try {
length = Long.parseLong(p[1]);
} catch (Exception ex) {}
if (length<0) {
data.setError(commandid, num, "missing or wrong file length parameter", OSDXErrorCode.ERROR_FILE_LENGTH_PARAM);
//OLD: data.setError(commandid, num, "missing or wrong file length parameter");
data.sendPackage();
server.log.logCommand(clientID, addr, "RESUMEPUT", param, "missing or wrong file length parameter");
} else {
if (p[0].startsWith("/")) {
file = new File(cs.getLocalRootPath()+p[0]);
if (!cs.isAllowed(file)) {
data.setError(commandid, num, "restricted file", OSDXErrorCode.ERROR_FILE_RESTRICTED);
//OLD: data.setError(commandid, num, "restricted file");
data.sendPackage();
server.log.logCommand(clientID, addr, "RESUMEPUT", param, "restricted file");
}
else if (!cs.isAllowedDepthFile(file)) {
String msg = "max directory depth = "+cs.getRightsAndDuties().getMaxDirectoryDepth();
data.setError(commandid, num, msg, OSDXErrorCode.ERROR_DIRECTORY_DEPTH);
//OLD: data.setError(commandid, num, msg);
data.sendPackage();
server.log.logCommand(clientID, addr, "RESUMEPUT", param, msg);
}
else if (file.exists()) {
long loaded = file.length();
if (loaded>=length) {
data.setAck(commandid, num, "upload already complete");
data.sendPackage();
server.log.logCommand(clientID, addr, "RESUMEPUT", param, "upload already complete");
} else {
//ack -> ready for upload
data.setAck(commandid, num,""+loaded); //Startpos = file.length
data.sendPackage();
server.log.logCommand(clientID, addr, "RESUMEPUT", param, "ACK");
//trigger upload_start event
HashMap<String, Object> context = buildContextParams(cs);
context.put(TriggerContext.RELATED_FILE,file);
context.put(TriggerContext.RELATED_FILENAME,file.getAbsolutePath());
context.put(TriggerContext.RELATED_START_DATE, System.currentTimeMillis());
cs.triggerEvent(Trigger.TRIGGER_UPLOAD_START, context);
if (length==0) {
file.createNewFile();
context = buildContextParams(cs);
cs.triggerEvent(Trigger.TRIGGER_UPLOAD_END, context);
} else {
FileUploadInfo info = new FileUploadInfo();
info.file = file;
info.length = length;
info.out = null;
info.loaded = loaded;
if (p.length>2) {
info.md5String = p[2];
} else {
info.md5String = null;
}
info.uploadStartDatetime = System.currentTimeMillis();
uploads.put(commandid,info);
}
}
} else {
//file does not exists -> normal PUT
//ensure directories exists
file.getParentFile().mkdirs();
//ack -> ready for upload
data.setAck(commandid, num,"0"); //Startpos 0
data.sendPackage();
server.log.logCommand(clientID, addr, "RESUMEPUT", param, "ACK");
//trigger upload_start event
HashMap<String, Object> context = buildContextParams(cs);
context.put(TriggerContext.RELATED_FILE,file);
context.put(TriggerContext.RELATED_FILENAME,file.getAbsolutePath());
context.put(TriggerContext.RELATED_START_DATE, System.currentTimeMillis());
cs.triggerEvent(Trigger.TRIGGER_UPLOAD_START, context);
if (length==0) {
file.createNewFile();
context = buildContextParams(cs);
cs.triggerEvent(Trigger.TRIGGER_UPLOAD_END, context);
} else {
FileUploadInfo info = new FileUploadInfo();
info.file = file;
info.length = length;
info.out = null;
info.loaded = 0L;
if (p.length>2) {
info.md5String = p[2];
} else {
info.md5String = null;
}
info.uploadStartDatetime = System.currentTimeMillis();
uploads.put(commandid,info);
}
}
} else {
data.setError(commandid, num, "path must be absolute", OSDXErrorCode.ERROR_PATH_IS_NOT_ABSOLUTE);
//OLD: data.setError(commandid, num, "path must be absolute");
data.sendPackage();
server.log.logCommand(clientID, addr, "RESUMEPUT", param, "path must be absolute");
}
}
}
}
public void handle_get(long commandid, int num, byte code, String param) throws Exception {
ensureAllowed(RightsAndDuties.ALLOW_DOWNLOAD, commandid, num, "Sorry, no right to download files.");
if (param!=null) {
File file = null;
if (param.startsWith("/")) {
file = new File(cs.getLocalRootPath()+param);
if (!cs.isAllowed(file)) {
data.setError(commandid, num, "restricted file", OSDXErrorCode.ERROR_FILE_RESTRICTED);
//OLD: data.setError(commandid, num, "restricted file");
data.sendPackage();
server.log.logCommand(clientID, addr, "GET", param, "restricted file");
}
else if (!file.exists()) {
data.setError(commandid, num, "file does not exist", OSDXErrorCode.ERROR_FILE_NOT_EXISTS);
//OLD: data.setError(commandid, num, "file does not exist");
data.sendPackage();
server.log.logCommand(clientID, addr, "GET", param, "file does not exist");
}
else if (file.isDirectory()) {
data.setError(commandid, num, "directory download not implemented", OSDXErrorCode.ERROR_DIRECTORY_DOWNLOAD_NOT_IMPLEMENTED);
//OLD: data.setError(commandid, num, "directory download not implemented");
data.sendPackage();
server.log.logCommand(clientID, addr, "GET", param, "directory download not implemented");
} else {
//send ack with file length and md5
String md5 = null;
if (file.length()<=maxFilelengthForMD5) {
try {
md5 = SecurityHelper.HexDecoder.encode(SecurityHelper.getMD5(file));
} catch (Exception ex) {
debugMSG(DEBUG_MSGVISIBILITY_LOW, DEBUG_MSGTYPE_INFO,"Error calculating md5 hash of "+file.getAbsolutePath());
ex.printStackTrace();
md5 = null;
}
}
String[] response;
if (md5==null) {
response = new String[]{""+file.length()};
} else {
response = new String[]{""+file.length(),md5};
}
data.setAck(commandid, num, Util.makeParamsString(response));
data.sendPackage();
server.log.logCommand(clientID, addr, "GET", param, "ACK + filelength + md5");
//trigger download_start event
HashMap<String, Object> context = buildContextParams(cs);
context.put(TriggerContext.RELATED_FILE,file);
context.put(TriggerContext.RELATED_FILENAME,file.getAbsolutePath());
context.put(TriggerContext.RELATED_START_DATE, System.currentTimeMillis());
cs.triggerEvent(Trigger.TRIGGER_DOWNLOAD_START, context);
//send file data
FileInputStream fileIn = new FileInputStream(file);
int read = 0;
byte[] content = new byte[maxPacketSize];
while ((read = fileIn.read(content))>0) {
num++;
if (read<maxPacketSize) {
data.setData(commandid, num, Arrays.copyOf(content, read));
} else {
data.setData(commandid, num, content);
}
data.sendPackage();
//TODO it would be a good idea to check for error or cancel messages
// when sending large files
}
//trigger download_end event
context.put(TriggerContext.RELATED_END_DATE,System.currentTimeMillis());
cs.triggerEvent(Trigger.TRIGGER_DOWNLOAD_END, context);
}
} else {
data.setError(commandid, num, "path must be absolute", OSDXErrorCode.ERROR_PATH_IS_NOT_ABSOLUTE);
//OLD: data.setError(commandid, num, "path must be absolute");
data.sendPackage();
server.log.logCommand(clientID, addr, "GET", param, "path must be absolute");
}
} else {
data.setError(commandid, num, "missing path/file parameter", OSDXErrorCode.ERROR_PATH_IS_MISSING);
//OLD: data.setError(commandid, num, "missing path/file parameter");
data.sendPackage();
server.log.logCommand(clientID, addr, "GET", param, "missing path/file parameter");
}
}
public void handle_resumeget(long commandid, int num, byte code, String param) throws Exception {
ensureAllowed(RightsAndDuties.ALLOW_DOWNLOAD, commandid, num, "Sorry, no right to download files.");
if (param!=null) {
//RESUMEPUT "filename",length
String[] p = Util.getParams(param);
File file = null;
long length = -1L;
try {
length = Long.parseLong(p[1]);
} catch (Exception ex) {}
if (length<0) {
data.setError(commandid, num, "missing or wrong file length parameter", OSDXErrorCode.ERROR_FILE_LENGTH_PARAM);
//OLDdata.setError(commandid, num, "missing or wrong file length parameter");
data.sendPackage();
server.log.logCommand(clientID, addr, "RESUMEGET", param, "missing or wrong file length parameter");
} else {
if (p[0].startsWith("/")) {
file = new File(cs.getLocalRootPath()+p[0]);
if (!cs.isAllowed(file)) {
data.setError(commandid, num, "restricted file", OSDXErrorCode.ERROR_FILE_RESTRICTED);
//OLD: data.setError(commandid, num, "restricted file");
data.sendPackage();
server.log.logCommand(clientID, addr, "RESUMEGET", param, "restricted file");
}
else if (!file.exists()) {
data.setError(commandid, num, "file does not exist", OSDXErrorCode.ERROR_FILE_NOT_EXISTS);
//OLD:data.setError(commandid, num, "file does not exist");
data.sendPackage();
server.log.logCommand(clientID, addr, "RESUMEGET", param, "file does not exist");
}
else if (file.isDirectory()) {
data.setError(commandid, num, "directory download not implemented", OSDXErrorCode.ERROR_DIRECTORY_DOWNLOAD_NOT_IMPLEMENTED);
//OLD: data.setError(commandid, num, "directory download not implemented");
data.sendPackage();
server.log.logCommand(clientID, addr, "RESUMEGET", param, "directory download not implemented");
} else {
//send ack with file length and md5
String md5 = null;
if (file.length()<=maxFilelengthForMD5) {
try {
md5 = SecurityHelper.HexDecoder.encode(SecurityHelper.getMD5(file));
} catch (Exception ex) {
debugMSG(DEBUG_MSGVISIBILITY_LOW, DEBUG_MSGTYPE_INFO,"Error calculating md5 hash of "+file.getAbsolutePath());
ex.printStackTrace();
md5 = null;
}
}
String[] response;
if (md5==null) {
response = new String[]{""+file.length()};
} else {
response = new String[]{""+file.length(),md5};
}
data.setAck(commandid, num, Util.makeParamsString(response));
data.sendPackage();
server.log.logCommand(clientID, addr, "RESUMEGET", param, "ACK + filelength + md5");
//trigger download_start event
HashMap<String, Object> context = buildContextParams(cs);
context.put(TriggerContext.RELATED_FILE,file);
context.put(TriggerContext.RELATED_FILENAME,file.getAbsolutePath());
context.put(TriggerContext.RELATED_START_DATE, System.currentTimeMillis());
cs.triggerEvent(Trigger.TRIGGER_DOWNLOAD_START, context);
//send file data, starting at position "length"
FileInputStream fileIn = new FileInputStream(file);
fileIn.skip(length);
int read = 0;
byte[] content = new byte[maxPacketSize];
while ((read = fileIn.read(content))>0) {
num++;
if (read<maxPacketSize) {
data.setData(commandid, num, Arrays.copyOf(content, read));
} else {
data.setData(commandid, num, content);
}
data.sendPackage();
//TODO it would be a good idea to check for error or cancel messages
// when sending large files
}
//trigger download_end event
context.put(TriggerContext.RELATED_END_DATE, System.currentTimeMillis());
cs.triggerEvent(Trigger.TRIGGER_DOWNLOAD_END, context);
}
} else {
data.setError(commandid, num, "path must be absolute", OSDXErrorCode.ERROR_PATH_IS_NOT_ABSOLUTE);
//OLD: data.setError(commandid, num, "path must be absolute");
data.sendPackage();
server.log.logCommand(clientID, addr, "RESUMEGET", param, "path must be absolute");
}
}
} else {
data.setError(commandid, num, "missing path/file parameter", OSDXErrorCode.ERROR_PATH_IS_MISSING);
//OLD: data.setError(commandid, num, "missing path/file parameter");
data.sendPackage();
server.log.logCommand(clientID, addr, "RESUMEGET", param,"missing path/file parameter" );
}
}
public void handle_quit(long commandid, int num, byte code, String param) throws Exception {
debugMSG(DEBUG_MSGVISIBILITY_HIGH, DEBUG_MSGTYPE_INFO,"handle_quit");
//do nothing... socket closes automatically on client disconnection
server.log.logCommand(clientID, addr, "QUIT", param, "");
//trigger logout event
HashMap<String, Object> context = buildContextParamsWithDate(cs);
cs.triggerEvent(Trigger.TRIGGER_LOGOUT, context);
}
private boolean ensureAllowed(int rightType, long id, int num, String msg) throws Exception {
if (cs==null || cs.getRightsAndDuties()==null || !cs.getRightsAndDuties().hasRight(rightType)) {
data.setError(id, num, msg, OSDXErrorCode.ERROR_RIGHTS_AND_DUTIES);
//OLD: data.setError(id, num, msg);
data.sendPackage();
return false;
}
return true;
}
private class FileUploadInfo {
public File file = null;
public long length = -1L;
public long loaded = 0L;
public FileOutputStream out = null;
public String md5String = null;
public MD5 md5 = null;
public long uploadStartDatetime = -1L;
public boolean fromStream = false;
public boolean canCancel = false;
}
private final static void debugMSG(int myLevel, int debugType, String msg) {
if(DEBUG_LEVEL >= myLevel) {
System.out.println(msg);
}
}
}