[Zope-Checkins] CVS: Zope3/lib/python/Zope/Server/FTP - CommonFTPActivityLogger.py:1.1.2.1 FTPCommandParser.py:1.1.2.1 FTPServer.py:1.1.2.1 FTPServerChannel.py:1.1.2.1 FTPTask.py:1.1.2.1 IFTPCommandHandler.py:1.1.2.1 PublisherFTPTask.py:1.1.2.1 __init__.py:1.1.2.1

Stephan Richter srichter@cbu.edu
Tue, 2 Apr 2002 00:08:07 -0500


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

Added Files:
      Tag: Zope3-Server-Branch
	CommonFTPActivityLogger.py FTPCommandParser.py FTPServer.py 
	FTPServerChannel.py FTPTask.py IFTPCommandHandler.py 
	PublisherFTPTask.py __init__.py 
Log Message:
Issue 53: Comment

- Created a bunch of interfaces that let us know what is going on.
- Split, updated and zopefied the Logger code.
- Reorganized dir structure in Zope.Server
- HTTP component split up in files (HTTP server works)
- Inserted Shane's skeleton FTP code (since I like his better than mine)
- Took a first cut at the Virtual File System (VFS) by copying and updating
  medusa'a old filesys.py code.



=== Added File Zope3/lib/python/Zope/Server/FTP/CommonFTPActivityLogger.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: CommonFTPActivityLogger.py,v 1.1.2.1 2002/04/02 05:08:06 srichter Exp $
"""

class CommonActivityLogger:
    """This logger's output is 

    """
    pass


=== Added File Zope3/lib/python/Zope/Server/FTP/FTPCommandParser.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: FTPCommandParser.py,v 1.1.2.1 2002/04/02 05:08:06 srichter Exp $
"""

from Zope.Server.IStreamConsumer import IStreamConsumer


class FTPCommandParser:
    """FTP Command parser. Arguments are left alone for now."""

    __implements__ = IStreamConsumer


    # See Zope.Server.IStreamConsumer.IStreamConsumer
    completed = 0
    inbuf = ''
    cmd = ''
    args = ''
    empty = 0


    max_line_length = 1024  # Not a hard limit


    def __init__(self, adj):
        """
        adj is an Adjustments object.
        """
        self.adj = adj


    ############################################################
    # Implementation methods for interface
    # Zope.Server.IStreamConsumer

    def received(self, data):
        'See Zope.Server.IStreamConsumer.IStreamConsumer'
        if self.completed:
            return 0  # Can't consume any more.
        pos = data.find('\n')
        datalen = len(data)
        if pos < 0:
            self.inbuf = self.inbuf + data
            if len(self.inbuf) > self.max_line_length:
                # Don't accept any more.
                self.completed = 1
            return datalen
        else:
            # Line finished.
            s = data[:pos + 1]
            self.inbuf = self.inbuf + s
            self.completed = 1
            line = self.inbuf.strip()
            self.parseLine(line)
            return len(s)

    #
    ############################################################


    def parseLine(self, line):
        parts = line.split(' ', 1)
        if len(parts) == 2:
            self.cmd, self.args = parts
        else:
            self.cmd = parts[0]


=== Added File Zope3/lib/python/Zope/Server/FTP/FTPServer.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: FTPServer.py,v 1.1.2.1 2002/04/02 05:08:06 srichter Exp $
"""

from FTPServerChannel import FTPServerChannel
from ServerBase import ServerBase


class FTPServer(ServerBase):
    """Generic FTP Server"""
    channel_class = FTPServerChannel
    SERVER_IDENT = 'Zope.Server.FTPServer'


if __name__ == '__main__':
    from TaskThreads import ThreadedTaskDispatcher
    td = ThreadedTaskDispatcher()
    td.setThreadCount(4)
    FTPServer('', 8021, task_dispatcher=td)
    try:
        while 1:
            asyncore.poll(5)
            print 'active channels:', FTPServerChannel.active_channels
    except KeyboardInterrupt:
        print 'shutting down...'
        td.shutdown()


=== Added File Zope3/lib/python/Zope/Server/FTP/FTPServerChannel.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: FTPServerChannel.py,v 1.1.2.1 2002/04/02 05:08:06 srichter Exp $
"""

