首頁 後端開發 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)

熱門話題

Java教學
1662
14
CakePHP 教程
1418
52
Laravel 教程
1311
25
PHP教程
1261
29
C# 教程
1234
24
如何透過PHP在FTP伺服器上監控檔案的存取和修改 如何透過PHP在FTP伺服器上監控檔案的存取和修改 Jul 28, 2023 pm 08:01 PM

如何透過PHP在FTP伺服器上監控檔案的存取和修改隨著互聯網的快速發展,FTP(檔案傳輸協定)作為一種常用的檔案傳輸工具,經常用於將檔案從本地上傳到伺服器或從伺服器下載到本地。在實際應用中,監控FTP伺服器上的檔案存取和修改情況非常重要,特別是對於一些敏感檔案。本文將介紹如何使用PHP編寫程式碼來實現對FTP伺服器上檔案的存取和修改監控。首先,我們需要確保服務

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 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