1====
2FAQs
3====
4
5.. contents:: Table of Contents
6
7Introduction
8============
9
10What is pyftpdlib?
11------------------
12
13pyftpdlib is a high-level library to easily write asynchronous portable FTP
14servers with `Python <http://www.python.org/>`__.
15
16What is Python?
17---------------
18
19Python is an interpreted, interactive, object-oriented, easy-to-learn
20programming language. It is often compared to *Tcl, Perl, Scheme* or *Java*.
21
22I'm not a python programmer. Can I use it anyway?
23-------------------------------------------------
24
25Yes. pyftpdlib is a fully working FTP server implementation that can be run
26"as is". For example you could run an anonymous ftp server from cmd-line by
27running:
28
29.. code-block:: sh
30
31    $ sudo python -m pyftpdlib
32    [I 13-02-20 14:16:36] >>> starting FTP server on 0.0.0.0:8021 <<<
33    [I 13-02-20 14:16:36] poller: <class 'pyftpdlib.ioloop.Epoll'>
34    [I 13-02-20 14:16:36] masquerade (NAT) address: None
35    [I 13-02-20 14:16:36] passive ports: None
36    [I 13-02-20 14:16:36] use sendfile(2): True
37
38This is useful in case you want a quick and dirty way to share a directory
39without, say, installing and configuring samba. Starting from version 0.6.0
40options can be passed to the command line (see ``python -m pyftpdlib --help``
41to see all available options). Examples:
42
43Anonymous FTP server with write access:
44
45.. code-block:: sh
46
47    $ sudo python -m pyftpdlib -w
48    ~pyftpdlib-1.3.1-py2.7.egg/pyftpdlib/authorizers.py:265: RuntimeWarning: write permissions assigned to anonymous user.
49    [I 13-02-20 14:16:36] >>> starting FTP server on 0.0.0.0:8021 <<<
50    [I 13-02-20 14:16:36] poller: <class 'pyftpdlib.ioloop.Epoll'>
51    [I 13-02-20 14:16:36] masquerade (NAT) address: None
52    [I 13-02-20 14:16:36] passive ports: None
53    [I 13-02-20 14:16:36] use sendfile(2): True
54
55Listen on a different ip/port:
56
57.. code-block:: sh
58
59    $ python -m pyftpdlib -i 127.0.0.1 -p 8021
60    [I 13-02-20 14:16:36] >>> starting FTP server on 0.0.0.0:8021 <<<
61    [I 13-02-20 14:16:36] poller: <class 'pyftpdlib.ioloop.Epoll'>
62    [I 13-02-20 14:16:36] masquerade (NAT) address: None
63    [I 13-02-20 14:16:36] passive ports: None
64    [I 13-02-20 14:16:36] use sendfile(2): True
65
66
67Customizing ftpd for basic tasks like adding users or deciding where log file
68should be placed is mostly simply editing variables. This is basically like
69learning how to edit a common unix ftpd.conf file and doesn't really require
70Python knowledge. Customizing ftpd more deeply requires a python script which
71imports pyftpdlib to be written separately. An example about how this could be
72done are the scripts contained in the
73`demo directory <https://github.com/giampaolo/pyftpdlib/tree/master/demo>`__.
74
75Getting help
76------------
77
78There's a mailing list available at:
79http://groups.google.com/group/pyftpdlib/topics
80
81Installing and compatibility
82============================
83
84How do I install pyftpdlib?
85---------------------------
86
87If you are not new to Python you probably don't need that, otherwise follow the
88`install instructions <install.html>`__.
89
90Which Python versions are compatible?
91-------------------------------------
92
93From *2.6* to *3.X*.
94
95On which platforms can pyftpdlib be used?
96-----------------------------------------
97
98pyftpdlib should work on any platform where **select()**, **poll()**,
99**epoll()** or **kqueue()** system calls are available and on any Python
100implementation which refers to *cPython 2.6* or superior.
101The development team has mainly tested it under various *Linux*, *Windows*,
102*OSX* and *FreeBSD* systems.
103For FreeBSD is also available a
104`pre-compiled package <http://www.freshports.org/ftp/py-pyftpdlib/>`__
105maintained by Li-Wen Hsu (lwhsu@freebsd.org).
106Other Python implementation like
107`PythonCE <http://pythonce.sourceforge.net/>`__ are known to work with
108pyftpdlib and every new version is usually tested against it.
109pyftpdlib currently does not work on `Jython <http://www.jython.org/>`__
110since the latest Jython release refers to CPython 2.2.x serie. The best way
111to know whether pyftpdlib works on your platform is installing it and running
112its test suite.
113
114Usage
115=====
116
117How can I run long-running tasks without blocking the server?
118-------------------------------------------------------------
119
120pyftpdlib is an *asynchronous* FTP server. That means that if you need to run a
121time consuming task you have to use a separate Python process or thread for the
122actual processing work otherwise the entire asynchronous loop will be blocked.
123
124Let's suppose you want to implement a long-running task every time the server
125receives a file. The code snippet below shows the correct way to do it by using
126a thread.
127
128With ``self.del_channel()`` we temporarily "sleep" the connection handler which
129will be removed from the async IO poller loop and won't be able to send or
130receive any more data. It won't be closed (disconnected) as long as we don't
131invoke ``self.add_channel()``. This is fundamental when working with threads to
132avoid race conditions, dead locks etc.
133
134.. code-block:: python
135
136    class MyHandler(FTPHandler):
137
138        def on_file_received(self, file):
139            def blocking_task():
140                time.sleep(5)
141                self.add_channel()
142
143            self.del_channel()
144            threading.Thread(target=blocking_task).start()
145
146Another possibility is to
147`change the default concurrency model <tutorial.html#changing-the-concurrency-model>`__.
148
149Why do I get socket.error "Permission denied" error on ftpd starting?
150---------------------------------------------------------------------
151
152Probably because you're on a Unix system and you're trying to start ftpd as an
153unprivileged user. FTP servers bind on port 21 by default and only super-user
154account  (e.g. root) can bind sockets on such ports. If you want to bind
155ftpd as non-privileged user you should set a port higher than 1024.
156
157How can I prevent the server version from being displayed?
158----------------------------------------------------------
159
160Just modify `FTPHandler.banner <api.html#pyftpdlib.handlers.FTPHandler.banner>`__.
161
162Can control upload/download ratios?
163-----------------------------------
164
165Yes. Starting from version 0.5.2 pyftpdlib provides a new class called
166`ThrottledDTPHandler <api.html#pyftpdlib.handlers.ThrottledDTPHandler>`__.
167You can set speed limits by modifying
168`read_limit <api.html#pyftpdlib.handlers.ThrottledDTPHandler.read_limit>`__
169and
170`write_limit <api.html#pyftpdlib.handlers.ThrottledDTPHandler.write_limit>`__
171class attributes as it is shown in
172`throttled_ftpd.py <https://github.com/giampaolo/pyftpdlib/blob/master/demo/throttled_ftpd.py>`__
173demo script.
174
175Are there ways to limit connections?
176------------------------------------
177
178`FTPServer <api.html#pyftpdlib.servers.FTPServer>`__. class comes with two
179overridable attributes defaulting to zero
180(no limit): `max_cons <api.html#pyftpdlib.servers.FTPServer.max_cons>`__,
181which sets a limit for maximum simultaneous
182connection to handle by ftpd and
183`max_cons_per_ip <api.html#pyftpdlib.servers.FTPServer.max_cons_per_ip>`__
184which set a limit for connections from the same IP address. Overriding these
185variables is always recommended to avoid DoS attacks.
186
187I'm behind a NAT / gateway
188--------------------------
189
190When behind a NAT a ftp server needs to replace the IP local address displayed
191in PASV replies and instead use the public address of the NAT to allow client
192to connect.  By overriding
193`masquerade_address <api.html#pyftpdlib.handlers.FTPHandler.masquerade_address>`__
194attribute of `FTPHandler <api.html#pyftpdlib.handlers.FTPHandler.masquerade_address>`__
195class you will force pyftpdlib to do such replacement. However, one problem
196still exists.  The passive FTP connections will use ports from 1024 and up,
197which means that you must forward all ports 1024-65535 from the NAT to the FTP
198server!  And you have to allow many (possibly) dangerous ports in your
199firewalling rules!  To resolve this, simply override
200`passive_ports <api.html#pyftpdlib.handlers.FTPHandler.passive_ports>`__
201attribute of `FTPHandler <api.html#pyftpdlib.handlers.FTPHandler.masquerade_address>`__
202class to control what ports pyftpdlib will use for its passive data transfers.
203Value expected by `passive_ports <api.html#pyftpdlib.handlers.FTPHandler.passive_ports>`__
204attribute is a list of integers (e.g. range(60000, 65535)) indicating which
205ports will be used for initializing the passive data channel. In case you run a
206FTP server with multiple private IP addresses behind a NAT firewall with
207multiple public IP addresses you can use
208`passive_ports <api.html#pyftpdlib.handlers.FTPHandler.passive_ports>`__ option
209which allows you to define multiple 1 to 1 mappings (**New in 0.6.0**).
210
211What is FXP?
212------------
213
214FXP is part of the name of a popular Windows FTP client:
215`http://www.flashfxp.com <http://www.flashfxp.com>`__. This client has made the
216name "FXP" commonly used as a synonym for site-to-site FTP transfers, for
217transferring a file between two remote FTP servers without the transfer going
218through the client's host.  Sometimes "FXP" is referred to as a protocol; in
219fact, it is not. The site-to-site transfer capability was deliberately designed
220into `RFC-959 <http://www.faqs.org/rfcs/rfc959.html>`__. More info can be found
221here: `http://www.proftpd.org/docs/howto/FXP.html
222<http://www.proftpd.org/docs/howto/FXP.html>`__.
223
224Does pyftpdlib support FXP?
225---------------------------
226
227Yes. It is disabled by default for security reasons (see
228`RFC-2257 <http://tools.ietf.org/html/rfc2577>`__ and
229`FTP bounce attack description <http://www.cert.org/advisories/CA-1997-27.html>`__)
230but in case you want to enable it just set to True the
231`permit_foreign_addresses <api.html#pyftpdlib.handlers.FTPHandler.permit_foreign_addresses>`__
232attribute of `FTPHandler <api.html#pyftpdlib.handlers.FTPHandler.masquerade_address>`__ class.
233
234Why timestamps shown by MDTM and ls commands (LIST, MLSD, MLST) are wrong?
235--------------------------------------------------------------------------
236
237If by "wrong" you mean "different from the timestamp of that file on my client
238machine", then that is the expected behavior.
239Starting from version 0.6.0 pyftpdlib uses
240`GMT times <http://en.wikipedia.org/wiki/Greenwich*Mean*Time>`__ as recommended
241in `RFC-3659 <http://tools.ietf.org/html/rfc3659>`__.
242In case you want such commands to report local times instead just set the
243`use_gmt_times <api.html#pyftpdlib.handlers.FTPHandler.use_gmt_times>`__ attribute to ``False``.
244For further information you might want to take a look at
245`this <http://www.proftpd.org/docs/howto/Timestamps.html>`__
246
247Implementation
248==============
249
250sendfile()
251----------
252
253Starting from version 0.7.0 if
254`pysendfile <https://github.com/giampaolo/pysendfile/>`__ module is installed
255sendfile(2) system call be used when uploading files (from server to client)
256via RETR command.
257Using sendfile(2) usually results in transfer rates from 2x to 3x faster
258and less CPU usage.
259Note: use of sendfile() might introduce some unexpected issues with "non
260regular filesystems" such as NFS, SMBFS/Samba, CIFS and network mounts in
261general, see: http://www.proftpd.org/docs/howto/Sendfile.html. If you bump into
262one this problems the fix consists in disabling sendfile() usage via
263`FTPHandler.use_sendfile <api.html#pyftpdlib.handlers.FTPHandler.use_sendfile>`__
264option:
265
266.. code-block:: python
267
268    from pyftpdlib.handlers import FTPHandler
269    handler = FTPHandler
270    handler.use_senfile = False
271    ...
272
273Globbing / STAT command implementation
274--------------------------------------
275
276Globbing is a common Unix shell mechanism for expanding wildcard patterns to
277match multiple filenames. When an argument is provided to the *STAT* command,
278ftpd should return directory listing over the command channel.
279`RFC-959 <http://tools.ietf.org/html/rfc959>`__ does not explicitly mention
280globbing; this means that FTP servers are not required to support globbing in
281order to be compliant.  However, many FTP servers do support globbing as a
282measure of convenience for FTP clients and users. In order to search for and
283match the given globbing expression, the code has to search (possibly) many
284directories, examine each contained filename, and build a list of matching
285files in memory. Since this operation can be quite intensive, both CPU- and
286memory-wise, pyftpdlib *does not* support globbing.
287
288ASCII transfers / SIZE command implementation
289---------------------------------------------
290
291Properly handling the SIZE command when TYPE ASCII is used would require to
292scan the entire file to perform the ASCII translation logic
293(file.read().replace(os.linesep, '\r\n')) and then calculating the len of such
294data which may be different than the actual size of the file on the server.
295Considering that calculating such result could be very resource-intensive it
296could be easy for a malicious client to try a DoS attack, thus pyftpdlib
297rejects SIZE when the current TYPE is ASCII. However, clients in general should
298not be resuming downloads in ASCII mode.  Resuming downloads in binary mode is
299the recommended way as specified in
300`RFC-3659 <http://tools.ietf.org/html/rfc3659>`__.
301
302IPv6 support
303------------
304
305Starting from version 0.4.0 pyftpdlib *supports* IPv6
306(`RFC-2428 <http://tools.ietf.org/html/rfc2428>`__). If you use IPv6 and want
307your FTP daemon to do so just pass a valid IPv6 address to the
308`FTPServer <api.html#pyftpdlib.servers.FTPServer>`__ class constructor.
309Example:
310
311.. code-block:: python
312
313    >>> from pyftpdlib.servers import FTPServer
314    >>> address = ("::1", 21)  # listen on localhost, port 21
315    >>> ftpd = FTPServer(address, FTPHandler)
316    >>> ftpd.serve_forever()
317    Serving FTP on ::1:21
318
319If your OS (for example: all recent UNIX systems) have an hybrid dual-stack
320IPv6/IPv4 implementation the code above will listen on both IPv4 and IPv6 by
321using a single IPv6 socket (**New in 0.6.0**).
322
323How do I install IPv6 support on my system?
324-------------------------------------------
325
326If you want to install IPv6 support on Linux run "modprobe ipv6", then
327"ifconfig". This should display the loopback adapter, with the address "::1".
328You should then be able to listen the server on that address, and connect to
329it.
330On Windows (XP SP2 and higher) run "netsh int ipv6 install". Again, you should
331be able to use IPv6 loopback afterwards.
332
333Can pyftpdlib be integrated with "real" users existing on the system?
334---------------------------------------------------------------------
335
336Yes. Starting from version 0.6.0 pyftpdlib provides the new `UnixAuthorizer <api.html#pyftpdlib.authorizers.UnixAuthorizer>`__
337and `WindowsAuthorizer <api.html#pyftpdlib.authorizers.WindowsAuthorizer>`__ classes. By using them pyftpdlib can look into the
338system account database to authenticate users. They also assume the id of real
339users every time the FTP server is going to access the filesystem (e.g. for
340creating or renaming a file) the authorizer will temporarily impersonate the
341currently logged on user, execute the filesystem call and then switch back to
342the user who originally started the server. Example UNIX and Windows FTP
343servers contained in the
344`demo directory <https://github.com/giampaolo/pyftpdlib/tree/master/demo>`__
345shows how to use `UnixAuthorizer <api.html#pyftpdlib.authorizers.UnixAuthorizer>`__ and `WindowsAuthorizer <api.html#pyftpdlib.authorizers.WindowsAuthorizer>`__ classes.
346
347Does pyftpdlib support FTP over TLS/SSL (FTPS)?
348-----------------------------------------------
349
350Yes, starting from version 0.6.0, see:
351`Does pyftpdlib support FTP over TLS/SSL (FTPS)?`_
352
353What about SITE commands?
354-------------------------
355
356The only supported SITE command is *SITE CHMOD* (change file mode). The user
357willing to add support for other specific SITE commands has to define a new
358``ftp_SITE_CMD`` method in the
359`FTPHandler <api.html#pyftpdlib.handlers.FTPHandler>`__ subclass and add a new
360entry in ``proto_cmds`` dictionary. Example:
361
362.. code-block:: python
363
364    from pyftpdlib.handlers import FTPHandler
365
366    proto_cmds = FTPHandler.proto_cmds.copy()
367    proto_cmds.update(
368        {'SITE RMTREE': dict(perm='R', auth=True, arg=True,
369          help='Syntax: SITE <SP> RMTREE <SP> path (remove directory tree).')}
370    )
371
372    class CustomizedFTPHandler(FTPHandler):
373        proto_cmds = proto_cmds
374
375    def ftp_SITE_RMTREE(self, line):
376        """Recursively remove a directory tree."""
377        # implementation here
378        # ...
379