[Zope3-checkins] CVS: Zope3/lib/python/Zope/Server/FTP - FTPServerChannel.py:1.5 FTPStatusMessages.py:1.3 IFTPCommandHandler.py:1.3 OSEmulators.py:1.3

Stephan Richter srichter@cbu.edu
Fri, 20 Dec 2002 04:26:16 -0500


Update of /cvs-repository/Zope3/lib/python/Zope/Server/FTP
In directory cvs.zope.org:/tmp/cvs-serv31450/Zope/Server/FTP

Modified Files:
	FTPServerChannel.py FTPStatusMessages.py IFTPCommandHandler.py 
	OSEmulators.py 
Log Message:
Refactoring and fixing VFS and FTP

I am glad to make this commit that fixes up a lot of the FTP
implementation. I fixed the behavior of many of the FTP commands,
including LIST, SIZE, and CWD.

I moved the original VFSFile/DirectoryView into abstract classes and wrote
a special implementation for each content type, which makes the code much
more flexible.

Also I finally implemented a smart way of adding files via VFS through
file extension introspection, based on Jim's ExtensionViewName proposal.

I am adding documentation in the DevelCookbook right now and will later
add a README file.

TODOs:

- make VFS View names flexible, so that file extensions specify views.

- Simplify ZCML directives, so that one can add new extensions for Add views
  quicker. A solution might look like that:

      <vfs:view
          name=".dtml"
          for="Zope.App.OFS.Container.IAdding."
          factory=".DTMLPageAdd."
          permission="Zope.ManageContent">
        <vfs:extension name=".html" />
        <vfs:extension name=".xul" /> 
        <vfs:extension name=".xml" /> 
        ...
      </vfs:view>
			   
  This method would also be good for defining a default fiel extension.

- Show an object with its default file extension.
								     


=== Zope3/lib/python/Zope/Server/FTP/FTPServerChannel.py 1.4 => 1.5 ===
--- Zope3/lib/python/Zope/Server/FTP/FTPServerChannel.py:1.4	Mon Nov 11 09:55:37 2002
+++ Zope3/lib/python/Zope/Server/FTP/FTPServerChannel.py	Fri Dec 20 04:25:44 2002
@@ -124,8 +124,9 @@
 
     def cmd_cwd(self, args):
         'See Zope.Server.FTP.IFTPCommandHandler.IFTPCommandHandler'
+        fs = self._getFilesystem()
         path = self._generatePath(args)
-        if self._getFilesystem().exists(path):
+        if fs.exists(path) and fs.isdir(path):
             self.cwd = path
             self.reply('SUCCESS_250', 'CWD')
         else:
@@ -156,9 +157,14 @@
 
     def cmd_list(self, args, long=1):
         'See Zope.Server.FTP.IFTPCommandHandler.IFTPCommandHandler'
+        fs = self._getFilesystem()
+        path = self._generatePath(args)
+        if not fs.exists(path):
+            self.reply('ERR_NO_DIR_FILE', path)
+            return
         args = args.split()
         try:
-            s = self.getDirectoryList(args, long)
+            s = self.getList(args, long)
         except OSError, err:
             self.reply('ERR_NO_LIST', str(err))
             return
@@ -174,12 +180,13 @@
 
     def cmd_mdtm(self, args):
         'See Zope.Server.FTP.IFTPCommandHandler.IFTPCommandHandler'
+        fs = self._getFilesystem()
         # We simply do not understand this non-standard extension to MDTM
         if len(args.split()) > 1:
             self.reply('ERR_ARGS')
             return
         path = self._generatePath(args)
