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://www.codeweblog.com/400%E5%A4%9A%E8%A1%8Cpython%E4%BB%A3%E7%A0%81%E5%AE%9E%E7%8E%B0%E4%BA%86%E4%B8%80%E4%B8%AAftp%E6%9C%8D%E5%8A%A1%E5%99%A8/
看完上面链接以后再回顾一下
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()