[Zope-Checkins] CVS: Zope3/lib/python/Zope/Server/FTP - FTPStatusMessages.py:1.1.2.1 PassiveAcceptor.py:1.1.2.1 RecvChannel.py:1.1.2.1 XmitChannel.py:1.1.2.1 FTPCommandParser.py:1.1.2.2

Stephan Richter srichter@cbu.edu
Wed, 3 Apr 2002 04:48:07 -0500


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

Modified Files:
      Tag: Zope3-Server-Branch
	FTPCommandParser.py 
Added Files:
      Tag: Zope3-Server-Branch
	FTPStatusMessages.py PassiveAcceptor.py RecvChannel.py 
	XmitChannel.py 
Log Message:
More FTP server work done. It now can at least display a list of files in
a directory, however the simple FTP client in Unix still does not work.

I am getting closer though!


=== Added File Zope3/lib/python/Zope/Server/FTP/FTPStatusMessages.py ===
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
# 
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
# 
##############################################################################
"""

$Id: FTPStatusMessages.py,v 1.1.2.1 2002/04/03 09:48:06 srichter Exp $
"""


status_msgs = {
    150: ('Opening %s mode data connection for file list',
          'Opening %s connection for %s',),
    200: ('%s command successful.',
          'Type set to %s.',
          'STRU F Ok.',
          'MODE S Ok.',),
    213: ('%4d%02d%02d%02d%02d%02d', # A date
          '%d'), # Size
    214: ('-The following commands are recognized',
          ''),
    215: ('%s Type: %s',),  # Server Type
    221: ('Goodbye.',),
    226: ('%s command successful.',
          'Transfer successful.'),
    227: ('Entering Passive Mode (%s,%d,%d)',),
    230: ('Login Successful.',),
    250: ('%s command successful.',),
    257: ('%s command successful.',
          "'%s' is the current directory.",),
    331: ('Password required',),
    350: ('Restarting at %d. Send STORE or RETRIEVE to initiate transfer.',),
    425: ("Can't build data connection",),
    426: ('Connection closed; transfer aborted.',),
    500: ("'%s': command not understood.",),
    502: ("Unimplemented MODE type",),
    504: ('Byte size must be 8',
          'Unimplemented STRU type',),
    530: ("You are not authorized to perform the '%s' command",
          'Please log in with USER and PASS',
          'The username and password do not match.',),
    550: ('Could not list directory: %s',
          '%s: No such directory.',
          '%s: No such file.',
          '%s: Is not a file',
          'Error creating file.',
          'Error creating directory.',
          'Error deleting file.',
          'Error removing directory.'),
    553: ('Could not open file for reading: %s',
          'Could not open file for writing: %s',
          'Restart on STOR not yet supported',),

    599: ("Unknown type '%s'.",),    
    }



=== Added File Zope3/lib/python/Zope/Server/FTP/PassiveAcceptor.py ===
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
# 
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
# 
##############################################################################
"""

$Id: PassiveAcceptor.py,v 1.1.2.1 2002/04/03 09:48:06 srichter Exp $
"""

import asyncore
import socket

        
class PassiveAcceptor(asyncore.dispatcher):
    """This socket accepts a data connection, used when the server has
       been placed in passive mode.  Although the RFC implies that we
       ought to be able to use the same acceptor over and over again,
       this presents a problem: how do we shut it off, so that we are
       accepting connections only when we expect them?  [we can't]

       wuftpd, and probably all the other servers, solve this by
       allowing only one connection to hit this acceptor.  They then
       close it.  Any subsequent data-connection command will then try
       for the default port on the client side [which is of course
       never there].  So the 'always-send-PORT/PASV' behavior seems
       required.

       Another note: wuftpd will also be listening on the channel as
       soon as the PASV command is sent.  It does not wait for a data
       command first.

       --- we need to queue up a particular behavior:
       1) xmit : queue up producer[s]
       2) recv : the file object

       It would be nice if we could make both channels the same.
       Hmmm.."""

    __implements__ = asyncore.dispatcher.__implements__

    ready = None
    
    def __init__ (self, control_channel):
        asyncore.dispatcher.__init__ (self)
        self.control_channel = control_channel
        self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
        # bind to an address on the interface that the
        # control connection is coming from.
        self.bind ( (self.control_channel.getsockname()[0], 0) )
        self.addr = self.getsockname()
        self.listen(1)
        

    def log (self, *ignore):
        pass

        
    def handle_accept (self):
        conn, addr = self.accept()
        dc = self.control_channel.client_dc
        if dc is not None:
            dc.set_socket(conn)
            dc.addr = addr
            dc.connected = 1
            self.control_channel.passive_acceptor = None
        else:
            self.ready = conn, addr
        self.close()


