| |downloads| |stars| |forks| |contributors| |coverage| | |version| |packages| |license| | |github-actions| |doc| |twitter|
.. |downloads| image:: https://img.shields.io/pypi/dm/pyftpdlib.svg :target: https://pepy.tech/project/pyftpdlib :alt: Downloads
.. |stars| image:: https://img.shields.io/github/stars/giampaolo/pyftpdlib.svg :target: https://github.com/giampaolo/pyftpdlib/stargazers :alt: Github stars
.. |forks| image:: https://img.shields.io/github/forks/giampaolo/pyftpdlib.svg :target: https://github.com/giampaolo/pyftpdlib/network/members :alt: Github forks
.. |contributors| image:: https://img.shields.io/github/contributors/giampaolo/pyftpdlib.svg :target: https://github.com/giampaolo/pyftpdlib/graphs/contributors :alt: Contributors
.. |github-actions| image:: https://img.shields.io/github/actions/workflow/status/giampaolo/pyftpdlib/.github/workflows/tests.yml :target: https://github.com/giampaolo/psutil/actions?query=workflow%3Abuild :alt: Linux, macOS, Windows tests
.. |coverage| image:: https://img.shields.io/codecov/c/github/giampaolo/pyftpdlib/update-ci?label=coverage :target: https://codecov.io/gh/giampaolo/pyftpdlib :alt: Test coverage
.. |doc| image:: https://readthedocs.org/projects/pyftpdlib/badge/?version=latest :target: https://pyftpdlib.readthedocs.io/en/latest/ :alt: Documentation Status
.. |version| image:: https://img.shields.io/pypi/v/pyftpdlib.svg?label=pypi :target: https://pypi.org/project/pyftpdlib :alt: Latest version
.. |py-versions| image:: https://img.shields.io/pypi/pyversions/psutil.svg :alt: Supported Python versions
.. |packages| image:: https://repology.org/badge/tiny-repos/python:pyftpdlib.svg :target: https://repology.org/metapackage/python:pyftpdlib/versions :alt: Binary packages
.. |license| image:: https://img.shields.io/pypi/l/pyftpdlib.svg :target: https://github.com/giampaolo/pyftpdlib/blob/master/LICENSE :alt: License
.. |twitter| image:: https://img.shields.io/twitter/follow/grodola.svg?label=follow&style=flat&logo=twitter&logoColor=4FADFF :target: https://twitter.com/grodola :alt: Twitter Follow
Home <https://github.com/giampaolo/pyftpdlib>
__Documentation <http://pyftpdlib.readthedocs.io>
__Download <https://pypi.python.org/pypi/pyftpdlib/>
__Blog <http://grodola.blogspot.com/search/label/pyftpdlib>
__Mailing list <http://groups.google.com/group/pyftpdlib/topics>
__What's new <https://github.com/giampaolo/pyftpdlib/blob/master/HISTORY.rst>
__Python FTP server library provides a high-level portable interface to easily
write very efficient, scalable and asynchronous FTP servers with Python. It is
the most complete RFC-959 <http://www.faqs.org/rfcs/rfc959.html>
FTP server
implementation available for Python <http://www.python.org/>
programming
language.
why <https://github.com/giampaolo/pyftpdlib/issues/203>
and
benchmarks <http://pyftpdlib.readthedocs.io/en/latest/benchmarks.html>
).pysendfile <https://github.com/giampaolo/pysendfile>
__)
system call for uploads.multiple thread / process <http://pyftpdlib.readthedocs.io/en/latest/tutorial.html#changing-the-concurrency-model>
__
model (as in: you'll be free to block or use slow filesystems).RFC-4217 <http://tools.ietf.org/html/rfc4217>
),
IPv6 (RFC-2428 <ftp://ftp.rfc-editor.org/in-notes/rfc2428.txt>
),
Unicode file names (RFC-2640 <http://tools.ietf.org/html/rfc2640>
),
MLSD/MLST commands (RFC-3659 <ftp://ftp.rfc-editor.org/in-notes/rfc3659.txt>
).UNIX <http://pyftpdlib.readthedocs.io/en/latest/tutorial.html#unix-ftp-server>
and
Windows <http://pyftpdlib.readthedocs.io/en/latest/tutorial.html#windows-ftp-server>
.Test coverage <https://github.com/giampaolo/pyftpdlib/blob/master/pyftpdlib/test/>
__
close to 100%.Despite being written in an interpreted language, pyftpdlib has transfer rates
comparable or superior to common UNIX FTP servers written in C. It usually tends
to scale better (see benchmarks <https://pyftpdlib.readthedocs.io/en/latest/benchmarks.html>
)
because whereas vsftpd and proftpd use multiple processes to
achieve concurrency, pyftpdlib only uses one (see the C10K problem <http://www.kegel.com/c10k.html>
).
+-----------------------------------------+----------------+----------------+-------------+ | benchmark type | pyftpdlib | proftpd | speedup | +-----------------------------------------+----------------+----------------+-------------+ | STOR (client -> server) | 585.90 MB/sec | 600.49 MB/sec | -0.02x | +-----------------------------------------+----------------+----------------+-------------+ | RETR (server -> client) | 1652.72 MB/sec | 1524.05 MB/sec | +0.08 | +-----------------------------------------+----------------+----------------+-------------+ | 300 concurrent clients (connect, login) | 0.19 secs | 9.98 secs | +51x | +-----------------------------------------+----------------+----------------+-------------+ | STOR (1 file with 300 idle clients) | 585.59 MB/sec | 518.55 MB/sec | +0.1x | +-----------------------------------------+----------------+----------------+-------------+ | RETR (1 file with 300 idle clients) | 1497.58 MB/sec | 1478.19 MB/sec | 0x | +-----------------------------------------+----------------+----------------+-------------+ | 300 concurrent clients (RETR 10M file) | 3.41 secs | 3.60 secs | +0.05x | +-----------------------------------------+----------------+----------------+-------------+ | 300 concurrent clients (STOR 10M file) | 8.60 secs | 11.56 secs | +0.3x | +-----------------------------------------+----------------+----------------+-------------+ | 300 concurrent clients (QUIT) | 0.03 secs | 0.39 secs | +12x | +-----------------------------------------+----------------+----------------+-------------+
+-----------------------------------------+----------------+----------------+-------------+ | benchmark type | pyftpdlib | vsftpd | speedup | +-----------------------------------------+----------------+----------------+-------------+ | STOR (client -> server) | 585.90 MB/sec | 611.73 MB/sec | -0.04x | +-----------------------------------------+----------------+----------------+-------------+ | RETR (server -> client) | 1652.72 MB/sec | 1512.92 MB/sec | +0.09 | +-----------------------------------------+----------------+----------------+-------------+ | 300 concurrent clients (connect, login) | 0.19 secs | 20.39 secs | +106x | +-----------------------------------------+----------------+----------------+-------------+ | STOR (1 file with 300 idle clients) | 585.59 MB/sec | 610.23 MB/sec | -0.04x | +-----------------------------------------+----------------+----------------+-------------+ | RETR (1 file with 300 idle clients) | 1497.58 MB/sec | 1493.01 MB/sec | 0x | +-----------------------------------------+----------------+----------------+-------------+ | 300 concurrent clients (RETR 10M file) | 3.41 secs | 3.67 secs | +0.07x | +-----------------------------------------+----------------+----------------+-------------+ | 300 concurrent clients (STOR 10M file) | 8.60 secs | 9.82 secs | +0.07x | +-----------------------------------------+----------------+----------------+-------------+ | 300 concurrent clients (QUIT) | 0.03 secs | 0.01 secs | +0.14x | +-----------------------------------------+----------------+----------------+-------------+
For more benchmarks see here <http://pyftpdlib.readthedocs.io/en/latest/benchmarks.html>
__.
.. code-block:: python
>>> from pyftpdlib.authorizers import DummyAuthorizer
>>> from pyftpdlib.handlers import FTPHandler
>>> from pyftpdlib.servers import FTPServer
>>>
>>> authorizer = DummyAuthorizer()
>>> authorizer.add_user("user", "12345", "/home/giampaolo", perm="elradfmwMT")
>>> authorizer.add_anonymous("/home/nobody")
>>>
>>> handler = FTPHandler
>>> handler.authorizer = authorizer
>>>
>>> server = FTPServer(("127.0.0.1", 21), handler)
>>> server.serve_forever()
[I 13-02-19 10:55:42] >>> starting FTP server on 127.0.0.1:21 <<<
[I 13-02-19 10:55:42] poller: <class 'pyftpdlib.ioloop.Epoll'>
[I 13-02-19 10:55:42] masquerade (NAT) address: None
[I 13-02-19 10:55:42] passive ports: None
[I 13-02-19 10:55:42] use sendfile(2): True
[I 13-02-19 10:55:45] 127.0.0.1:34178-[] FTP session opened (connect)
[I 13-02-19 10:55:48] 127.0.0.1:34178-[user] USER 'user' logged in.
[I 13-02-19 10:56:27] 127.0.0.1:34179-[user] RETR /home/giampaolo/.vimrc completed=1 bytes=1700 seconds=0.001
[I 13-02-19 10:56:39] 127.0.0.1:34179-[user] FTP session closed (disconnect).
other code samples <http://pyftpdlib.readthedocs.io/en/latest/tutorial.html>
__
A lot of time and effort went into making pyftpdlib as it is right now.
If you feel pyftpdlib is useful to you or your business and want to support its
future development please consider donating <https://gmpy.dev/donate>
__ me some money.
I stuck in the simple sample, help me please. Thank you first. I just copy and run the sample from a-base-ftp-server I got the 530 Authentication failed error when login with user name and password in FileZilla. But it ok when I login with anonymous user. I don't know what is wrong with this.
OS: Windows10 Python vertion: 3.10.10 pyftpdlib: 1.5.7
Here is the log
[I 2023-03-31 00:52:58] concurrency model: async
[I 2023-03-31 00:52:58] masquerade (NAT) address: None
[I 2023-03-31 00:52:58] passive ports: None
[D 2023-03-31 00:52:58] poller: 'pyftpdlib.ioloop.Select'
[D 2023-03-31 00:52:58] authorizer: 'pyftpdlib.authorizers.DummyAuthorizer'
[D 2023-03-31 00:52:58] handler: 'pyftpdlib.handlers.type'
[D 2023-03-31 00:52:58] max connections: 512
[D 2023-03-31 00:52:58] max connections per ip: unlimited
[D 2023-03-31 00:52:58] timeout: 300
[D 2023-03-31 00:52:58] banner: 'pyftpdlib 1.5.7 ready.'
[D 2023-03-31 00:52:58] max login attempts: 3
[I 2023-03-31 00:52:58] >>> starting FTP server on 0.0.0.0:2121, pid=17404 <<<
[I 2023-03-31 00:53:34] 192.168.0.104:58186-[] FTP session opened (connect)
[D 2023-03-31 00:53:34] 192.168.0.104:58186-[] -> 220 pyftpdlib 1.5.7 ready.
[D 2023-03-31 00:53:34] 192.168.0.104:58186-[] <- USER user
[D 2023-03-31 00:53:34] 192.168.0.104:58186-[] -> 331 Username ok, send password.
[D 2023-03-31 00:53:34] 192.168.0.104:58186-[user] <- PASS ******
[D 2023-03-31 00:53:38] 192.168.0.104:58186-[] -> 530 Authentication failed.
[I 2023-03-31 00:53:38] 192.168.0.104:58186-[] USER 'user' failed login.
[D 2023-03-31 00:53:38] [debug] call: close() (<FTPHandler(id=1975293646816, addr='192.168.0.104:58186')>)
[I 2023-03-31 00:53:38] 192.168.0.104:58186-[] FTP session closed (disconnect).
[I 2023-03-31 00:53:46] 192.168.0.104:58188-[] FTP session opened (connect)
[D 2023-03-31 00:53:46] 192.168.0.104:58188-[] -> 220 pyftpdlib 1.5.7 ready.
[D 2023-03-31 00:53:46] 192.168.0.104:58188-[] <- USER anonymous
[D 2023-03-31 00:53:46] 192.168.0.104:58188-[] -> 331 Username ok, send password.
[D 2023-03-31 00:53:46] 192.168.0.104:58188-[anonymous] <- PASS ******
[D 2023-03-31 00:53:46] 192.168.0.104:58188-[anonymous] -> 230 Login successful.
[I 2023-03-31 00:53:46] 192.168.0.104:58188-[anonymous] USER 'anonymous' logged in.
[D 2023-03-31 00:53:46] 192.168.0.104:58188-[anonymous] <- SYST
[D 2023-03-31 00:53:46] 192.168.0.104:58188-[anonymous] -> 215 UNIX Type: L8
[D 2023-03-31 00:53:46] 192.168.0.104:58188-[anonymous] <- FEAT
[D 2023-03-31 00:53:46] 192.168.0.104:58188-[anonymous] -> 211 End FEAT.
[D 2023-03-31 00:53:46] 192.168.0.104:58188-[anonymous] <- OPTS UTF8 ON
[D 2023-03-31 00:53:46] 192.168.0.104:58188-[anonymous] -> 501 Invalid argument.
[D 2023-03-31 00:53:46] 192.168.0.104:58188-[anonymous] <- PWD
[D 2023-03-31 00:53:46] 192.168.0.104:58188-[anonymous] -> 257 "/" is the current directory.
[D 2023-03-31 00:53:46] 192.168.0.104:58188-[anonymous] <- TYPE I
[D 2023-03-31 00:53:46] 192.168.0.104:58188-[anonymous] -> 200 Type set to: Binary.
[D 2023-03-31 00:53:46] 192.168.0.104:58188-[anonymous] <- PORT 192,168,0,104,227,77
[D 2023-03-31 00:53:46] 192.168.0.104:58188-[anonymous] -> 200 Active data connection established.
[D 2023-03-31 00:53:46] 192.168.0.104:58188-[anonymous] <- MLSD
[D 2023-03-31 00:53:46] 192.168.0.104:58188-[anonymous] -> 125 Data connection already open. Transfer starting.
[D 2023-03-31 00:53:46] [debug] starting transfer using send() (<DTPHandler(id=1975293646816, addr='192.168.0.104:58188', user='anonymous')>)
[D 2023-03-31 00:53:46] [debug] call: close() (<DTPHandler(id=1975293646816, addr='192.168.0.104:58188', user='anonymous')>)
[D 2023-03-31 00:53:46] 192.168.0.104:58188-[anonymous] -> 226 Transfer complete.
1)Max upload speed for user accounts Limits the maximum upload bandwidth shared between all sessions associated with an individual User account. Setting a limit of 0 KB/s means unlimited bandwidth.
2) Max download speed for user accounts Limits the maximum download bandwidth shared between all sessions associated with an individual user account. Setting a limit of 0 KB/s means unlimited bandwidth.
Fix #266 and #531. Took inspiration from vsftpd for 200 reply message - https://github.com/dagwieers/vsftpd/blob/ce2fa4288b92683f832c3b5179d5cb2a77d0750e/opts.c#L20
Let me know if additional changes are needed in order to merge it.
hello,
I will like ad a user in windows server
``` from pyftpdlib.handlers import FTPHandler from pyftpdlib.servers import FTPServer from pyftpdlib.authorizers import WindowsAuthorizer import sys
authorizer = WindowsAuthorizer( "elradfmwM") authorizer.add_user("isra", "12345", "f:/", perm="elradfmwMT") handler = FTPHandler handler.authorizer = authorizer
server = FTPServer(("127.0.0.1", 2121), handler) server.serve_forever() ```
it is posible?
reggards
Because os.path.realpath can return bytes
issue: on Python2 this warning is printed even if the homedir argument is unicode:
/opt/lib/python2.7/site-packages/pyftpdlib/handlers.py:2530: RuntimeWarning: DummyAuthorizerWithUnicodeHomeDir.get_home_dir returned a non-unicode string; now casting to unicode
RuntimeWarning)
I am using pyftpdlib to provide an FTP frontend to files indexed in a database using a class inherited from AbstractFS
that retrieves the information from the database's API.
The API is accessed using the credentials provided by the FTP-Client (set by a custom DummyAuthorizer
).
When implementing stat, I first used some static values and everything worked fine.
The database's API also provides information like size etc. and I updated filesystem's stat-method to retrieve this information.
Now, listing of a directory fails, as the API rejects the request.
Looking into the code, I found that in handler.py
/ ftp_MLSD
only listdir
is called within run_as_current_user
.
So the stat
call later within format_mlsx
is not called as current user (and therefore the credentials in the API-call are not set).
I am not sure, why format_mlsx
is not enclosed within run_as_current_user
, as I would have assumed that any call on the filesystem should be run as the current user.
TLDR; AbstractFS.format_mlsx
is called by FTPHandler
without run_as_current_user
, thereby filesystem has no user credentials available (for stat
).
Python enthusiast and core developer, author of psutil and pyftpdlib python libs
GitHub Repositorypython ftp ftps ftp-server asynchronous