-        if not self._getFilesystem().isfile(path):
+        if not fs.exists(path) or not fs.isfile(path):
             self.reply('ERR_IS_NOT_FILE', path)
         else:
             mtime = time.gmtime(
@@ -249,7 +256,6 @@
 
     def cmd_port(self, args):
         'See Zope.Server.FTP.IFTPCommandHandler.IFTPCommandHandler'
-
         info = args.split(',')
         ip = '.'.join(info[:4])
         port = int(info[4])*256 + int(info[5])
@@ -274,11 +280,12 @@
 
     def cmd_retr(self, args):
         'See Zope.Server.FTP.IFTPCommandHandler.IFTPCommandHandler'
+        fs = self._getFilesystem()
         if not args:
             self.reply('CMD_UNKNOWN', 'RETR')
         path = self._generatePath(args)
 
-        if not self._getFilesystem().isfile(path):
+        if not fs.exists(path) or not fs.isfile(path):
             self.reply('ERR_IS_NOT_FILE', path)
             return
 
@@ -295,8 +302,7 @@
         outstream = ApplicationXmitStream(cdc)
 
         try:
-            self._getFilesystem().readfile(
-                path, mode, outstream, start)
+            fs.readfile(path, mode, outstream, start)
             cdc.close_when_done()
         except OSError, err:
             cdc.close('ERR_OPEN_READ', str(err))
@@ -357,7 +363,7 @@
         'See Zope.Server.FTP.IFTPCommandHandler.IFTPCommandHandler'
         path = self._generatePath(args)
         fs = self._getFilesystem()
-        if not fs.isfile(path):
+        if not fs.exists(path) or not fs.isfile(path):
             self.reply('ERR_NO_FILE', path)
         else:
             self.reply('FILE_SIZE', fs.stat(path)[stat.ST_SIZE])
@@ -471,17 +477,9 @@
         return self.passive_acceptor
 
 
-    def listdir(self, path, long=0):
-        """returns a string"""
-        path = self._generatePath(path)
-        file_list = self._getFilesystem().listdir(path, long)
-        if long:
-            file_list = map(ls_longify, file_list)
-        return ''.join(map(lambda line: line + '\r\n', file_list))
-
-
-    def getDirectoryList(self, args, long=0):
+    def getList(self, args, long=0):
         # we need to scan the command line for arguments to '/bin/ls'...
+        fs = self._getFilesystem()
         path_args = []
         for arg in args:
             if arg[0] != '-':
@@ -490,12 +488,20 @@
                 # ignore arguments
                 pass
         if len(path_args) < 1:
-            dir = '.'
+            path = '.'
+        else:
+            path = path_args[0]
+
+        path = self._generatePath(path)
+        if fs.isdir(path):
+            file_list = fs.listdir(path, long)
         else:
-            dir = path_args[0]
+            file_list = [ (posixpath.split(path)[1], fs.stat(path)) ]
+        # Make a pretty unix-like FTP output
+        if long:
+            file_list = map(ls_longify, file_list)
+        return ''.join(map(lambda line: line + '\r\n', file_list))
 
-        dir = self._generatePath(dir)
-        return self.listdir(dir, long)
 
 
     def connectDataChannel(self, cdc):


=== Zope3/lib/python/Zope/Server/FTP/FTPStatusMessages.py 1.2 => 1.3 ===
--- Zope3/lib/python/Zope/Server/FTP/FTPStatusMessages.py:1.2	Mon Jun 10 19:29:35 2002
+++ Zope3/lib/python/Zope/Server/FTP/FTPStatusMessages.py	Fri Dec 20 04:25:44 2002
@@ -54,9 +54,10 @@
                          "'%s' command",
     'LOGIN_REQUIRED'   : '530 Please log in with USER and PASS',
     'LOGIN_MISMATCH'   : '530 The username and password do not match.',
-    'ERR_NO_LIST'      : '550 Could not list directory: %s',
+    'ERR_NO_LIST'      : '550 Could not list directory or file: %s',
     'ERR_NO_DIR'       : '550 "%s": No such directory.',
     'ERR_NO_FILE'      : '550 "%s": No such file.',
+    'ERR_NO_DIR_FILE'  : '550 "%s": No such file or directory.',
     'ERR_IS_NOT_FILE'  : '550 "%s": Is not a file',
     'ERR_CREATE_FILE'  : '550 Error creating file.',
     'ERR_CREATE_DIR'   : '550 Error creating directory: %s',


=== Zope3/lib/python/Zope/Server/FTP/IFTPCommandHandler.py 1.2 => 1.3 ===
--- Zope3/lib/python/Zope/Server/FTP/IFTPCommandHandler.py:1.2	Mon Jun 10 19:29:35 2002
+++ Zope3/lib/python/Zope/Server/FTP/IFTPCommandHandler.py	Fri Dec 20 04:25:44 2002
@@ -35,7 +35,7 @@
         """
 
     def cmd_list(args):
-        """Give list files in a directory.
+        """Give list files in a directory or displays the info of one file.
         """
 
     def cmd_mdtm(args):


=== Zope3/lib/python/Zope/Server/FTP/OSEmulators.py 1.2 => 1.3 ===
--- Zope3/lib/python/Zope/Server/FTP/OSEmulators.py:1.2	Mon Jun 10 19:29:35 2002
+++ Zope3/lib/python/Zope/Server/FTP/OSEmulators.py	Fri Dec 20 04:25:44 2002
@@ -11,16 +11,16 @@
 # FOR A PARTICULAR PURPOSE.
 #
 ##############################################################################
-"""
+"""OS-Emulator Package
+
+Simulates OS-level directory listing output for *nix and MS-DOS (including
+Windows NT).  
 
 $Id$
 """
 
 import stat
-import time
-
-months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
-          'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
+import datetime
 
 mode_table = {
         '0':'---',
@@ -49,7 +49,7 @@
         dirchar = 'd'
     else:
         dirchar = '-'
-    date = ls_date (long(time.time()), stat_info[stat.ST_MTIME])
+    date = ls_date(datetime.datetime.now(), stat_info[stat.ST_MTIME])
     return '%s%s %3d %-8s %-8s %8d %s %s' % (
             dirchar,
             mode,
@@ -62,7 +62,7 @@
             )
 
 
-def ls_date (now, t):
+def ls_date(now, t):
     """Emulate the 'ls' command's date field.  It has two formats.
        If the date is more than 180 days in the past or future, then
        it's like this:
@@ -70,25 +70,10 @@
        otherwise, it looks like this:
          Oct 19 17:33
     """
-    try:
-        info = time.localtime(t)
-    except:
-        info = time.localtime(0)
-
-    # 15,600,000 == 86,400 * 180
-    if abs((now - t) > 15600000):
-        return '%s %2d %5d' % (
-                months[info[1]-1],
-                info[2],
-                info[0]
-                )
+    if abs((now - t).days) > 180:
+        return t.strftime('%b %d, %Y')
     else:
-        return '%s %2d %02d:%02d' % (
-                months[info[1]-1],
-                info[2],
-                info[3],
-                info[4]
-                )
+        return t.strftime('%b %d %H:%M')
 
 
 def msdos_longify((file, stat_info)):
@@ -104,17 +89,7 @@
 
 
 def msdos_date(t):
-    try:
-        info = time.gmtime(t)
-    except:
-        info = time.gmtime(0)
-
-    # year, month, day, hour, minute, second, ...
-    if info[3] > 11:
-        merid = 'PM'
-        info[3] = info[3] - 12
-    else:
-        merid = 'AM'
-
-    return '%02d-%02d-%02d  %02d:%02d%s' % (
-            info[1], info[2], info[0]%100, info[3], info[4], merid )
+    """Emulate MS-DOS 'dir' command. Example:
+         09-19-95 05:33PM
+    """
+    return t.strftime('%m-%d-%y %H:%M%p')