0001: /*
0002: * This file is part of DrFTPD, Distributed FTP Daemon.
0003: *
0004: * DrFTPD is free software; you can redistribute it and/or modify it under the
0005: * terms of the GNU General Public License as published by the Free Software
0006: * Foundation; either version 2 of the License, or (at your option) any later
0007: * version.
0008: *
0009: * DrFTPD is distributed in the hope that it will be useful, but WITHOUT ANY
0010: * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
0011: * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
0012: *
0013: * You should have received a copy of the GNU General Public License along with
0014: * DrFTPD; if not, write to the Free Software Foundation, Inc., 59 Temple Place,
0015: * Suite 330, Boston, MA 02111-1307 USA
0016: */
0017: package net.sf.drftpd.master.command.plugins;
0018:
0019: import java.io.FileNotFoundException;
0020: import java.io.IOException;
0021: import java.io.PrintWriter;
0022: import java.net.InetAddress;
0023: import java.net.InetSocketAddress;
0024: import java.net.ServerSocket;
0025: import java.net.Socket;
0026: import java.net.UnknownHostException;
0027: import java.util.Collections;
0028: import java.util.Iterator;
0029: import java.util.List;
0030: import java.util.Map;
0031: import java.util.ResourceBundle;
0032: import java.util.StringTokenizer;
0033:
0034: import javax.net.ServerSocketFactory;
0035: import javax.net.SocketFactory;
0036: import javax.net.ssl.HandshakeCompletedEvent;
0037: import javax.net.ssl.HandshakeCompletedListener;
0038: import javax.net.ssl.SSLContext;
0039: import javax.net.ssl.SSLSocket;
0040:
0041: import net.sf.drftpd.NoAvailableSlaveException;
0042: import net.sf.drftpd.NoSFVEntryException;
0043: import net.sf.drftpd.ObjectNotFoundException;
0044: import net.sf.drftpd.SlaveUnavailableException;
0045: import net.sf.drftpd.event.TransferEvent;
0046: import net.sf.drftpd.master.BaseFtpConnection;
0047: import net.sf.drftpd.master.FtpRequest;
0048: import net.sf.drftpd.master.command.CommandManager;
0049: import net.sf.drftpd.master.command.CommandManagerFactory;
0050: import net.sf.drftpd.master.config.ZipscriptConfig;
0051:
0052: import org.apache.log4j.Logger;
0053: import org.drftpd.ActiveConnection;
0054: import org.drftpd.Bytes;
0055: import org.drftpd.Checksum;
0056: import org.drftpd.PassiveConnection;
0057: import org.drftpd.SFVFile;
0058: import org.drftpd.SSLGetContext;
0059: import org.drftpd.commands.CommandHandler;
0060: import org.drftpd.commands.CommandHandlerFactory;
0061: import org.drftpd.commands.Reply;
0062: import org.drftpd.commands.ReplyException;
0063: import org.drftpd.commands.ReplySlaveUnavailableException;
0064: import org.drftpd.commands.UnhandledCommandException;
0065: import org.drftpd.commands.UserManagement;
0066: import org.drftpd.master.RemoteSlave;
0067: import org.drftpd.master.RemoteTransfer;
0068: import org.drftpd.remotefile.FileStillTransferringException;
0069: import org.drftpd.remotefile.LinkedRemoteFile;
0070: import org.drftpd.remotefile.LinkedRemoteFileInterface;
0071: import org.drftpd.remotefile.ListUtils;
0072: import org.drftpd.remotefile.StaticRemoteFile;
0073: import org.drftpd.slave.ConnectInfo;
0074: import org.drftpd.slave.Connection;
0075: import org.drftpd.slave.RemoteIOException;
0076: import org.drftpd.slave.Transfer;
0077: import org.drftpd.slave.TransferFailedException;
0078: import org.drftpd.slave.TransferStatus;
0079: import org.drftpd.usermanager.UserFileException;
0080: import org.tanesha.replacer.ReplacerEnvironment;
0081: import org.drftpd.SFVFile.SFVStatus;
0082:
0083: /**
0084: * @author mog
0085: * @author zubov
0086: * @version $Id: DataConnectionHandler.java 1593 2007-01-31 22:56:52Z zubov $
0087: */
0088: public class DataConnectionHandler implements CommandHandler,
0089: CommandHandlerFactory, Cloneable, HandshakeCompletedListener {
0090: private static final Logger logger = Logger
0091: .getLogger(DataConnectionHandler.class);
0092: private SSLContext _ctx;
0093: private boolean _encryptedDataChannel;
0094: private boolean _SSLHandshakeClientMode = false;
0095: protected boolean _isPasv = false;
0096: protected boolean _isPort = false;
0097:
0098: /**
0099: * Holds the address that getDataSocket() should connect to in PORT mode.
0100: */
0101: private InetSocketAddress _portAddress;
0102: protected boolean _preTransfer = false;
0103: private RemoteSlave _preTransferRSlave;
0104: private long _resumePosition = 0;
0105: private RemoteSlave _rslave;
0106:
0107: /**
0108: * ServerSocket for PASV mode.
0109: */
0110: private PassiveConnection _passiveConnection;
0111: private RemoteTransfer _transfer;
0112: private LinkedRemoteFileInterface _transferFile;
0113: private char _type = 'A';
0114: private boolean _handshakeCompleted;
0115:
0116: public DataConnectionHandler() {
0117: super ();
0118: _handshakeCompleted = false;
0119: try {
0120: _ctx = SSLGetContext.getSSLContext();
0121: } catch (FileNotFoundException e) {
0122: _ctx = null;
0123: logger.warn("Couldn't load SSLContext, SSL/TLS disabled");
0124: } catch (Exception e) {
0125: _ctx = null;
0126: logger
0127: .warn("Couldn't load SSLContext, SSL/TLS disabled",
0128: e);
0129: }
0130: }
0131:
0132: private Reply doAUTH(BaseFtpConnection conn) {
0133: if (_ctx == null) {
0134: return new Reply(500, "TLS not configured");
0135: }
0136:
0137: Socket s = conn.getControlSocket();
0138:
0139: //reply success
0140: conn.getControlWriter().write(
0141: new Reply(234, conn.getRequest().getCommandLine()
0142: + " successful").toString());
0143: conn.getControlWriter().flush();
0144: SSLSocket s2 = null;
0145: try {
0146: s2 = (SSLSocket) _ctx.getSocketFactory().createSocket(s,
0147: s.getInetAddress().getHostAddress(), s.getPort(),
0148: true);
0149: conn.setControlSocket(s2);
0150: s2.setUseClientMode(false);
0151: s2.addHandshakeCompletedListener(this );
0152: s2.startHandshake();
0153: while (!_handshakeCompleted) {
0154: synchronized (this ) {
0155: try {
0156: wait(10000);
0157: } catch (InterruptedException e) {
0158: s2.close();
0159: conn.stop("Took too long to negotiate SSL");
0160: return new Reply(400,
0161: "Took too long to negotiate SSL");
0162: }
0163: }
0164: }
0165: // reset for possible auth later
0166: _handshakeCompleted = false;
0167: } catch (IOException e) {
0168: logger.warn("", e);
0169: if (s2 != null) {
0170: try {
0171: s2.close();
0172: } catch (IOException e2) {
0173: logger.debug("error closing SSLSocket connection");
0174: }
0175: }
0176: conn.stop(e.getMessage());
0177:
0178: return null;
0179: }
0180: s2 = null;
0181:
0182: return null;
0183: }
0184:
0185: /**
0186: * <code>MODE <SP> <mode-code> <CRLF></code><br>
0187: *
0188: * The argument is a single Telnet character code specifying the data
0189: * transfer modes described in the Section on Transmission Modes.
0190: */
0191: private Reply doMODE(BaseFtpConnection conn) {
0192: FtpRequest request = conn.getRequest();
0193:
0194: // argument check
0195: if (!request.hasArgument()) {
0196: return Reply.RESPONSE_501_SYNTAX_ERROR;
0197: }
0198:
0199: if (request.getArgument().equalsIgnoreCase("S")) {
0200: return Reply.RESPONSE_200_COMMAND_OK;
0201: }
0202:
0203: return Reply.RESPONSE_504_COMMAND_NOT_IMPLEMENTED_FOR_PARM;
0204: }
0205:
0206: /**
0207: * <code>PASV <CRLF></code><br>
0208: *
0209: * This command requests the server-DTP to "listen" on a data port (which is
0210: * not its default data port) and to wait for a connection rather than
0211: * initiate one upon receipt of a transfer command. The response to this
0212: * command includes the host and port address this server is listening on.
0213: */
0214: private Reply doPASVandCPSV(BaseFtpConnection conn) {
0215: if (!_preTransfer) {
0216: return new Reply(500,
0217: "You need to use a client supporting PRET (PRE Transfer) to use PASV");
0218: }
0219:
0220: //reset();
0221: _preTransfer = false;
0222:
0223: if (isPort() == true) {
0224: throw new RuntimeException();
0225: }
0226:
0227: if (conn.getRequest().getCommand().equals("CPSV")) {
0228: _SSLHandshakeClientMode = true;
0229: }
0230:
0231: InetSocketAddress address = null;
0232:
0233: if (_preTransferRSlave == null) {
0234: try {
0235: _passiveConnection = new PassiveConnection(
0236: _encryptedDataChannel ? _ctx : null, conn
0237: .getGlobalContext().getPortRange(),
0238: false);
0239: try {
0240: address = new InetSocketAddress(conn
0241: .getGlobalContext().getConfig()
0242: .getPasvAddress(), _passiveConnection
0243: .getLocalPort());
0244: } catch (NullPointerException e) {
0245: address = new InetSocketAddress(conn
0246: .getControlSocket().getLocalAddress(),
0247: _passiveConnection.getLocalPort());
0248: }
0249: _isPasv = true;
0250: } catch (Exception ex) {
0251: logger.warn("", ex);
0252:
0253: return new Reply(550, ex.getMessage());
0254: }
0255: } else {
0256: try {
0257: String index = _preTransferRSlave.issueListenToSlave(
0258: _encryptedDataChannel, _SSLHandshakeClientMode);
0259: ConnectInfo ci = _preTransferRSlave
0260: .fetchTransferResponseFromIndex(index);
0261: _transfer = _preTransferRSlave.getTransfer(ci
0262: .getTransferIndex());
0263: address = new InetSocketAddress(_preTransferRSlave
0264: .getPASVIP(), _transfer.getAddress().getPort());
0265: _isPasv = true;
0266: } catch (SlaveUnavailableException e) {
0267: reset(conn);
0268: return Reply.RESPONSE_530_SLAVE_UNAVAILABLE;
0269: } catch (RemoteIOException e) {
0270: reset(conn);
0271: _preTransferRSlave
0272: .setOffline("Slave could not listen for a connection");
0273: logger.error("Slave could not listen for a connection",
0274: e);
0275:
0276: return new Reply(500,
0277: "Slave could not listen for a connection");
0278: }
0279: }
0280:
0281: if (conn.getRequest().getCommand().equals("CPSV")) {
0282: // can only reset it if the transfer was setup with CPSV
0283: _SSLHandshakeClientMode = false;
0284: }
0285:
0286: if (address.getAddress() == null
0287: || address.getAddress().getHostAddress() == null) {
0288: return new Reply(500,
0289: "Address for is unresolvable, check pasv_addr setting");
0290: }
0291:
0292: String addrStr = address.getAddress().getHostAddress().replace(
0293: '.', ',')
0294: + ','
0295: + (address.getPort() >> 8)
0296: + ','
0297: + (address.getPort() & 0xFF);
0298:
0299: return new Reply(227, "Entering Passive Mode (" + addrStr
0300: + ").");
0301: }
0302:
0303: private Reply doPBSZ(BaseFtpConnection conn)
0304: throws UnhandledCommandException {
0305: String cmd = conn.getRequest().getArgument();
0306:
0307: if ((cmd == null) || !cmd.equals("0")) {
0308: return Reply.RESPONSE_501_SYNTAX_ERROR;
0309: }
0310:
0311: return Reply.RESPONSE_200_COMMAND_OK;
0312: }
0313:
0314: /**
0315: * <code>PORT <SP> <host-port> <CRLF></code><br>
0316: *
0317: * The argument is a HOST-PORT specification for the data port to be used in
0318: * data connection. There are defaults for both the user and server data
0319: * ports, and under normal circumstances this command and its reply are not
0320: * needed. If this command is used, the argument is the concatenation of a
0321: * 32-bit internet host address and a 16-bit TCP port address. This address
0322: * information is broken into 8-bit fields and the value of each field is
0323: * transmitted as a decimal number (in character string representation). The
0324: * fields are separated by commas. A port command would be:
0325: *
0326: * PORT h1,h2,h3,h4,p1,p2
0327: *
0328: * where h1 is the high order 8 bits of the internet host address.
0329: */
0330: private Reply doPORT(BaseFtpConnection conn) {
0331: FtpRequest request = conn.getRequest();
0332: reset(conn);
0333:
0334: InetAddress clientAddr = null;
0335:
0336: // argument check
0337: if (!request.hasArgument()) {
0338: //Syntax error in parameters or arguments
0339: return Reply.RESPONSE_501_SYNTAX_ERROR;
0340: }
0341:
0342: StringTokenizer st = new StringTokenizer(request.getArgument(),
0343: ",");
0344:
0345: if (st.countTokens() != 6) {
0346: return Reply.RESPONSE_501_SYNTAX_ERROR;
0347: }
0348:
0349: // get data server
0350: String dataSrvName = st.nextToken() + '.' + st.nextToken()
0351: + '.' + st.nextToken() + '.' + st.nextToken();
0352:
0353: try {
0354: clientAddr = InetAddress.getByName(dataSrvName);
0355: } catch (UnknownHostException ex) {
0356: return Reply.RESPONSE_501_SYNTAX_ERROR;
0357: }
0358:
0359: String portHostAddress = clientAddr.getHostAddress();
0360: String clientHostAddress = conn.getControlSocket()
0361: .getInetAddress().getHostAddress();
0362:
0363: if ((portHostAddress.startsWith("192.168.") && !clientHostAddress
0364: .startsWith("192.168."))
0365: || (portHostAddress.startsWith("10.") && !clientHostAddress
0366: .startsWith("10."))) {
0367: Reply response = new Reply(501);
0368: response.addComment("==YOU'RE BEHIND A NAT ROUTER==");
0369: response
0370: .addComment("Configure the firewall settings of your FTP client");
0371: response.addComment(" to use your real IP: "
0372: + conn.getControlSocket().getInetAddress()
0373: .getHostAddress());
0374: response
0375: .addComment("And set up port forwarding in your router.");
0376: response
0377: .addComment("Or you can just use a PRET capable client, see");
0378: response
0379: .addComment(" http://drftpd.org/ for PRET capable clients");
0380:
0381: return response;
0382: }
0383:
0384: int clientPort;
0385:
0386: // get data server port
0387: try {
0388: int hi = Integer.parseInt(st.nextToken());
0389: int lo = Integer.parseInt(st.nextToken());
0390: clientPort = (hi << 8) | lo;
0391: } catch (NumberFormatException ex) {
0392: reset(conn);
0393: return Reply.RESPONSE_501_SYNTAX_ERROR;
0394:
0395: //out.write(ftpStatus.getResponse(552, request, user, null));
0396: }
0397:
0398: _isPort = true;
0399: _portAddress = new InetSocketAddress(clientAddr, clientPort);
0400:
0401: if (portHostAddress.startsWith("127.")) {
0402: return new Reply(200,
0403: "Ok, but distributed transfers won't work with local addresses");
0404: }
0405:
0406: //Notify the user that this is not his IP.. Good for NAT users that
0407: // aren't aware that their IP has changed.
0408: if (!clientAddr
0409: .equals(conn.getControlSocket().getInetAddress())) {
0410: return new Reply(200,
0411: "FXP allowed. If you're not FXPing then set your IP to "
0412: + conn.getControlSocket().getInetAddress()
0413: .getHostAddress()
0414: + " (usually in firewall settings)");
0415: }
0416:
0417: return Reply.RESPONSE_200_COMMAND_OK;
0418: }
0419:
0420: private Reply doPRET(BaseFtpConnection conn) {
0421: reset(conn);
0422:
0423: FtpRequest request = conn.getRequest();
0424: FtpRequest ghostRequest = new FtpRequest(request.getArgument());
0425: String cmd = ghostRequest.getCommand();
0426:
0427: if (cmd.equals("LIST") || cmd.equals("NLST")
0428: || cmd.equals("MLSD")) {
0429: _preTransferRSlave = null;
0430: _preTransfer = true;
0431:
0432: return new Reply(200,
0433: "OK, will use master for upcoming transfer");
0434: } else if (cmd.equals("RETR")) {
0435: try {
0436: LinkedRemoteFileInterface downFile = conn
0437: .getCurrentDirectory().lookupFile(
0438: ghostRequest.getArgument());
0439: _preTransferRSlave = conn.getGlobalContext()
0440: .getSlaveSelectionManager().getASlave(
0441: downFile.getAvailableSlaves(),
0442: Transfer.TRANSFER_SENDING_DOWNLOAD,
0443: conn, downFile);
0444: _preTransfer = true;
0445:
0446: return new Reply(200, "OK, will use "
0447: + _preTransferRSlave.getName()
0448: + " for upcoming transfer");
0449: } catch (NoAvailableSlaveException e) {
0450: return Reply.RESPONSE_530_SLAVE_UNAVAILABLE;
0451: } catch (FileNotFoundException e) {
0452: return Reply.RESPONSE_550_REQUESTED_ACTION_NOT_TAKEN;
0453: }
0454: } else if (cmd.equals("STOR")) {
0455: LinkedRemoteFile.NonExistingFile nef = conn
0456: .getCurrentDirectory().lookupNonExistingFile(
0457: ghostRequest.getArgument());
0458:
0459: if (nef.exists()) {
0460: return Reply.RESPONSE_553_REQUESTED_ACTION_NOT_TAKEN_FILE_EXISTS;
0461: }
0462:
0463: if (!ListUtils.isLegalFileName(nef.getPath())) {
0464: return Reply.RESPONSE_553_REQUESTED_ACTION_NOT_TAKEN;
0465: }
0466:
0467: try {
0468: _preTransferRSlave = conn.getGlobalContext()
0469: .getSlaveSelectionManager().getASlave(
0470: conn.getGlobalContext()
0471: .getSlaveManager()
0472: .getAvailableSlaves(),
0473: Transfer.TRANSFER_RECEIVING_UPLOAD,
0474: conn, nef.getFile());
0475: _preTransfer = true;
0476:
0477: return new Reply(200, "OK, will use "
0478: + _preTransferRSlave.getName()
0479: + " for upcoming transfer");
0480: } catch (NoAvailableSlaveException e) {
0481: return Reply.RESPONSE_530_SLAVE_UNAVAILABLE;
0482: }
0483: } else {
0484: return Reply.RESPONSE_504_COMMAND_NOT_IMPLEMENTED_FOR_PARM;
0485: }
0486: }
0487:
0488: private Reply doSSCN(BaseFtpConnection conn) {
0489: if (_ctx == null) {
0490: return new Reply(500, "TLS not configured");
0491: }
0492:
0493: if (!(conn.getControlSocket() instanceof SSLSocket)) {
0494: return new Reply(500, "You are not on a secure channel");
0495: }
0496:
0497: if (!_encryptedDataChannel) {
0498: return new Reply(500,
0499: "SSCN only works for encrypted transfers");
0500: }
0501:
0502: if (conn.getRequest().hasArgument()) {
0503: if (conn.getRequest().getArgument().equalsIgnoreCase("ON")) {
0504: _SSLHandshakeClientMode = true;
0505: }
0506: if (conn.getRequest().getArgument().equalsIgnoreCase("OFF")) {
0507: _SSLHandshakeClientMode = false;
0508: }
0509: }
0510: return new Reply(220, "SSCN:"
0511: + (_SSLHandshakeClientMode ? "CLIENT" : "SERVER")
0512: + " METHOD");
0513: }
0514:
0515: private Reply doPROT(BaseFtpConnection conn)
0516: throws UnhandledCommandException {
0517: if (_ctx == null) {
0518: return new Reply(500, "TLS not configured");
0519: }
0520:
0521: if (!(conn.getControlSocket() instanceof SSLSocket)) {
0522: return new Reply(500, "You are not on a secure channel");
0523: }
0524:
0525: FtpRequest req = conn.getRequest();
0526:
0527: if (!req.hasArgument()) {
0528: //clear
0529: _encryptedDataChannel = false;
0530:
0531: return Reply.RESPONSE_200_COMMAND_OK;
0532: }
0533:
0534: if (!req.hasArgument() || (req.getArgument().length() != 1)) {
0535: return Reply.RESPONSE_501_SYNTAX_ERROR;
0536: }
0537:
0538: switch (Character.toUpperCase(req.getArgument().charAt(0))) {
0539: case 'C':
0540:
0541: //clear
0542: _encryptedDataChannel = false;
0543:
0544: return Reply.RESPONSE_200_COMMAND_OK;
0545:
0546: case 'P':
0547:
0548: //private
0549: _encryptedDataChannel = true;
0550:
0551: return Reply.RESPONSE_200_COMMAND_OK;
0552:
0553: default:
0554: return Reply.RESPONSE_501_SYNTAX_ERROR;
0555: }
0556: }
0557:
0558: /**
0559: * <code>REST <SP> <marker> <CRLF></code><br>
0560: *
0561: * The argument field represents the server marker at which file transfer is
0562: * to be restarted. This command does not cause file transfer but skips over
0563: * the file to the specified data checkpoint. This command shall be
0564: * immediately followed by the appropriate FTP service command which shall
0565: * cause file transfer to resume.
0566: */
0567: private Reply doREST(BaseFtpConnection conn) {
0568: FtpRequest request = conn.getRequest();
0569:
0570: // argument check
0571: if (!request.hasArgument()) {
0572: reset(conn);
0573: return Reply.RESPONSE_501_SYNTAX_ERROR;
0574: }
0575:
0576: String skipNum = request.getArgument();
0577:
0578: try {
0579: _resumePosition = Long.parseLong(skipNum);
0580: } catch (NumberFormatException ex) {
0581: reset(conn);
0582: return Reply.RESPONSE_501_SYNTAX_ERROR;
0583: }
0584:
0585: if (_resumePosition < 0) {
0586: _resumePosition = 0;
0587: reset(conn);
0588: return Reply.RESPONSE_501_SYNTAX_ERROR;
0589: }
0590:
0591: return Reply.RESPONSE_350_PENDING_FURTHER_INFORMATION;
0592: }
0593:
0594: private Reply doSITE_RESCAN(BaseFtpConnection conn) {
0595: FtpRequest request = conn.getRequest();
0596: boolean forceRescan = (request.hasArgument() && request
0597: .getArgument().equalsIgnoreCase("force"));
0598: LinkedRemoteFileInterface directory = conn
0599: .getCurrentDirectory();
0600: SFVFile sfv;
0601:
0602: try {
0603: sfv = conn.getCurrentDirectory().lookupSFVFile();
0604: } catch (Exception e) {
0605: return new Reply(200, "Error getting SFV File: "
0606: + e.getMessage());
0607: }
0608:
0609: PrintWriter out = conn.getControlWriter();
0610:
0611: for (Iterator i = sfv.getEntries().entrySet().iterator(); i
0612: .hasNext();) {
0613: Map.Entry entry = (Map.Entry) i.next();
0614: String fileName = (String) entry.getKey();
0615: Long checkSum = (Long) entry.getValue();
0616: LinkedRemoteFileInterface file;
0617:
0618: try {
0619: file = directory.lookupFile(fileName);
0620: } catch (FileNotFoundException ex) {
0621: out.write("200- SFV: "
0622: + Checksum.formatChecksum(checkSum.longValue())
0623: + " SLAVE: " + fileName + " MISSING"
0624: + BaseFtpConnection.NEWLINE);
0625:
0626: continue;
0627: }
0628:
0629: String status;
0630: long fileCheckSum = 0;
0631:
0632: try {
0633: if (forceRescan) {
0634: fileCheckSum = file.getCheckSumFromSlave();
0635: } else {
0636: fileCheckSum = file.getCheckSum();
0637: }
0638: } catch (NoAvailableSlaveException e1) {
0639: out.println("200- " + fileName + "SFV: "
0640: + Checksum.formatChecksum(checkSum.longValue())
0641: + " SLAVE: OFFLINE");
0642:
0643: continue;
0644: } catch (IOException ex) {
0645: out.print("200- " + fileName + " SFV: "
0646: + Checksum.formatChecksum(checkSum.longValue())
0647: + " SLAVE: IO error: " + ex.getMessage());
0648:
0649: continue;
0650: }
0651:
0652: if (fileCheckSum == 0L) {
0653: status = "FAILED - failed to checksum file";
0654: } else if (checkSum.longValue() == fileCheckSum) {
0655: status = "OK";
0656: } else {
0657: status = "FAILED - checksum mismatch";
0658: }
0659:
0660: out.println("200- " + fileName + " SFV: "
0661: + Checksum.formatChecksum(checkSum.longValue())
0662: + " SLAVE: "
0663: + Checksum.formatChecksum(fileCheckSum) + " "
0664: + status);
0665:
0666: continue;
0667: }
0668:
0669: return Reply.RESPONSE_200_COMMAND_OK;
0670: }
0671:
0672: private Reply doSITE_XDUPE(BaseFtpConnection conn) {
0673: return Reply.RESPONSE_502_COMMAND_NOT_IMPLEMENTED;
0674:
0675: // resetState();
0676: //
0677: // if (!request.hasArgument()) {
0678: // if (this.xdupe == 0) {
0679: // out.println("200 Extended dupe mode is disabled.");
0680: // } else {
0681: // out.println(
0682: // "200 Extended dupe mode " + this.xdupe + " is enabled.");
0683: // }
0684: // return;
0685: // }
0686: //
0687: // short myXdupe;
0688: // try {
0689: // myXdupe = Short.parseShort(request.getArgument());
0690: // } catch (NumberFormatException ex) {
0691: // out.print(FtpResponse.RESPONSE_501_SYNTAX_ERROR);
0692: // return;
0693: // }
0694: //
0695: // if (myXdupe > 0 || myXdupe < 4) {
0696: // out.print(
0697: // FtpResponse.RESPONSE_504_COMMAND_NOT_IMPLEMENTED_FOR_PARM);
0698: // return;
0699: // }
0700: // this.xdupe = myXdupe;
0701: // out.println("200 Activated extended dupe mode " + myXdupe + ".");
0702: }
0703:
0704: /**
0705: * <code>STRU <SP> <structure-code> <CRLF></code><br>
0706: *
0707: * The argument is a single Telnet character code specifying file structure.
0708: */
0709: private Reply doSTRU(BaseFtpConnection conn) {
0710: FtpRequest request = conn.getRequest();
0711:
0712: // argument check
0713: if (!request.hasArgument()) {
0714: return Reply.RESPONSE_501_SYNTAX_ERROR;
0715: }
0716:
0717: if (request.getArgument().equalsIgnoreCase("F")) {
0718: return Reply.RESPONSE_200_COMMAND_OK;
0719: }
0720:
0721: return Reply.RESPONSE_504_COMMAND_NOT_IMPLEMENTED_FOR_PARM;
0722: }
0723:
0724: /**
0725: * <code>SYST <CRLF></code><br>
0726: *
0727: * This command is used to find out the type of operating system at the
0728: * server.
0729: */
0730: private Reply doSYST(BaseFtpConnection conn) {
0731: /*
0732: * String systemName = System.getProperty("os.name"); if(systemName ==
0733: * null) { systemName = "UNKNOWN"; } else { systemName =
0734: * systemName.toUpperCase(); systemName = systemName.replace(' ', '-'); }
0735: * String args[] = {systemName};
0736: */
0737: return Reply.RESPONSE_215_SYSTEM_TYPE;
0738:
0739: //String args[] = { "UNIX" };
0740: //out.write(ftpStatus.getResponse(215, request, user, args));
0741: }
0742:
0743: /**
0744: * <code>TYPE <SP> <type-code> <CRLF></code><br>
0745: *
0746: * The argument specifies the representation type.
0747: */
0748: private Reply doTYPE(BaseFtpConnection conn) {
0749: FtpRequest request = conn.getRequest();
0750:
0751: // get type from argument
0752: if (!request.hasArgument()) {
0753: return Reply.RESPONSE_501_SYNTAX_ERROR;
0754: }
0755:
0756: // set it
0757: if (setType(request.getArgument().charAt(0))) {
0758: return Reply.RESPONSE_200_COMMAND_OK;
0759: }
0760:
0761: return Reply.RESPONSE_504_COMMAND_NOT_IMPLEMENTED_FOR_PARM;
0762: }
0763:
0764: public Reply execute(BaseFtpConnection conn) throws ReplyException {
0765: String cmd = conn.getRequest().getCommand();
0766:
0767: if ("MODE".equals(cmd)) {
0768: return doMODE(conn);
0769: }
0770:
0771: if ("PASV".equals(cmd) || "CPSV".equals(cmd)) {
0772: return doPASVandCPSV(conn);
0773: }
0774:
0775: if ("PORT".equals(cmd)) {
0776: return doPORT(conn);
0777: }
0778:
0779: if ("PRET".equals(cmd)) {
0780: return doPRET(conn);
0781: }
0782:
0783: if ("REST".equals(cmd)) {
0784: return doREST(conn);
0785: }
0786:
0787: if ("RETR".equals(cmd) || "STOR".equals(cmd)
0788: || "APPE".equals(cmd)) {
0789: return transfer(conn);
0790: }
0791:
0792: if ("SITE RESCAN".equals(cmd)) {
0793: return doSITE_RESCAN(conn);
0794: }
0795:
0796: if ("SITE XDUPE".equals(cmd)) {
0797: return doSITE_XDUPE(conn);
0798: }
0799:
0800: if ("STRU".equals(cmd)) {
0801: return doSTRU(conn);
0802: }
0803:
0804: if ("SYST".equals(cmd)) {
0805: return doSYST(conn);
0806: }
0807:
0808: if ("TYPE".equals(cmd)) {
0809: return doTYPE(conn);
0810: }
0811:
0812: if ("AUTH".equals(cmd)) {
0813: return doAUTH(conn);
0814: }
0815:
0816: if ("PROT".equals(cmd)) {
0817: return doPROT(conn);
0818: }
0819:
0820: if ("PBSZ".equals(cmd)) {
0821: return doPBSZ(conn);
0822: }
0823:
0824: if ("SSCN".equals(cmd)) {
0825: return doSSCN(conn);
0826: }
0827:
0828: throw UnhandledCommandException.create(
0829: DataConnectionHandler.class, conn.getRequest());
0830: }
0831:
0832: /**
0833: * Get the data socket.
0834: *
0835: * Used by LIST and NLST and MLST.
0836: */
0837: public Socket getDataSocket() throws IOException {
0838: Socket dataSocket;
0839:
0840: // get socket depending on the selection
0841: if (isPort()) {
0842: try {
0843: ActiveConnection ac = new ActiveConnection(
0844: _encryptedDataChannel ? _ctx : null,
0845: _portAddress, _SSLHandshakeClientMode);
0846: dataSocket = ac.connect(0);
0847: } catch (IOException ex) {
0848: logger.warn("Error opening data socket", ex);
0849: dataSocket = null;
0850: throw ex;
0851: }
0852: } else if (isPasv()) {
0853: try {
0854: dataSocket = _passiveConnection.connect(0);
0855: } finally {
0856: if (_passiveConnection != null) {
0857: _passiveConnection.abort();
0858: _passiveConnection = null;
0859: }
0860: }
0861: } else {
0862: throw new IllegalStateException("Neither PASV nor PORT");
0863: }
0864: // Already done since we are using ActiveConnection and PasvConnection
0865: /* dataSocket.setSoTimeout(Connection.TIMEOUT); // 15 seconds timeout
0866:
0867: if (dataSocket instanceof SSLSocket) {
0868: SSLSocket ssldatasocket = (SSLSocket) dataSocket;
0869: ssldatasocket.setUseClientMode(false);
0870: ssldatasocket.startHandshake();
0871: }*/
0872:
0873: return dataSocket;
0874: }
0875:
0876: public String[] getFeatReplies() {
0877: if (_ctx != null) {
0878: return new String[] { "PRET", "AUTH SSL", "PBSZ", "CPSV",
0879: "SSCN" };
0880: }
0881:
0882: return new String[] { "PRET" };
0883: }
0884:
0885: public String getHelp(String cmd) {
0886: ResourceBundle bundle = ResourceBundle
0887: .getBundle(DataConnectionHandler.class.getName());
0888: if ("".equals(cmd))
0889: return bundle.getString("help.general") + "\n";
0890: else if ("rescan".equals(cmd))
0891: return bundle.getString("help.rescan") + "\n";
0892: else
0893: return "";
0894: }
0895:
0896: public RemoteSlave getTranferSlave() {
0897: return _rslave;
0898: }
0899:
0900: public synchronized RemoteTransfer getTransfer()
0901: throws ObjectNotFoundException {
0902: if (_transfer == null)
0903: throw new ObjectNotFoundException();
0904: return _transfer;
0905: }
0906:
0907: public LinkedRemoteFileInterface getTransferFile() {
0908: return _transferFile;
0909: }
0910:
0911: /**
0912: * Get the user data type.
0913: */
0914: public char getType() {
0915: return _type;
0916: }
0917:
0918: public CommandHandler initialize(BaseFtpConnection conn,
0919: CommandManager initializer) {
0920: try {
0921: return (DataConnectionHandler) clone();
0922: } catch (CloneNotSupportedException e) {
0923: throw new RuntimeException(e);
0924: }
0925: }
0926:
0927: public boolean isEncryptedDataChannel() {
0928: return _encryptedDataChannel;
0929: }
0930:
0931: /**
0932: * Guarantes pre transfer is set up correctly.
0933: */
0934: public boolean isPasv() {
0935: return _isPasv;
0936: }
0937:
0938: public boolean isPort() {
0939: return _isPort;
0940: }
0941:
0942: public boolean isPreTransfer() {
0943: return _preTransfer || isPasv();
0944: }
0945:
0946: public synchronized boolean isTransfering() {
0947: return _transfer != null && _rslave != null
0948: && _transferFile != null;
0949: }
0950:
0951: public void load(CommandManagerFactory initializer) {
0952: }
0953:
0954: protected synchronized void reset(BaseFtpConnection conn) {
0955: _rslave = null;
0956: if (_transfer != null) {
0957: try {
0958: _transfer.abort("reset");
0959: } catch (Throwable t) {
0960: logger.debug(
0961: "reset failed to abort transfer on the slave",
0962: t);
0963: }
0964: }
0965: _transfer = null;
0966: if (_transferFile != null) {
0967: if ((conn.getRequest().getCommand().equals("STOR") == true)
0968: && (_transferFile.getXfertime() == -1)) {
0969:
0970: // Transfer failed on STOR
0971:
0972: _transferFile.setXfertime(0);
0973: }
0974: _transferFile = null;
0975: }
0976: _preTransfer = false;
0977: _preTransferRSlave = null;
0978:
0979: if (_passiveConnection != null) { //isPasv() && _preTransferRSlave == null
0980: _passiveConnection.abort();
0981: }
0982:
0983: _isPasv = false;
0984: _passiveConnection = null;
0985: _isPort = false;
0986: _resumePosition = 0;
0987: }
0988:
0989: /**
0990: * Set the data type. Supported types are A (ascii) and I (binary).
0991: *
0992: * @return true if success
0993: */
0994: private boolean setType(char type) {
0995: type = Character.toUpperCase(type);
0996:
0997: if ((type != 'A') && (type != 'I')) {
0998: return false;
0999: }
1000:
1001: _type = type;
1002:
1003: return true;
1004: }
1005:
1006: /**
1007: * <code>STOU <CRLF></code><br>
1008: *
1009: * This command behaves like STOR except that the resultant file is to be
1010: * created in the current directory under a name unique to that directory.
1011: * The 250 Transfer Started response must include the name generated.
1012: */
1013:
1014: //TODO STOU
1015: /*
1016: * public void doSTOU(FtpRequest request, PrintWriter out) {
1017: * // reset state variables resetState();
1018: * // get filenames String fileName =
1019: * user.getVirtualDirectory().getAbsoluteName("ftp.dat"); String
1020: * physicalName = user.getVirtualDirectory().getPhysicalName(fileName); File
1021: * requestedFile = new File(physicalName); requestedFile =
1022: * IoUtils.getUniqueFile(requestedFile); fileName =
1023: * user.getVirtualDirectory().getVirtualName(requestedFile.getAbsolutePath());
1024: * String args[] = {fileName};
1025: * // check permission
1026: * if(!user.getVirtualDirectory().hasCreatePermission(fileName, false)) {
1027: * out.write(ftpStatus.getResponse(550, request, user, null)); return; }
1028: * // now transfer file data out.print(FtpResponse.RESPONSE_150_OK);
1029: * InputStream is = null; OutputStream os = null; try { Socket dataSoc =
1030: * mDataConnection.getDataSocket(); if (dataSoc == null) {
1031: * out.write(ftpStatus.getResponse(550, request, user, args)); return; }
1032: *
1033: *
1034: * is = dataSoc.getInputStream(); os = user.getOutputStream( new
1035: * FileOutputStream(requestedFile) );
1036: *
1037: * StreamConnector msc = new StreamConnector(is, os);
1038: * msc.setMaxTransferRate(user.getMaxUploadRate()); msc.setObserver(this);
1039: * msc.connect();
1040: *
1041: * if(msc.hasException()) { out.write(ftpStatus.getResponse(451, request,
1042: * user, null)); return; } else {
1043: * mConfig.getStatistics().setUpload(requestedFile, user,
1044: * msc.getTransferredSize()); }
1045: *
1046: * out.write(ftpStatus.getResponse(226, request, user, null));
1047: * mDataConnection.reset(); out.write(ftpStatus.getResponse(250, request,
1048: * user, args)); } catch(IOException ex) {
1049: * out.write(ftpStatus.getResponse(425, request, user, null)); } finally {
1050: * IoUtils.close(is); IoUtils.close(os); mDataConnection.reset(); } }
1051: */
1052:
1053: /**
1054: * <code>RETR <SP> <pathname> <CRLF></code><br>
1055: *
1056: * This command causes the server-DTP to transfer a copy of the file,
1057: * specified in the pathname, to the server- or user-DTP at the other end of
1058: * the data connection. The status and contents of the file at the server
1059: * site shall be unaffected.
1060: *
1061: * RETR 125, 150 (110) 226, 250 425, 426, 451 450, 550 500, 501, 421, 530
1062: * <p>
1063: * <code>STOR <SP> <pathname> <CRLF></code><br>
1064: *
1065: * This command causes the server-DTP to accept the data transferred via the
1066: * data connection and to store the data as a file at the server site. If
1067: * the file specified in the pathname exists at the server site, then its
1068: * contents shall be replaced by the data being transferred. A new file is
1069: * created at the server site if the file specified in the pathname does not
1070: * already exist.
1071: *
1072: * STOR 125, 150 (110) 226, 250 425, 426, 451, 551, 552 532, 450, 452, 553
1073: * 500, 501, 421, 530
1074: *
1075: * ''zipscript?? renames bad uploads to .bad, how do we handle this with
1076: * resumes?
1077: */
1078: //TODO add APPE support
1079: private Reply transfer(BaseFtpConnection conn)
1080: throws ReplyException {
1081: ReplacerEnvironment env = new ReplacerEnvironment();
1082: if (!_encryptedDataChannel
1083: && conn.getGlobalContext().getConfig().checkPermission(
1084: "denydatauncrypted", conn.getUserNull())) {
1085: reset(conn);
1086: return new Reply(530, "USE SECURE DATA CONNECTION");
1087: }
1088:
1089: try {
1090: FtpRequest request = conn.getRequest();
1091: char direction = conn.getDirection();
1092: String cmd = conn.getRequest().getCommand();
1093: boolean isStor = cmd.equals("STOR");
1094: boolean isRetr = cmd.equals("RETR");
1095: boolean isAppe = cmd.equals("APPE");
1096: boolean isStou = cmd.equals("STOU");
1097: String eventType = isRetr ? "RETR" : "STOR";
1098:
1099: if (isAppe || isStou) {
1100: throw UnhandledCommandException.create(
1101: DataConnectionHandler.class, conn.getRequest());
1102: }
1103:
1104: // argument check
1105: if (!request.hasArgument()) {
1106: // reset(); already done in finally block
1107: return Reply.RESPONSE_501_SYNTAX_ERROR;
1108: }
1109:
1110: // Checks maxsim up/down
1111: // _simup OR _simdown = 0, exempt
1112: int comparison = 0;
1113: int count = conn.transferCounter(direction);
1114: env.add("maxsim", count);
1115:
1116: if (direction == Transfer.TRANSFER_RECEIVING_UPLOAD) {
1117: comparison = conn.getUserNull().getMaxSimUp();
1118: env.add("direction", "upload");
1119: } else {
1120: comparison = conn.getUserNull().getMaxSimDown();
1121: env.add("direction", "download");
1122: }
1123:
1124: if (comparison != 0 && count >= comparison)
1125: return new Reply(550, conn.jprintf(
1126: DataConnectionHandler.class,
1127: "transfer.err.maxsim", env));
1128:
1129: // get filenames
1130: LinkedRemoteFileInterface targetDir;
1131: String targetFileName;
1132:
1133: if (isRetr) {
1134: try {
1135: _transferFile = conn.getCurrentDirectory()
1136: .lookupFile(request.getArgument());
1137:
1138: if (!_transferFile.isFile()) {
1139: // reset(); already done in finally block
1140: return new Reply(550, "Not a plain file");
1141: }
1142:
1143: targetDir = _transferFile.getParentFileNull();
1144: targetFileName = _transferFile.getName();
1145: } catch (FileNotFoundException ex) {
1146: // reset(); already done in finally block
1147: return new Reply(550, ex.getMessage());
1148: }
1149: } else if (isStor) {
1150: LinkedRemoteFile.NonExistingFile ret = conn
1151: .getCurrentDirectory().lookupNonExistingFile(
1152: conn.getGlobalContext().getConfig()
1153: .getFileName(
1154: request.getArgument()));
1155: targetDir = ret.getFile();
1156: targetFileName = ret.getPath();
1157:
1158: if (ret.exists()) {
1159: // target exists, this could be overwrite or resume
1160: //TODO overwrite & resume files.
1161: // reset(); already done in finally block
1162: return Reply.RESPONSE_553_REQUESTED_ACTION_NOT_TAKEN_FILE_EXISTS;
1163:
1164: //_transferFile = targetDir;
1165: //targetDir = _transferFile.getParent();
1166: //if(_transfereFile.getOwner().equals(getUser().getUsername()))
1167: // {
1168: // // allow overwrite/resume
1169: //}
1170: //if(directory.isDirectory()) {
1171: // return FtpReply.RESPONSE_550_REQUESTED_ACTION_NOT_TAKEN;
1172: //}
1173: }
1174:
1175: if (!ListUtils.isLegalFileName(targetFileName)
1176: || !conn.getGlobalContext().getConfig()
1177: .checkPathPermission("privpath",
1178: conn.getUserNull(), targetDir,
1179: true)) {
1180: // reset(); already done in finally block
1181: return new Reply(553,
1182: "Requested action not taken. File name not allowed.");
1183: }
1184:
1185: //do our zipscript sfv checks
1186: String checkName = targetFileName.toLowerCase();
1187: ZipscriptConfig zsCfg = conn.getGlobalContext()
1188: .getZsConfig();
1189: boolean SfvFirstEnforcedPath = zsCfg
1190: .checkSfvFirstEnforcedPath(targetDir, conn
1191: .getUserNull());
1192: try {
1193: SFVFile sfv;
1194: try {
1195: sfv = conn.getCurrentDirectory()
1196: .lookupSFVFile();
1197: } catch (FileStillTransferringException e) {
1198: // I have no idea how to handle this
1199: return new Reply(400,
1200: "SFVFile still transferring.");
1201: }
1202: if (checkName.endsWith(".sfv")
1203: && !zsCfg.multiSfvAllowed()) {
1204: return new Reply(533,
1205: "Requested action not taken. Multiple SFV files not allowed.");
1206: }
1207: if (SfvFirstEnforcedPath
1208: && !zsCfg.checkAllowedExtension(checkName)) {
1209: // filename not explicitly permitted, check for sfv entry
1210: boolean allow = false;
1211: if (zsCfg.restrictSfvEnabled()) {
1212: for (Iterator iter = sfv.getNames()
1213: .iterator(); iter.hasNext();) {
1214: String name = (String) iter.next();
1215: if (name.toLowerCase()
1216: .equals(checkName)) {
1217: allow = true;
1218: break;
1219: }
1220: }
1221: if (!allow) {
1222: return new Reply(533,
1223: "Requested action not taken. File not found in sfv.");
1224: }
1225: }
1226: }
1227: } catch (FileNotFoundException e1) {
1228: // no sfv found in dir
1229: if (!zsCfg.checkAllowedExtension(checkName)
1230: && SfvFirstEnforcedPath) {
1231: // filename not explicitly permitted
1232: // ForceSfvFirst is on, and file is in an enforced path.
1233: return new Reply(533,
1234: "Requested action not taken. You must upload sfv first.");
1235: }
1236: } catch (IOException e1) {
1237: //error reading sfv, do nothing
1238: } catch (NoAvailableSlaveException e1) {
1239: //sfv not online, do nothing
1240: }
1241: } else {
1242: // reset(); already done in finally block
1243: throw UnhandledCommandException.create(
1244: DataConnectionHandler.class, request);
1245: }
1246:
1247: // check access
1248: if (!conn.getGlobalContext().getConfig()
1249: .checkPathPermission("privpath",
1250: conn.getUserNull(), targetDir, true)) {
1251: // reset(); already done in finally block
1252: return new Reply(550, request.getArgument()
1253: + ": No such file");
1254: }
1255:
1256: switch (direction) {
1257: case Transfer.TRANSFER_SENDING_DOWNLOAD:
1258:
1259: if (!conn.getGlobalContext().getConfig()
1260: .checkPathPermission("download",
1261: conn.getUserNull(), targetDir)) {
1262: // reset(); already done in finally block
1263: return Reply.RESPONSE_530_ACCESS_DENIED;
1264: }
1265:
1266: break;
1267:
1268: case Transfer.TRANSFER_RECEIVING_UPLOAD:
1269:
1270: if (!conn.getGlobalContext().getConfig()
1271: .checkPathPermission("upload",
1272: conn.getUserNull(), targetDir)) {
1273: // reset(); already done in finally block
1274: return Reply.RESPONSE_530_ACCESS_DENIED;
1275: }
1276:
1277: break;
1278:
1279: default:
1280: // reset(); already done in finally block
1281: throw UnhandledCommandException.create(
1282: DataConnectionHandler.class, request);
1283: }
1284:
1285: //check credits
1286: if (isRetr) {
1287: if ((conn.getUserNull().getKeyedMap().getObjectFloat(
1288: UserManagement.RATIO) != 0)
1289: && (conn.getGlobalContext().getConfig()
1290: .getCreditLossRatio(_transferFile,
1291: conn.getUserNull()) != 0)
1292: && (conn.getUserNull().getCredits() < _transferFile
1293: .length())) {
1294: // reset(); already done in finally block
1295: return new Reply(550, "Not enough credits.");
1296: }
1297: }
1298:
1299: //setup _rslave
1300: //if (isCpsv)
1301: if (isPasv()) {
1302: // isPasv() means we're setup correctly
1303: // if (!_preTransfer || _preTransferRSlave == null)
1304: // return FtpReply.RESPONSE_503_BAD_SEQUENCE_OF_COMMANDS;
1305: //check pretransfer
1306: if (isRetr
1307: && !_transferFile.getSlaves().contains(
1308: _preTransferRSlave)) {
1309: // reset(); already done in finally block
1310: return Reply.RESPONSE_503_BAD_SEQUENCE_OF_COMMANDS;
1311: }
1312:
1313: _rslave = _preTransferRSlave;
1314:
1315: //_preTransferRSlave = null;
1316: //_preTransfer = false;
1317: //code above to be handled by reset()
1318: } else {
1319: try {
1320: if (direction == Transfer.TRANSFER_SENDING_DOWNLOAD) {
1321: _rslave = conn
1322: .getGlobalContext()
1323: .getSlaveSelectionManager()
1324: .getASlave(
1325: _transferFile
1326: .getAvailableSlaves(),
1327: Transfer.TRANSFER_SENDING_DOWNLOAD,
1328: conn, _transferFile);
1329: } else if (direction == Transfer.TRANSFER_RECEIVING_UPLOAD) {
1330: _rslave = conn
1331: .getGlobalContext()
1332: .getSlaveSelectionManager()
1333: .getASlave(
1334: conn.getGlobalContext()
1335: .getSlaveManager()
1336: .getAvailableSlaves(),
1337: Transfer.TRANSFER_RECEIVING_UPLOAD,
1338: conn, targetDir);
1339: } else {
1340: // reset(); already done in finally block
1341: throw new RuntimeException();
1342: }
1343: } catch (NoAvailableSlaveException ex) {
1344: //TODO Might not be good to 450 reply always
1345: //from rfc: 450 Requested file action not taken. File unavailable (e.g., file busy).
1346: // reset(); already done in finally block
1347: throw new ReplySlaveUnavailableException(ex, 450);
1348: }
1349: }
1350:
1351: if (isStor) {
1352: //setup upload
1353: if (_rslave == null) {
1354: // reset(); already done in finally block
1355: throw new NullPointerException();
1356: }
1357:
1358: List rslaves = Collections.singletonList(_rslave);
1359: StaticRemoteFile uploadFile = new StaticRemoteFile(
1360: rslaves, targetFileName, conn.getUserNull()
1361: .getName(), conn.getUserNull()
1362: .getGroup(), 0L, System
1363: .currentTimeMillis(), 0L);
1364: synchronized (this ) {
1365: uploadFile.setXfertime(-1); // used for new files to be
1366: // uploaded, see getXfertime()
1367: _transferFile = targetDir.addFile(uploadFile);
1368: }
1369: }
1370:
1371: // setup _transfer
1372:
1373: if (isPort()) {
1374: try {
1375: String index = _rslave.issueConnectToSlave(
1376: _portAddress.getAddress().getHostAddress(),
1377: _portAddress.getPort(),
1378: _encryptedDataChannel,
1379: _SSLHandshakeClientMode);
1380: ConnectInfo ci = _rslave
1381: .fetchTransferResponseFromIndex(index);
1382: synchronized (this ) {
1383: _transfer = _rslave.getTransfer(ci
1384: .getTransferIndex());
1385: }
1386: } catch (Exception ex) {
1387: logger.fatal("rslave=" + _rslave, ex);
1388: // reset(); already done in finally block
1389: return new Reply(450, ex.getClass().getName()
1390: + " from slave: " + ex.getMessage());
1391: }
1392: } else if (isPasv()) {
1393: //_transfer is already set up by doPASV()
1394: } else {
1395: // reset(); already done in finally block
1396: if (isStor) {
1397: _transferFile.delete();
1398: }
1399: return Reply.RESPONSE_503_BAD_SEQUENCE_OF_COMMANDS;
1400: }
1401:
1402: {
1403: PrintWriter out = conn.getControlWriter();
1404: out.write(new Reply(150,
1405: "File status okay; about to open data connection "
1406: + (isRetr ? "from " : "to ")
1407: + _rslave.getName() + ".").toString());
1408: out.flush();
1409: }
1410:
1411: TransferStatus status = null;
1412:
1413: //transfer
1414: try {
1415: //TODO ABORtable transfers
1416: if (isRetr) {
1417: _transfer.sendFile(_transferFile.getPath(),
1418: getType(), _resumePosition);
1419:
1420: while (true) {
1421: status = _transfer.getTransferStatus();
1422:
1423: if (status.isFinished()) {
1424: break;
1425: }
1426:
1427: try {
1428: Thread.sleep(100);
1429: } catch (InterruptedException e1) {
1430: }
1431: }
1432: } else if (isStor) {
1433: _transfer.receiveFile(_transferFile.getPath(),
1434: getType(), _resumePosition);
1435:
1436: while (true) {
1437: status = _transfer.getTransferStatus();
1438: _transferFile.setLength(status.getTransfered());
1439: if (status.isFinished()) {
1440: break;
1441: }
1442: try {
1443: Thread.sleep(100);
1444: } catch (InterruptedException e1) {
1445: }
1446: }
1447: } else {
1448: throw new RuntimeException();
1449: }
1450: } catch (IOException ex) {
1451: logger.debug("", ex);
1452: if (ex instanceof TransferFailedException) {
1453: // the below chunk makes no sense, we don't process it anywhere
1454: /* status = ((TransferFailedException) ex).getStatus();
1455: conn.getGlobalContext()
1456: .dispatchFtpEvent(new TransferEvent(conn, eventType,
1457: _transferFile, conn.getClientAddress(), _rslave,
1458: _transfer.getAddress().getAddress(), _type, false));
1459: */
1460: if (isRetr) {
1461: conn.getUserNull().updateCredits(
1462: -status.getTransfered());
1463: }
1464: }
1465:
1466: Reply reply = null;
1467:
1468: if (isStor) {
1469: _transferFile.delete();
1470: logger
1471: .error(
1472: "IOException during transfer, deleting file",
1473: ex);
1474: reply = new Reply(426,
1475: "Transfer failed, deleting file");
1476: } else {
1477: logger.error("IOException during transfer", ex);
1478: reply = new Reply(426, ex.getMessage());
1479: }
1480:
1481: reply.addComment(ex.getMessage());
1482: // reset(); already done in finally block
1483: return reply;
1484: } catch (SlaveUnavailableException e) {
1485: logger.debug("", e);
1486: Reply reply = null;
1487:
1488: if (isStor) {
1489: _transferFile.delete();
1490: logger
1491: .error(
1492: "Slave went offline during transfer, deleting file",
1493: e);
1494: reply = new Reply(426,
1495: "Slave went offline during transfer, deleting file");
1496: } else {
1497: logger.error("Slave went offline during transfer",
1498: e);
1499: reply = new Reply(426,
1500: "Slave went offline during transfer");
1501: }
1502:
1503: reply.addComment(e.getLocalizedMessage());
1504: // reset(); already done in finally block
1505: return reply;
1506: }
1507:
1508: // TransferThread transferThread = new TransferThread(rslave,
1509: // transfer);
1510: // System.err.println("Calling interruptibleSleepUntilFinished");
1511: // try {
1512: // transferThread.interruptibleSleepUntilFinished();
1513: // } catch (Throwable e1) {
1514: // e1.printStackTrace();
1515: // }
1516: // System.err.println("Finished");
1517: env = new ReplacerEnvironment();
1518: env.add("bytes", Bytes.formatBytes(status.getTransfered()));
1519: env.add("speed", Bytes.formatBytes(status.getXferSpeed())
1520: + "/s");
1521: env.add("seconds", ""
1522: + ((float) status.getElapsed() / 1000F));
1523: env.add("checksum", Checksum.formatChecksum(status
1524: .getChecksum()));
1525:
1526: Reply response = new Reply(226, conn.jprintf(
1527: DataConnectionHandler.class, "transfer.complete",
1528: env));
1529: synchronized (conn.getGlobalContext()) { // need to synchronize
1530: // here so only one
1531: // TransferEvent can be sent at a time
1532: if (isStor) {
1533: if (_resumePosition == 0) {
1534: _transferFile.setCheckSum(status.getChecksum());
1535: } else {
1536: // try {
1537: // checksum = _transferFile.getCheckSumFromSlave();
1538: // } catch (NoAvailableSlaveException e) {
1539: // response.addComment(
1540: // "No available slaves when getting checksum from
1541: // slave: "
1542: // + e.getMessage());
1543: // logger.warn("", e);
1544: // checksum = 0;
1545: // } catch (IOException e) {
1546: // response.addComment(
1547: // "IO error getting checksum from slave: "
1548: // + e.getMessage());
1549: // logger.warn("", e);
1550: // checksum = 0;
1551: // }
1552: }
1553:
1554: _transferFile.setLastModified(System
1555: .currentTimeMillis());
1556: _transferFile.setLength(status.getTransfered());
1557: _transferFile.setXfertime(status.getElapsed());
1558: }
1559:
1560: boolean zipscript = zipscript(isRetr, isStor, status
1561: .getChecksum(), response, targetFileName,
1562: targetDir);
1563:
1564: if (zipscript) {
1565: // transferstatistics
1566: if (isRetr) {
1567:
1568: float ratio = conn.getGlobalContext()
1569: .getConfig().getCreditLossRatio(
1570: _transferFile,
1571: conn.getUserNull());
1572:
1573: if (ratio != 0) {
1574: conn
1575: .getUserNull()
1576: .updateCredits(
1577: (long) (-status
1578: .getTransfered() * ratio));
1579: }
1580:
1581: if (!conn.getGlobalContext().getConfig()
1582: .checkPathPermission("nostatsdn",
1583: conn.getUserNull(),
1584: conn.getCurrentDirectory())) {
1585: conn.getUserNull().updateDownloadedBytes(
1586: status.getTransfered());
1587: conn.getUserNull().updateDownloadedTime(
1588: status.getElapsed());
1589: conn.getUserNull().updateDownloadedFiles(1);
1590: }
1591: } else {
1592:
1593: conn.getUserNull().updateCredits(
1594: (long) (status.getTransfered() * conn
1595: .getGlobalContext().getConfig()
1596: .getCreditCheckRatio(
1597: _transferFile,
1598: conn.getUserNull())));
1599: if (!conn.getGlobalContext().getConfig()
1600: .checkPathPermission("nostatsup",
1601: conn.getUserNull(),
1602: conn.getCurrentDirectory())) {
1603: conn.getUserNull().updateUploadedBytes(
1604: status.getTransfered());
1605: conn.getUserNull().updateUploadedTime(
1606: status.getElapsed());
1607: conn.getUserNull().updateUploadedFiles(1);
1608: }
1609: }
1610:
1611: try {
1612: conn.getUserNull().commit();
1613: } catch (UserFileException e) {
1614: logger.warn("", e);
1615: }
1616: }
1617:
1618: // Dispatch for both STOR and RETR
1619: conn.getGlobalContext().dispatchFtpEvent(
1620: new TransferEvent(conn, eventType,
1621: _transferFile, conn.getClientAddress(),
1622: _rslave, _transfer.getAddress()
1623: .getAddress(), getType()));
1624: return response;
1625: }
1626: } finally {
1627: reset(conn);
1628: }
1629: }
1630:
1631: public void unload() {
1632: }
1633:
1634: /**
1635: * @param isRetr
1636: * @param isStor
1637: * @param status
1638: * @param response
1639: * @param targetFileName
1640: * @param targetDir
1641: * Returns true if crc check was okay, i.e, if credits should be
1642: * altered
1643: */
1644: private boolean zipscript(boolean isRetr, boolean isStor,
1645: long checksum, Reply response, String targetFileName,
1646: LinkedRemoteFileInterface targetDir) {
1647: // zipscript
1648: logger.debug("Running zipscript on file " + targetFileName
1649: + " with CRC of " + checksum);
1650:
1651: if (isRetr) {
1652: //compare checksum from transfer to cached checksum
1653: logger.debug("checksum from transfer = " + checksum);
1654:
1655: if (checksum != 0) {
1656: response.addComment("Checksum from transfer: "
1657: + Checksum.formatChecksum(checksum));
1658:
1659: long cachedChecksum;
1660: cachedChecksum = _transferFile.getCheckSumCached();
1661:
1662: if (cachedChecksum == 0) {
1663: _transferFile.setCheckSum(checksum);
1664: } else if (cachedChecksum != checksum) {
1665: response
1666: .addComment("WARNING: checksum from transfer didn't match cached checksum");
1667: logger.info("checksum from transfer "
1668: + Checksum.formatChecksum(checksum)
1669: + "didn't match cached checksum"
1670: + Checksum.formatChecksum(cachedChecksum)
1671: + " for " + _transferFile.toString()
1672: + " from slave " + _rslave.getName(),
1673: new Throwable());
1674: }
1675:
1676: //compare checksum from transfer to checksum from sfv
1677: try {
1678: long sfvChecksum;
1679: try {
1680: sfvChecksum = _transferFile.getParentFileNull()
1681: .lookupSFVFile().getChecksum(
1682: _transferFile.getName());
1683: if (sfvChecksum == checksum) {
1684: response
1685: .addComment("checksum from transfer matched checksum in .sfv");
1686: } else {
1687: response
1688: .addComment("WARNING: checksum from transfer didn't match checksum in .sfv");
1689: }
1690: } catch (FileStillTransferringException e) {
1691: response
1692: .addComment("checksum from sfv doesn't exist yet, SFVFile is being uploaded");
1693: }
1694: } catch (NoAvailableSlaveException e1) {
1695: response
1696: .addComment("slave with .sfv offline, checksum not verified");
1697: } catch (FileNotFoundException e1) {
1698: //continue without verification
1699: } catch (NoSFVEntryException e1) {
1700: //file not found in .sfv, continue
1701: } catch (IOException e1) {
1702: logger.info("", e1);
1703: response.addComment("IO Error reading sfv file: "
1704: + e1.getMessage());
1705: }
1706: } else { // slave has disabled download crc
1707:
1708: //response.addComment("Slave has disabled download checksum");
1709: }
1710: } else if (isStor) {
1711: if (!targetFileName.toLowerCase().endsWith(".sfv")) {
1712: try {
1713: long sfvChecksum;
1714: try {
1715: sfvChecksum = targetDir.lookupSFVFile()
1716: .getChecksum(targetFileName);
1717: if (checksum == sfvChecksum) {
1718: response
1719: .addComment("checksum match: SLAVE/SFV:"
1720: + Long
1721: .toHexString(checksum));
1722: } else if (checksum == 0) {
1723: response
1724: .addComment("checksum match: SLAVE/SFV: DISABLED");
1725: } else {
1726: response
1727: .addComment("checksum mismatch: SLAVE: "
1728: + Long
1729: .toHexString(checksum)
1730: + " SFV: "
1731: + Long
1732: .toHexString(sfvChecksum));
1733: response.addComment(" deleting file");
1734: response
1735: .setMessage("Checksum mismatch, deleting file");
1736: _transferFile.delete();
1737:
1738: // getUser().updateCredits(
1739: // - ((long) getUser().getRatio() * transferedBytes));
1740: // getUser().updateUploadedBytes(-transferedBytes);
1741: // response.addComment(conn.status());
1742: return false; // don't modify credits
1743:
1744: // String badtargetFilename = targetFilename + ".bad";
1745: //
1746: // try {
1747: // LinkedRemoteFile badtargetFile =
1748: // targetDir.getFile(badtargetFilename);
1749: // badtargetFile.delete();
1750: // response.addComment(
1751: // "zipscript - removing "
1752: // + badtargetFilename
1753: // + " to be replaced with new file");
1754: // } catch (FileNotFoundException e2) {
1755: // //good, continue...
1756: // response.addComment(
1757: // "zipscript - checksum mismatch, renaming to "
1758: // + badtargetFilename);
1759: // }
1760: // targetFile.renameTo(targetDir.getPath() +
1761: // badtargetFilename);
1762: }
1763: } catch (FileStillTransferringException e) {
1764: response
1765: .addComment("No sfv to compare to (SFVTransferring), file allowed");
1766: }
1767: } catch (NoAvailableSlaveException e) {
1768: response
1769: .addComment("zipscript - SFV unavailable, slave(s) with .sfv file is offline");
1770: } catch (NoSFVEntryException e) {
1771: response
1772: .addComment("zipscript - no entry in sfv for file");
1773: } catch (IOException e) {
1774: response
1775: .addComment("zipscript - SFV unavailable, IO error: "
1776: + e.getMessage());
1777: }
1778: }
1779: }
1780:
1781: return true; // modify credits, transfer was okay
1782: }
1783:
1784: public synchronized void handshakeCompleted(
1785: HandshakeCompletedEvent arg0) {
1786: _handshakeCompleted = true;
1787: notifyAll();
1788: }
1789: }
|