首页 后端开发 Python教程 400多行Python代码实现了一个FTP服务器

400多行Python代码实现了一个FTP服务器

Jun 06, 2016 am 11:27 AM
ftp服务器

Python版本
实现了比之前的xxftp更多更完善的功能
1、继续支持多用户
2、继续支持虚拟目录
3、增加支持用户根目录以及映射虚拟目录的权限设置
4、增加支持限制用户根目录或者虚拟目录的空间大小

xxftp的特点
1、开源、跨平台
2、简单、易用
3、不需要数据库
4、可扩展性超强
5、你可以免费使用xxftp假设自己的私人FTP服务器

测试地址
ftp://xiaoxia.org
匿名帐号可以使用!
匿名根目录只读,映射了一个虚拟目录,可以上传文件但不允许更改!

使用方法
跟之前用C语言写的xxftp使用方法一样:

1. Create a root directory to hold the user directories.
Configure it in config.xml.
2. Create user directories under the root directory.
If you want to specify a password, create a directory named ".xxftp",
under which create a text file named "password" containing the MD5
code of the password.
3. If you want to specify the welcome and goodbye message, write it in
xxftp.welcome and xxftp.goodbye under the root directory.
4. Configure config.xml.

The structure of your FTP server root may be like:

-/root
-xxftp.welcome
-xxftp.goodbye

-user1
-.xxftp
-password
-...
-user2
-.xxftp
-password
-...
-anonymous源代码

代码如下:



import socket, threading, os, sys, time
import hashlib, platform, stat

listen_ip = "localhost"
listen_port = 21
conn_list = []
root_dir = "./home"
max_connections = 500
conn_timeout = 120

class FtpConnection(threading.Thread):
def __init__(self, fd):
threading.Thread.__init__(self)
self.fd = fd
self.running = True
self.setDaemon(True)
self.alive_time = time.time()
self.option_utf8 = False
self.identified = False
self.option_pasv = True
self.username = ""
def process(self, cmd, arg):
cmd = cmd.upper();
if self.option_utf8:
arg = unicode(arg, "utf8").encode(sys.getfilesystemencoding())
print "# Ftp Command
if 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 = False
return
elif cmd == "USER":
# Set Anonymous User
if arg == "": arg = "anonymous"
for c in arg:
if not c.isalpha() and not c.isdigit() and c!="_":
self.message(530, "Incorrect username.")
return
self.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.")
return
self.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 = True
return
elif cmd == "PASS":
if open(self.pass_path).read() == hashlib.md5(arg).hexdigest():
self.message(230, "Identified!")
self.identified = True
else:
self.message(530, "Not identified!")
self.identified = False
return
elif not self.identified:
self.message(530, "Please login with USER and PASS.")
return

self.alive_time = time.time()
finish = True
if 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 = True
try:
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 = False
self.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 parameters
remote, local, perm, vdir_list, limit_size, is_virtual = self.parse_path(arg)
if not os.path.exists(local):
self.message(550, "failed.")
return
if not self.establish(): return
self.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] == ".": continue
path = local + "/" + f
if 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 = 0
elif 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 = True
self.message(200, "ok")
elif arg == "UTF8 OFF":
self.option_utf8 = False
self.message(200, "ok")
else:
self.message(500, "unrecognized option")
elif cmd == "CDUP":
finish = False
arg = ".."
else:
finish = False
if finish: return
# Parse argument ( It's a path )
if arg == "":
self.message(500, "where's my argument?")
return
remote, local, permission, vdir_list, limit_size, is_virtual = \
self.parse_path(arg)
# can not do anything to virtual directory
if is_virtual: permission = "none"
can_read, can_write, can_modify = "read" in permission, "write" in permission, "modify" in permission
newpath = local
try:
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.")
return
os.mkdir(newpath)
self.message(250, "ok")
elif cmd == "RNFR":
if not can_modify:
self.message(550, "permission denied.")
return
self.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.")
return
os.rmdir(newpath)
self.message(250, "ok")
elif cmd == "DELE":
if not can_modify:
self.message(550, "permission denied.")
return
os.remove(newpath)
self.message(250, "ok")
elif cmd == "RETR":
if not os.path.isfile(newpath):
self.message(550, "failed")
return
if not can_read:
self.message(550, "permission denied.")
return
if not self.establish(): return
self.message(150, "ok")
f = open(newpath, "rb")
while self.running:
self.alive_time = time.time()
data = f.read(8192)
if len(data) == 0: break
self.data_fd.send(data)
f.close()
self.data_fd.close()
self.data_fd = 0
self.message(226, "ok")
elif cmd == "STOR" or cmd == "APPE":
if not can_write:
self.message(550, "permission denied.")
return
if os.path.exists(newpath) and not can_modify:
self.message(550, "permission denied.")
return
# Check space size remained!
used_size = 0
if limit_size > 0:
used_size = self.get_dir_size(os.path.dirname(newpath))
if not self.establish(): return
self.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: break
if limit_size > 0:
used_size = used_size + len(data)
if used_size > limit_size: break
f.write(data)
f.close()
self.data_fd.close()
self.data_fd = 0
if 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 False
if self.option_pasv:
fd = self.data_fd.accept()[0]
self.data_fd.close()
self.data_fd = fd
else:
try:
self.data_fd.connect((self.data_ip, self.data_port))
except:
self.message(500, "failed to establish data connection")
return False
return 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 = 0
for 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 = 0
path = 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 permission
vdir_list = self.read_virtual(local)
limit_size = self.read_size(local)
permission = self.read_permission(local)
remote = ""
is_virtual = False
for name in s:
name = name.lstrip(".")
if name == "": continue
remote = remote + "/" + name
is_virtual = False
for v in vdir_list:
if v[0] == name:
permission = v[2]
local = v[1]
limit_size = self.read_size(local)
is_virtual = True
if 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 = False
return
# Welcome Message
if 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 Loop
line = ""
while self.running:
data = self.fd.recv(4096)
if len(data) == 0: break
line += data
if line[-2:] != "\r\n": continue
line = 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 = False
self.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 timeout
try:
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 = False
conn_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()

