Monday, September 2, 2013

Twistd upload FTP server

How to fix "Failed to retrieve directory listing"


I was trying to get a quick ftp server up and running, and it seems liked `twistd` is getting to be installed just about everywhere these days so it seemed simple enough:

twistd -n ftp -r .

Got a twistd up and serving ftp out of the current working directly for anonymous download.

But I actually wanted the server to let me upload (side note: people hate windows because it's terrible, what a wasteland, it's a terrible chore to get even the most basic things done without tools like scp?).

So I started with the simple ftpserver.py example.

But nothing is ever "simple" in Twisted.  It turns out the FTPRealm blah blah blah - no body cares.

I was getting an error doing the first directory listing when I connected to the server as an authenticated user, anonymous listing worked fine.  Here was the not helpful at all traceback:

2013-09-02 13:03:54-0700 [FTP (ProtocolWrapper),0,192.168.42.68] DTPFactory.setTimeout set to 10 seconds
2013-09-02 13:03:54-0700 [FTP (ProtocolWrapper),0,192.168.42.68] DTPFactory starting on 62816
2013-09-02 13:03:54-0700 [FTP (ProtocolWrapper),0,192.168.42.68] Starting factory
2013-09-02 13:03:54-0700 [twisted.protocols.ftp.DTPFactory] DTPFactory.buildProtocol
2013-09-02 13:03:54-0700 [twisted.protocols.ftp.DTPFactory] cancelling DTP timeout

Oh of course!  The DTPFactory/ProtcolWrapper blah blah - no body cares!

Silly thing was trying to read `/home/` - which while probably useful in on a Linux machine wasn't so helpful on my MacBook.

Here's what I ended up with:

"simple" FTP server on a Mac

#! /usr/bin/env python
import os

from twisted.internet import reactor
from twisted.protocols.ftp  import FTPFactory
from twisted.protocols.ftp  import FTPRealm 
from twisted.cred.portal    import Portal
from twisted.cred.checkers  import AllowAnonymousAccess
from twisted.cred.checkers import InMemoryUsernamePasswordDatabaseDontUse as \
        InMemoryDB

PASSWORD = ''

users = {
    os.environ['USER']: PASSWORD
}

p = Portal(FTPRealm('./', userHome='/Users'), 
    (   AllowAnonymousAccess(),
        InMemoryDB(**users),)
    )

f = FTPFactory(p)

reactor.listenTCP(2121, f)
reactor.run()


^ login with your username and blank password; it's not secure, duh.