[Zope3-checkins] SVN: Zope3/trunk/src/zope/app/ Fix the FTP server. The twisted interface changed and these fixes reflect

Michael Kerrin michael.kerrin at openapp.biz
Mon Oct 17 13:04:18 EDT 2005


Log message for revision 39487:
  Fix the FTP server. The twisted interface changed and these fixes reflect
  that. The zope.app.ftp component received a new method `readable' that works
  in a similar mannor to the writable method.
  

Changed:
  U   Zope3/trunk/src/zope/app/ftp/__init__.py
  U   Zope3/trunk/src/zope/app/ftp/configure.zcml
  U   Zope3/trunk/src/zope/app/ftp/tests/test_ftpview.py
  U   Zope3/trunk/src/zope/app/twisted/ftp/ftp.py
  U   Zope3/trunk/src/zope/app/twisted/ftp/test/test_zope_ftp.py
  U   Zope3/trunk/src/zope/app/twisted/ftp/tests/demofs.py
  U   Zope3/trunk/src/zope/app/twisted/ftp/utils.py
  U   Zope3/trunk/src/zope/app/twisted/interfaces.py

-=-
Modified: Zope3/trunk/src/zope/app/ftp/__init__.py
===================================================================
--- Zope3/trunk/src/zope/app/ftp/__init__.py	2005-10-17 15:57:08 UTC (rev 39486)
+++ Zope3/trunk/src/zope/app/ftp/__init__.py	2005-10-17 17:04:17 UTC (rev 39487)
@@ -22,6 +22,7 @@
 from zope.component import queryAdapter
 from zope.publisher.interfaces.ftp import IFTPPublisher
 from zope.security.proxy import removeSecurityProxy
+from zope.security.checker import canAccess
 
 from zope.app.filerepresentation.interfaces import IReadFile, IWriteFile
 from zope.app.filerepresentation.interfaces import IReadDirectory
@@ -75,7 +76,6 @@
                     if filter(name)]
 
     def _lsinfo(self, name, file):
-
         info = {'name': name,
                 'mtime': self._mtime(file),
                 }
@@ -222,8 +222,18 @@
     def writable(self, name):
         if name in self._dir:
             f = IWriteFile(self._dir[name], None)
-            return hasattr(f, 'write')
+            if f is not None:
+                return canAccess(f, 'write')
+            return False
         d = IWriteDirectory(self.context, None)
-        return hasattr(d, '__setitem__')
+        return canAccess(d, '__setitem__')
 
-
+    def readable(self, name):
+        if name in self._dir:
+            f = IReadFile(self._dir[name], None)
+            if f is not None:
+                return canAccess(f, 'read')
+            d = IReadDirectory(self._dir[name], name)
+            if d is not None:
+                return canAccess(d, 'get')
+        return False

Modified: Zope3/trunk/src/zope/app/ftp/configure.zcml
===================================================================
--- Zope3/trunk/src/zope/app/ftp/configure.zcml	2005-10-17 15:57:08 UTC (rev 39486)
+++ Zope3/trunk/src/zope/app/ftp/configure.zcml	2005-10-17 17:04:17 UTC (rev 39487)
@@ -148,4 +148,14 @@
           "zope.app.publisher.interfaces.ftp.IFTPDirectoryPublisher"
       />
 
+    <view 
+      for="zope.app.container.interfaces.IReadContainer"
+      name="readable"
+      type="zope.publisher.interfaces.ftp.IFTPRequest"
+      factory=".FTPView"
+      permission="zope.Public"
+      allowed_interface=
+          "zope.app.publisher.interfaces.ftp.IFTPDirectoryPublisher"
+      />
+
 </configure>

Modified: Zope3/trunk/src/zope/app/ftp/tests/test_ftpview.py
===================================================================
--- Zope3/trunk/src/zope/app/ftp/tests/test_ftpview.py	2005-10-17 15:57:08 UTC (rev 39486)
+++ Zope3/trunk/src/zope/app/ftp/tests/test_ftpview.py	2005-10-17 17:04:17 UTC (rev 39487)
@@ -20,6 +20,7 @@
 from unittest import TestCase, TestSuite, main, makeSuite
 
 from zope.interface import implements
+from zope.security.checker import defineChecker, NamesChecker
 
 from zope.app.testing import ztapi
 from zope.app.filerepresentation.interfaces import IReadFile, IWriteFile
@@ -112,12 +113,21 @@
 
     def setUp(self):
         super(Test, self).setUp()
+
+        ## Make objects
+        filechecker = NamesChecker(['write', 'read'])
+        dirchecker = NamesChecker(['__setitem__', 'get'])
+        defineChecker(File, filechecker)
+        defineChecker(Directory, dirchecker)
+
         root = Directory()
         root['test'] = Directory()
         root['test2'] = Directory()
         root['f'] = File('contents of\nf')
         root['g'] = File('contents of\ng')
         self.__view = FTPView(root, None)
+
+        ## declare adapters
         ztapi.provideAdapter(IContainer, IContainerItemRenamer,
             ContainerItemRenamer)
 
@@ -258,6 +268,11 @@
         self.assert_(self.__view.writable('notthere'))
         self.assert_(not self.__view.writable('test'))
 
+    def test_readable(self):
+        self.assert_(self.__view.readable('f'))
+        self.assert_(not self.__view.readable('notthere'))
+        self.assert_(self.__view.readable('test'))
+
 def test_suite():
     return TestSuite((
         makeSuite(Test),

Modified: Zope3/trunk/src/zope/app/twisted/ftp/ftp.py
===================================================================
--- Zope3/trunk/src/zope/app/twisted/ftp/ftp.py	2005-10-17 15:57:08 UTC (rev 39486)
+++ Zope3/trunk/src/zope/app/twisted/ftp/ftp.py	2005-10-17 17:04:17 UTC (rev 39487)
@@ -50,6 +50,50 @@
         self.buffer.append(bytes)
 
 
+class ReadFileObj(object):
+    implements(ftp.IReadFile)
+
+    def __init__(self, fs, path):
+        self.fs_access = fs
+        self.path = path
+
+    def send(self, consumer):
+        def failed(failure):
+            consumer.unregisterProducer()
+
+        def success(passthrough):
+            consumer.unregisterProducer()
+
+        d = threads.deferToThread(self.fs_access.readfile, self.path, consumer)
+        d.addCallback(success)
+        d.addErrback(failed)
+
+        return d
+
+
+class WriteFileObj(object):
+    implements(ftp.IWriteFile)
+
+    def __init__(self, fs, path):
+        self.fs_access = fs
+        self.path = path
+
+    def receive(self):
+        def accessok(result, fs):
+            if not result:
+                raise ftp.PermissionDeniedError(self.path)
+            return ConsumerObject(fs, self.path)
+
+        def failure(failure):
+            raise ftp.PermissionDeniedError(self.path)
+
+        d = threads.deferToThread(self.fs_access.writable, self.path)
+        d.addCallback(accessok, self.fs_access)
+        d.addErrback(failure)
+
+        return d
+
+
 class ZopeFTPShell(object):
     """An abstraction of the shell commands used by the FTP protocol
     for a given user account
@@ -128,7 +172,6 @@
 
     def _stat(self, path, keys):
         if self.fs_access.type(path) == 'd':
-            import pdb; pdb.set_trace()
             raise ftp.WrongFiletype()
         result = self._gotlisting(self.fs_access.lsinfo(path), keys)
         return result[1]
@@ -201,37 +244,36 @@
             ret |= 0040000
         return ret
 
-    def send(self, path, consumer):
-        def finished(result, consumer):
-            consumer.transport.loseConnection()
+    def openForReading(self, path):
+        p = self._path(path)
 
-        def failed(failure, consumer):
-            consumer.transport.loseConnection()
-            if failure.type is NotFound:
-                raise ftp.FileNotFoundError(self._path(path))
-            ## Unauthorized error - is there any other errors I should
-            ## be catching.
-            raise ftp.PermissionDeniedError(self._path(path))
+        def succeed(result):
+            if not result:
+                raise ftp.PermissionDeniedError(p)
+            return ReadFileObj(self.fs_access, p)
 
-        p = self._path(path)
-        d = threads.deferToThread(self.fs_access.readfile, p, consumer)
-        d.addCallback(finished, consumer)
-        d.addErrback(failed, consumer)
+        def failed(failure):
+            raise ftp.PermissionDeniedError(p)
+
+        d = threads.deferToThread(self.fs_access.readable, p)
+        d.addCallback(succeed)
+        d.addErrback(failed)
+
         return d
 
-    def receive(self, path):
-        def accessok(result, fs):
-            if not result:
-                raise ftp.PermissionDeniedError(self._path(path))
-            return ConsumerObject(fs, p)
+    def openForWriting(self, path):
+        p = self._path(path)
 
-        def failure(result):
-            ## XXX - should be a better exception
-            raise ftp.PermissionDeniedError(path)
+        def succeed(result):
+            if result:
+                return WriteFileObj(self.fs_access, p)
+            raise ftp.PermissionDeniedError(p)
 
-        p = self._path(path)
+        def failed(failure):
+            raise ftp.PermissionDeniedError(p)
+
         d = threads.deferToThread(self.fs_access.writable, p)
-        d.addCallback(accessok, self.fs_access)
-        d.addErrback(failure)
+        d.addCallback(succeed)
+        d.addErrback(failed)
 
         return d

Modified: Zope3/trunk/src/zope/app/twisted/ftp/test/test_zope_ftp.py
===================================================================
--- Zope3/trunk/src/zope/app/twisted/ftp/test/test_zope_ftp.py	2005-10-17 15:57:08 UTC (rev 39486)
+++ Zope3/trunk/src/zope/app/twisted/ftp/test/test_zope_ftp.py	2005-10-17 17:04:17 UTC (rev 39487)
@@ -279,8 +279,14 @@
         filenames.sort()
         self.assertEqual([self.filename], filenames)
 
+    def testRETR_wo_Permission(self):
+        self._michaelLogin()
+
         downloader = self._makeDataConnection()
         d = self.client.queueStringCommand('RETR %s' % self.filename)
         failureResponseLines = self._waitForCommandFailure(d)
-        self.failUnless(failureResponseLines[-1].startswith('426'),
-                        "Response didn't start with 426 i.e. data connection closed error")
+        self.failUnless(failureResponseLines[-1].startswith('550'),
+                        "Response didn't start with 550: %r" %
+                        failureResponseLines[-1])
+        if downloader.transport.connected:
+            downloader.transport.loseConnection()

Modified: Zope3/trunk/src/zope/app/twisted/ftp/tests/demofs.py
===================================================================
--- Zope3/trunk/src/zope/app/twisted/ftp/tests/demofs.py	2005-10-17 15:57:08 UTC (rev 39486)
+++ Zope3/trunk/src/zope/app/twisted/ftp/tests/demofs.py	2005-10-17 17:04:17 UTC (rev 39487)
@@ -294,6 +294,19 @@
         f = d[name]
         return f.type == 'f' and f.accessable(self.user, write)
 
+    def readable(self, path):
+        path, name = posixpath.split(path)
+        try:
+            d = self.getdir(path)
+        except OSError:
+            return False
+
+        if name not in d:
+            return False
+
+        f = d[name]
+        return f.type == 'f' and f.accessable(self.user, read)
+
 ## class DemoFileSystemAccess(object):
 ##     __doc__ = IFileSystemAccess.__doc__
 

Modified: Zope3/trunk/src/zope/app/twisted/ftp/utils.py
===================================================================
--- Zope3/trunk/src/zope/app/twisted/ftp/utils.py	2005-10-17 15:57:08 UTC (rev 39486)
+++ Zope3/trunk/src/zope/app/twisted/ftp/utils.py	2005-10-17 17:04:17 UTC (rev 39487)
@@ -139,6 +139,9 @@
         'See IWriteFileSystem'
         return self._execute(path, 'writable')
 
+    def readable(self, path):
+        return self._execute(path, 'readable')
+
     def _execute(self, path, command, split=True, **kw):
         env = {}
         env.update(kw)

Modified: Zope3/trunk/src/zope/app/twisted/interfaces.py
===================================================================
--- Zope3/trunk/src/zope/app/twisted/interfaces.py	2005-10-17 15:57:08 UTC (rev 39486)
+++ Zope3/trunk/src/zope/app/twisted/interfaces.py	2005-10-17 17:04:17 UTC (rev 39487)
@@ -255,3 +255,9 @@
         exist but its directory is writable.
 
         """
+
+    def readable(path):
+        """Return boolean indicating whether a file at path is readable.
+
+        Return False if the file doesn't exist.
+        """



More information about the Zope3-Checkins mailing list