from IFTPCommandHandler import IFTPCommandHandler 
from ServerBase import ServerChannelBase


class FTPServerChannel(ServerChannelBase):
    """The FTP Server Channel represents a connection to a particular
       client. We can therefore store information here."""

    __implements__ = IFTPCommandHandler

    task_class = FTPTask
    parser_class = FTPCommandParser


    active_channels = {}        # Class-specific channel tracker
    next_channel_cleanup = [0]  # Class-specific cleanup time


    user_name = None
    user_password = None
    ascii_mode = 0
    passive_mode = 0


    def process_request(self, command):
        """Processes an FTP command.

        Some commands use an alternate thread.
        """
        assert isinstance(command, FTPCommandParser)
        cmd = command.cmd
        m = 'do_' + cmd.lower()
        if hasattr(self, m):
            # Quick processing
            getattr(self, m)(command.args)
        else:
            # Process in another thread.
            task = self.task_class(self, command, m)
            if hasattr(task, m):
                self.set_sync()
                self.server.addTask(task)
            else:
                # TODO: reply "command unknown"
                pass


    def do_user(self, args):
        self.user_name = args
        self.reply(200)  # Or whatever the code should be


    def do_pass(self, args):
        self.user_password = args
        self.reply(200)  # Or whatever the code should be


