Onde podemos usar e por que fazer um servidor de FTP?
Nos dias de hoje, no mundo Linux, temos alguns servidores de FTP muito bons (vsftpd, proftpd e outros), mas quando vamos para o ambiente Microsoft Windows não conheço muitos servidores leves. Por esse motivo, acho muito interessante termos alguma solução para esse ambiente usando Python.
Vou explicar como fazer o daemon, depois vai da criatividade de cada um de como aplicar no dia a dia.
Tenho um cluster de servidores onde rodo sistemas de cliente - a parte de FTP rodava com vsFTPd, hoje está rodando com um daemon em Python, o mesmo que vamos fazer aqui no artigo, só que trabalho com uma base de usuário no MongoDB (usuário, senha e pasta).
Por que eu migrei de vsFTPd para um daemon em Python?
- A base de dados onde estão as informações dos meus clientes é MongoDB, onde estou centralizando todas as informações.
- Consigo fazer meu daemon interagir com outros aplicativos Python que rodam no meu servidor
Para escrever este artigo, vamos usar a biblioteca pyftpdlib, que é mantida pelo Giampaolo Rodola. É uma biblioteca muito robusta e fácil de utilizar, onde podemos escrever poderosos servidores FTP.
Instalando o pyftpflib
cd /usr/src/
svn checkout http://pyftpdlib.googlecode.com/svn/trunk/ pyftpdlib-read-only
cd pyftpdlib-read-only
python setup.py build
python setup.py install
Agora que já está tudo instalado, vamos começar a escrever o código de nosso servidor:
from pyftpdlib import ftpserver
authorizer = ftpserver.DummyAuthorizer()
authorizer.add_user("avelino.us", "mudar123", "/home/avelino.us", perm="elradfmw")
authorizer.add_anonymous("/home/nobody")
handler = ftpserver.FTPHandler
handler.authorizer = authorizer
address = ("127.0.0.1", 21)
ftpd = ftpserver.FTPServer(address, handler)
ftpd.serve_forever()O pyftpdlib tem uma biblioteca chamada ftpserver que usamos para declarar o servidor. Vamos passar dois parâmetros: o primeiro é o endereço de onde ele vai abrir a conexão/porta; o segundo é o servidor Handler.
A última linha é "ftpd.serve_forever()", que mantém o servidor rodando.
Explicando como funciona a parte de user
Temos como adicionar usuários normais ou anônimos.
authorizer.add_user("avelino.us", "mudar123", "/home/avelino.us", perm="elradfmw")A linha na parte superior adiciona um usuário normal, com nome "avelino.us", senha "mudar123", com acesso à pasta "/home/avelino.us".
authorizer.add_anonymous("/home/nobody")No usuário anônimo, basta colocar na pasta a qual ele vai ter acesso "/home/nobody".
A parte de log funciona exatamente igual ao de qualquer FTP, vou colocar abaixo um log dessa aplicação rodando local:
Serving FTP on 127.0.0.1:21
[]127.0.0.1:56026 Connected.
127.0.0.1:56026 ==> 220 pyftpdlib 0.6.0 ready.
127.0.0.1:56026 <== USER avelino.us
127.0.0.1:56026 ==> 331 Username ok, send password.
127.0.0.1:56026 <== PASS ******
127.0.0.1:56026 ==> 530 Authentication failed.
[]@127.0.0.1:56026 Authentication failed.
[]@127.0.0.1:56026 Disconnected.
[]127.0.0.1:56562 Connected.
127.0.0.1:56562 ==> 220 pyftpdlib 0.6.0 ready.
127.0.0.1:56562 <== USER avelino.us
127.0.0.1:56562 ==> 331 Username ok, send password.
127.0.0.1:56562 <== PASS ******
127.0.0.1:56562 ==> 530 Authentication failed.
[]@127.0.0.1:56562 Authentication failed.
[]@127.0.0.1:56562 Disconnected.
[]127.0.0.1:56567 Connected.
127.0.0.1:56567 ==> 220 pyftpdlib 0.6.0 ready.
127.0.0.1:56567 <== USER avelino.us
127.0.0.1:56567 ==> 331 Username ok, send password.
127.0.0.1:56567 <== PASS ******
127.0.0.1:56567 ==> 230 Login successful.
[avelino.us]@127.0.0.1:56567 User avelino.us logged in.
127.0.0.1:56567 <== SYST
127.0.0.1:56567 ==> 215 UNIX Type: L8
127.0.0.1:56567 <== FEAT
127.0.0.1:56567 ==> 211 End FEAT.
127.0.0.1:56567 <== OPTS MLST type;perm;size;modify;unix.mode;unix.uid;unix.gid;
127.0.0.1:56567 ==> 200 MLST OPTS type;perm;size;modify;unix.mode;unix.uid;unix.gid;
127.0.0.1:56567 <== PWD
127.0.0.1:56567 ==> 257 "/" is the current directory.
127.0.0.1:56567 <== TYPE I
127.0.0.1:56567 ==> 200 Type set to: Binary.
127.0.0.1:56567 <== PASV
127.0.0.1:56567 ==> 227 Entering passive mode (127,0,0,1,220,248).
127.0.0.1:56567 <== MLSD
[avelino.us]@127.0.0.1:56567 OK MLSD "/". Transfer starting.
127.0.0.1:56567 ==> 150 File status okay. About to open data connection.
127.0.0.1:56567 ==> 226 Transfer complete.
127.0.0.1:56567 <== MKD test
[avelino.us]@127.0.0.1:56567 OK MKD "/test".
127.0.0.1:56567 ==> 257 "/test" directory created.
127.0.0.1:56567 <== CWD test
[avelino.us]@127.0.0.1:56567 OK CWD "/test".
127.0.0.1:56567 ==> 250 "/test" is the current directory.
127.0.0.1:56567 <== PWD
127.0.0.1:56567 ==> 257 "/test" is the current directory.
127.0.0.1:56567 <== PASV
127.0.0.1:56567 ==> 227 Entering passive mode (127,0,0,1,220,253).
127.0.0.1:56567 <== MLSD
[avelino.us]@127.0.0.1:56567 OK MLSD "/test". Transfer starting.
127.0.0.1:56567 ==> 150 File status okay. About to open data connection.
127.0.0.1:56567 ==> 226 Transfer complete.
[]127.0.0.1:56575 Connected.
127.0.0.1:56575 ==> 220 pyftpdlib 0.6.0 ready.
127.0.0.1:56575 <== USER avelino.us
127.0.0.1:56575 ==> 331 Username ok, send password.
127.0.0.1:56575 <== PASS ******
127.0.0.1:56575 ==> 230 Login successful.
[avelino.us]@127.0.0.1:56575 User avelino.us logged in.
127.0.0.1:56575 <== OPTS MLST type;perm;size;modify;unix.mode;unix.uid;unix.gid;
127.0.0.1:56575 ==> 200 MLST OPTS type;perm;size;modify;unix.mode;unix.uid;unix.gid;
127.0.0.1:56575 <== CWD /test
[avelino.us]@127.0.0.1:56575 OK CWD "/test".
127.0.0.1:56575 ==> 250 "/test" is the current directory.
127.0.0.1:56575 <== PWD
127.0.0.1:56575 ==> 257 "/test" is the current directory.
127.0.0.1:56575 <== TYPE A
127.0.0.1:56575 ==> 200 Type set to: ASCII.
127.0.0.1:56575 <== PASV
127.0.0.1:56575 ==> 227 Entering passive mode (127,0,0,1,221,0).
127.0.0.1:56575 <== STOR test.py
[avelino.us]@127.0.0.1:56575 OK STOR "/test/test.py". Upload starting.
127.0.0.1:56575 ==> 150 File status okay. About to open data connection.
127.0.0.1:56575 ==> 226 Transfer complete.
[avelino.us]@127.0.0.1:56575 /home/avelino.us/test/test.py received in 0.001 seconds.
127.0.0.1:56575 <== TYPE I
127.0.0.1:56575 ==> 200 Type set to: Binary.
127.0.0.1:56575 <== PASV
127.0.0.1:56575 ==> 227 Entering passive mode (127,0,0,1,221,2).
127.0.0.1:56575 <== MLSD
[avelino.us]@127.0.0.1:56575 OK MLSD "/test". Transfer starting.
127.0.0.1:56575 ==> 150 File status okay. About to open data connection.
127.0.0.1:56575 ==> 226 Transfer complete.Se você ler o log, vai reparar que tentei logar duas vez com a senha errada, depois fiz a autenticação com a correta, criei uma pasta e mandei um arquivo test.py para o FTP.
Agora vai da criatividade do programador para colocar mais funcionalidade para o serviço. Boa sorte!
artigo publicado originalmente no iMasters, por Thiago Avelino
