| 1 | """ |
|---|
| 2 | An example FTP server, with minimal user authentication. |
|---|
| 3 | """ |
|---|
| 4 | |
|---|
| 5 | from twisted.protocols.ftp import FTPFactory |
|---|
| 6 | from twisted.protocols.ftp import FTPRealm |
|---|
| 7 | from twisted.cred.portal import Portal |
|---|
| 8 | from twisted.cred.checkers import AllowAnonymousAccess |
|---|
| 9 | from twisted.cred.checkers import FilePasswordDB |
|---|
| 10 | |
|---|
| 11 | # |
|---|
| 12 | # First, set up a portal (twisted.cred.portal.Portal). This will be used |
|---|
| 13 | # to authenticate user logins, including anonymous logins. |
|---|
| 14 | # |
|---|
| 15 | # Part of this will be to establish the "realm" of the server - the most important |
|---|
| 16 | # task in this case is to establish where anonymous users will have default access |
|---|
| 17 | # to. In a real world scenario this would typically point to something like '/pub' |
|---|
| 18 | # but for this example it is pointed at the current working directory for giggles. |
|---|
| 19 | # |
|---|
| 20 | # The other important part of the portal setup is to point it to a tuple of |
|---|
| 21 | # credential checkers. The first of these is used to grant access to anonymous |
|---|
| 22 | # users and is relatively simple. The second of these is a very primitive, but |
|---|
| 23 | # functional, password checker. For this example I a using a plain text password |
|---|
| 24 | # file that has one username:password pair per line. This checker *does* provide |
|---|
| 25 | # a hashing interface, and one would normally want to use it instead of plain text |
|---|
| 26 | # storage for anything remotely resembling a 'live' network. In this case, the file |
|---|
| 27 | # "pass.dat" is used, and stored in the same directory as the server. BAD. |
|---|
| 28 | # |
|---|
| 29 | # pass.dat looks like this: |
|---|
| 30 | # |
|---|
| 31 | # ===================== |
|---|
| 32 | # jeff:bozo |
|---|
| 33 | # grimmtooth:bozo2 |
|---|
| 34 | # ===================== |
|---|
| 35 | # |
|---|
| 36 | p = Portal(FTPRealm('./'), |
|---|
| 37 | ( AllowAnonymousAccess(), |
|---|
| 38 | FilePasswordDB("pass.dat"),) |
|---|
| 39 | ) |
|---|
| 40 | |
|---|
| 41 | # |
|---|
| 42 | # Once the portal is set up, start up the FTPFactory and pass the portal to it |
|---|
| 43 | # on startup. FTPFactory will start up a twisted.protocols.ftp.FTP() handler |
|---|
| 44 | # for each incoming OPEN request. Business as usual in Twisted land. |
|---|
| 45 | # |
|---|
| 46 | f = FTPFactory(p) |
|---|
| 47 | |
|---|
| 48 | # |
|---|
| 49 | # You know this part. Point the reactor to port 21 coupled with the above factory, |
|---|
| 50 | # and start the event loop. |
|---|
| 51 | # |
|---|
| 52 | from twisted.internet import reactor |
|---|
| 53 | reactor.listenTCP(21, f) |
|---|
| 54 | reactor.run() |
|---|