=== Added File Zope3/lib/python/Zope/Server/FTP/FTPTask.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: FTPTask.py,v 1.1.2.1 2002/04/02 05:08:06 srichter Exp $
"""

import socket
from Zope.Server.ITask import ITask


class FTPTask:
    """
    """

    __implements__ = ITask


    def __init__(self, channel, command, m_name):
        self.channel = channel
        self.m_name = m_name
        self.args = command.args


    ############################################################
    # Implementation methods for interface
    # Zope.Server.ITask

    def service(self):
        """Called to execute the task.
        """
        try:
            try:
                self.start()
                getattr(self, self.m_name)(self.args)
                self.finish()
            except socket.error:
                self.close_on_finish = 1
                if self.channel.adj.log_socket_errors:
                    raise
        finally:
            self.channel.end_task(self.close_on_finish)


    def cancel(self):
        'See Zope.Server.ITask.ITask'
        self.channel.close_when_done()


    def defer(self):
        'See Zope.Server.ITask.ITask'
        pass

    #
    ############################################################

    def start(self):
        now = time.time()
        self.start_time = now


    def finish(self):
        hit_log = self.channel.server.hit_log
        if hit_log is not None:
            hit_log.log(self)


=== Added File Zope3/lib/python/Zope/Server/FTP/IFTPCommandHandler.py ===


from Interface import Interface

class IFTPCommandHandler(Interface):
    """This interface defines all the FTP commands that are supported by the
       server.

       Every command takes the command line as first arguments, since it is
       responsible 
    """

    def cmd_abor():
        """Abort operation. No read access required.
        """

    def cmd_appe(filename, mode):
        """Append to a file. Write access required.
        """

    def cmd_cdup():
        """Change to parent of current working directory.
        """

    def cmd_cwd():
        """Change working directory.
        """

    def cmd_dele():
        """Delete a file. Write access required.
        """
        
    def cmd_help():
        """Give help information. No read access required.
        """

    def cmd_list(path, match_pattern, long=1, recursive=0):
        """Give list files in a directory.
        """

    def cmd_mdtm(filename):
        """Show last modification time of file.

           Example output: 213 19960301204320
        """

    def cmd_mkd(path):
        """Make a directory. Write access required.
        """

    def cmd_mode(type):
        """Set file transfer mode.  No read access required. Obselete.
        """

    def cmd_nlst(path, match_pattern, long=0, recursive=0):
        """Give name list of files in directory.
        """

    def cmd_noop():
        """Do nothing. No read access required.
        """

    def cmd_pass(password):
        """Specify password.
        """

    def cmd_pasv():
        """Prepare for server-to-server transfer. No read access required.
        """

    def cmd_port(ip, port):
        """Specify data connection port. No read access required.
        """

    def cmd_pwd():
        """Print the current working directory.
        """

    def cmd_quit():
        """Terminate session. No read access required.
        """

    def cmd_rest(position):
        """Restart incomplete transfer.
        """

    def cmd_retr(filename):
        """Retrieve a file.
        """

    def cmd_rmd(path):
        """Remove a directory. Write access required.
        """

    def cmd_rnfr(filename):
        """Specify rename-from file name. Write access required.
        """

    def cmd_rnto(filename):
        """Specify rename-to file name. Write access required.
        """

    def cmd_size(filename):
        """Return size of file.
        """

    def cmd_stat(filename):
        """Return status of server. No read access required.
        """
        
    def cmd_stor(filename, mode):
        """Store a file. Write access required.
        """

    def cmd_stru(type):
        """Set file transfer structure. Obselete."""

    def cmd_syst():
        """Show operating system type of server system.

           No read access required.

           Replying to this command is of questionable utility,
           because this server does not behave in a predictable way
           w.r.t. the output of the LIST command.  We emulate Unix ls
           output, but on win32 the pathname can contain drive
           information at the front Currently, the combination of
           ensuring that os.sep == '/' and removing the leading slash
           when necessary seems to work.  [cd'ing to another drive
           also works]
        
           This is how wuftpd responds, and is probably the most
           expected.  The main purpose of this reply is so that the
           client knows to expect Unix ls-style LIST output.
        
           one disadvantage to this is that some client programs
           assume they can pass args to /bin/ls.  a few typical
           responses:
           
           215 UNIX Type: L8 (wuftpd)
           215 Windows_NT version 3.51
           215 VMS MultiNet V3.3
           500 'SYST': command not understood. (SVR4)
        """

    def cmd_type(type, byte_size):
        """Specify data transfer type. No read access required.
        """

    def cmd_user(username):
        """Specify user name. No read access required.
        """



# this is the command list from the wuftpd man page
# '!' requires write access
#
not_implemented_commands = {
        'acct':	'specify account (ignored)',
        'allo':	'allocate storage (vacuously)',
        'site':	'non-standard commands (see next section)',
        'stou':	'store a file with a unique name',	                    #!
        'xcup':	'change to parent of current working directory (deprecated)',
        'xcwd':	'change working directory (deprecated)',
        'xmkd':	'make a directory (deprecated)',	                    #!
        'xpwd':	'print the current working directory (deprecated)',
        'xrmd':	'remove a directory (deprecated)',	                    #!
}


=== Added File Zope3/lib/python/Zope/Server/FTP/PublisherFTPTask.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: PublisherFTPTask.py,v 1.1.2.1 2002/04/02 05:08:06 srichter Exp $
"""

from FTPTask import FTPTask
from Zope.Publisher.Publish import publish


class PublisherFTPTask(FTPTask):
    """ """

    __implements__ = FTPTask.__implements__
    

    def execute(self):
        """ """
        server = self.channel.server
        env = self.create_environment()
        instream = self.request_data.getBodyStream()

        request = server.request_factory(instream, self, env)
        publish(request)


    def create_environment(self):
        request_data = self.request_data
        channel = self.channel
        server = channel.server

        # This should probably change to reflect calling the FileSystem
        # methods
        env = {'command': request_data.command
               'args': request_data.args
               }


        return env


=== Added File Zope3/lib/python/Zope/Server/FTP/__init__.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: __init__.py,v 1.1.2.1 2002/04/02 05:08:06 srichter Exp $
"""