本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

Video Face Swap

Video Face Swap

使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

ftp服务器地址如何查看 ftp服务器地址如何查看 Jan 29, 2024 pm 03:11 PM

查看FTP服务器地址的方法:1、在浏览器的地址栏中输入FTP服务器的域名或IP地址,然后按下回车键。如果能够成功连接到FTP服务器,则表明FTP服务器的地址是正确的;2、在命令行界面中输入“ftp”命令,然后输入“ftp”网站的域名或IP地址。如果连接成功,则表明FTP服务器的地址是正确的;3、在IP设置页面中,可以看到设备的IP地址,这就是设备的FTP服务器地址等等。

如何通过PHP在FTP服务器上监控文件的访问和修改 如何通过PHP在FTP服务器上监控文件的访问和修改 Jul 28, 2023 pm 08:01 PM

如何通过PHP在FTP服务器上监控文件的访问和修改随着互联网的快速发展,FTP(文件传输协议)作为一种常用的文件传输工具,经常用于将文件从本地上传到服务器或从服务器下载到本地。在实际应用中,监控FTP服务器上的文件访问和修改情况非常重要,特别是对于一些敏感文件。本文将介绍如何使用PHP编写代码来实现对FTP服务器上文件的访问和修改监控。首先,我们需要确保服务

如何通过PHP在FTP服务器上压缩和解压缩文件 如何通过PHP在FTP服务器上压缩和解压缩文件 Jul 30, 2023 pm 03:15 PM

如何通过PHP在FTP服务器上压缩和解压缩文件简介:在开发和管理网站过程中,我们经常需要处理文件的压缩和解压缩操作。而如果网站的文件存储采用的是FTP服务器,如何在服务器上通过PHP实现文件的压缩和解压缩就成为了一个关键问题。本文将介绍如何通过PHP在FTP服务器上进行文件的压缩和解压缩操作,并提供相关的代码示例,以供参考。链接到FTP服务器在执行文件压缩和

ftp服务器怎么搭建 ftp服务器怎么搭建 Dec 12, 2023 am 10:37 AM

ftp服务器可以通过选择合适的FTP服务器软件、安装FTP服务器软件、配置FTP服务器软件、启动FTP服务器软件和测试FTP服务器的运行等步骤搭建。详细介绍:1、选择合适的FTP服务器软件,包括vsftpd、FileZilla Server、ProFTPD等;2、安装FTP服务器软件等等。

ftp服务器如何用 ftp服务器如何用 Oct 13, 2023 pm 02:25 PM

ftp服务器用法:1、安装一个FTP服务器软件,例如FileZilla Server、vsftpd、ProFTPD等;2、安装完成后,需要设置FTP服务器的监听端口,设置FTP服务器的访问权限,包括允许哪些用户访问、允许哪些文件上传和下载等;3、创建FTP用户,FTP服务器允许创建多个用户,并为每个用户分配不同的权限;4、设置用户的登录权限,确保这些用户具有FTP访问权限即可。

在Linux操作系统上设置一个FTP服务器,请按照以下步骤进行 在Linux操作系统上设置一个FTP服务器,请按照以下步骤进行 Jan 26, 2024 pm 10:33 PM

要在Linux上搭建FTP服务器linux命令,您须要根据以下步骤操作:1.安装FTP服务器软件:您可以使用以下命令在Linux系统中安装FTP服务器软件:```sudoapt-getinstallvsftpd```2.配置FTP服务器:您须要编辑FTP服务器的配置文件来设置FTP服务器的配置选项。默认情况下linux安装ftp服务器,vsftpd.conf文件坐落/etc/vsftpd/目录下。您可以通过以下命令打开配置文件:```sudonano/etc/vsftpd.conf```在该文件

如何使用PHP监控FTP服务器的文件变化 如何使用PHP监控FTP服务器的文件变化 Aug 01, 2023 pm 09:46 PM

如何使用PHP监控FTP服务器的文件变化引言:FTP(文件传输协议)是一种常用的文件传输协议,广泛应用于文件的上传、下载和管理。当我们需要监控FTP服务器上的文件变化时,可以使用PHP来实现这个功能。本文将介绍如何使用PHP监控FTP服务器的文件变化,并提供代码示例供读者参考。第一部分:连接到FTP服务器首先,我们需要使用PHP的FTP函数与FTP服务器建立

探讨如何保护你的 Linux 服务器 探讨如何保护你的 Linux 服务器 Jan 01, 2024 pm 03:56 PM

无论你使用的哪种Linux发行版,你都需要使用基于iptables的防火墙来保护它。啊哈!你已经设置好了你的第一台Linux服务器并且已经准备发车了!是么?嗯,慢着。默认情况下,你的Linux系统对攻击者来说并非是足够安全的。当然,它比WindowsXP要安全多了,但这说明不了什么。想要使你的Linux系统真正稳固,你需要按照Linode的服务器安全指南来操作。总的来说,首先你必须关闭那些你不需要的服务。当然要这样做的话,你先要知道你正在使用哪些网络服务。你可以使用shell命令来找到是哪些服务

See all articles