[Zope3-checkins] CVS: Zope3/src/zope/server/ftp - README.txt:1.1 logger.py:1.1 publisher.py:1.1 server.py:1.1 commonftpactivitylogger.py:NONE ftpserver.py:NONE ftpserverchannel.py:NONE ftpstatusmessages.py:NONE osemulators.py:NONE passiveacceptor.py:NONE publisherfilesystemaccess.py:NONE publisherftpserver.py:NONE publisherftpserverchannel.py:NONE recvchannel.py:NONE testfilesystemaccess.py:NONE xmitchannel.py:NONE

Jim Fulton jim@zope.com
Mon, 3 Feb 2003 10:09:03 -0500


Update of /cvs-repository/Zope3/src/zope/server/ftp
In directory cvs.zope.org:/tmp/cvs-serv15846/src/zope/server/ftp

Added Files:
	README.txt logger.py publisher.py server.py 
Removed Files:
	commonftpactivitylogger.py ftpserver.py ftpserverchannel.py 
	ftpstatusmessages.py osemulators.py passiveacceptor.py 
	publisherfilesystemaccess.py publisherftpserver.py 
	publisherftpserverchannel.py recvchannel.py 
	testfilesystemaccess.py xmitchannel.py 
Log Message:
Refactored the ftp framework to make it much simpler, less general,
and easier to maintain.  This included ripping out the vfs framework.


=== Added File Zope3/src/zope/server/ftp/README.txt ===
FTP Framework

  This file contains documentation on the FTP server
  framework.

  The core server is implemented in server.py. This relies on a
  file-system abstraction, defined in zope.server.interface.ft.

  The publisher module provides the connection to the object
  publsihing system by providing a file-system implementation that
  delegates file-system operations to objects through the publisher.


=== Added File Zope3/src/zope/server/ftp/logger.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: logger.py,v 1.1 2003/02/03 15:08:55 jim Exp $
"""

import time
import sys

from zope.server.logger.filelogger import FileLogger
from zope.server.logger.resolvinglogger import ResolvingLogger
from zope.server.logger.unresolvinglogger import UnresolvingLogger

class CommonFTPActivityLogger:
    """Outputs hits in common HTTP log format.
    """

    def __init__(self, logger_object=None, resolver=None):
        if logger_object is None:
            logger_object = FileLogger(sys.stdout)

        if resolver is not None:
            self.output = ResolvingLogger(resolver, logger_object)
        else:
            self.output = UnresolvingLogger(logger_object)


    def log(self, task):
        """
        Receives a completed task and logs it in the
        common log format.
        """

        now = time.localtime(time.time())

        message = '%s [%s] "%s %s"' % (task.channel.username,
                                       time.strftime('%Y/%m/%d %H:%M', now),
                                       task.m_name[4:].upper(),
                                       task.channel.cwd,
                                       )

        self.output.logRequest('127.0.0.1', message)


=== Added File Zope3/src/zope/server/ftp/publisher.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: publisher.py,v 1.1 2003/02/03 15:08:55 jim Exp $
"""

import re
import stat
import time
import posixpath

from cStringIO import StringIO

from zope.server.interfaces.ftp import IFileSystem
from zope.server.interfaces.ftp import IFileSystemAccess

from zope.server.ftp.server import FTPServer
from zope.publisher.publish import publish


class PublisherFileSystem:
    """Generic Publisher FileSystem implementation.
    """

    __implements__ = IFileSystem

    def __init__ (self, credentials, request_factory):
        self.credentials = credentials
        self.request_factory = request_factory

    def type(self, path):
        if path == '/':
            return 'd'

        return self._execute(path, 'type')

    def names(self, path, filter=None):
        return self._execute(path, 'names', split=False, filter=filter)

    def ls(self, path, filter=None):
        return self._execute(path, 'ls', split=False, filter=filter)

    def readfile(self, path, outstream, start=0, end=None):
        return self._execute(path, 'readfile', 
                             outstream=outstream, start=start, end=end)

    def lsinfo(self, path):
        return self._execute(path, 'lsinfo')

    def mtime(self, path):
        return self._execute(path, 'mtime')

    def size(self, path):
        return self._execute(path, 'size')

    def mkdir(self, path):
        return self._execute(path, 'mkdir')

    def remove(self, path):
        return self._execute(path, 'remove')

    def rmdir(self, path):
        return self._execute(path, 'rmdir')

    def rename(self, old, new):
        'See IWriteFileSystem'
        old = self._translate(old)
        new = self._translate(new)
        path0, old = posixpath.split(old)
        path1, new = posixpath.split(new)
        assert path0 == path1
        return self._execute(path0, 'rename', split=False, old=old, new=new)

    def writefile(self, path, instream, start=None, end=None, append=False):
        'See IWriteFileSystem'
        return self._execute(
            path, 'writefile',
            instream=instream, start=start, end=end, append=append)

    def writable(self, path):
        'See IWriteFileSystem'
        return self._execute(path, 'writable')

    def _execute(self, path, command, split=True, **kw):
        env = {}
        env.update(kw)
        env['command'] = command

        path = self._translate(path)

        if split:
            env['path'], env['name'] = posixpath.split(path)
        else:
            env['path'] = path
            
        env['credentials'] = self.credentials
        # NoOutput avoids creating a black hole.
        request = self.request_factory(StringIO(''), NoOutput(), env)

        # Note that publish() calls close() on request, which deletes the
        # response from the request, so that we need to keep track of it.
        response = request.response
        publish(request)
        return response.getResult()

    def _translate (self, path):
        # Normalize
        path = posixpath.normpath(path)
        if path.startswith('..'):
            # Someone is trying to get lower than the permitted root.
            # We just ignore it.
            path = '/'
        return path

