= Tahoe FTP Frontend = All Tahoe client nodes can run a frontend FTP server, allowing regular FTP clients to access the virtual filesystem. Since Tahoe does not use user accounts or passwords, the FTP server must be configured with a way to translate USER+PASS into a root directory cap. Two mechanisms are provided. The first is a simple flat file with one account per line. The second is an HTTP-based login mechanism, backed by simple PHP script and a database. The latter form is used by allmydata.com to provide secure access to customer rootcaps. == Configuring an Account File == To configure the first form, create a file (probably in BASEDIR/private/ftp.accounts) in which each non-comment/non-blank line is a space-separated line of (USERNAME, PASSWORD, ROOTCAP), like so: % cat BASEDIR/private/ftp.accounts # This is a password file, (username, password, rootcap) alice password URI:DIR2:ioej8xmzrwilg772gzj4fhdg7a:wtiizszzz2rgmczv4wl6bqvbv33ag4kvbr6prz3u6w3geixa6m6a bob sekrit URI:DIR2:6bdmeitystckbl9yqlw7g56f4e:serp5ioqxnh34mlbmzwvkp3odehsyrr7eytt5f64we3k9hhcrcja Then add the following lines to the BASEDIR/tahoe.cfg file: [ftpd] enabled = true ftp.port = 8021 ftp.accounts.file = private/ftp.accounts The FTP server will listen on the given port number. The ftp.accounts.file pathname will be interpreted relative to the node's BASEDIR. == Configuring an Account Server == Determine the URL of the account server, say https://example.com/login . Then add the following lines to BASEDIR/tahoe.cfg: [ftpd] enabled = true ftp.port = 8021 ftp.accounts.url = https://example.com/login == Dependencies == The FTP server requires Twisted's "vfs" component, which is not included in the Twisted-8.1.0 release. If there is no newer release available, it may be necessary to run Twisted from the SVN trunk to obtain this component. In addition, the following patch must be applied (as of r24943) to enable asynchronous closing of file-upload operations: Index: twisted/protocols/ftp.py =================================================================== --- twisted/protocols/ftp.py (revision 24956) +++ twisted/protocols/ftp.py (working copy) @@ -1049,7 +1049,6 @@ cons = ASCIIConsumerWrapper(cons) d = self.dtpInstance.registerConsumer(cons) - d.addCallbacks(cbSent, ebSent) # Tell them what to doooo if self.dtpInstance.isConnected: @@ -1062,6 +1061,8 @@ def cbOpened(file): d = file.receive() d.addCallback(cbConsumer) + d.addCallback(lambda ignored: file.close()) + d.addCallbacks(cbSent, ebSent) return d def ebOpened(err): @@ -1434,7 +1435,14 @@ @rtype: C{Deferred} of C{IConsumer} """ + def close(): + """ + Perform any post-write work that needs to be done. This method may + only be invoked once on each provider, and will always be invoked + after receive(). + @rtype: C{Deferred} of anything: the value is ignored + """ def _getgroups(uid): """Return the primary and supplementary groups for the given UID. @@ -1795,6 +1803,8 @@ # FileConsumer will close the file object return defer.succeed(FileConsumer(self.fObj)) + def close(self): + return defer.succeed(None) class FTPRealm: Index: twisted/vfs/adapters/ftp.py =================================================================== --- twisted/vfs/adapters/ftp.py (revision 24956) +++ twisted/vfs/adapters/ftp.py (working copy) @@ -295,6 +295,11 @@ """ return defer.succeed(IConsumer(self.node)) + def close(self): + """ + Perform post-write actions. + """ + return defer.succeed(None) class _FileToConsumerAdapter(object):