]> git.rkrishnan.org Git - tahoe-lafs/tahoe-lafs.git/blobdiff - src/allmydata/frontends/ftpd.py
Fix ftp 'ls' to work with current Twisted-15.0.0
[tahoe-lafs/tahoe-lafs.git] / src / allmydata / frontends / ftpd.py
index c187d86aa92f061886e25e216f91d90bc3ae4792..dbcb8318a5a15658f4b52aa3fc9f59f730b6220d 100644 (file)
@@ -1,24 +1,26 @@
 
-import tempfile
+from types import NoneType
+
 from zope.interface import implements
 from twisted.application import service, strports
 from twisted.internet import defer
 from twisted.internet.interfaces import IConsumer
 from twisted.cred import portal
+from twisted.python import filepath
 from twisted.protocols import ftp
 
 from allmydata.interfaces import IDirectoryNode, ExistingChildError, \
      NoSuchChildError
-from allmydata.immutable.download import ConsumerAdapter
 from allmydata.immutable.upload import FileHandle
+from allmydata.util.fileutil import EncryptedTemporaryFile
+from allmydata.util.assertutil import precondition
 
 class ReadFile:
     implements(ftp.IReadFile)
     def __init__(self, node):
         self.node = node
     def send(self, consumer):
-        ad = ConsumerAdapter(consumer)
-        d = self.node.download(ad)
+        d = self.node.read(consumer)
         return d # when consumed
 
 class FileWriter:
@@ -29,7 +31,7 @@ class FileWriter:
             raise NotImplementedError("Non-streaming producer not supported.")
         # we write the data to a temporary file, since Tahoe can't do
         # streaming upload yet.
-        self.f = tempfile.TemporaryFile()
+        self.f = EncryptedTemporaryFile()
         return None
 
     def unregisterProducer(self):
@@ -60,6 +62,19 @@ class WriteFile:
 class NoParentError(Exception):
     pass
 
+if hasattr(filepath, "Permissions"):
+    # filepath.Permissions was added in Twisted-11.1.0, but we're compatible
+    # back to 11.0.0 (on windows). Fortunately we don't really need to
+    # provide anything more than an int until Twisted-15.0.0 .
+    class IntishPermissions(filepath.Permissions):
+        def __init__(self, statModeInt):
+            self.statModeInt = statModeInt
+            filepath.Permissions.__init__(self, statModeInt)
+        def __and__(self, other):
+            return self.statModeInt & other
+else:
+    IntishPermissions = lambda statModeInt: statModeInt
+
 class Handler:
     implements(ftp.IFTPShell)
     def __init__(self, client, rootnode, username, convergence):
@@ -85,7 +100,7 @@ class Handler:
         d = node.get(path[0])
         def _maybe_create(f):
             f.trap(NoSuchChildError)
-            return node.create_empty_directory(path[0])
+            return node.create_subdirectory(path[0])
         d.addErrback(_maybe_create)
         d.addCallback(self._get_or_create_directories, path[1:])
         return d
@@ -195,15 +210,23 @@ class Handler:
                 if isdir:
                     value = 0
                 else:
-                    value = childnode.get_size()
+                    value = childnode.get_size() or 0
             elif key == "directory":
                 value = isdir
             elif key == "permissions":
-                value = 0600
+                # Twisted-14.0.2 expected an int, and used it in a rendering
+                # function that did (mode & NUMBER). Twisted-15.0.0 expects a
+                # twisted.python.filepath.Permissions , and calls its
+                # .shorthand() method. Try to provide both.
+                value = IntishPermissions(0600)
             elif key == "hardlinks":
                 value = 1
             elif key == "modified":
-                value = metadata.get("mtime", 0)
+                # follow sftpd convention (i.e. linkmotime in preference to mtime)
+                if "linkmotime" in metadata.get("tahoe", {}):
+                    value = metadata["tahoe"]["linkmotime"]
+                else:
+                    value = metadata.get("mtime", 0)
             elif key == "owner":
                 value = self.username
             elif key == "group":
@@ -267,7 +290,7 @@ class Handler:
         d.addCallback(_got_parent)
         return d
 
-from auth import AccountURLChecker, AccountFileChecker, NeedRootcapLookupScheme
+from allmydata.frontends.auth import AccountURLChecker, AccountFileChecker, NeedRootcapLookupScheme
 
 
 class Dispatcher:
@@ -286,12 +309,9 @@ class Dispatcher:
 
 class FTPServer(service.MultiService):
     def __init__(self, client, accountfile, accounturl, ftp_portstr):
+        precondition(isinstance(accountfile, (unicode, NoneType)), accountfile)
         service.MultiService.__init__(self)
 
-        # make sure we're using a patched Twisted that uses IWriteFile.close:
-        # see docs/frontends/ftp.txt for details.
-        assert "close" in ftp.IWriteFile.names(), "your twisted is lacking"
-
         r = Dispatcher(client)
         p = portal.Portal(r)