[Zope3-checkins] CVS: Zope3/src/zope/app/ftp - README.txt:1.1 __init__.py:1.1 configure.zcml:1.1

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


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

Added Files:
	README.txt __init__.py configure.zcml 
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/app/ftp/README.txt ===

Here are some notes about the way FTP actually works in zope.

  - The FTP server implementation is in zope.server.ftp.server. See
    the README.txt file there.

    The publisher-based ftp server is in zope.server.ftp.publisher.

    The FTP server gets wired up with a request factory that creates
    ftp requests with ftp publication objects.

  - The ftp request object is defined in zope.publisher.ftp.

  - The ftp publication object is defined in zope.app.publication.ftp.

    The publication object gets views to handle ftp requests.  It
    looks up a separate view for each method defined in
    zope.publisher.interfaces.ftp.IFTPPublisher.

    We provide a single class here that implements all of these
    methods. 

    The view, in turn, uses adapters for the IReadFile, IWriteFile,
    IReadDirectory, IWriteDirectory, IFileFactory, and
    IDirectoryFactory, defined in zope.app.interfaces.file.



=== Added File Zope3/src/zope/app/ftp/__init__.py ===
##############################################################################
# Copyright (c) 2003 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.
##############################################################################
"""Views implementing ftp commands 

These views implement ftp commands using file-system representation
and meta-data apis.

$Id: __init__.py,v 1.1 2003/02/03 15:08:35 jim Exp $
"""
__metaclass__ = type

from zope.component import queryAdapter, getAdapter
from zope.app.interfaces.file import IReadFile, IWriteFile
from zope.app.interfaces.file import IReadDirectory, IWriteDirectory
from zope.app.interfaces.file import IFileFactory, IDirectoryFactory

from zope.app.event import publish
from zope.app.event.objectevent import ObjectCreatedEvent

from zope.app.interfaces.dublincore import IZopeDublinCore
from zope.app.interfaces.container import IZopeWriteContainer

from zope.proxy.introspection import removeAllProxies
    