=== Added File Zope3/lib/python/Zope/Server/FTP/RecvChannel.py ===
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
# 
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
# 
##############################################################################
"""

$Id: RecvChannel.py,v 1.1.2.1 2002/04/03 09:48:06 srichter Exp $
"""
import asyncore

        
class RecvChannel(asyncore.dispatcher):
    """ """

    def __init__ (self, channel, client_addr, fd):
        self.channel = channel
        self.client_addr = client_addr
        self.fd = fd
        asyncore.dispatcher.__init__ (self)
        self.bytes_in = counter()

        
    def log (self, *ignore):
        pass

        
    def handle_connect (self):
        pass

        
    def writable (self):
        return 0

        
    def recv (*args):
        result = apply (asyncore.dispatcher.recv, args)
        self = args[0]
        self.bytes_in.increment(len(result))
        return result

        
    buffer_size = 8192

    
    def handle_read (self):
        block = self.recv (self.buffer_size)
        if block:
            try:
                self.fd.write (block)
            except IOError:
                self.log_info ('got exception writing block...', 'error')

                
    def handle_close (self):
        s = self.channel.server
        s.total_files_in.increment()
        s.total_bytes_in.increment(self.bytes_in.as_long())
        self.fd.close()
        self.channel.reply(226, 1)
        self.close()


=== Added File Zope3/lib/python/Zope/Server/FTP/XmitChannel.py ===
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
# 
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
# 
##############################################################################
"""

$Id: XmitChannel.py,v 1.1.2.1 2002/04/03 09:48:06 srichter Exp $
"""

import asynchat


class XmitChannel(asynchat.async_chat, object):

    # for an ethernet, you want this to be fairly large, in fact, it
    # _must_ be large for performance comparable to an ftpd.  [64k] we
    # ought to investigate automatically-sized buffers...
    ac_out_buffer_size = 16384
    
    bytes_out = 0
    
    def __init__ (self, channel, client_addr=None):
        self.channel = channel
        self.client_addr = client_addr
        super(XmitChannel, self).__init__()
        
        
    def log (*args):
        pass

        
    def readable (self):
        return not self.connected

        
    def writable (self):
        return 1

        
    def send (self, data):
        result = super(XmitChannel, self).send(data)
        self.bytes_out = self.bytes_out + result
        return result

        
    def handle_error (self):
        # usually this is to catch an unexpected disconnect.
        # XXX: Helpfule for debugging
        import traceback
        traceback.print_exc()
        self.log_info ('unexpected disconnect on data xmit channel', 'error')
        try:
            self.close()
        except:
            pass
            
    # TODO: there's a better way to do this.  we need to be able to
    # put 'events' in the producer fifo.  to do this cleanly we need
    # to reposition the 'producer' fifo as an 'event' fifo.
    
    # dummy function to suppress warnings caused by some FTP clients
    def handle_connect(self):
        pass

            
    def close (self):
        c = self.channel
        s = c.server
        c.client_dc = None
        s.total_files_out.increment()
        s.total_bytes_out.increment (self.bytes_out)
        if not len(self.producer_fifo):
            c.reply(226, 1)
        elif not c.closed:
            c.reply(426)
        del c
        del s
        del self.channel
        asynchat.async_chat.close(self)


=== Zope3/lib/python/Zope/Server/FTP/FTPCommandParser.py 1.1.2.1 => 1.1.2.2 ===
             self.completed = 1
             line = self.inbuf.strip()
+            print line
             self.parseLine(line)
             return len(s)