import java.net.InetAddress;
import com.base.module.util.SessionThread;
import android.util.Log;
/**
*这个类 跟 PASV 模式的类中基本一样,只是StringBuilder 中的内容不同。可以去下载完整的FTP 源码看一下CmdPASV
*类 当然还需要在FtpCmd 类中 protected static CmdMap[] cmdClasses 中加上new CmdMap("EPSV", CmdEPSV.class)
**/
public class CmdEPSV extends FtpCmd implements Runnable {
//public static final String message = "TEMPLATE!!";
String input;
public CmdEPSV(SessionThread sessionThread, String input) {
super(sessionThread, CmdEPSV.class.toString());
this.input = input;
}
public CmdEPSV() {
super(null, CmdEPSV.class.toString());
}
public void run() {
myLog.l(Log.DEBUG, "EPSV running");
String cantOpen = "502 Couldn't open a port\r\n";
int port;
if((port = sessionThread.onPasv()) == 0) {
// There was a problem opening a port
myLog.l(Log.ERROR, "Couldn't open a port for EPSV");
sessionThread.writeString(cantOpen);
return;
}
InetAddress addr = sessionThread.getDataSocketPasvIp();
if(addr == null) {
myLog.l(Log.ERROR, "EPSV IP string invalid");
sessionThread.writeString(cantOpen);
return;
}
myLog.d("EPSV sending IP: " + addr.getHostAddress());
if(port < 1) {
myLog.l(Log.ERROR, "EPSV port number invalid");
sessionThread.writeString(cantOpen);
return;
}
//上面一堆的判断只是为了确认地址是否有效等,下面才是关键
//最主要的就是 "229 Entering Extended Passive Mode (|||" 这句,具体网络协议我也不是很懂,
//但是自己参考 http://my.oschina.net/u/572632/blog/283460
StringBuilder response = new StringBuilder(
"229 Entering Extended Passive Mode (|||");
response.append(port);
response.append("|).\r\n");
String responseString = response.toString();
sessionThread.writeString(responseString);
myLog.l(Log.DEBUG, "EPSV completed, sent: " + responseString);
}
}
看完上面链接以后再回顾一下
http://my.oschina.net/u/572632/blog/283460
的讲解,基本能理通一些知识了,深入研究还需要时间。。。
下面是上面第一个链接里的代码,可以搜索 EPSV 的书写方式,在我们JAVA 里尝试使用,还真的行的通,所以代码语言都是相通的,要大胆尝试。。o(∩∩)o...哈哈 下面代码中的判断之后就写了
if cmd == "EPSV":
self.message(229, "Entering Extended Passive Mode (|||" + str(port) + "|)")
import socket, threading, os, sys, timeimport hashlib, platform, stat
listen_ip = "localhost"listen_port = 21conn_list = []
root_dir = "./home"max_connections = 500conn_timeout = 120 class FtpConnection(threading.Thread):def __init__(self, fd):threading.Thread.__init__(self)
self.fd = fd
self.running = Trueself.setDaemon(True)
self.alive_time = time.time()
self.option_utf8 = Falseself.identified = Falseself.option_pasv = Trueself.username = ""def process(self, cmd, arg):cmd = cmd.upper();if self.option_utf8:
arg = unicode(arg, "utf8").encode(sys.getfilesystemencoding())print "<<", cmd, arg, self.fd# Ftp Commandif cmd == "BYE" or cmd == "QUIT":if os.path.exists(root_dir + "/xxftp.goodbye"):
self.message(221, open(root_dir + "/xxftp.goodbye").read())else:
self.message(221, "Bye!")
self.running = Falsereturnelif cmd == "USER":# Set Anonymous Userif arg == "": arg = "anonymous"for c in arg:if not c.isalpha() and not c.isdigit() and c!="_":
self.message(530, "Incorrect username.")returnself.username = arg
self.home_dir = root_dir + "/" + self.username
self.curr_dir = "/"self.curr_dir, self.full_path, permission, self.vdir_list, \
limit_size, is_virtual = self.parse_path("/")if not os.path.isdir(self.home_dir):
self.message(530, "User " + self.username + " not exists.")returnself.pass_path = self.home_dir + "/.xxftp/password"if os.path.isfile(self.pass_path):
self.message(331, "Password required for " + self.username)else:
self.message(230, "Identified!")
self.identified = Truereturnelif cmd == "PASS":if open(self.pass_path).read() == hashlib.md5(arg).hexdigest():
self.message(230, "Identified!")
self.identified = Trueelse:
self.message(530, "Not identified!")
self.identified = Falsereturnelif not self.identified:
self.message(530, "Please login with USER and PASS.")return self.alive_time = time.time()
finish = Trueif cmd == "NOOP":
self.message(200, "ok")elif cmd == "TYPE":
self.message(200, "ok")elif cmd == "SYST":
self.message(200, "UNIX")elif cmd == "EPSV" or cmd == "PASV":
self.option_pasv = Truetry:
self.data_fd = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.data_fd.bind((listen_ip, 0))
self.data_fd.listen(1)
ip, port = self.data_fd.getsockname()if cmd == "EPSV":
self.message(229, "Entering Extended Passive Mode (|||" + str(port) + "|)")else:
ipnum = socket.inet_aton(ip)
self.message(227, "Entering Passive Mode (%s,%u,%u)." %
(",".join(ip.split(".")), (port>>8&0xff), (port&0xff)))except:
self.message(500, "failed to create data socket.")elif cmd == "EPRT":
self.message(500, "implement EPRT later...")elif cmd == "PORT":
self.option_pasv = Falseself.data_fd = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s = arg.split(",")
self.data_ip = ".".join(s[:4])
self.data_port = int(s[4])*256 + int(s[5])
self.message(200, "ok")elif cmd == "PWD" or cmd == "XPWD":if self.curr_dir == "": self.curr_dir = "/"self.message(257, '"' + self.curr_dir + '"')elif cmd == "LIST" or cmd == "NLST":if arg != "" and arg[0] == "-": arg = "" # omit parametersremote, local, perm, vdir_list, limit_size, is_virtual = self.parse_path(arg)if not os.path.exists(local):
self.message(550, "failed.")returnif not self.establish(): returnself.message(150, "ok")for v in vdir_list:
f = v[0]if self.option_utf8:
f = unicode(f, sys.getfilesystemencoding()).encode("utf8")if cmd == "NLST":
info = f + "\r\n"else:
info = "d%s%s------- %04u %8s %8s %8lu %s %s\r\n" % ("r" if "read" in perm else "-","w" if "write" in perm else "-",1, "0", "0", 0,
time.strftime("%b %d %Y", time.localtime(time.time())),
f)
self.data_fd.send(info)for f in os.listdir(local):if f[0] == ".": continuepath = local + "/" + fif self.option_utf8:
f = unicode(f, sys.getfilesystemencoding()).encode("utf8")if cmd == "NLST":
info = f + "\r\n"else:
st = os.stat(path)
info = "%s%s%s------- %04u %8s %8s %8lu %s %s\r\n" % ("-" if os.path.isfile(path) else "d","r" if "read" in perm else "-","w" if "write" in perm else "-",1, "0", "0", st[stat.ST_SIZE],
time.strftime("%b %d %Y", time.localtime(st[stat.ST_MTIME])),
f)
self.data_fd.send(info)
self.message(226, "Limit size: " + str(limit_size))
self.data_fd.close()
self.data_fd = 0elif cmd == "REST":
self.file_pos = int(arg)
self.message(250, "ok")elif cmd == "FEAT":
features = "211-Features:\r\nSITES\r\nEPRT\r\nEPSV\r\nMDTM\r\nPASV\r\n"\"REST STREAM\r\nSIZE\r\nUTF8\r\n211 End\r\n"self.fd.send(features)elif cmd == "OPTS":
arg = arg.upper()if arg == "UTF8 ON":
self.option_utf8 = Trueself.message(200, "ok")elif arg == "UTF8 OFF":
self.option_utf8 = Falseself.message(200, "ok")else:
self.message(500, "unrecognized option")elif cmd == "CDUP":
finish = Falsearg = ".."else:
finish = Falseif finish: return# Parse argument ( It's a path )if arg == "":
self.message(500, "where's my argument?")returnremote, local, permission, vdir_list, limit_size, is_virtual = \
self.parse_path(arg)# can not do anything to virtual directoryif is_virtual: permission = "none"can_read, can_write, can_modify = "read" in permission, "write" in permission, "modify" in permission
newpath = localtry:if cmd == "CWD":if(os.path.isdir(newpath)):
self.curr_dir = remote
self.full_path = newpath
self.message(250, '"' + remote + '"')else:
self.message(550, "failed")elif cmd == "MDTM":if os.path.exists(newpath):
self.message(213, time.strftime("%Y%m%d%I%M%S", time.localtime(
os.path.getmtime(newpath))))else:
self.message(550, "failed")elif cmd == "SIZE":
self.message(231, os.path.getsize(newpath))elif cmd == "XMKD" or cmd == "MKD":if not can_modify:
self.message(550, "permission denied.")returnos.mkdir(newpath)
self.message(250, "ok")elif cmd == "RNFR":if not can_modify:
self.message(550, "permission denied.")returnself.temp_path = newpath
self.message(350, "rename from " + remote)elif cmd == "RNTO":
os.rename(self.temp_path, newpath)
self.message(250, "RNTO to " + remote)elif cmd == "XRMD" or cmd == "RMD":if not can_modify:
self.message(550, "permission denied.")returnos.rmdir(newpath)
self.message(250, "ok")elif cmd == "DELE":if not can_modify:
self.message(550, "permission denied.")returnos.remove(newpath)
self.message(250, "ok")elif cmd == "RETR":if not os.path.isfile(newpath):
self.message(550, "failed")returnif not can_read:
self.message(550, "permission denied.")returnif not self.establish(): returnself.message(150, "ok")
f = open(newpath, "rb")while self.running:
self.alive_time = time.time()
data = f.read(8192)if len(data) == 0: breakself.data_fd.send(data)
f.close()
self.data_fd.close()
self.data_fd = 0self.message(226, "ok")elif cmd == "STOR" or cmd == "APPE":if not can_write:
self.message(550, "permission denied.")returnif os.path.exists(newpath) and not can_modify:
self.message(550, "permission denied.")return# Check space size remained!used_size = 0if limit_size > 0:
used_size = self.get_dir_size(os.path.dirname(newpath))if not self.establish(): returnself.message(150, "ok")
f = open(newpath, ("ab" if cmd == "APPE" else "wb") )while self.running:
self.alive_time = time.time()
data = self.data_fd.recv(8192)if len(data) == 0: breakif limit_size > 0:
used_size = used_size + len(data)if used_size > limit_size: breakf.write(data)
f.close()
self.data_fd.close()
self.data_fd = 0if limit_size > 0 and used_size > limit_size:
self.message(550, "Exceeding user space limit: " + str(limit_size) + " bytes")else:
self.message(226, "ok")else:
self.message(500, cmd + " not implemented")except:
self.message(550, "failed.")
def establish(self):if self.data_fd == 0:
self.message(500, "no data connection")return Falseif self.option_pasv:
fd = self.data_fd.accept()[0]
self.data_fd.close()
self.data_fd = fdelse:try:
self.data_fd.connect((self.data_ip, self.data_port))except:
self.message(500, "failed to establish data connection")return Falsereturn True def read_virtual(self, path):vdir_list = []
path = path + "/.xxftp/virtual"if os.path.isfile(path):for v in open(path, "r").readlines():
items = v.split()
items[1] = items[1].replace("$root", root_dir)
vdir_list.append(items)return vdir_list
def get_dir_size(self, folder):size = 0for path, dirs, files in os.walk(folder):for f in files:
size += os.path.getsize(os.path.join(path, f))return size
def read_size(self, path):size = 0path = path + "/.xxftp/size"if os.path.isfile(path):
size = int(open(path, "r").readline())return size
def read_permission(self, path):permission = "read,write,modify"path = path + "/.xxftp/permission"if os.path.isfile(path):
permission = open(path, "r").readline()return permission
def parse_path(self, path):if path == "": path = "."if path[0] != "/":
path = self.curr_dir + "/" + path
s = os.path.normpath(path).replace("\\", "/").split("/")
local = self.home_dir# reset directory permissionvdir_list = self.read_virtual(local)
limit_size = self.read_size(local)
permission = self.read_permission(local)
remote = ""is_virtual = Falsefor name in s:
name = name.lstrip(".")if name == "": continueremote = remote + "/" + name
is_virtual = Falsefor v in vdir_list:if v[0] == name:
permission = v[2]
local = v[1]
limit_size = self.read_size(local)
is_virtual = Trueif not is_virtual: local = local + "/" + name
vdir_list = self.read_virtual(local)return (remote, local, permission, vdir_list, limit_size, is_virtual)
def run(self):''' Connection Process '''try:if len(conn_list) > max_connections:
self.message(500, "too many connections!")
self.fd.close()
self.running = Falsereturn# Welcome Messageif os.path.exists(root_dir + "/xxftp.welcome"):
self.message(220, open(root_dir + "/xxftp.welcome").read())else:
self.message(220, "xxftp(Python) www.xiaoxia.org")# Command Loopline = ""while self.running:
data = self.fd.recv(4096)if len(data) == 0: breakline += dataif line[-2:] != "\r\n": continueline = line[:-2]
space = line.find(" ")if space == -1:
self.process(line, "")else:
self.process(line[:space], line[space+1:])
line = ""except:print "error", sys.exc_info()
self.running = Falseself.fd.close()print "connection end", self.fd, "user", self.username
def message(self, code, s):''' Send Ftp Message '''s = str(s).replace("\r", "")
ss = s.split("\n")if len(ss) > 1:
r = (str(code) + "-") + ("\r\n" + str(code) + "-").join(ss[:-1])
r += "\r\n" + str(code) + " " + ss[-1] + "\r\n"else:
r = str(code) + " " + ss[0] + "\r\n"if self.option_utf8:
r = unicode(r, sys.getfilesystemencoding()).encode("utf8")
self.fd.send(r)
def server_listen():global conn_list
listen_fd = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
listen_fd.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
listen_fd.bind((listen_ip, listen_port))
listen_fd.listen(1024)
conn_lock = threading.Lock()print "ftpd is listening on ", listen_ip + ":" + str(listen_port)
while True:
conn_fd, remote_addr = listen_fd.accept()print "connection from ", remote_addr, "conn_list", len(conn_list)
conn = FtpConnection(conn_fd)
conn.start()
conn_lock.acquire()
conn_list.append(conn)# check timeouttry:
curr_time = time.time()for conn in conn_list:if int(curr_time - conn.alive_time) > conn_timeout:if conn.running == True:
conn.fd.shutdown(socket.SHUT_RDWR)
conn.running = Falseconn_list = [conn for conn in conn_list if conn.running]except:print sys.exc_info()
conn_lock.release()
def main():server_listen()
if __name__ == "__main__":
main()