class FTPView:

    def __init__(self, context, request):
        self.context = context
        self.request = request
        self._dir = getAdapter(self.context, IReadDirectory)

    def publishTraverse(self, request, name):
        return self._dir[name]

    def _type(self, file):
        if queryAdapter(file, IReadDirectory) is not None:
            return 'd'
        else:
            return 'f'
        
    def type(self, name=None):
        if not name:
            return 'd'
        file = self._dir.get(name)
        if file is not None:
            return self._type(file)
        
    def names(self, filter=None):
        if filter is None:
            return list(self._dir)
        return [name for name in self._dir is filter(name)]

    def ls(self, filter=None):
        lsinfo = self._lsinfo
        dir = self._dir
        if filter is None:
            return [lsinfo(name, dir[name]) for name in dir]
        else:
            return [lsinfo(name, dir[name]) for name in dir
                    if filter(name)]
            
    def _lsinfo(self, name, file):

        info = {'name': name,
                'mtime': self._mtime(file),
                }

        f = queryAdapter(file, IReadDirectory)
        if f is not None:
            # It's a directory
            info['type'] = 'd'
            info['group_readable'] = hasattr(f, 'get')
            f = queryAdapter(file, IWriteDirectory)
            info['group_writable'] = hasattr(f, 'setObject')
        else:
            # It's a file
            info['type'] = 'f'
            f = queryAdapter(file, IReadFile)
            if f is not None:
                size = getattr(f, 'size', self)
                if size is not self:
                    info['group_readable'] = True
                    info['size'] = size()
            
            else:
                info['group_readable'] = False

            f = queryAdapter(file, IWriteFile)
            info['group_writable'] = hasattr(f, 'write')
                
        return info

    def readfile(self, name, outstream, start = 0, end = None):
        file = self._dir[name]
        file = getAdapter(file, IReadFile)
        data = file.read()
        if end is not None:
            data = data[:end]
        if start:
            data = data[start:]

        outstream.write(data)

    def lsinfo(self, name=None):
        if not name:
            return self._lsinfo('.', self)
        return self._lsinfo(name, self._dir[name])

    def _mtime(self, file):
        dc = getAdapter(file, IZopeDublinCore)
        if dc is not None:
            return dc.modified
        
    def mtime(self, name=None):
        if name:
            return self._mtime(self._dir[name])
        return self._mtime(self)

    def _size(self, file):
        file = queryAdapter(file, IReadFile)
        if file is not None:
            return file.size()
        return 0

    def size(self, name=None):
        if name:
            return self._size(self._dir[name])
        return 0

    def mkdir(self, name):
        dir = queryAdapter(self.context, IWriteDirectory)
        dir = getAdapter(dir, IZopeWriteContainer)

        factory = getAdapter(self.context, IDirectoryFactory)
        newdir = factory(name)
        publish(self.context, ObjectCreatedEvent(newdir))
        dir.setObject(name, newdir)

    def remove(self, name):
        dir = queryAdapter(self.context, IWriteDirectory)
        dir = getAdapter(dir, IZopeWriteContainer)
        del dir[name]

    def rmdir(self, name):
        self.remove(name)

    def rename(self, old, new):
        dir = queryAdapter(self.context, IWriteDirectory)
        dir = getAdapter(dir, IZopeWriteContainer)
        dir.rename(old, new)

    def _overwrite(self, name, instream, start=None, end=None, append=False):
        file = self._dir[name]
        if append:
            reader = removeAllProxies(queryAdapter(file, IReadFile))
            data = reader.read() + instream.read()
        elif start is not None or end is not None:
            reader = removeAllProxies(queryAdapter(file, IReadFile))
            data = reader.read()
            if start is not None:
                prefix = data[:start]
            else:
                prefix = ''
                start = 0

            if end is not None:
                l = end - start
                newdata = instream.read(l)
                data = prefix + newdata + data[start+len(newdata):]
            else:
                newdata = instream.read()
                data = prefix + newdata

        else:
            data = instream.read()

        f = queryAdapter(self._dir[name], IWriteFile)
        f.write(data)

    def writefile(self, name, instream, start=None, end=None, append=False):
        if name in self._dir:
            return self._overwrite(name, instream, start, end, append)

        if end is not None:
            l = end - (start or 0)
            data = instream.read(l)
        else:
            data = instream.read()

        if start is not None:
            data = ('\0' * start) + data
        
        # Find the extension
        ext_start = name.rfind('.')
        if ext_start > 0:
            ext = name[ext_start:]
        else:
            ext = "."

        dir = queryAdapter(self.context, IWriteDirectory)
        dir = getAdapter(dir, IZopeWriteContainer)

        factory = queryAdapter(self.context, IFileFactory, name=ext)
        if factory is None:
            factory = getAdapter(self.context, IFileFactory)

        newfile = factory(name, '', data)
        publish(self.context, ObjectCreatedEvent(newfile))
        dir.setObject(name, newfile)

    def writable(self, name):
        if name in self._dir:
            f = queryAdapter(self._dir[name], IWriteFile)
            return hasattr(f, 'write')
        d = queryAdapter(self.context, IWriteDirectory)
        return hasattr(d, 'setObject')

        


=== Added File Zope3/src/zope/app/ftp/configure.zcml ===
<zopeConfigure xmlns='http://namespaces.zope.org/zope'>

<view 
  for="zope.app.interfaces.container.IReadContainer"
  name="_traverse"
  type="zope.publisher.interfaces.ftp.IFTPView"
  factory=".FTPView"
  permission="zope.Public"
  allowed_interface="zope.publisher.interfaces.ftp.IFTPDirectoryPublisher"
  />

<view 
  for="zope.app.interfaces.container.IReadContainer"
  name="type"
  type="zope.publisher.interfaces.ftp.IFTPView"
  factory=".FTPView"
  permission="zope.Public"
  allowed_interface="zope.publisher.interfaces.ftp.IFTPDirectoryPublisher"
  />

