/*
* This file is part of DrFTPD, Distributed FTP Daemon.
*
* DrFTPD is free software; you can redistribute it and/or modify it under the
* terms of the GNU General Public License as published by the Free Software
* Foundation; either version 2 of the License, or (at your option) any later
* version.
*
* DrFTPD 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 General Public License along with
* DrFTPD; if not, write to the Free Software Foundation, Inc., 59 Temple Place,
* Suite 330, Boston, MA 02111-1307 USA
*/
//modifications used
//identifier:regex - regex filter
package net.sf.drftpd.master.command.plugins;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.StringTokenizer;
import javax.net.ServerSocketFactory;
import javax.net.SocketFactory;
import javax.net.ssl.HandshakeCompletedEvent;
import javax.net.ssl.HandshakeCompletedListener;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocket;
import net.sf.drftpd.NoAvailableSlaveException;
import net.sf.drftpd.NoSFVEntryException;
import net.sf.drftpd.ObjectNotFoundException;
import net.sf.drftpd.SlaveUnavailableException;
import net.sf.drftpd.event.TransferEvent;
import net.sf.drftpd.master.BaseFtpConnection;
import net.sf.drftpd.master.FtpRequest;
import net.sf.drftpd.master.command.CommandManager;
import net.sf.drftpd.master.command.CommandManagerFactory;
import net.sf.drftpd.master.config.ZipscriptConfig;
import org.apache.log4j.Logger;
import org.drftpd.ActiveConnection;
import org.drftpd.Bytes;
import org.drftpd.Checksum;
import org.drftpd.PassiveConnection;
import org.drftpd.SFVFile;
import org.drftpd.SSLGetContext;
import org.drftpd.commands.CommandHandler;
import org.drftpd.commands.CommandHandlerFactory;
import org.drftpd.commands.Reply;
import org.drftpd.commands.ReplyException;
import org.drftpd.commands.ReplySlaveUnavailableException;
import org.drftpd.commands.UnhandledCommandException;
import org.drftpd.commands.UserManagement;
import org.drftpd.master.RemoteSlave;
import org.drftpd.master.RemoteTransfer;
import org.drftpd.remotefile.FileStillTransferringException;
import org.drftpd.remotefile.LinkedRemoteFile;
import org.drftpd.remotefile.LinkedRemoteFileInterface;
import org.drftpd.remotefile.ListUtils;
import org.drftpd.remotefile.StaticRemoteFile;
import org.drftpd.slave.ConnectInfo;
import org.drftpd.slave.Connection;
import org.drftpd.slave.RemoteIOException;
import org.drftpd.slave.Transfer;
import org.drftpd.slave.TransferFailedException;
import org.drftpd.slave.TransferStatus;
import org.drftpd.usermanager.UserFileException;
import org.tanesha.replacer.ReplacerEnvironment;
import org.drftpd.SFVFile.SFVStatus;
/**
* @author mog
* @author zubov
* @version $Id: DataConnectionHandler.java 1593 2007-01-31 22:56:52Z zubov $
*/
public class DataConnectionHandler implements CommandHandler, CommandHandlerFactory,
Cloneable, HandshakeCompletedListener {
private static final Logger logger = Logger.getLogger(DataConnectionHandler.class);
private SSLContext _ctx;
private boolean _encryptedDataChannel;
private boolean _SSLHandshakeClientMode = false;
protected boolean _isPasv = false;
protected boolean _isPort = false;
/**
* Holds the address that getDataSocket() should connect to in PORT mode.
*/
private InetSocketAddress _portAddress;
protected boolean _preTransfer = false;
private RemoteSlave _preTransferRSlave;
private long _resumePosition = 0;
private RemoteSlave _rslave;
/**
* ServerSocket for PASV mode.
*/
private PassiveConnection _passiveConnection;
private RemoteTransfer _transfer;
private LinkedRemoteFileInterface _transferFile;
private char _type = 'A';
private boolean _handshakeCompleted;
public DataConnectionHandler() {
super();
_handshakeCompleted = false;
try {
_ctx = SSLGetContext.getSSLContext();
} catch (FileNotFoundException e) {
_ctx = null;
logger.warn("Couldn't load SSLContext, SSL/TLS disabled");
} catch (Exception e) {
_ctx = null;
logger.warn("Couldn't load SSLContext, SSL/TLS disabled", e);
}
}
private Reply doAUTH(BaseFtpConnection conn) {
if (_ctx == null) {
return new Reply(500, "TLS not configured");
}
Socket s = conn.getControlSocket();
//reply success
conn.getControlWriter().write(new Reply(234,
conn.getRequest().getCommandLine() + " successful").toString());
conn.getControlWriter().flush();
SSLSocket s2 = null;
try {
s2 = (SSLSocket) _ctx.getSocketFactory().createSocket(s,
s.getInetAddress().getHostAddress(), s.getPort(), true);
conn.setControlSocket(s2);
s2.setUseClientMode(false);
s2.addHandshakeCompletedListener(this);
s2.startHandshake();
while(!_handshakeCompleted) {
synchronized(this) {
try {
wait(10000);
} catch (InterruptedException e) {
s2.close();
conn.stop("Took too long to negotiate SSL");
return new Reply(400, "Took too long to negotiate SSL");
}
}
}
// reset for possible auth later
_handshakeCompleted = false;
} catch (IOException e) {
logger.warn("", e);
if (s2 != null) {
try {
s2.close();
} catch (IOException e2){
logger.debug("error closing SSLSocket connection");
}
}
conn.stop(e.getMessage());
return null;
}
s2 = null;
return null;
}
/**
* MODE <SP>
*
* The argument is a single Telnet character code specifying the data
* transfer modes described in the Section on Transmission Modes.
*/
private Reply doMODE(BaseFtpConnection conn) {
FtpRequest request = conn.getRequest();
// argument check
if (!request.hasArgument()) {
return Reply.RESPONSE_501_SYNTAX_ERROR;
}
if (request.getArgument().equalsIgnoreCase("S")) {
return Reply.RESPONSE_200_COMMAND_OK;
}
return Reply.RESPONSE_504_COMMAND_NOT_IMPLEMENTED_FOR_PARM;
}
/**
* PASV <CRLF>
*
* This command requests the server-DTP to "listen" on a data port (which is
* not its default data port) and to wait for a connection rather than
* initiate one upon receipt of a transfer command. The response to this
* command includes the host and port address this server is listening on.
*/
private Reply doPASVandCPSV(BaseFtpConnection conn) {
if (!_preTransfer) {
return new Reply(500,
"You need to use a client supporting PRET (PRE Transfer) to use PASV");
}
//reset();
_preTransfer = false;
if (isPort() == true) {
throw new RuntimeException();
}
if (conn.getRequest().getCommand().equals("CPSV")) {
_SSLHandshakeClientMode=true;
}
InetSocketAddress address = null;
if (_preTransferRSlave == null) {
try {
_passiveConnection = new PassiveConnection(_encryptedDataChannel ? _ctx : null, conn.getGlobalContext().getPortRange(), false);
try {
address = new InetSocketAddress(conn.getGlobalContext()
.getConfig().getPasvAddress(), _passiveConnection
.getLocalPort());
} catch (NullPointerException e) {
address = new InetSocketAddress(conn.getControlSocket()
.getLocalAddress(), _passiveConnection.getLocalPort());
}
_isPasv = true;
} catch (Exception ex) {
logger.warn("", ex);
return new Reply(550, ex.getMessage());
}
} else {
try {
String index = _preTransferRSlave.issueListenToSlave(_encryptedDataChannel, _SSLHandshakeClientMode);
ConnectInfo ci = _preTransferRSlave.fetchTransferResponseFromIndex(index);
_transfer = _preTransferRSlave.getTransfer(ci.getTransferIndex());
address = new InetSocketAddress(_preTransferRSlave.getPASVIP(),_transfer.getAddress().getPort());
_isPasv = true;
} catch (SlaveUnavailableException e) {
reset(conn);
return Reply.RESPONSE_530_SLAVE_UNAVAILABLE;
} catch (RemoteIOException e) {
reset(conn);
_preTransferRSlave.setOffline(
"Slave could not listen for a connection");
logger.error("Slave could not listen for a connection", e);
return new Reply(500,
"Slave could not listen for a connection");
}
}
if (conn.getRequest().getCommand().equals("CPSV")) {
// can only reset it if the transfer was setup with CPSV
_SSLHandshakeClientMode = false;
}
if (address.getAddress() == null || address.getAddress().getHostAddress() == null) {
return new Reply(500, "Address for is unresolvable, check pasv_addr setting");
}
String addrStr = address.getAddress().getHostAddress().replace('.', ',') +
',' + (address.getPort() >> 8) + ',' + (address.getPort() & 0xFF);
return new Reply(227, "Entering Passive Mode (" + addrStr + ").");
}
private Reply doPBSZ(BaseFtpConnection conn)
throws UnhandledCommandException {
String cmd = conn.getRequest().getArgument();
if ((cmd == null) || !cmd.equals("0")) {
return Reply.RESPONSE_501_SYNTAX_ERROR;
}
return Reply.RESPONSE_200_COMMAND_OK;
}
/**
* PORT <SP>
*
* The argument is a HOST-PORT specification for the data port to be used in
* data connection. There are defaults for both the user and server data
* ports, and under normal circumstances this command and its reply are not
* needed. If this command is used, the argument is the concatenation of a
* 32-bit internet host address and a 16-bit TCP port address. This address
* information is broken into 8-bit fields and the value of each field is
* transmitted as a decimal number (in character string representation). The
* fields are separated by commas. A port command would be:
*
* PORT h1,h2,h3,h4,p1,p2
*
* where h1 is the high order 8 bits of the internet host address.
*/
private Reply doPORT(BaseFtpConnection conn) {
FtpRequest request = conn.getRequest();
reset(conn);
InetAddress clientAddr = null;
// argument check
if (!request.hasArgument()) {
//Syntax error in parameters or arguments
return Reply.RESPONSE_501_SYNTAX_ERROR;
}
StringTokenizer st = new StringTokenizer(request.getArgument(), ",");
if (st.countTokens() != 6) {
return Reply.RESPONSE_501_SYNTAX_ERROR;
}
// get data server
String dataSrvName = st.nextToken() + '.' + st.nextToken() + '.' +
st.nextToken() + '.' + st.nextToken();
try {
clientAddr = InetAddress.getByName(dataSrvName);
} catch (UnknownHostException ex) {
return Reply.RESPONSE_501_SYNTAX_ERROR;
}
String portHostAddress = clientAddr.getHostAddress();
String clientHostAddress = conn.getControlSocket().getInetAddress()
.getHostAddress();
if ((portHostAddress.startsWith("192.168.") &&
!clientHostAddress.startsWith("192.168.")) ||
(portHostAddress.startsWith("10.") &&
!clientHostAddress.startsWith("10."))) {
Reply response = new Reply(501);
response.addComment("==YOU'RE BEHIND A NAT ROUTER==");
response.addComment(
"Configure the firewall settings of your FTP client");
response.addComment(" to use your real IP: " +
conn.getControlSocket().getInetAddress().getHostAddress());
response.addComment("And set up port forwarding in your router.");
response.addComment(
"Or you can just use a PRET capable client, see");
response.addComment(" http://drftpd.org/ for PRET capable clients");
return response;
}
int clientPort;
// get data server port
try {
int hi = Integer.parseInt(st.nextToken());
int lo = Integer.parseInt(st.nextToken());
clientPort = (hi << 8) | lo;
} catch (NumberFormatException ex) {
reset(conn);
return Reply.RESPONSE_501_SYNTAX_ERROR;
//out.write(ftpStatus.getResponse(552, request, user, null));
}
_isPort = true;
_portAddress = new InetSocketAddress(clientAddr, clientPort);
if (portHostAddress.startsWith("127.")) {
return new Reply(200,
"Ok, but distributed transfers won't work with local addresses");
}
//Notify the user that this is not his IP.. Good for NAT users that
// aren't aware that their IP has changed.
if (!clientAddr.equals(conn.getControlSocket().getInetAddress())) {
return new Reply(200,
"FXP allowed. If you're not FXPing then set your IP to " +
conn.getControlSocket().getInetAddress().getHostAddress() +
" (usually in firewall settings)");
}
return Reply.RESPONSE_200_COMMAND_OK;
}
private Reply doPRET(BaseFtpConnection conn) {
reset(conn);
FtpRequest request = conn.getRequest();
FtpRequest ghostRequest = new FtpRequest(request.getArgument());
String cmd = ghostRequest.getCommand();
if (cmd.equals("LIST") || cmd.equals("NLST") || cmd.equals("MLSD")) {
_preTransferRSlave = null;
_preTransfer = true;
return new Reply(200, "OK, will use master for upcoming transfer");
} else if (cmd.equals("RETR")) {
try {
LinkedRemoteFileInterface downFile = conn.getCurrentDirectory()
.lookupFile(ghostRequest.getArgument());
_preTransferRSlave = conn.getGlobalContext().getSlaveSelectionManager().getASlave(downFile.getAvailableSlaves(),
Transfer.TRANSFER_SENDING_DOWNLOAD, conn, downFile);
_preTransfer = true;
return new Reply(200,
"OK, will use " + _preTransferRSlave.getName() +
" for upcoming transfer");
} catch (NoAvailableSlaveException e) {
return Reply.RESPONSE_530_SLAVE_UNAVAILABLE;
} catch (FileNotFoundException e) {
return Reply.RESPONSE_550_REQUESTED_ACTION_NOT_TAKEN;
}
} else if (cmd.equals("STOR")) {
LinkedRemoteFile.NonExistingFile nef = conn.getCurrentDirectory()
.lookupNonExistingFile(ghostRequest.getArgument());
if (nef.exists()) {
return Reply.RESPONSE_553_REQUESTED_ACTION_NOT_TAKEN_FILE_EXISTS;
}
if (!ListUtils.isLegalFileName(nef.getPath())) {
return Reply.RESPONSE_553_REQUESTED_ACTION_NOT_TAKEN;
}
try {
_preTransferRSlave = conn.getGlobalContext().getSlaveSelectionManager().getASlave(conn.getGlobalContext()
.getSlaveManager()
.getAvailableSlaves(),
Transfer.TRANSFER_RECEIVING_UPLOAD, conn,
nef.getFile());
_preTransfer = true;
return new Reply(200,
"OK, will use " + _preTransferRSlave.getName() +
" for upcoming transfer");
} catch (NoAvailableSlaveException e) {
return Reply.RESPONSE_530_SLAVE_UNAVAILABLE;
}
} else {
return Reply.RESPONSE_504_COMMAND_NOT_IMPLEMENTED_FOR_PARM;
}
}
private Reply doSSCN(BaseFtpConnection conn) {
if (_ctx == null) {
return new Reply(500, "TLS not configured");
}
if (!(conn.getControlSocket() instanceof SSLSocket)) {
return new Reply(500, "You are not on a secure channel");
}
if (!_encryptedDataChannel) {
return new Reply(500, "SSCN only works for encrypted transfers");
}
if (conn.getRequest().hasArgument()) {
if (conn.getRequest().getArgument().equalsIgnoreCase("ON")) {
_SSLHandshakeClientMode = true;
}
if (conn.getRequest().getArgument().equalsIgnoreCase("OFF")) {
_SSLHandshakeClientMode = false;
}
}
return new Reply(220, "SSCN:"
+ (_SSLHandshakeClientMode ? "CLIENT" : "SERVER") + " METHOD");
}
private Reply doPROT(BaseFtpConnection conn)
throws UnhandledCommandException {
if (_ctx == null) {
return new Reply(500, "TLS not configured");
}
if (!(conn.getControlSocket() instanceof SSLSocket)) {
return new Reply(500, "You are not on a secure channel");
}
FtpRequest req = conn.getRequest();
if (!req.hasArgument()) {
//clear
_encryptedDataChannel = false;
return Reply.RESPONSE_200_COMMAND_OK;
}
if (!req.hasArgument() || (req.getArgument().length() != 1)) {
return Reply.RESPONSE_501_SYNTAX_ERROR;
}
switch (Character.toUpperCase(req.getArgument().charAt(0))) {
case 'C':
//clear
_encryptedDataChannel = false;
return Reply.RESPONSE_200_COMMAND_OK;
case 'P':
//private
_encryptedDataChannel = true;
return Reply.RESPONSE_200_COMMAND_OK;
default:
return Reply.RESPONSE_501_SYNTAX_ERROR;
}
}
/**
* REST <SP>
*
* The argument field represents the server marker at which file transfer is
* to be restarted. This command does not cause file transfer but skips over
* the file to the specified data checkpoint. This command shall be
* immediately followed by the appropriate FTP service command which shall
* cause file transfer to resume.
*/
private Reply doREST(BaseFtpConnection conn) {
FtpRequest request = conn.getRequest();
// argument check
if (!request.hasArgument()) {
reset(conn);
return Reply.RESPONSE_501_SYNTAX_ERROR;
}
String skipNum = request.getArgument();
try {
_resumePosition = Long.parseLong(skipNum);
} catch (NumberFormatException ex) {
reset(conn);
return Reply.RESPONSE_501_SYNTAX_ERROR;
}
if (_resumePosition < 0) {
_resumePosition = 0;
reset(conn);
return Reply.RESPONSE_501_SYNTAX_ERROR;
}
return Reply.RESPONSE_350_PENDING_FURTHER_INFORMATION;
}
private Reply doSITE_RESCAN(BaseFtpConnection conn) {
FtpRequest request = conn.getRequest();
boolean forceRescan = (request.hasArgument() &&
request.getArgument().equalsIgnoreCase("force"));
LinkedRemoteFileInterface directory = conn.getCurrentDirectory();
SFVFile sfv;
try {
sfv = conn.getCurrentDirectory().lookupSFVFile();
} catch (Exception e) {
return new Reply(200, "Error getting SFV File: " +
e.getMessage());
}
PrintWriter out = conn.getControlWriter();
for (Iterator i = sfv.getEntries().entrySet().iterator(); i.hasNext();) {
Map.Entry entry = (Map.Entry) i.next();
String fileName = (String) entry.getKey();
Long checkSum = (Long) entry.getValue();
LinkedRemoteFileInterface file;
try {
file = directory.lookupFile(fileName);
} catch (FileNotFoundException ex) {
out.write("200- SFV: " +
Checksum.formatChecksum(checkSum.longValue()) + " SLAVE: " +
fileName + " MISSING" + BaseFtpConnection.NEWLINE);
continue;
}
String status;
long fileCheckSum = 0;
try {
if (forceRescan) {
fileCheckSum = file.getCheckSumFromSlave();
} else {
fileCheckSum = file.getCheckSum();
}
} catch (NoAvailableSlaveException e1) {
out.println("200- " + fileName + "SFV: " +
Checksum.formatChecksum(checkSum.longValue()) +
" SLAVE: OFFLINE");
continue;
} catch (IOException ex) {
out.print("200- " + fileName + " SFV: " +
Checksum.formatChecksum(checkSum.longValue()) +
" SLAVE: IO error: " + ex.getMessage());
continue;
}
if (fileCheckSum == 0L) {
status = "FAILED - failed to checksum file";
} else if (checkSum.longValue() == fileCheckSum) {
status = "OK";
} else {
status = "FAILED - checksum mismatch";
}
out.println("200- " + fileName + " SFV: " +
Checksum.formatChecksum(checkSum.longValue()) + " SLAVE: " +
Checksum.formatChecksum(fileCheckSum) + " " + status);
continue;
}
return Reply.RESPONSE_200_COMMAND_OK;
}
private Reply doSITE_XDUPE(BaseFtpConnection conn) {
return Reply.RESPONSE_502_COMMAND_NOT_IMPLEMENTED;
// resetState();
//
// if (!request.hasArgument()) {
// if (this.xdupe == 0) {
// out.println("200 Extended dupe mode is disabled.");
// } else {
// out.println(
// "200 Extended dupe mode " + this.xdupe + " is enabled.");
// }
// return;
// }
//
// short myXdupe;
// try {
// myXdupe = Short.parseShort(request.getArgument());
// } catch (NumberFormatException ex) {
// out.print(FtpResponse.RESPONSE_501_SYNTAX_ERROR);
// return;
// }
//
// if (myXdupe > 0 || myXdupe < 4) {
// out.print(
// FtpResponse.RESPONSE_504_COMMAND_NOT_IMPLEMENTED_FOR_PARM);
// return;
// }
// this.xdupe = myXdupe;
// out.println("200 Activated extended dupe mode " + myXdupe + ".");
}
/**
* STRU <SP> <structure-code> <CRLF>
*
* The argument is a single Telnet character code specifying file structure.
*/
private Reply doSTRU(BaseFtpConnection conn) {
FtpRequest request = conn.getRequest();
// argument check
if (!request.hasArgument()) {
return Reply.RESPONSE_501_SYNTAX_ERROR;
}
if (request.getArgument().equalsIgnoreCase("F")) {
return Reply.RESPONSE_200_COMMAND_OK;
}
return Reply.RESPONSE_504_COMMAND_NOT_IMPLEMENTED_FOR_PARM;
}
/**
* SYST <CRLF>
*
* This command is used to find out the type of operating system at the
* server.
*/
private Reply doSYST(BaseFtpConnection conn) {
/*
* String systemName = System.getProperty("os.name"); if(systemName ==
* null) { systemName = "UNKNOWN"; } else { systemName =
* systemName.toUpperCase(); systemName = systemName.replace(' ', '-'); }
* String args[] = {systemName};
*/
return Reply.RESPONSE_215_SYSTEM_TYPE;
//String args[] = { "UNIX" };
//out.write(ftpStatus.getResponse(215, request, user, args));
}
/**
* TYPE <SP> <type-code> <CRLF>
*
* The argument specifies the representation type.
*/
private Reply doTYPE(BaseFtpConnection conn) {
FtpRequest request = conn.getRequest();
// get type from argument
if (!request.hasArgument()) {
return Reply.RESPONSE_501_SYNTAX_ERROR;
}
// set it
if (setType(request.getArgument().charAt(0))) {
return Reply.RESPONSE_200_COMMAND_OK;
}
return Reply.RESPONSE_504_COMMAND_NOT_IMPLEMENTED_FOR_PARM;
}
public Reply execute(BaseFtpConnection conn)
throws ReplyException {
String cmd = conn.getRequest().getCommand();
if ("MODE".equals(cmd)) {
return doMODE(conn);
}
if ("PASV".equals(cmd) || "CPSV".equals(cmd)) {
return doPASVandCPSV(conn);
}
if ("PORT".equals(cmd)) {
return doPORT(conn);
}
if ("PRET".equals(cmd)) {
return doPRET(conn);
}
if ("REST".equals(cmd)) {
return doREST(conn);
}
if ("RETR".equals(cmd) || "STOR".equals(cmd) || "APPE".equals(cmd)) {
return transfer(conn);
}
if ("SITE RESCAN".equals(cmd)) {
return doSITE_RESCAN(conn);
}
if ("SITE XDUPE".equals(cmd)) {
return doSITE_XDUPE(conn);
}
if ("STRU".equals(cmd)) {
return doSTRU(conn);
}
if ("SYST".equals(cmd)) {
return doSYST(conn);
}
if ("TYPE".equals(cmd)) {
return doTYPE(conn);
}
if ("AUTH".equals(cmd)) {
return doAUTH(conn);
}
if ("PROT".equals(cmd)) {
return doPROT(conn);
}
if ("PBSZ".equals(cmd)) {
return doPBSZ(conn);
}
if ("SSCN".equals(cmd)) {
return doSSCN(conn);
}
throw UnhandledCommandException.create(DataConnectionHandler.class,
conn.getRequest());
}
/**
* Get the data socket.
*
* Used by LIST and NLST and MLST.
*/
public Socket getDataSocket() throws IOException {
Socket dataSocket;
// get socket depending on the selection
if (isPort()) {
try {
ActiveConnection ac = new ActiveConnection(_encryptedDataChannel ? _ctx : null, _portAddress, _SSLHandshakeClientMode);
dataSocket = ac.connect(0);
} catch (IOException ex) {
logger.warn("Error opening data socket", ex);
dataSocket = null;
throw ex;
}
} else if (isPasv()) {
try {
dataSocket = _passiveConnection.connect(0);
} finally {
if (_passiveConnection != null) {
_passiveConnection.abort();
_passiveConnection = null;
}
}
} else {
throw new IllegalStateException("Neither PASV nor PORT");
}
// Already done since we are using ActiveConnection and PasvConnection
/* dataSocket.setSoTimeout(Connection.TIMEOUT); // 15 seconds timeout
if (dataSocket instanceof SSLSocket) {
SSLSocket ssldatasocket = (SSLSocket) dataSocket;
ssldatasocket.setUseClientMode(false);
ssldatasocket.startHandshake();
}*/
return dataSocket;
}
public String[] getFeatReplies() {
if (_ctx != null) {
return new String[] { "PRET", "AUTH SSL", "PBSZ", "CPSV" , "SSCN"};
}
return new String[] { "PRET" };
}
public String getHelp(String cmd) {
ResourceBundle bundle = ResourceBundle.getBundle(DataConnectionHandler.class.getName());
if ("".equals(cmd))
return bundle.getString("help.general")+"\n";
else if("rescan".equals(cmd))
return bundle.getString("help.rescan")+"\n";
else
return "";
}
public RemoteSlave getTranferSlave() {
return _rslave;
}
public synchronized RemoteTransfer getTransfer() throws ObjectNotFoundException {
if (_transfer == null)
throw new ObjectNotFoundException();
return _transfer;
}
public LinkedRemoteFileInterface getTransferFile() {
return _transferFile;
}
/**
* Get the user data type.
*/
public char getType() {
return _type;
}
public CommandHandler initialize(BaseFtpConnection conn,
CommandManager initializer) {
try {
return (DataConnectionHandler) clone();
} catch (CloneNotSupportedException e) {
throw new RuntimeException(e);
}
}
public boolean isEncryptedDataChannel() {
return _encryptedDataChannel;
}
/**
* Guarantes pre transfer is set up correctly.
*/
public boolean isPasv() {
return _isPasv;
}
public boolean isPort() {
return _isPort;
}
public boolean isPreTransfer() {
return _preTransfer || isPasv();
}
public synchronized boolean isTransfering() {
return _transfer != null && _rslave != null && _transferFile != null;
}
public void load(CommandManagerFactory initializer) {
}
protected synchronized void reset(BaseFtpConnection conn) {
_rslave = null;
if (_transfer != null) {
try {
_transfer.abort("reset");
} catch (Throwable t) {
logger.debug("reset failed to abort transfer on the slave", t);
}
}
_transfer = null;
if (_transferFile != null) {
if ((conn.getRequest().getCommand().equals("STOR") == true) &&
(_transferFile.getXfertime() == -1)) {
// Transfer failed on STOR
_transferFile.setXfertime(0);
}
_transferFile = null;
}
_preTransfer = false;
_preTransferRSlave = null;
if (_passiveConnection != null) { //isPasv() && _preTransferRSlave == null
_passiveConnection.abort();
}
_isPasv = false;
_passiveConnection = null;
_isPort = false;
_resumePosition = 0;
}
/**
* Set the data type. Supported types are A (ascii) and I (binary).
*
* @return true if success
*/
private boolean setType(char type) {
type = Character.toUpperCase(type);
if ((type != 'A') && (type != 'I')) {
return false;
}
_type = type;
return true;
}
/**
* STOU <CRLF>
*
* This command behaves like STOR except that the resultant file is to be
* created in the current directory under a name unique to that directory.
* The 250 Transfer Started response must include the name generated.
*/
//TODO STOU
/*
* public void doSTOU(FtpRequest request, PrintWriter out) {
* // reset state variables resetState();
* // get filenames String fileName =
* user.getVirtualDirectory().getAbsoluteName("ftp.dat"); String
* physicalName = user.getVirtualDirectory().getPhysicalName(fileName); File
* requestedFile = new File(physicalName); requestedFile =
* IoUtils.getUniqueFile(requestedFile); fileName =
* user.getVirtualDirectory().getVirtualName(requestedFile.getAbsolutePath());
* String args[] = {fileName};
* // check permission
* if(!user.getVirtualDirectory().hasCreatePermission(fileName, false)) {
* out.write(ftpStatus.getResponse(550, request, user, null)); return; }
* // now transfer file data out.print(FtpResponse.RESPONSE_150_OK);
* InputStream is = null; OutputStream os = null; try { Socket dataSoc =
* mDataConnection.getDataSocket(); if (dataSoc == null) {
* out.write(ftpStatus.getResponse(550, request, user, args)); return; }
*
*
* is = dataSoc.getInputStream(); os = user.getOutputStream( new
* FileOutputStream(requestedFile) );
*
* StreamConnector msc = new StreamConnector(is, os);
* msc.setMaxTransferRate(user.getMaxUploadRate()); msc.setObserver(this);
* msc.connect();
*
* if(msc.hasException()) { out.write(ftpStatus.getResponse(451, request,
* user, null)); return; } else {
* mConfig.getStatistics().setUpload(requestedFile, user,
* msc.getTransferredSize()); }
*
* out.write(ftpStatus.getResponse(226, request, user, null));
* mDataConnection.reset(); out.write(ftpStatus.getResponse(250, request,
* user, args)); } catch(IOException ex) {
* out.write(ftpStatus.getResponse(425, request, user, null)); } finally {
* IoUtils.close(is); IoUtils.close(os); mDataConnection.reset(); } }
*/
/**
* RETR <SP> <pathname> <CRLF>
*
* This command causes the server-DTP to transfer a copy of the file,
* specified in the pathname, to the server- or user-DTP at the other end of
* the data connection. The status and contents of the file at the server
* site shall be unaffected.
*
* RETR 125, 150 (110) 226, 250 425, 426, 451 450, 550 500, 501, 421, 530
*
* STOR <SP> <pathname> <CRLF>
*
* This command causes the server-DTP to accept the data transferred via the
* data connection and to store the data as a file at the server site. If
* the file specified in the pathname exists at the server site, then its
* contents shall be replaced by the data being transferred. A new file is
* created at the server site if the file specified in the pathname does not
* already exist.
*
* STOR 125, 150 (110) 226, 250 425, 426, 451, 551, 552 532, 450, 452, 553
* 500, 501, 421, 530
*
* ''zipscript?? renames bad uploads to .bad, how do we handle this with
* resumes?
*/
//TODO add APPE support
private Reply transfer(BaseFtpConnection conn)
throws ReplyException {
ReplacerEnvironment env = new ReplacerEnvironment();
if (!_encryptedDataChannel &&
conn.getGlobalContext().getConfig().checkPermission("denydatauncrypted", conn.getUserNull())) {
reset(conn);
return new Reply(530, "USE SECURE DATA CONNECTION");
}
try {
FtpRequest request = conn.getRequest();
char direction = conn.getDirection();
String cmd = conn.getRequest().getCommand();
boolean isStor = cmd.equals("STOR");
boolean isRetr = cmd.equals("RETR");
boolean isAppe = cmd.equals("APPE");
boolean isStou = cmd.equals("STOU");
String eventType = isRetr ? "RETR" : "STOR";
if (isAppe || isStou) {
throw UnhandledCommandException.create(DataConnectionHandler.class,
conn.getRequest());
}
// argument check
if (!request.hasArgument()) {
// reset(); already done in finally block
return Reply.RESPONSE_501_SYNTAX_ERROR;
}
// Checks maxsim up/down
// _simup OR _simdown = 0, exempt
int comparison = 0;
int count = conn.transferCounter(direction);
env.add("maxsim", count);
if (direction == Transfer.TRANSFER_RECEIVING_UPLOAD) {
comparison = conn.getUserNull().getMaxSimUp();
env.add("direction", "upload");
} else {
comparison = conn.getUserNull().getMaxSimDown();
env.add("direction", "download");
}
if (comparison != 0 && count >= comparison)
return new Reply(550, conn.jprintf(DataConnectionHandler.class, "transfer.err.maxsim", env));
// get filenames
LinkedRemoteFileInterface targetDir;
String targetFileName;
if (isRetr) {
try {
_transferFile = conn.getCurrentDirectory().lookupFile(request.getArgument());
if (!_transferFile.isFile()) {
// reset(); already done in finally block
return new Reply(550, "Not a plain file");
}
targetDir = _transferFile.getParentFileNull();
targetFileName = _transferFile.getName();
} catch (FileNotFoundException ex) {
// reset(); already done in finally block
return new Reply(550, ex.getMessage());
}
} else if (isStor) {
LinkedRemoteFile.NonExistingFile ret = conn.getCurrentDirectory()
.lookupNonExistingFile(conn.getGlobalContext()
.getConfig()
.getFileName(request.getArgument()));
targetDir = ret.getFile();
targetFileName = ret.getPath();
if (ret.exists()) {
// target exists, this could be overwrite or resume
//TODO overwrite & resume files.
// reset(); already done in finally block
return Reply.RESPONSE_553_REQUESTED_ACTION_NOT_TAKEN_FILE_EXISTS;
//_transferFile = targetDir;
//targetDir = _transferFile.getParent();
//if(_transfereFile.getOwner().equals(getUser().getUsername()))
// {
// // allow overwrite/resume
//}
//if(directory.isDirectory()) {
// return FtpReply.RESPONSE_550_REQUESTED_ACTION_NOT_TAKEN;
//}
}
if(!conn.getGlobalContext().getCheckConfig().checkAllowedFilename(conn.getUserNull(), ret)) {
return new Reply(553, "Upload file denied - Due to Regexp configuration");
}
if (!ListUtils.isLegalFileName(targetFileName) ||
!conn.getGlobalContext().getConfig().checkPathPermission("privpath", conn.getUserNull(), targetDir, true)) {
// reset(); already done in finally block
return new Reply(553,
"Requested action not taken. File name not allowed.");
}
//do our zipscript sfv checks
String checkName = targetFileName.toLowerCase();
ZipscriptConfig zsCfg = conn.getGlobalContext().getZsConfig();
boolean SfvFirstEnforcedPath = zsCfg.checkSfvFirstEnforcedPath(targetDir,
conn.getUserNull());
try {
SFVFile sfv;
try {
sfv = conn.getCurrentDirectory().lookupSFVFile();
} catch (FileStillTransferringException e) {
// I have no idea how to handle this
return new Reply(400, "SFVFile still transferring.");
}
if (checkName.endsWith(".sfv") &&
!zsCfg.multiSfvAllowed()) {
return new Reply(533,
"Requested action not taken. Multiple SFV files not allowed.");
}
if (SfvFirstEnforcedPath &&
!zsCfg.checkAllowedExtension(checkName)) {
// filename not explicitly permitted, check for sfv entry
boolean allow = false;
if (zsCfg.restrictSfvEnabled()) {
for (Iterator iter = sfv.getNames().iterator(); iter.hasNext();) {
String name = (String) iter.next();
if (name.toLowerCase().equals(checkName)) {
allow = true;
break;
}
}
if (!allow) {
return new Reply(533,
"Requested action not taken. File not found in sfv.");
}
}
}
} catch (FileNotFoundException e1) {
// no sfv found in dir
if(checkName.endsWith(".sfv") && SfvFirstEnforcedPath && conn.getGlobalContext().getCheckConfig().checkSfvDenyUpload(targetDir)) { //identifier:regex
return new Reply(533, "Requested action not taken. SFV not allowed due to subdir");
}
if ( !zsCfg.checkAllowedExtension(checkName) &&
SfvFirstEnforcedPath ) {
// filename not explicitly permitted
// ForceSfvFirst is on, and file is in an enforced path.
return new Reply(533,
"Requested action not taken. You must upload sfv first.");
}
} catch (IOException e1) {
//error reading sfv, do nothing
} catch (NoAvailableSlaveException e1) {
//sfv not online, do nothing
}
} else {
// reset(); already done in finally block
throw UnhandledCommandException.create(
DataConnectionHandler.class, request);
}
// check access
if (!conn.getGlobalContext().getConfig().checkPathPermission("privpath", conn.getUserNull(), targetDir, true)) {
// reset(); already done in finally block
return new Reply(550,
request.getArgument() + ": No such file");
}
switch (direction) {
case Transfer.TRANSFER_SENDING_DOWNLOAD:
if (!conn.getGlobalContext().getConfig().checkPathPermission("download", conn.getUserNull(), targetDir)) {
// reset(); already done in finally block
return Reply.RESPONSE_530_ACCESS_DENIED;
}
break;
case Transfer.TRANSFER_RECEIVING_UPLOAD:
if (!conn.getGlobalContext().getConfig().checkPathPermission("upload", conn.getUserNull(), targetDir)) {
// reset(); already done in finally block
return Reply.RESPONSE_530_ACCESS_DENIED;
}
break;
default:
// reset(); already done in finally block
throw UnhandledCommandException.create(DataConnectionHandler.class,
request);
}
//check credits
if (isRetr) {
if ((conn.getUserNull().getKeyedMap().getObjectFloat(
UserManagement.RATIO) != 0)
&& (conn.getGlobalContext().getConfig()
.getCreditLossRatio(_transferFile,
conn.getUserNull()) != 0)
&& (conn.getUserNull().getCredits() < _transferFile
.length())) {
// reset(); already done in finally block
return new Reply(550, "Not enough credits.");
}
}
//setup _rslave
//if (isCpsv)
if (isPasv()) {
// isPasv() means we're setup correctly
// if (!_preTransfer || _preTransferRSlave == null)
// return FtpReply.RESPONSE_503_BAD_SEQUENCE_OF_COMMANDS;
//check pretransfer
if (isRetr &&
!_transferFile.getSlaves().contains(_preTransferRSlave)) {
// reset(); already done in finally block
return Reply.RESPONSE_503_BAD_SEQUENCE_OF_COMMANDS;
}
_rslave = _preTransferRSlave;
//_preTransferRSlave = null;
//_preTransfer = false;
//code above to be handled by reset()
} else {
try {
if (direction == Transfer.TRANSFER_SENDING_DOWNLOAD) {
_rslave = conn.getGlobalContext().getSlaveSelectionManager().getASlave(_transferFile.getAvailableSlaves(),
Transfer.TRANSFER_SENDING_DOWNLOAD, conn,
_transferFile);
} else if (direction == Transfer.TRANSFER_RECEIVING_UPLOAD) {
_rslave = conn.getGlobalContext().getSlaveSelectionManager().getASlave(conn.getGlobalContext()
.getSlaveManager()
.getAvailableSlaves(),
Transfer.TRANSFER_RECEIVING_UPLOAD, conn,
targetDir);
} else {
// reset(); already done in finally block
throw new RuntimeException();
}
} catch (NoAvailableSlaveException ex) {
//TODO Might not be good to 450 reply always
//from rfc: 450 Requested file action not taken. File unavailable (e.g., file busy).
// reset(); already done in finally block
throw new ReplySlaveUnavailableException(ex, 450);
}
}
/*
if(!conn.getGlobalContext().getTraficSlave().checkSlave(_rslave)) {
return new Reply(553, "File upload denied - trafic limit has been reached on this slave");
}
*/
if (isStor) {
//setup upload
if (_rslave == null) {
// reset(); already done in finally block
throw new NullPointerException();
}
List rslaves = Collections.singletonList(_rslave);
StaticRemoteFile uploadFile = new StaticRemoteFile(rslaves,
targetFileName, conn.getUserNull().getName(),
conn.getUserNull().getGroup(), 0L,
System.currentTimeMillis(), 0L);
synchronized (this) {
uploadFile.setXfertime(-1); // used for new files to be
// uploaded, see getXfertime()
_transferFile = targetDir.addFile(uploadFile);
}
}
// setup _transfer
if (isPort()) {
try {
String index = _rslave.issueConnectToSlave(_portAddress
.getAddress().getHostAddress(), _portAddress
.getPort(), _encryptedDataChannel, _SSLHandshakeClientMode);
ConnectInfo ci = _rslave.fetchTransferResponseFromIndex(index);
synchronized (this) {
_transfer = _rslave.getTransfer(ci.getTransferIndex());
}
} catch (Exception ex) {
logger.fatal("rslave=" + _rslave, ex);
// reset(); already done in finally block
return new Reply(450,
ex.getClass().getName() + " from slave: " +
ex.getMessage());
}
} else if (isPasv()) {
//_transfer is already set up by doPASV()
} else {
// reset(); already done in finally block
if (isStor) {
_transferFile.delete();
}
return Reply.RESPONSE_503_BAD_SEQUENCE_OF_COMMANDS;
}
{
PrintWriter out = conn.getControlWriter();
out.write(new Reply(150,
"File status okay; about to open data connection " +
(isRetr ? "from " : "to ") + _rslave.getName() + ".").toString());
out.flush();
}
TransferStatus status = null;
//transfer
try {
//TODO ABORtable transfers
if (isRetr) {
_transfer.sendFile(_transferFile.getPath(), getType(),
_resumePosition);
while (true) {
status = _transfer.getTransferStatus();
if (status.isFinished()) {
break;
}
try {
Thread.sleep(100);
} catch (InterruptedException e1) {
}
}
} else if (isStor) {
_transfer.receiveFile(_transferFile.getPath(), getType(),
_resumePosition);
while (true) {
status = _transfer.getTransferStatus();
_transferFile.setLength(status.getTransfered());
if (status.isFinished()) {
break;
}
try {
Thread.sleep(100);
} catch (InterruptedException e1) {
}
}
} else {
throw new RuntimeException();
}
} catch (IOException ex) {
logger.debug("", ex);
if (ex instanceof TransferFailedException) {
// the below chunk makes no sense, we don't process it anywhere
/* status = ((TransferFailedException) ex).getStatus();
conn.getGlobalContext()
.dispatchFtpEvent(new TransferEvent(conn, eventType,
_transferFile, conn.getClientAddress(), _rslave,
_transfer.getAddress().getAddress(), _type, false));
*/
if (isRetr) {
conn.getUserNull().updateCredits(-status.getTransfered());
}
}
Reply reply = null;
if (isStor) {
_transferFile.delete();
logger.error("IOException during transfer, deleting file",
ex);
reply = new Reply(426, "Transfer failed, deleting file");
} else {
logger.error("IOException during transfer", ex);
reply = new Reply(426, ex.getMessage());
}
reply.addComment(ex.getMessage());
// reset(); already done in finally block
return reply;
} catch (SlaveUnavailableException e) {
logger.debug("", e);
Reply reply = null;
if (isStor) {
_transferFile.delete();
logger.error("Slave went offline during transfer, deleting file",
e);
reply = new Reply(426,
"Slave went offline during transfer, deleting file");
} else {
logger.error("Slave went offline during transfer", e);
reply = new Reply(426,
"Slave went offline during transfer");
}
reply.addComment(e.getLocalizedMessage());
// reset(); already done in finally block
return reply;
}
// TransferThread transferThread = new TransferThread(rslave,
// transfer);
// System.err.println("Calling interruptibleSleepUntilFinished");
// try {
// transferThread.interruptibleSleepUntilFinished();
// } catch (Throwable e1) {
// e1.printStackTrace();
// }
// System.err.println("Finished");
env = new ReplacerEnvironment();
env.add("bytes", Bytes.formatBytes(status.getTransfered()));
env.add("speed", Bytes.formatBytes(status.getXferSpeed()) + "/s");
env.add("seconds", "" + ((float)status.getElapsed() / 1000F));
env.add("checksum", Checksum.formatChecksum(status.getChecksum()));
Reply response = new Reply(226,
conn.jprintf(DataConnectionHandler.class,
"transfer.complete", env));
synchronized (conn.getGlobalContext()) { // need to synchronize
// here so only one
// TransferEvent can be sent at a time
if (isStor) {
if (_resumePosition == 0) {
_transferFile.setCheckSum(status.getChecksum());
} else {
// try {
// checksum = _transferFile.getCheckSumFromSlave();
// } catch (NoAvailableSlaveException e) {
// response.addComment(
// "No available slaves when getting checksum from
// slave: "
// + e.getMessage());
// logger.warn("", e);
// checksum = 0;
// } catch (IOException e) {
// response.addComment(
// "IO error getting checksum from slave: "
// + e.getMessage());
// logger.warn("", e);
// checksum = 0;
// }
}
_transferFile.setLastModified(System.currentTimeMillis());
_transferFile.setLength(status.getTransfered());
_transferFile.setXfertime(status.getElapsed());
}
boolean zipscript = zipscript(isRetr, isStor, status
.getChecksum(), response, targetFileName, targetDir);
if (zipscript) {
// transferstatistics
if (isRetr) {
float ratio = conn.getGlobalContext().getConfig()
.getCreditLossRatio(_transferFile,
conn.getUserNull());
if (ratio != 0) {
conn.getUserNull().updateCredits(
(long) (-status.getTransfered() * ratio));
}
if (!conn.getGlobalContext().getConfig()
.checkPathPermission("nostatsdn",
conn.getUserNull(),
conn.getCurrentDirectory())) {
conn.getUserNull().updateDownloadedBytes(
status.getTransfered());
conn.getUserNull().updateDownloadedTime(
status.getElapsed());
conn.getUserNull().updateDownloadedFiles(1);
}
} else {
conn.getUserNull().updateCredits(
(long) (status.getTransfered() * conn
.getGlobalContext().getConfig()
.getCreditCheckRatio(_transferFile,
conn.getUserNull())));
if (!conn.getGlobalContext().getConfig()
.checkPathPermission("nostatsup",
conn.getUserNull(),
conn.getCurrentDirectory())) {
conn.getUserNull().updateUploadedBytes(
status.getTransfered());
conn.getUserNull().updateUploadedTime(
status.getElapsed());
conn.getUserNull().updateUploadedFiles(1);
}
}
try {
conn.getUserNull().commit();
} catch (UserFileException e) {
logger.warn("", e);
}
}
// Dispatch for both STOR and RETR
conn.getGlobalContext().dispatchFtpEvent(
new TransferEvent(conn, eventType, _transferFile, conn
.getClientAddress(), _rslave, _transfer
.getAddress().getAddress(), getType()));
return response;
}
} finally {
reset(conn);
}
}
public void unload() {
}
/**
* @param isRetr
* @param isStor
* @param status
* @param response
* @param targetFileName
* @param targetDir
* Returns true if crc check was okay, i.e, if credits should be
* altered
*/
private boolean zipscript(boolean isRetr, boolean isStor, long checksum,
Reply response, String targetFileName,
LinkedRemoteFileInterface targetDir) {
// zipscript
logger.debug("Running zipscript on file " + targetFileName +
" with CRC of " + checksum);
if (isRetr) {
//compare checksum from transfer to cached checksum
logger.debug("checksum from transfer = " + checksum);
if (checksum != 0) {
response.addComment("Checksum from transfer: " +
Checksum.formatChecksum(checksum));
long cachedChecksum;
cachedChecksum = _transferFile.getCheckSumCached();
if (cachedChecksum == 0) {
_transferFile.setCheckSum(checksum);
} else if (cachedChecksum != checksum) {
response.addComment(
"WARNING: checksum from transfer didn't match cached checksum");
logger.info("checksum from transfer " +
Checksum.formatChecksum(checksum) +
"didn't match cached checksum" +
Checksum.formatChecksum(cachedChecksum) + " for " +
_transferFile.toString() + " from slave " +
_rslave.getName(), new Throwable());
}
//compare checksum from transfer to checksum from sfv
try {
long sfvChecksum;
try {
sfvChecksum = _transferFile.getParentFileNull()
.lookupSFVFile()
.getChecksum(_transferFile.getName());
if (sfvChecksum == checksum) {
response.addComment(
"checksum from transfer matched checksum in .sfv");
} else {
response.addComment(
"WARNING: checksum from transfer didn't match checksum in .sfv");
}
} catch (FileStillTransferringException e) {
response
.addComment("checksum from sfv doesn't exist yet, SFVFile is being uploaded");
}
} catch (NoAvailableSlaveException e1) {
response.addComment(
"slave with .sfv offline, checksum not verified");
} catch (FileNotFoundException e1) {
//continue without verification
} catch (NoSFVEntryException e1) {
//file not found in .sfv, continue
} catch (IOException e1) {
logger.info("", e1);
response.addComment("IO Error reading sfv file: " +
e1.getMessage());
}
} else { // slave has disabled download crc
//response.addComment("Slave has disabled download checksum");
}
} else if (isStor) {
if (!targetFileName.toLowerCase().endsWith(".sfv")) {
try {
long sfvChecksum;
try {
sfvChecksum = targetDir.lookupSFVFile().getChecksum(targetFileName);
if (checksum == sfvChecksum) {
response.addComment("checksum match: SLAVE/SFV:" +
Long.toHexString(checksum));
} else if (checksum == 0) {
response.addComment(
"checksum match: SLAVE/SFV: DISABLED");
} else {
response.addComment("checksum mismatch: SLAVE: " +
Long.toHexString(checksum) + " SFV: " +
Long.toHexString(sfvChecksum));
response.addComment(" deleting file");
response.setMessage("Checksum mismatch, deleting file");
_transferFile.delete();
// getUser().updateCredits(
// - ((long) getUser().getRatio() * transferedBytes));
// getUser().updateUploadedBytes(-transferedBytes);
// response.addComment(conn.status());
return false; // don't modify credits
// String badtargetFilename = targetFilename + ".bad";
//
// try {
// LinkedRemoteFile badtargetFile =
// targetDir.getFile(badtargetFilename);
// badtargetFile.delete();
// response.addComment(
// "zipscript - removing "
// + badtargetFilename
// + " to be replaced with new file");
// } catch (FileNotFoundException e2) {
// //good, continue...
// response.addComment(
// "zipscript - checksum mismatch, renaming to "
// + badtargetFilename);
// }
// targetFile.renameTo(targetDir.getPath() +
// badtargetFilename);
}
} catch (FileStillTransferringException e) {
response.addComment("No sfv to compare to (SFVTransferring), file allowed");
}
} catch (NoAvailableSlaveException e) {
response.addComment(
"zipscript - SFV unavailable, slave(s) with .sfv file is offline");
} catch (NoSFVEntryException e) {
response.addComment("zipscript - no entry in sfv for file");
} catch (IOException e) {
response.addComment(
"zipscript - SFV unavailable, IO error: " +
e.getMessage());
}
}
}
return true; // modify credits, transfer was okay
}
public synchronized void handshakeCompleted(HandshakeCompletedEvent arg0) {
_handshakeCompleted = true;
notifyAll();
}
}