[Zope-Checkins] CVS: Zope3/lib/python/Zope/Server/FTP - CommonFTPActivityLogger.py:1.1.4.1 FTPServer.py:1.1.4.1 FTPServerChannel.py:1.1.4.1 FTPStatusMessages.py:1.1.4.1 IFTPCommandHandler.py:1.1.4.1 OSEmulators.py:1.1.4.1 PassiveAcceptor.py:1.1.4.1 PublisherFTPServer.py:1.1.4.1 PublisherFTPServerChannel.py:1.1.4.1 PublisherFTPTask.py:1.1.4.1 PublisherFilesystemAccess.py:1.1.4.1 RecvChannel.py:1.1.4.1 TestFilesystemAccess.py:1.1.4.1 XmitChannel.py:1.1.4.1 __init__.py:1.1.4.1
Shane Hathaway
shane@cvs.zope.org
Fri, 12 Apr 2002 17:30:57 -0400
Update of /cvs-repository/Zope3/lib/python/Zope/Server/FTP
In directory cvs.zope.org:/tmp/cvs-serv20835/lib/python/Zope/Server/FTP
Added Files:
Tag: Zope-3x-branch
CommonFTPActivityLogger.py FTPServer.py FTPServerChannel.py
FTPStatusMessages.py IFTPCommandHandler.py OSEmulators.py
PassiveAcceptor.py PublisherFTPServer.py
PublisherFTPServerChannel.py PublisherFTPTask.py
PublisherFilesystemAccess.py RecvChannel.py
TestFilesystemAccess.py XmitChannel.py __init__.py
Log Message:
Merged Zope3-Server-Branch.
=== 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.4.1 2002/04/12 21:30:55 shane 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.log('127.0.0.1', message)
=== 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.4.1 2002/04/12 21:30:55 shane Exp $
"""
import asyncore
from FTPServerChannel import FTPServerChannel
from Zope.Server.ServerBase import ServerBase
from Zope.Server.VFS.IFilesystemAccess import IFilesystemAccess
class FTPServer(ServerBase):
"""Generic FTP Server"""
channel_class = FTPServerChannel
SERVER_IDENT = 'Zope.Server.FTPServer'
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)
if __name__ == '__main__':
from Zope.Server.TaskThreads import ThreadedTaskDispatcher
from Zope.Server.VFS.OSFileSystem import OSFileSystem
from Zope.Server.VFS.TestFilesystemAccess import TestFilesystemAccess
td = ThreadedTaskDispatcher()
td.setThreadCount(4)
fs = OSFileSystem('/')
fs_access = TestFilesystemAccess(fs)
FTPServer('', 8021, fs_access, 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 === (432/532 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: FTPServerChannel.py,v 1.1.4.1 2002/04/12 21:30:55 shane Exp $
"""
import posixpath
import stat
import sys
import socket
import time
from Zope.Server.LineReceiver.LineServerChannel import LineServerChannel
from FTPStatusMessages import status_msgs
from OSEmulators import unix_longify as longify
from IFTPCommandHandler import IFTPCommandHandler
from PassiveAcceptor import PassiveAcceptor
from RecvChannel import RecvChannel
from XmitChannel import XmitChannel, ApplicationXmitStream
from Zope.Server.VFS.UsernamePassword import UsernamePassword
from Zope.Exceptions import Unauthorized
class FTPServerChannel(LineServerChannel):
"""The FTP Server Channel represents a connection to a particular
client. We can therefore store information here."""
__implements__ = LineServerChannel.__implements__, IFTPCommandHandler
# List of commands that are always available
special_commands = ('cmd_quit', 'cmd_type', 'cmd_noop', 'cmd_user',
'cmd_pass')
# These are the commands that are accessing the filesystem.
# Since this could be also potentially a longer process, these commands
# are also the ones that are executed in a different thread.
[-=- -=- -=- 432 lines omitted -=- -=- -=-]
def getDirectoryList(self, args, long=0):
# we need to scan the command line for arguments to '/bin/ls'...
path_args = []
for arg in args:
if arg[0] != '-':
path_args.append (arg)
else:
# ignore arguments
pass
if len(path_args) < 1:
dir = '.'
else:
dir = path_args[0]
dir = self._generatePath(dir)
return self.listdir(dir, long)
def connectDataChannel(self, cdc):
pa = self.passive_acceptor
if pa:
# PASV mode.
if pa.ready:
# a connection has already been made.
conn, addr = pa.ready
cdc.set_socket (conn)
cdc.connected = 1
self.passive_acceptor.close()
self.passive_acceptor = None
# else we're still waiting for a connect to the PASV port.
# FTP Explorer is known to do this.
else:
# not in PASV mode.
ip, port = self.client_addr
cdc.create_socket(socket.AF_INET, socket.SOCK_STREAM)
if self.bind_local_minus_one:
cdc.bind(('', self.server.port - 1))
try:
cdc.connect((ip, port))
except socket.error, err:
cdc.close('NO_DATA_CONN')
def notifyClientDCClosing(self, *reply_args):
if self.client_dc is not None:
self.client_dc = None
if reply_args:
self.reply(*reply_args)
=== 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.4.1 2002/04/12 21:30:55 shane Exp $
"""
status_msgs = {
'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.',
'SUCCESS_250' : '250 %s command successful.',
'SUCCESS_257' : '257 %s command successful.',
'ALREADY_CURRENT' : '257 "%s" is the current directory.',
'PASS_REQUIRED' : '331 Password required',
'RESTART_TRANSFER' : '350 Restarting at %d. Send STORE or '
'RETRIEVE to initiate transfer.',
'READY_FOR_DEST' : '350 File exists, ready for destination.',
'NO_DATA_CONN' : "425 Can't build data connection",
'TRANSFER_ABORTED' : '426 Connection closed; transfer aborted.',
'CMD_UNKNOWN' : "500 '%s': command not understood.",
'INTERNAL_ERROR' : "500 Internal error: %s",
'ERR_ARGS' : '500 Bad command arguments',
'MODE_UNKOWN' : '502 Unimplemented MODE type',
'WRONG_BYTE_SIZE' : '504 Byte size must be 8',
'STRU_UNKNOWN' : '504 Unimplemented STRU type',
'NOT_AUTH' : "530 You are not authorized to perform the "
"'%s' command",
'LOGIN_REQUIRED' : '530 Please log in with USER and PASS',
'LOGIN_MISMATCH' : '530 The username and password do not match.',
'ERR_NO_LIST' : '550 Could not list directory: %s',
'ERR_NO_DIR' : '550 "%s": No such directory.',
'ERR_NO_FILE' : '550 "%s": No such file.',
'ERR_IS_NOT_FILE' : '550 "%s": Is not a file',
'ERR_CREATE_FILE' : '550 Error creating file.',
'ERR_CREATE_DIR' : '550 Error creating directory: %s',
'ERR_DELETE_FILE' : '550 Error deleting file: %s',
'ERR_DELETE_DIR' : '550 Error removing directory: %s',
'ERR_OPEN_READ' : '553 Could not open file for reading: %s',
'ERR_OPEN_WRITE' : '553 Could not open file for writing: %s',
'ERR_IO' : '553 I/O Error: %s',
'ERR_RENAME' : '560 Could not rename "%s" to "%s": %s',
'ERR_RNFR_SOURCE' : '560 No source filename specify. Call RNFR first.',
}
=== 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(args):
"""Abort operation. No read access required.
"""
def cmd_appe(args):
"""Append to a file. Write access required.
"""
def cmd_cdup(args):
"""Change to parent of current working directory.
"""
def cmd_cwd(args):
"""Change working directory.
"""
def cmd_dele(args):
"""Delete a file. Write access required.
"""
def cmd_help(args):
"""Give help information. No read access required.
"""
def cmd_list(args):
"""Give list files in a directory.
"""
def cmd_mdtm(args):
"""Show last modification time of file.
Example output: 213 19960301204320
Geez, there seems to be a second syntax for this fiel, where one
can also set the modification time using:
MDTM datestring pathname
"""
def cmd_mkd(args):
"""Make a directory. Write access required.
"""
def cmd_mode(args):
"""Set file transfer mode. No read access required. Obselete.
"""
def cmd_nlst(args):
"""Give name list of files in directory.
"""
def cmd_noop(args):
"""Do nothing. No read access required.
"""
def cmd_pass(args):
"""Specify password.
"""
def cmd_pasv(args):
"""Prepare for server-to-server transfer. No read access required.
"""
def cmd_port(args):
"""Specify data connection port. No read access required.
"""
def cmd_pwd(args):
"""Print the current working directory.
"""
def cmd_quit(args):
"""Terminate session. No read access required.
"""
def cmd_rest(args):
"""Restart incomplete transfer.
"""
def cmd_retr(args):
"""Retrieve a file.
"""
def cmd_rmd(args):
"""Remove a directory. Write access required.
"""
def cmd_rnfr(args):
"""Specify rename-from file name. Write access required.
"""
def cmd_rnto(args):
"""Specify rename-to file name. Write access required.
"""
def cmd_size(args):
"""Return size of file.
"""
def cmd_stat(args):
"""Return status of server. No read access required.
"""
def cmd_stor(args):
"""Store a file. Write access required.
"""
def cmd_stru(args):
"""Set file transfer structure. Obselete."""
def cmd_syst(args):
"""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(args):
"""Specify data transfer type. No read access required.
"""
def cmd_user(args):
"""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/OSEmulators.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: OSEmulators.py,v 1.1.4.1 2002/04/12 21:30:55 shane Exp $
"""
import stat
import time
import pwd, grp
months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
mode_table = {
'0':'---',
'1':'--x',
'2':'-w-',
'3':'-wx',
'4':'r--',
'5':'r-x',
'6':'rw-',
'7':'rwx'
}
def unix_longify((file, stat_info)):
# for now, only pay attention to the lower bits
try: username = pwd.getpwuid(int(stat_info[stat.ST_UID]))[0]
except: username = stat_info[stat.ST_UID]
try: grpname = grp.getgrgid(int(stat_info[stat.ST_GID]))[0]
except: grpname = stat_info[stat.ST_GID]
mode = ('%o' % stat_info[stat.ST_MODE])[-3:]
mode = ''.join(map (lambda x: mode_table[x], mode))
if stat.S_ISDIR (stat_info[stat.ST_MODE]):
dirchar = 'd'
else:
dirchar = '-'
date = ls_date (long(time.time()), stat_info[stat.ST_MTIME])
return '%s%s %3d %-8s %-8s %8d %s %s' % (
dirchar,
mode,
stat_info[stat.ST_NLINK],
username,
grpname,
stat_info[stat.ST_SIZE],
date,
file
)
def ls_date (now, t):
"""Emulate the unix 'ls' command's date field. it has two formats
- if the date is more than 180 days in the past, then it's like
this: Oct 19 1995 otherwise, it looks like this: Oct 19 17:33
"""
try:
info = time.gmtime(t)
except:
info = time.gmtime(0)
# 15,600,000 == 86,400 * 180
if (now - t) > 15600000:
return '%s %2d %d' % (
months[info[1]-1],
info[2],
info[0]
)
else:
return '%s %2d %02d:%02d' % (
months[info[1]-1],
info[2],
info[3],
info[4]
)
def msdos_longify((file, stat_info)):
"""This matches the output of NT's ftp server (when in MSDOS mode)
exactly.
"""
if stat.S_ISDIR(stat_info[stat.ST_MODE]):
dir = '<DIR>'
else:
dir = ' '
date = msdos_date(stat_info[stat.ST_MTIME])
return '%s %s %8d %s' % (date, dir, stat_info[stat.ST_SIZE], file)
def msdos_date(t):
try:
info = time.gmtime(t)
except:
info = time.gmtime(0)
# year, month, day, hour, minute, second, ...
if info[3] > 11:
merid = 'PM'
info[3] = info[3] - 12
else:
merid = 'AM'
return '%02d-%02d-%02d %02d:%02d%s' % (
info[1], info[2], info[0]%100, info[3], info[4], merid )
=== 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.4.1 2002/04/12 21:30:55 shane 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()
conn.setblocking(0)
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/PublisherFTPServer.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: PublisherFTPServer.py,v 1.1.4.1 2002/04/12 21:30:55 shane Exp $
"""
from FTPServer import FTPServer
from Zope.Server.FTP.PublisherFilesystemAccess import PublisherFilesystemAccess
class PublisherFTPServer(FTPServer):
"""Generic FTP Server"""
def __init__(self, request_factory, name, ip, port, *args, **kw):
self.request_factory = request_factory
fs_access = PublisherFilesystemAccess(request_factory)
super(PublisherFTPServer, self).__init__(ip, port, fs_access,
*args, **kw)
=== Added File Zope3/lib/python/Zope/Server/FTP/PublisherFTPServerChannel.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: PublisherFTPServerChannel.py,v 1.1.4.1 2002/04/12 21:30:55 shane Exp $
"""
from FTPServerChannel import FTPServerChannel
class PublisherFTPServerChannel(FTPServerChannel):
"""The FTP Server Channel represents a connection to a particular
client. We can therefore store information here."""
__implements__ = FTPServerChannel.__implements__
def authenticate(self):
if self._getFilesystem()._authenticate():
return 1, 'User successfully authenticated.'
else:
return 0, 'User could not be authenticated.'
=== 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.4.1 2002/04/12 21:30:55 shane 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/PublisherFilesystemAccess.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.
#
##############################################################################
"""Implementation of IFilesystemAccess intended only for testing.
$Id: PublisherFilesystemAccess.py,v 1.1.4.1 2002/04/12 21:30:55 shane Exp $
"""
from cStringIO import StringIO
from Zope.Exceptions import Unauthorized
from Zope.App.Security.PrincipalRegistry import principalRegistry
from Zope.Server.VFS.PublisherFileSystem import PublisherFileSystem
from Zope.Server.VFS.IFilesystemAccess import IFilesystemAccess
from Zope.Server.VFS.IUsernamePassword import IUsernamePassword
class PublisherFilesystemAccess:
__implements__ = IFilesystemAccess
def __init__(self, request_factory):
self.request_factory = request_factory
def authenticate(self, credentials):
assert IUsernamePassword.isImplementedBy(credentials)
env = {'credentials' : credentials}
request = self.request_factory(StringIO(''), StringIO(), env)
id = principalRegistry.authenticate(request)
if id is None:
raise Unauthorized
def open(self, credentials):
return PublisherFileSystem(credentials, self.request_factory)
=== 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.4.1 2002/04/12 21:30:55 shane Exp $
"""
from Zope.Server.ServerChannelBase import ChannelBaseClass
from Zope.Server.Buffers import OverflowableBuffer
from Zope.Server.ITask import ITask
class RecvChannel(ChannelBaseClass):
""" """
complete_transfer = 0
_fileno = None # provide a default for asyncore.dispatcher._fileno
def __init__ (self, control_channel, finish_args):
self.control_channel = control_channel
self.finish_args = finish_args
self.inbuf = OverflowableBuffer(control_channel.adj.inbuf_overflow)
ChannelBaseClass.__init__(self, None, None, control_channel.adj)
# Note that this channel starts out in async mode.
def writable (self):
return 0
def handle_connect (self):
pass
def received (self, data):
if data:
self.inbuf.append(data)
def handle_close (self):
"""Client closed, indicating EOF."""
c = self.control_channel
task = FinishedRecvTask(c, self.inbuf, self.finish_args)
self.complete_transfer = 1
self.close()
c.start_task(task)
def close(self, *reply_args):
try:
c = self.control_channel
if c is not None:
self.control_channel = None
if not self.complete_transfer and not reply_args:
# 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 FinishedRecvTask:
__implements__ = ITask
def __init__(self, control_channel, inbuf, finish_args):
self.control_channel = control_channel
self.inbuf = inbuf
self.finish_args = finish_args
############################################################
# Implementation methods for interface
# Zope.Server.ITask
def service(self):
"""Called to execute the task.
"""
close_on_finish = 0
c = self.control_channel
try:
try:
c.finishedRecv(self.inbuf, self.finish_args)
except socket.error:
close_on_finish = 1
if c.adj.log_socket_errors:
raise
finally:
c.end_task(close_on_finish)
def cancel(self):
'See Zope.Server.ITask.ITask'
self.control_channel.close_when_done()
def defer(self):
'See Zope.Server.ITask.ITask'
pass
#
############################################################
=== Added File Zope3/lib/python/Zope/Server/FTP/TestFilesystemAccess.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.
#
##############################################################################
"""Implementation of IFilesystemAccess intended only for testing.
$Id: TestFilesystemAccess.py,v 1.1.4.1 2002/04/12 21:30:55 shane Exp $
"""
from Zope.Server.VFS.IFilesystemAccess import IFilesystemAccess
from Zope.Server.VFS.IUsernamePassword import IUsernamePassword
from Zope.Exceptions import Unauthorized
class TestFilesystemAccess:
__implements__ = IFilesystemAccess
passwords = {'foo': 'bar'}
def __init__(self, fs):
self.fs = fs
def authenticate(self, credentials):
if not IUsernamePassword.isImplementedBy(credentials):
raise Unauthorized
name = credentials.getUserName()
if not self.passwords.has_key(name):
raise Unauthorized
if credentials.getPassword() != self.passwords[name]:
raise Unauthorized
def open(self, credentials):
self.authenticate(credentials)
return self.fs
=== 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.4.1 2002/04/12 21:30:55 shane Exp $
"""
from Zope.Server.ServerChannelBase import ChannelBaseClass
class XmitChannel(ChannelBaseClass):
opened = 0
_fileno = None # provide a default for asyncore.dispatcher._fileno
def __init__ (self, control_channel, ok_reply_args):
self.control_channel = control_channel
self.ok_reply_args = ok_reply_args
self.set_sync()
ChannelBaseClass.__init__(self, None, None, control_channel.adj)
def _open(self):
"""Signal the client to open the connection."""
self.opened = 1
self.control_channel.reply(*self.ok_reply_args)
self.control_channel.connectDataChannel(self)
def write(self, data):
if self.control_channel is None:
raise IOError, 'Client FTP connection closed'
if not self.opened:
self._open()
ChannelBaseClass.write(self, data)
def readable(self):
return not self.connected
def handle_read(self):
# This is only called when making the connection.
try:
self.recv(1)
except:
# The connection failed.
self.close('NO_DATA_CONN')
def handle_connect(self):
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
=== 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.4.1 2002/04/12 21:30:55 shane Exp $
"""