[Zope3-checkins]
SVN: Zope3/branches/srichter-twisted-integration/src/zope/app/server/
upgraded the FTP shell to be compatible with the next version
of the twisted
Michael Kerrin
michael.kerrin at openapp.biz
Fri Jul 8 08:07:49 EDT 2005
Log message for revision 31034:
upgraded the FTP shell to be compatible with the next version of the twisted
ftp shell.
Changed:
U Zope3/branches/srichter-twisted-integration/src/zope/app/server/ftp/__init__.py
U Zope3/branches/srichter-twisted-integration/src/zope/app/server/ftp/ftp.py
U Zope3/branches/srichter-twisted-integration/src/zope/app/server/ftp/server.py
U Zope3/branches/srichter-twisted-integration/src/zope/app/server/ftp/sftpserver.py
U Zope3/branches/srichter-twisted-integration/src/zope/app/server/ftp/tests/test_ftpserver.py
A Zope3/branches/srichter-twisted-integration/src/zope/app/server/ftp/utils.py
U Zope3/branches/srichter-twisted-integration/src/zope/app/server/tests/test_docs.py
U Zope3/branches/srichter-twisted-integration/src/zope/app/server/tests/test_publisher.py
D Zope3/branches/srichter-twisted-integration/src/zope/app/server/utils.py
-=-
Modified: Zope3/branches/srichter-twisted-integration/src/zope/app/server/ftp/__init__.py
===================================================================
--- Zope3/branches/srichter-twisted-integration/src/zope/app/server/ftp/__init__.py 2005-07-08 11:30:46 UTC (rev 31033)
+++ Zope3/branches/srichter-twisted-integration/src/zope/app/server/ftp/__init__.py 2005-07-08 12:07:48 UTC (rev 31034)
@@ -14,10 +14,11 @@
"""FTP and SFTP server factories.
"""
-from zope.app.server.utils import FTPRequestFactory
from zope.app.server.server import ServerType
from zope.app.server.ftp.server import FTPFactory
from zope.app.server.server import SSHServerType
+
+from utils import FTPRequestFactory
from sftpserver import SFTPFactory
def createFTPFactory(db):
Modified: Zope3/branches/srichter-twisted-integration/src/zope/app/server/ftp/ftp.py
===================================================================
--- Zope3/branches/srichter-twisted-integration/src/zope/app/server/ftp/ftp.py 2005-07-08 11:30:46 UTC (rev 31033)
+++ Zope3/branches/srichter-twisted-integration/src/zope/app/server/ftp/ftp.py 2005-07-08 12:07:48 UTC (rev 31034)
@@ -15,74 +15,35 @@
"""
__docformat__="restructuredtext"
-## cStringIO is causing me problems with unicode charactors.
from cStringIO import StringIO
-import posixpath
-from datetime import date, timedelta
+from types import StringTypes
from zope.interface import implements
+from twisted.internet import threads, defer
from twisted.protocols import ftp
-from zope.app.server.utils import PublisherFileSystem
+from utils import PublisherFileSystem
-def ls(ls_info):
- """Formats a directory entry similarly to the 'ls' command.
- """
+class ConsumerObject(object):
+ def __init__(self, fs, name):
+ self.fs = fs
+ self.name = name
+ self.total = 0
- info = {
- 'owner_name': 'na',
- 'owner_readable': True,
- 'owner_writable': True,
- 'group_name': "na",
- 'group_readable': True,
- 'group_writable': True,
- 'other_readable': False,
- 'other_writable': False,
- 'nlinks': 1,
- 'size': 0,
- }
+ def registerProducer(self, producer, streaming):
+ assert streaming
- if ls_info['type'] == 'd':
- info['owner_executable'] = True
- info['group_executable'] = True
- info['other_executable'] = True
- else:
- info['owner_executable'] = False
- info['group_executable'] = False
- info['other_executable'] = False
+ def unregisterProducer(self):
+ pass
- info.update(ls_info)
+ def write(self, bytes):
+ ## XXX - this is going to mess up the transaction machinary since
+ ## for a big file this method could be called hundreds of times.
+ instream = StringIO(bytes)
+ self.fs.writefile(self.name, instream, start = self.total)
+ self.total += len(bytes)
- mtime = info.get('mtime')
- if mtime is not None:
- if date.today() - mtime.date() > timedelta(days=180):
- mtime = mtime.strftime('%b %d %Y')
- else:
- mtime = mtime.strftime('%b %d %H:%M')
- else:
- mtime = "Jan 02 0000"
-
- return "%s%s%s%s%s%s%s%s%s%s %3d %-8s %-8s %8d %s %s" % (
- info['type'] == 'd' and 'd' or '-',
- info['owner_readable'] and 'r' or '-',
- info['owner_writable'] and 'w' or '-',
- info['owner_executable'] and 'x' or '-',
- info['group_readable'] and 'r' or '-',
- info['group_writable'] and 'w' or '-',
- info['group_executable'] and 'x' or '-',
- info['other_readable'] and 'r' or '-',
- info['other_writable'] and 'w' or '-',
- info['other_executable'] and 'x' or '-',
- info['nlinks'],
- info['owner_name'],
- info['group_name'],
- info['size'],
- mtime,
- info['name'],
- )
-
-## this should correspond to zope.server.ftp.server.FTPServerChannel
class ZopeFTPShell(object):
"""An abstraction of the shell commands used by the FTP protocol
for a given user account
@@ -92,218 +53,135 @@
def __init__(self, username, password, request_factory):
self.fs_access = PublisherFileSystem((username, password),
request_factory)
- self._dir = '/'
- def mapCPathToSPath(self, path):
- if not path or path[0] != '/':
- path = posixpath.join(self._dir, path)
+ def _path(self, path):
+ return '/' + '/'.join(path)
- path = posixpath.normpath(path)
- if path.startswith('..'):
- path = '/'
+ def makeDirectory(self, path):
+ return threads.deferToThread(self.fs_access.mkdir, self._path(path))
- return path, path
+ def removeDirectory(self, path):
+ return threads.deferToThread(self.fs_access.rmdir, self._path(path))
- def pwd(self):
- return self._dir
+ def removeFile(self, path):
+ return threads.deferToThread(self.fs_access.remove, self._path(path))
- def cwd(self, path):
- dummy, path = self.mapCPathToSPath(path)
+ def rename(self, fromPath, toPath):
+ return threads.deferToThread(self.fs_access.rename,
+ self._path(fromPath),
+ self._path(toPath))
- if self.fs_access.type(path) is None:
- raise ftp.FileNotFoundError(path)
+ def access(self, path):
+ def success(result):
+ return None
+ def failure(result):
+ ## XXX - return more appropriate results
+ raise ftp.PermissionDeniedError(path)
- if self.fs_access.type(path) == 'd':
- self._dir = path
- else:
- raise ftp.FileNotFoundError(path)
+ ## XXX - is ls the right method to use here - seems a bit slow.
+ d = threads.deferToThread(self.fs_access.ls, self._path(path))
+ d.addCallback(success)
+ d.addErrback(failure)
- def cdup(self):
- self.cwd('..')
+ return d
- def size(self, path):
- dummy, path = self.mapCPathToSPath(path)
+ def _gotlisting(self, result, keys = ()):
+ ent = []
+ for key in keys:
+ val = getattr(self, '_list_' + key)(result)
+ if isinstance(val, StringTypes):
+ ent.append(val.encode('utf-8'))
+ else:
+ ent.append(val)
+ return result['name'].encode('utf-8'), ent
- fs = self.fs_access
- if fs.type(path) != 'f':
- raise ftp.FileNotFoundError(path)
- return fs.size(path)
+ def _stat(self, path, keys):
+ if self.fs_access.type(path) == 'd':
+ raise ftp.WrongFileType()
+ result = self._gotlisting(self.fs_access.lsinfo(path), keys)
+ return result[1]
- def _generatePath(self, args):
- """Convert relative paths to absolute paths."""
- # We use posixpath even on non-Posix platforms because we don't want
- # slashes converted to backslashes.
- path = posixpath.join(self._dir, args)
- return posixpath.normpath(path)
+ def stat(self, path, keys=()):
+ return threads.deferToThread(self._stat, self._path(path), keys)
- def mkd(self, path):
- if not path:
- raise ftp.CmdSyntaxError('Bad command arguments.')
- path = self._generatePath(path)
- try:
- self.fs_access.mkdir(path)
- except OSError, err:
- raise ftp.CmdActionNotTaken('creating directory %s' % path)
+ def list(self, path, keys=()):
+ def gotresults(results, keys):
+ ret = []
+ for result in results:
+ ret.append(self._gotlisting(result, keys))
+ return ret
- def rmd(self, path):
- if not path:
- raise ftp.CmdSyntaxError('Bad command arguments.')
- path = self._generatePath(path)
- try:
- self.fs_access.rmdir(path)
- except OSError, err:
- raise ftp.CmdActionNotTaken('deleting directory %s' % path)
+ d = threads.deferToThread(self.fs_access.ls, self._path(path))
+ d.addCallback(gotresults, keys)
- def dele(self, path):
- if not path:
- raise ftp.CmdSyntaxError('Bad command arguments.')
- path = self._generatePath(path)
+ return d
- try:
- self.fs_access.remove(path)
- except OSError, err:
- raise ftp.CmdOpenReadError(path)
-
- def getList(self, args, long=0, directory=0):
- # we need to scan the command line for arguments to '/bin/ls'...
- ## fs = self._getFileSystem()
- fs = self.fs_access
- path_args = []
- for arg in args:
- if arg[0] != '-':
- path_args.append (arg)
- else:
- # ignore arguments
- pass
- if len(path_args) < 1:
- path = '.'
+ def _list_size(self, value):
+ return value.get('size', 0)
+ def _list_hardlinks(self, value):
+ return value.get('nlinks', 1)
+ def _list_owner(self, value):
+ return value.get('owner_name', 'na')
+ def _list_group(self, value):
+ return value.get('group_name', 'na')
+ def _list_directory(self, value):
+ return value['type'] == 'd'
+ def _list_modified(self, value):
+ mtime = value.get('mtime', None)
+ if mtime:
+ return int(mtime.strftime('%s'))
+ return 0
+ def _list_permissions(self, value):
+ ret = 0
+ if value.get('other_executable', False):
+ ret |= 0001
+ if value.get('other_writable', False):
+ ret |= 0002
+ if value.get('other_readable', False):
+ ret |= 0004
+ if value.get('group_executable', True):
+ ret |= 0010
+ if value.get('group_writable', True):
+ ret |= 0020
+ if value.get('group_readable', True):
+ ret |= 0040
+ if value.get('owner_executable', True):
+ ret |= 0100
+ if value.get('owner_writable', True):
+ ret |= 0200
+ if value.get('owner_readable', True):
+ ret |= 0400
+ if value.get('type', 'f') == 'f':
+ ret |= 0100000
else:
- path = path_args[0]
+ ret |= 0040000
+ return ret
- path = self._generatePath(path)
+ def send(self, path, consumer):
+ def finished(result, consumer):
+ consumer.transport.loseConnection()
+ def failed(result, path):
+ consumer.transport.loseConnection()
+ ## XXX - a more appropriate exception here.
+ raise ftp.PermissionDeniedError(path)
- if fs.type(path) == 'd' and not directory:
- if long:
- file_list = map(ls, fs.ls(path))
- else:
- file_list = fs.names(path)
- else:
- if long:
- file_list = [ls(fs.lsinfo(path))]
- else:
- file_list = [posixpath.split(path)[1]]
+ p = self._path(path)
+ d = threads.deferToThread(self.fs_access.readfile, p, consumer)
+ d.addCallback(finished, consumer)
+ d.addErrback(failed, consumer, p)
+ return d
- return '\r\n'.join(file_list) + '\r\n'
+ def receive(self, path):
+ def accessok(result, fs, path):
+ if not result:
+ raise ftp.PermissionDeniedError(path)
+ return ConsumerObject(fs, p)
+ def failure(result, path):
+ ## XXX - should be a better exception
+ raise ftp.PermissionDeniedError(path)
+ p = self._path(path)
+ d = threads.deferToThread(self.fs_access.writable, p)
+ d.addCallback(accessok, self.fs_access, p)
+ d.addErrback(failure, p)
- def _list(self, path, long = 1, directory = False, *args):
- path = self._generatePath(path)
-
- dummy, path = self.mapCPathToSPath(path)
-
- if not self.fs_access.type(path):
- raise ftp.FileNotFoundError(path)
-
- s = self.getList(args, long, directory)
-
- return StringIO(str(s)), len(s)
-
- def list(self, path):
- return self._list(path)
-
- def nlst(self, path):
- return self._list(path, long = 0)
-
- def retr(self, path):
- fs = self.fs_access
- if not path:
- raise ftp.CmdSyntaxError('Bad command arguments.')
- path = self._generatePath(path)
-
- if not (fs.type(path) == 'f'):
- raise ftp.FileNotFoundError(path)
-
-## start = 0
-## if self.restart_position:
-## start = self.restart_position
-## self.restart_position = 0
-
-## ok_reply = 'OPEN_CONN', (self.type_map[self.transfer_mode], path)
-## cdc = RETRChannel(self, ok_reply)
-## outstream = ApplicationOutputStream(cdc)
-
- start = 0
- outstream = StringIO()
-
- try:
- fs.readfile(path, outstream, start)
- except OSError, err:
- raise ftp.CmdOpenReadError(path)
-
- l = len(outstream.getvalue())
- outstream.seek(0)
-
- return outstream, l
-
-## try:
-## fs.readfile(path, outstream, start)
-## cdc.close_when_done()
-## except OSError, err:
-## self.reply('ERR_OPEN_READ', str(err))
-## cdc.reported = True
-## cdc.close_when_done()
-## except IOError, err:
-## self.reply('ERR_IO', str(err))
-## cdc.reported = True
-## cdc.close_when_done()
-
-
- def stor(self, params):
- """This command causes the server-DTP to accept the data
- transferred via the data connection and to store the data as
- a file at the server site. If the file specified in the
- pathname exists at the server site, then its contents shall
- be replaced by the data being transferred. A new file is
- created at the server site if the file specified in the
- pathname does not already exist.
- """
- if not params:
- raise ftp.CmdSyntaxError('Bad command arguments.')
- path = self._generatePath(params)
-
-## start = 0
-## if self.restart_position:
-## self.start = self.restart_position
-## mode = write_mode + self.type_mode_map[self.transfer_mode]
-
- if not self.fs_access.writable(path):
-## self.reply('ERR_OPEN_WRITE', "Can't write file")
-## return
- raise ftp.CmdOpenWriteError(path)
-
-## cdc = STORChannel(self, (path, mode, start))
-## self.syncConnectData(cdc)
-## self.reply('OPEN_CONN', (self.type_map[self.transfer_mode], path))
-
- def writefile(self, path, data):
- """
- this is not in IFTPShell but needed to upload the data into Zope.
- """
- path = self._generatePath(path)
-
- try:
- self.fs_access.writefile(path, data)
- except OSError, err:
- raise ftp.CmdFileActionError()
-
- def mdtm(self, args):
- fs = self.fs_access
- # We simply do not understand this non-standard extension to MDTM
- if len(args.split()) > 1:
- raise ftp.CmdSyntaxError('Bad command arguments.')
- path = self._generatePath(args)
-
- if fs.type(path) != 'f':
- raise ftp.FileNotFoundError(path)
- else:
- mtime = fs.mtime(path)
- return mtime.strftime('%Y%m%d%H%M%S')
+ return d
Modified: Zope3/branches/srichter-twisted-integration/src/zope/app/server/ftp/server.py
===================================================================
--- Zope3/branches/srichter-twisted-integration/src/zope/app/server/ftp/server.py 2005-07-08 11:30:46 UTC (rev 31033)
+++ Zope3/branches/srichter-twisted-integration/src/zope/app/server/ftp/server.py 2005-07-08 12:07:48 UTC (rev 31034)
@@ -18,27 +18,22 @@
from zope.interface import implements
from twisted.cred import portal, credentials
-from twisted.protocols import ftp, policies
-from twisted.internet import reactor, defer
+from twisted.protocols import ftp
-from zope.app.server.server import ServerType
+from utils import ZopeSimpleAuthenticatation
+from ftp import ZopeFTPShell
-from zope.app.server.utils import PublisherFileSystem, \
- ZopeSimpleAuthenticatation
-from zope.app.server.ftp.ftp import ZopeFTPShell
-
class FTPRealm(object):
implements(portal.IRealm)
- def __init__(self, request_factory, logout = None):
+ def __init__(self, request_factory):
self.request_factory = request_factory
- self.logout = logout
def requestAvatar(self, avatarId, mind, *interfaces):
"""
>>> from ZODB.tests.util import DB
- >>> from zope.app.server.utils import FTPRequestFactory
+ >>> from utils import FTPRequestFactory
>>> creds = credentials.UsernamePassword('bob', '123')
>>> db = DB()
>>> request_factory = FTPRequestFactory(db)
@@ -57,7 +52,7 @@
ZopeFTPShell should contain a PublisherFileSystem istance assigned to
its fs_access attribute.
- >>> from zope.app.server.utils import PublisherFileSystem
+ >>> from utils import PublisherFileSystem
>>> print isinstance(result[1].fs_access, PublisherFileSystem)
True
@@ -82,15 +77,13 @@
if ftp.IFTPShell in interfaces:
avatar = ZopeFTPShell(avatarId.username, avatarId.password,
self.request_factory)
- avatar.logout = self.logout
- return ftp.IFTPShell, avatar, avatar.logout
+ return ftp.IFTPShell, avatar, lambda : None
raise NotImplementedError, \
"Only IFTPShell interface is supported by this realm."
-class FTPFactory(policies.LimitTotalConnectionsFactory):
- protocol = ftp.FTP
- overflowProtocol = ftp.FTPOverflowProtocol
+class FTPFactory(ftp.FTPFactory):
allowAnonymous = False
+
timeOut = 600
def __init__(self, request_factory):
@@ -98,7 +91,7 @@
The portal performs a simple authentication
>>> from ZODB.tests.util import DB
- >>> from zope.app.server.utils import FTPRequestFactory
+ >>> from utils import FTPRequestFactory
>>> db = DB()
>>> request_factory = FTPRequestFactory(db)
>>> ftpfactory = FTPFactory(request_factory)
@@ -122,20 +115,4 @@
p = portal.Portal(r)
p.registerChecker(ZopeSimpleAuthenticatation(),
credentials.IUsernamePassword)
-
- self.portal = p
- self.instances = []
-
- def buildProtocol(self, addr):
- p = policies.LimitTotalConnectionsFactory.buildProtocol(self, addr)
- if p is not None:
- p.wrappedProtocol.portal = self.portal
- p.wrappedProtocol.timeOut = self.timeOut
- self.instances.append(p.wrappedProtocol)
- return p
-
- def stopFactory(self):
- # make sure ftp instance's timeouts are set to None
- # to avoid reactor complaints
- [p.setTimeout(None) for p in self.instances if p.timeOut is not None]
- policies.LimitTotalConnectionsFactory.stopFactory(self)
+ ftp.FTPFactory.__init__(self, p)
Modified: Zope3/branches/srichter-twisted-integration/src/zope/app/server/ftp/sftpserver.py
===================================================================
--- Zope3/branches/srichter-twisted-integration/src/zope/app/server/ftp/sftpserver.py 2005-07-08 11:30:46 UTC (rev 31033)
+++ Zope3/branches/srichter-twisted-integration/src/zope/app/server/ftp/sftpserver.py 2005-07-08 12:07:48 UTC (rev 31034)
@@ -15,8 +15,6 @@
"""
from zope.interface import implements
-from zope.app.server.utils import ZopeSimpleAuthenticatation
-from zope.app.server.utils import PublisherFileSystem
from zope.app.server.interfaces import IFileSystem
from twisted.cred.portal import IRealm, Portal
@@ -42,6 +40,7 @@
from twisted.conch.avatar import ConchUser
from twisted.conch.interfaces import IConchUser
+from utils import PublisherFileSystem, ZopeSimpleAuthenticatation
class ZopeAvatar(ConchUser):
implements(IConchUser)
@@ -68,7 +67,7 @@
def requestAvatar(self, avatarId, mind, *interfaces):
"""
- >>> from zope.app.server.utils import FTPRequestFactory
+ >>> from utils import FTPRequestFactory
>>> from ZODB.tests.util import DB
>>> from twisted.cred import credentials
>>> creds = credentials.UsernamePassword('bob', '123')
@@ -89,7 +88,7 @@
ZopeAvatar should contain a PublisherFileSystem instance assigned to
its fs_access attribute.
- >>> from zope.app.server.utils import PublisherFileSystem
+ >>> from utils import PublisherFileSystem
>>> print isinstance(result[1].fs_access, PublisherFileSystem)
True
@@ -149,7 +148,7 @@
The portal performs a simple authentication
>>> from ZODB.tests.util import DB
- >>> from zope.app.server.utils import FTPRequestFactory
+ >>> from utils import FTPRequestFactory
>>> db = DB()
>>> request_factory = FTPRequestFactory(db)
>>> sftpfactory = SFTPFactory(request_factory, hostkey = None)
Modified: Zope3/branches/srichter-twisted-integration/src/zope/app/server/ftp/tests/test_ftpserver.py
===================================================================
--- Zope3/branches/srichter-twisted-integration/src/zope/app/server/ftp/tests/test_ftpserver.py 2005-07-08 11:30:46 UTC (rev 31033)
+++ Zope3/branches/srichter-twisted-integration/src/zope/app/server/ftp/tests/test_ftpserver.py 2005-07-08 12:07:48 UTC (rev 31034)
@@ -62,6 +62,11 @@
del self.serverProtocol
+ ## for some reason the threadpool isn't cleaning up.
+ from twisted.internet import reactor
+ if getattr(reactor, 'threadpool', None):
+ reactor.threadpool.stop()
+
def test_serverUp(self):
# test if we can bring the server up and down.
pass
@@ -77,13 +82,15 @@
def test_MLD(self):
self._authLogin()
responseLines = wait(self.client.queueStringCommand('MKD /newdir'))
- self.assertEqual(['257 "/newdir" created.'], responseLines)
+ self.assertEqual(['257 "/newdir" created'], responseLines)
def test_suite():
return TestSuite((
makeSuite(TestServerSetup),
doctest.DocTestSuite('zope.app.server.ftp.server'),
+ doctest.DocTestSuite('zope.app.server.ftp.utils'),
+ doctest.DocTestSuite('zope.app.server.ftp.sftpserver'),
))
if __name__=='__main__':
Added: Zope3/branches/srichter-twisted-integration/src/zope/app/server/ftp/utils.py
===================================================================
--- Zope3/branches/srichter-twisted-integration/src/zope/app/server/ftp/utils.py 2005-07-08 11:30:46 UTC (rev 31033)
+++ Zope3/branches/srichter-twisted-integration/src/zope/app/server/ftp/utils.py 2005-07-08 12:07:48 UTC (rev 31034)
@@ -0,0 +1,184 @@
+##############################################################################
+#
+# Copyright (c) 2004 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (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.
+#
+##############################################################################
+"""Contains the implementation of the Publisher File System for use within
+the FTP and SFTP servers. It also contains an implementation of
+twisted.cred.checkers.ICredentialsChecker for authentiating users against
+the twisted.cred.portal.Portal.
+"""
+__docformat__="restructuredtext"
+import posixpath
+from cStringIO import StringIO
+
+from zope.interface import implements
+from zope.publisher.publish import publish
+
+from zope.publisher.ftp import FTPRequest
+from zope.app.publication.interfaces import IPublicationRequestFactory
+from zope.app.publication.ftp import FTPPublication
+
+from zope.app.server.interfaces import IFileSystem
+
+from twisted.cred import checkers, credentials
+from twisted.internet import defer
+
+class ZopeSimpleAuthenticatation(object):
+
+ implements(checkers.ICredentialsChecker)
+
+ credentialInterfaces = credentials.IUsernamePassword
+
+ def requestAvatarId(self, credentials):
+ """
+ see zope.server.ftp.publisher.PublisherFileSystemAccess
+
+ We can't actually do any authentication initially, as the
+ user may not be defined at the root.
+ """
+ # -> the user = username, password so we can authenticate later on.
+ return defer.succeed(credentials)
+
+
+class FTPRequestFactory(object):
+ """FTP Request factory
+
+ FTP request factories for a given database create FTP requets with
+ publications on the given database:
+
+ >>> from ZODB.tests.util import DB
+ >>> db = DB()
+ >>> factory = FTPRequestFactory(db)
+ >>> from cStringIO import StringIO
+ >>> request = factory(StringIO(''), StringIO(),
+ ... {'credentials': None, 'path': '/'})
+ >>> request.publication.db is db
+ True
+ >>> db.close()
+
+ """
+ implements(IPublicationRequestFactory)
+
+ def __init__(self, db):
+ self.publication = FTPPublication(db)
+
+ def __call__(self, input_stream, output_steam, env):
+ request = FTPRequest(input_stream, output_steam, env)
+ request.setPublication(self.publication)
+ return request
+
+
+class NoOutput(object):
+ """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
+
+## this is the old zope.server.ftp.publisher.PublisherFileSystem class
+class PublisherFileSystem(object):
+ """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
Modified: Zope3/branches/srichter-twisted-integration/src/zope/app/server/tests/test_docs.py
===================================================================
--- Zope3/branches/srichter-twisted-integration/src/zope/app/server/tests/test_docs.py 2005-07-08 11:30:46 UTC (rev 31033)
+++ Zope3/branches/srichter-twisted-integration/src/zope/app/server/tests/test_docs.py 2005-07-08 12:07:48 UTC (rev 31034)
@@ -54,7 +54,7 @@
doctest.DocFileSuite('../log.txt',
globs={'pprint': doctestunit.pprint},
optionflags=doctest.NORMALIZE_WHITESPACE),
- doctest.DocTestSuite('zope.app.server.utils')
+ doctest.DocTestSuite('zope.app.server.ftp.utils')
))
Modified: Zope3/branches/srichter-twisted-integration/src/zope/app/server/tests/test_publisher.py
===================================================================
--- Zope3/branches/srichter-twisted-integration/src/zope/app/server/tests/test_publisher.py 2005-07-08 11:30:46 UTC (rev 31033)
+++ Zope3/branches/srichter-twisted-integration/src/zope/app/server/tests/test_publisher.py 2005-07-08 12:07:48 UTC (rev 31034)
@@ -20,7 +20,7 @@
from fstests import FileSystemTests
from StringIO import StringIO
from zope.publisher.publish import mapply
-from zope.app.server.utils import PublisherFileSystem
+from zope.app.server.ftp.utils import PublisherFileSystem
class DemoFileSystem(demofs.DemoFileSystem):
Deleted: Zope3/branches/srichter-twisted-integration/src/zope/app/server/utils.py
===================================================================
--- Zope3/branches/srichter-twisted-integration/src/zope/app/server/utils.py 2005-07-08 11:30:46 UTC (rev 31033)
+++ Zope3/branches/srichter-twisted-integration/src/zope/app/server/utils.py 2005-07-08 12:07:48 UTC (rev 31034)
@@ -1,184 +0,0 @@
-##############################################################################
-#
-# Copyright (c) 2004 Zope Corporation and Contributors.
-# All Rights Reserved.
-#
-# This software is subject to the provisions of the Zope Public License,
-# Version 2.1 (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.
-#
-##############################################################################
-"""Contains the implementation of the Publisher File System for use within
-the FTP and SFTP servers. It also contains an implementation of
-twisted.cred.checkers.ICredentialsChecker for authentiating users against
-the twisted.cred.portal.Portal.
-"""
-__docformat__="restructuredtext"
-import posixpath
-from cStringIO import StringIO
-
-from zope.interface import implements
-from zope.publisher.publish import publish
-
-from zope.publisher.ftp import FTPRequest
-from zope.app.publication.interfaces import IPublicationRequestFactory
-from zope.app.publication.ftp import FTPPublication
-
-from interfaces import IFileSystem
-
-from twisted.cred import checkers, credentials
-from twisted.internet import defer
-
-class ZopeSimpleAuthenticatation(object):
-
- implements(checkers.ICredentialsChecker)
-
- credentialInterfaces = credentials.IUsernamePassword
-
- def requestAvatarId(self, credentials):
- """
- see zope.server.ftp.publisher.PublisherFileSystemAccess
-
- We can't actually do any authentication initially, as the
- user may not be defined at the root.
- """
- # -> the user = username, password so we can authenticate later on.
- return defer.succeed(credentials)
-
-
-class FTPRequestFactory(object):
- """FTP Request factory
-
- FTP request factories for a given database create FTP requets with
- publications on the given database:
-
- >>> from ZODB.tests.util import DB
- >>> db = DB()
- >>> factory = FTPRequestFactory(db)
- >>> from cStringIO import StringIO
- >>> request = factory(StringIO(''), StringIO(),
- ... {'credentials': None, 'path': '/'})
- >>> request.publication.db is db
- True
- >>> db.close()
-
- """
- implements(IPublicationRequestFactory)
-
- def __init__(self, db):
- self.publication = FTPPublication(db)
-
- def __call__(self, input_stream, output_steam, env):
- request = FTPRequest(input_stream, output_steam, env)
- request.setPublication(self.publication)
- return request
-
-
-class NoOutput(object):
- """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
-
-## this is the old zope.server.ftp.publisher.PublisherFileSystem class
-class PublisherFileSystem(object):
- """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
More information about the Zope3-Checkins
mailing list