class NoOutput:
    """An output stream lookalike that warns you if you try to
    dump anything into it."""

    def write(self, data):
        raise RuntimeError, "Not a writable stream"

    def flush(self):
        pass

    close = flush

class PublisherFTPServer(FTPServer):
    """Generic FTP Server"""

    def __init__(self, request_factory, name, ip, port, *args, **kw):
        fs_access = PublisherFileSystemAccess(request_factory)
        super(PublisherFTPServer, self).__init__(ip, port, fs_access,
                                                 *args, **kw)

class PublisherFileSystemAccess:

    __implements__ = IFileSystemAccess

    def __init__(self, request_factory):
        self.request_factory = request_factory

    def authenticate(self, credentials):
        # We can't actually do any authentication initially, as the
        # user may not be defined at the root.
        pass
    
    def open(self, credentials):
        return PublisherFileSystem(credentials, self.request_factory)



=== Added File Zope3/src/zope/server/ftp/server.py === (796/896 lines abridged)
##############################################################################
#
# 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: server.py,v 1.1 2003/02/03 15:08:55 jim Exp $
"""

import posixpath
import socket
from datetime import date, timedelta
from getopt import getopt, GetoptError
import asyncore
from zope.exceptions import Unauthorized
from zope.server.serverbase import ServerBase
from zope.server.interfaces.ftp import IFileSystemAccess
from zope.server.linereceiver.lineserverchannel import LineServerChannel
from zope.server.interfaces.ftp import IFTPCommandHandler
from zope.server.serverchannelbase import ChannelBaseClass
from zope.server.buffers import OverflowableBuffer
from zope.server.interfaces import ITask

status_messages = {
    'OPEN_DATA_CONN'   : '150 Opening %s mode data connection for file list',
    'OPEN_CONN'        : '150 Opening %s connection for %s',
    'SUCCESS_200'      : '200 %s command successful.',
    'TYPE_SET_OK'      : '200 Type set to %s.',
    'STRU_OK'          : '200 STRU F Ok.',
    'MODE_OK'          : '200 MODE S Ok.',
    'FILE_DATE'        : '213 %4d%02d%02d%02d%02d%02d',
    'FILE_SIZE'        : '213 %d Bytes',
    'HELP_START'       : '214-The following commands are recognized',
    'HELP_END'         : '214 Help done.',
    'SERVER_TYPE'      : '215 %s Type: %s',
    'SERVER_READY'     : '220 %s FTP server (Zope Async/Thread V0.1) ready.',
    'GOODBYE'          : '221 Goodbye.',
    'SUCCESS_226'      : '226 %s command successful.',
    'TRANS_SUCCESS'    : '226 Transfer successful.',
    'PASV_MODE_MSG'    : '227 Entering Passive Mode (%s,%d,%d)',
    'LOGIN_SUCCESS'    : '230 Login Successful.',

[-=- -=- -=- 796 lines omitted -=- -=- -=-]

        pass

    def handle_comm_error(self):
        self.close('TRANSFER_ABORTED')

    def close(self, *reply_args):
        try:
            c = self.control_channel
            if c is not None:
                self.control_channel = None
                if not reply_args:
                    if not len(self.outbuf):
                        # All data transferred
                        if not self.opened:
                            # Zero-length file
                            self._open()
                        reply_args = ('TRANS_SUCCESS',)
                    else:
                        # Not all data transferred
                        reply_args = ('TRANSFER_ABORTED',)
                c.notifyClientDCClosing(*reply_args)
        finally:
            if self.socket is not None:
                # XXX asyncore.dispatcher.close() doesn't like socket == None
                ChannelBaseClass.close(self)


class ApplicationXmitStream:
    """Provide stream output, remapping close() to close_when_done().
    """

    def __init__(self, xmit_channel):
        self.write = xmit_channel.write
        self.flush = xmit_channel.flush
        self.close = xmit_channel.close_when_done


class FTPServer(ServerBase):
    """Generic FTP Server"""

    channel_class = FTPServerChannel
    SERVER_IDENT = 'zope.server.ftp'


    def __init__(self, ip, port, fs_access, *args, **kw):

        assert IFileSystemAccess.isImplementedBy(fs_access)
        self.fs_access = fs_access

        super(FTPServer, self).__init__(ip, port, *args, **kw)

=== Removed File Zope3/src/zope/server/ftp/commonftpactivitylogger.py ===

=== Removed File Zope3/src/zope/server/ftp/ftpserver.py ===

=== Removed File Zope3/src/zope/server/ftp/ftpserverchannel.py ===

=== Removed File Zope3/src/zope/server/ftp/ftpstatusmessages.py ===

=== Removed File Zope3/src/zope/server/ftp/osemulators.py ===

=== Removed File Zope3/src/zope/server/ftp/passiveacceptor.py ===

=== Removed File Zope3/src/zope/server/ftp/publisherfilesystemaccess.py ===

=== Removed File Zope3/src/zope/server/ftp/publisherftpserver.py ===

=== Removed File Zope3/src/zope/server/ftp/publisherftpserverchannel.py ===

=== Removed File Zope3/src/zope/server/ftp/recvchannel.py ===

=== Removed File Zope3/src/zope/server/ftp/testfilesystemaccess.py ===

=== Removed File Zope3/src/zope/server/ftp/xmitchannel.py ===