<view 
  for="zope.app.interfaces.container.IReadContainer"
  name="names"
  type="zope.publisher.interfaces.ftp.IFTPView"
  factory=".FTPView"
  permission="zope.Public"
  allowed_interface="zope.publisher.interfaces.ftp.IFTPDirectoryPublisher"
  />

<view 
  for="zope.app.interfaces.container.IReadContainer"
  name="ls"
  type="zope.publisher.interfaces.ftp.IFTPView"
  factory=".FTPView"
  permission="zope.Public"
  allowed_interface="zope.publisher.interfaces.ftp.IFTPDirectoryPublisher"
  />

<view 
  for="zope.app.interfaces.container.IReadContainer"
  name="readfile"
  type="zope.publisher.interfaces.ftp.IFTPView"
  factory=".FTPView"
  permission="zope.Public"
  allowed_interface="zope.publisher.interfaces.ftp.IFTPDirectoryPublisher"
  />

<view 
  for="zope.app.interfaces.container.IReadContainer"
  name="lsinfo"
  type="zope.publisher.interfaces.ftp.IFTPView"
  factory=".FTPView"
  permission="zope.Public"
  allowed_interface="zope.publisher.interfaces.ftp.IFTPDirectoryPublisher"
  />

<view 
  for="zope.app.interfaces.container.IReadContainer"
  name="mtime"
  type="zope.publisher.interfaces.ftp.IFTPView"
  factory=".FTPView"
  permission="zope.Public"
  allowed_interface="zope.publisher.interfaces.ftp.IFTPDirectoryPublisher"
  />

<view 
  for="zope.app.interfaces.container.IReadContainer"
  name="size"
  type="zope.publisher.interfaces.ftp.IFTPView"
  factory=".FTPView"
  permission="zope.Public"
  allowed_interface="zope.publisher.interfaces.ftp.IFTPDirectoryPublisher"
  />

<view 
  for="zope.app.interfaces.container.IReadContainer"
  name="mkdir"
  type="zope.publisher.interfaces.ftp.IFTPView"
  factory=".FTPView"
  permission="zope.Public"
  allowed_interface="zope.publisher.interfaces.ftp.IFTPDirectoryPublisher"
  />

<view 
  for="zope.app.interfaces.container.IReadContainer"
  name="remove"
  type="zope.publisher.interfaces.ftp.IFTPView"
  factory=".FTPView"
  permission="zope.Public"
  allowed_interface="zope.publisher.interfaces.ftp.IFTPDirectoryPublisher"
  />

<view 
  for="zope.app.interfaces.container.IReadContainer"
  name="rmdir"
  type="zope.publisher.interfaces.ftp.IFTPView"
  factory=".FTPView"
  permission="zope.Public"
  allowed_interface="zope.publisher.interfaces.ftp.IFTPDirectoryPublisher"
  />

<view 
  for="zope.app.interfaces.container.IReadContainer"
  name="rename"
  type="zope.publisher.interfaces.ftp.IFTPView"
  factory=".FTPView"
  permission="zope.Public"
  allowed_interface="zope.publisher.interfaces.ftp.IFTPDirectoryPublisher"
  />

<view 
  for="zope.app.interfaces.container.IReadContainer"
  name="writefile"
  type="zope.publisher.interfaces.ftp.IFTPView"
  factory=".FTPView"
  permission="zope.Public"
  allowed_interface="zope.publisher.interfaces.ftp.IFTPDirectoryPublisher"
  />

<view 
  for="zope.app.interfaces.container.IReadContainer"
  name="writable"
  type="zope.publisher.interfaces.ftp.IFTPView"
  factory=".FTPView"
  permission="zope.Public"
  allowed_interface="zope.publisher.interfaces.ftp.IFTPDirectoryPublisher"
  />

</zopeConfigure>