?? ftpconnection.java
字號:
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE file.
*/
package server.ftp;
import io.IoUtils;
import io.StreamConnector;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.RandomAccessFile;
import java.io.Writer;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.StringTokenizer;
/**
* This class handles each ftp connection. Here all the ftp command
* methods take two arguments - a ftp request and a writer object.
* This is the main backbone of the ftp server.
* <br>
* The ftp command method signature is:
* <code>public void doXYZ(FtpRequest request, FtpWriter out) throws IOException</code>.
* <br>
* Here <code>XYZ</code> is the capitalized ftp command.
*
* @author <a href="mailto:rana_b@yahoo.com">Rana Bhattacharyya</a>
*/
public
class FtpConnection extends BaseFtpConnection {
// as SimpleDateFormat is not thread-safe we have to use ThreadLocal
private final static ThreadLocal DATE_FMT = new ThreadLocal() {
protected Object initialValue() {
return new SimpleDateFormat("yyyyMMddHHmmss.SSS");
}
};
// command state specific temporary variables
private boolean mbReset = false;
private long mlSkipLen = 0;
private boolean mbRenFr = false;
private String mstRenFr = null;
private boolean mbUser = false;
private boolean mbPass = false;
/**
* Set configuration file and the control socket.
*/
public FtpConnection(FtpConfig cfg, Socket soc) {
super(cfg, soc);
}
/**
* Check the user permission to execute this command.
*/
protected boolean hasPermission(FtpRequest request) {
String cmd = request.getCommand();
return mUser.hasLoggedIn() ||
cmd.equals("USER") ||
cmd.equals("PASS") ||
cmd.equals("HELP") ||
cmd.equals("SYST");
}
/**
* Reset temporary state variables.
*/
private void resetState() {
mbRenFr = false;
mstRenFr = null;
mbReset = false;
mlSkipLen = 0;
mbUser = false;
mbPass = false;
}
////////////////////////////////////////////////////////////
///////////////// all the FTP handlers /////////////////
////////////////////////////////////////////////////////////
/**
* <code>ABOR <CRLF></code><br>
*
* This command tells the server to abort the previous FTP
* service command and any associated transfer of data.
* No action is to be taken if the previous command
* has been completed (including data transfer). The control
* connection is not to be closed by the server, but the data
* connection must be closed.
* Current implementation does not do anything. As here data
* transfers are not multi-threaded.
*/
public void doABOR(FtpRequest request, FtpWriter out) throws IOException {
// reset state variables
resetState();
// and abort any data connection
mDataConnection.closeDataSocket();
out.write(mFtpStatus.getResponse(226, request, mUser, null));
}
/**
* <code>APPE <SP> <pathname> <CRLF></code><br>
*
* This command causes the server-DTP to accept the data
* transferred via the data connection and to store the data in
* a file at the server site. If the file specified in the
* pathname exists at the server site, then the data shall be
* appended to that file; otherwise the file specified in the
* pathname shall be created at the server site.
*/
public void doAPPE(FtpRequest request, FtpWriter out) throws IOException {
InputStream is = null;
OutputStream os = null;
String[] args = null;
try {
// reset state variables
resetState();
// argument check
if(!request.hasArgument()) {
out.write(mFtpStatus.getResponse(501, request, mUser, null));
return;
}
// get filenames
String fileName = request.getArgument();
fileName = mUser.getVirtualDirectory().getAbsoluteName(fileName);
String physicalName = mUser.getVirtualDirectory().getPhysicalName(fileName);
File requestedFile = new File(physicalName);
args = new String[] {fileName};
// check file existance
if( !(requestedFile.exists() && requestedFile.isFile()) ) {
out.write(mFtpStatus.getResponse(550, request, mUser, args));
return;
}
// check permission
if(!mUser.getVirtualDirectory().hasWritePermission(physicalName, true)) {
out.write(mFtpStatus.getResponse(450, request, mUser, args));
return;
}
// now transfer file data
out.write(mFtpStatus.getResponse(150, request, mUser, args));
Socket dataSoc = mDataConnection.getDataSocket();
if (dataSoc == null) {
out.write(mFtpStatus.getResponse(550, request, mUser, args));
return;
}
// go to the end of the file
is = dataSoc.getInputStream();
RandomAccessFile raf = new RandomAccessFile(requestedFile, "rw");
raf.seek(raf.length());
os = mUser.getOutputStream( new FileOutputStream(raf.getFD()) );
// receive data from client
StreamConnector msc = new StreamConnector(is, os);
msc.setMaxTransferRate(mUser.getMaxUploadRate());
msc.setObserver(this);
msc.connect();
if(msc.hasException()) {
out.write(mFtpStatus.getResponse(451, request, mUser, args));
}
else {
mConfig.getStatistics().setUpload(requestedFile, mUser, msc.getTransferredSize());
}
out.write(mFtpStatus.getResponse(226, request, mUser, args));
}
catch(IOException ex) {
out.write(mFtpStatus.getResponse(425, request, mUser, args));
}
finally {
IoUtils.close(is);
IoUtils.close(os);
mDataConnection.closeDataSocket();
}
}
/**
* <code>CDUP <CRLF></code><br>
*
* This command is a special case of CWD, and is included to
* simplify the implementation of programs for transferring
* directory trees between operating systems having different
* syntaxes for naming the parent directory. The reply codes
* shall be identical to the reply codes of CWD.
*/
public void doCDUP(FtpRequest request, FtpWriter out) throws IOException {
// reset state variables
resetState();
// change directory
if (mUser.getVirtualDirectory().changeDirectory("..")) {
String args[] = {mUser.getVirtualDirectory().getCurrentDirectory()};
out.write(mFtpStatus.getResponse(200, request, mUser, args));
}
else {
out.write(mFtpStatus.getResponse(431, request, mUser, null));
}
}
/**
* <code>CWD <SP> <pathname> <CRLF></code><br>
*
* This command allows the user to work with a different
* directory for file storage or retrieval without
* altering his login or accounting information. Transfer
* parameters are similarly unchanged. The argument is a
* pathname specifying a directory.
*/
public void doCWD(FtpRequest request, FtpWriter out) throws IOException {
// reset state variables
resetState();
// get new directory name
String dirName = "/";
if (request.hasArgument()) {
dirName = request.getArgument();
}
// change directory
if (mUser.getVirtualDirectory().changeDirectory(dirName)) {
String args[] = {mUser.getVirtualDirectory().getCurrentDirectory()};
out.write(mFtpStatus.getResponse(200, request, mUser, args));
}
else {
out.write(mFtpStatus.getResponse(431, request, mUser, null));
}
}
/**
* <code>DELE <SP> <pathname> <CRLF></code><br>
*
* This command causes the file specified in the pathname to be
* deleted at the server site.
*/
public void doDELE(FtpRequest request, FtpWriter out) throws IOException {
// reset state variables
resetState();
// argument check
if(!request.hasArgument()) {
out.write(mFtpStatus.getResponse(501, request, mUser, null));
return;
}
// get filenames
String fileName = request.getArgument();
fileName = mUser.getVirtualDirectory().getAbsoluteName(fileName);
String physicalName = mUser.getVirtualDirectory().getPhysicalName(fileName);
File requestedFile = new File(physicalName);
String[] args = {fileName};
// check permission
if(!mUser.getVirtualDirectory().hasWritePermission(physicalName, true)) {
out.write(mFtpStatus.getResponse(450, request, mUser, args));
return;
}
// now delete
if(requestedFile.delete()) {
out.write(mFtpStatus.getResponse(250, request, mUser, args));
mConfig.getStatistics().setDelete(requestedFile, mUser);
}
else {
out.write(mFtpStatus.getResponse(450, request, mUser, args));
}
}
/**
* <code>HELP [<SP> <string>] <CRLF></code><br>
*
* This command shall cause the server to send helpful
* information regarding its implementation status over the
* control connection to the user. The command may take an
* argument (e.g., any command name) and return more specific
* information as a response.
*/
public void doHELP(FtpRequest request, FtpWriter out) throws IOException {
resetState();
// print global help
if(!request.hasArgument()) {
out.write(mFtpStatus.getResponse(214, null, mUser, null));
return;
}
// print command specific help
String ftpCmd = request.getArgument().toUpperCase();
String args[] = null;
FtpRequest tempRequest = new FtpRequest(ftpCmd);
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -