ftp.py :  » Network » FtpCube » ftpcube-0.5.1 » libftpcube » transports » Python Open Source

Home
Python Open Source
1.3.1.2 Python
2.Ajax
3.Aspect Oriented
4.Blog
5.Build
6.Business Application
7.Chart Report
8.Content Management Systems
9.Cryptographic
10.Database
11.Development
12.Editor
13.Email
14.ERP
15.Game 2D 3D
16.GIS
17.GUI
18.IDE
19.Installer
20.IRC
21.Issue Tracker
22.Language Interface
23.Log
24.Math
25.Media Sound Audio
26.Mobile
27.Network
28.Parser
29.PDF
30.Project Management
31.RSS
32.Search
33.Security
34.Template Engines
35.Test
36.UML
37.USB Serial
38.Web Frameworks
39.Web Server
40.Web Services
41.Web Unit
42.Wiki
43.Windows
44.XML
Python Open Source » Network » FtpCube 
FtpCube » ftpcube 0.5.1 » libftpcube » transports » ftp.py
"""
FtpCube
Copyright (C) Michael Gilfix

This file is part of FtpCube.

You should have received a file COPYING containing license terms
along with this program; if not, write to Michael Gilfix
(mgilfix@eecs.tufts.edu) for a copy.

This version of FtpCube is open source; you can redistribute it and/or
modify it under the terms listed in the file COPYING.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
"""

import libftpcube.dispatcher
import libftpcube.protocol
from libftpcube.logger import Logger

import os
import socket
import select
import errno
import threading
import ftplib
import time
import exceptions

CRLF = '\r\n'

class FtpException(libftpcube.protocol.ProtocolException):
    """Base exception for FTP communications."""

    def __init__(self, args=None):
        libftpcube.protocol.ProtocolException.__init__(self, args)

class FTPDispatcher(libftpcube.dispatcher.Dispatcher):
    """FTP command dispatcher.

    This class extends the base FTP dispatcher to allow for dispatching of FTP
    command objects. Unless otherwise noted, the extension methods should be
    executed within the dispatcher thread. The FTP dispatcher provides the low
    level handling of socket communication to the FTP command session. The
    extension methods allow commands to wait on the dispatcher for received
    messages from the FTP session. It is recommended that command objects have
    access to the dispatcher in order to send/receive data while executing from
    within the dispatcher thread. This allows the command classes to make use
    of the dispatcher capabilities without worrying about thread safety issues.
    """

    def __init__(self, **kwargs):
        """Creates an FTP dispatcher instance.

        This initializes private variables for use within the dispatcher command
        execution thread. These variables are either set once during
        initialization or serve as variables for the thread stack. Although
        command objects may have reference to the dispatcher object, instance
        variables should never be accessed directly -only through accessor
        functions.
        
        This method defines the following keywords:
            'host' : The host to connect to - default is localhost.
            'port' : The FTP port for the control session - default is 21.
        """
        libftpcube.dispatcher.Dispatcher.__init__(self, **kwargs)

        # Initialize instance variables for use in the thread context
        self.sock = None
        self.file = None
        self.multiline_code = None
        self.incoming = [ ]

        try:
            self.host = kwargs['host']
        except KeyError:
            self.host = 'localhost'

        try:
            self.port = kwargs['port']
        except KeyError:
            self.port = 21

    def connect(self):
        """Opens an FTP connection.

        This method should be executed either as initialization or within the thread
        context of the dispatcher. Otherwise, this method should not be considered
        thread safe. If an error occurs establishing a connection, then this method
        will raise an FtpException.
        
        The establishment of the connection is performed asynchronously. This allows
        for destruction of the dispatcher object (i.e., tearing down the connection)
        while a connection is being established."""
        proto = socket.getprotobyname('tcp')
        try:
            self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM, proto)
        except socket.error, strerror:
            self.closeSocket()
            raise FtpException(_("Error opening ftp socket: %(err)s") %{ 'err' : strerror })

        self.log(Logger.LOCAL, _("Connecting to %(h)s on port %(p)d.")
             %{ 'h' : self.host, 'p' : self.port })

        self.sock.setblocking(0)
        try:
            err = self.sock.connect_ex((self.host, self.port))
        except socket.error, strerror:
            self.closeSocket()
            raise FtpException(_("Error connecting to host %(h)s: %(err)s")
                %{ 'h' : self.host, 'err' : strerror })

        self.log(Logger.LOCAL, _("Connected to %(h)s.") %{ 'h' : self.host })

        while (err == errno.EINPROGRESS or err == errno.EWOULDBLOCK) and not self.die.isSet():
            time.sleep(0.25)
            err = self.sock.getsockopt(socket.SOL_SOCKET, socket.SO_ERROR)

        if err != 0 or self.die.isSet():
            self.closeSocket()
            raise FtpException(_("Error connecting to host %(h)s: %(err)s")
                %{ 'h' : self.host, 'err' : os.strerror(err) })
        else:
            self.sock.setblocking(1)
            self.file = self.sock.makefile('rb')

    def getHost(self):
        """Returns the host to which this FTP session is connected."""
        return self.host

    def getPort(self):
        """Returns the port to which this FTP session is connected."""
        return self.port

    def closeSocket(self):
        """Closes the FTP session socket."""
        if self.file is not None:
            self.file.close()
            self.file = None
        if self.sock is not None:
            self.sock.close()
            self.sock = None

    def removeEndLine(self, line):
        """Removes the line ending characters from FTP strings."""
        if line[-2:] == CRLF:
            line = line[:-2]
        elif line[-1:] in CRLF:
            line = line[:-1]
        return line

    def waitFtpResponse(self):
        """Waits for an FTP response.

        Calling this within an FTP command will pause execution of the command
        until a response is retrieved. This method will return the data retrieved
        or None if no data could be retrieved."""
        while 1:
            line = self.file.readline()
            if not line:
                self.log(Logger.LOCAL, _("Connection to %(h)s terminated...")
                   %{ 'h' : self.host })
                self.closeSocket()
                return
            line = self.removeEndLine(line)
            data = self.processData(line)
            if data is not None:
                return data

    def waitFtpResponseWithTimeout(self, timeout):
        """Waits for an FTP response within a timeout period."""
        read, write, ex = select.select([ self.sock ], [ ], [ ], timeout)
        if self.sock in read:
            self.waitFtpResponse()

    def processData(self, line):
        """Processes FTP response data.

        This method handles the receipt of multi-line FTP data, appending incoming
        data to the incoming queue. This method returns None as long as multi-line
        data is being received, otherwise it returns a list of the aggregated
        data."""
        self.log(Logger.REMOTE, line)
        self.incoming.append(line)

        if line[3:4] == '-':
            if self.multiline_code is None:
                self.multiline_code = line[:3]
                return None
            elif line[:3] == self.multiline_code and line[3:4] != '-':
                self.multiline_code = None
            else:
                return None
        data = self.incoming
        self.incoming = [ ]
        return data

    def send(self, message, alternate=None):
        """Sends a data string through the FTP session."""
        if alternate is not None:
            self.log(Logger.LOCAL, alternate)
        else:
            self.log(Logger.LOCAL, message)
        return self.sock.sendall(message + CRLF)

    def sendOOB(self, message):
        """Sends an out-of-band messages.

        This is useful for sending FTP ABORT commands immediately."""
        sent = self.sock.sendall(message + CRLF, socket.MSG_OOB)
        # Log after the send in case we throw an exception
        self.log(Logger.LOCAL, message)
        return sent

    def run(self):
        """Overrides the dispatcher execute method to clean up sockets upon
        termination."""
        try:
            libftpcube.dispatcher.Dispatcher.run(self)
        finally:
            # Try to quit the session gracefully
            try:
                self.quit()
            except Exception, strerror:
                pass
            self.closeSocket()

    def quit(self):
        """Quits the current control session."""
        self.dispatcher.sendOOB('QUIT')
        self.dispatcher.waitFtpResponseWithTimeout(1.0)

class FTPAbstractCommand(libftpcube.dispatcher.DispatcherCommand):
    """Abstract base class for all FTP commands."""
    pass

class FTPConnect(FTPAbstractCommand):
    """FTP connection command.

    This performs an asynchronous opening of an FTP connection.
    The connection can be aborted by destroying the dispatcher
    instance while a connection is outstanding."""

    def execute(self, dispatcher):
        dispatcher.connect()

class FTPAbortCommand(FTPAbstractCommand):
    """FTP abort command.

    This command can be used to cancel outstanding commands. The
    procedure for cancelling an outstanding command is to empty the
    dispatcher queue, and then schedule an abort command. When the
    abort command is next scheduled, then an abort will be sent
    out of band."""

    def execute(self, dispatcher):
        dispatcher.sendOOB('ABOR')
        self.response = dispatcher.waitFtpResponse()
        if self.response[-1][:3] not in ('426', '226'):
            raise FtpException(self.response[-1])
        return self.response

class FTPGetWelcomeMessage(FTPAbstractCommand):
    """FTP Get Welcome Message.

    Receives a welcome message. This should be called immediately
    upon opening the session to receive the welcome message, before
    commands are sent."""

    def execute(self, dispatcher):
        self.response = dispatcher.waitFtpResponse()
        if self.response[-1][:1] not in '123':
            raise FtpException(response[-1])

class FTPCommand(FTPAbstractCommand):
    """Base class for FTP session commands.

    This base class provides a common execution for sending a
    command and receiving a synchronize response. The response code
    is examined to determine whether the command is successful. If
    an error is received, then an FtpException is raised."""

    def __init__(self, cmd):
        FTPAbstractCommand.__init__(self)
        self.cmd = cmd

    def execute(self, dispatcher):
        dispatcher.send(self.cmd)
        self.response = dispatcher.waitFtpResponse()
        if self.response[-1][0] != '2':
            raise FtpException(self.response[-1])
        return self.response

class FTPResponseCommand(FTPCommand):
    """Base class for FTP commands that must access the response.

    The response is made available for post retrieval within other
    commands through the getResponse() method."""

    def __init__(self, cmd):
        FTPCommand.__init__(self, cmd)
        self.response = None

    def getResponse(self):
        # Set in FTPCommand.execute()
        return self.response

class FTP257Command(FTPResponseCommand):
    """Sends a command that has a 257 FTP success response.

    These commands return response data regarding directories that
    needs to be processed by the FTP client. This command accepts a
    reference to a status object, whose optional data is set to the
    received data."""

    def __init__(self, cmd, status=None):
        FTPResponseCommand.__init__(self, cmd)
        if status and not isinstance(status, libftpcube.dispatcher.DispatchStatus):
            raise FtpException(_("Invalid status object"))
        self.status = status

    def execute(self, dispatcher):
        dispatcher.send(self.cmd)
        self.response = dispatcher.waitFtpResponse()
        self.response = self.response[-1]
        if self.response[:3] != '257':
            raise FtpException(_("Invalid 257 response: %(res)s") %{ 'res' : self.response })
        if self.status:
            dir = ftplib.parse257(self.response)
            self.status.setOptionalData(dir)
            self.status.finished()

class FTPPendingCommand(FTPAbstractCommand):
    """Sends an FTP session command that can receive a pending response.

    An FTPException is raised if the expected pending response code is
    not received."""

    def __init__(self, cmd):
        FTPAbstractCommand.__init__(self)
        self.cmd = cmd

    def execute(self, dispatcher):
        dispatcher.send(self.cmd)
        self.response = dispatcher.waitFtpResponse()
        if self.response[-1][0] != '3':
            raise FtpException(self.response[-1])

class FTPLogin(FTPAbstractCommand):
    """Performs an FTP log-in.

    The username, password, and accounting information must be provided
    for the login. Anonymous users would simply use the anonymous user
    and pssword convention. If an error occurs during log-in, then an
    FtpException will be raised."""

    def __init__(self, user, pw, acct):
        FTPAbstractCommand.__init__(self)
        self.user = user
        self.pw = pw
        self.acct = acct

    def execute(self, dispatcher):
        user_string = 'USER ' + self.user
        dispatcher.send(user_string)
        response = dispatcher.waitFtpResponse()

        if response[-1][0] == '3':
            pw_string = 'PASS ' + self.pw
            # So we don't print the password to screen
            alternate = 'PASS ' + '*' * len(self.pw)
            dispatcher.send(pw_string, alternate)
            response = dispatcher.waitFtpResponse()

        if response[-1][0] == '3':
            # For now, assume ACCT to be ''
            acct_string = 'ACCT '
            dispatcher.send(acct_string)
            response = dispatcher.waitFtpResponse()

        if response[-1][0] != '2':
            raise FtpException(_("Error logging in to %(host)s: %(resp)s")
                %{ 'host' : dispatcher.getHost(), 'resp' : response[-1] })

class FTPTransferConnection:
    """FTP Data Transfer Connection base class.

    This class establishes a data connection for sending/receiving file
    data. Sub-classes should implement the socket lock according to the
    direction of the transfer and type of data being transferred. Each
    transfer connection has a unique host and port used for the data
    session."""

    def __init__(self, host, port, dispatcher=None, passive=True):
        """Creates a new transfer connection.

        There are two types of transfer connections: active and passive connections. Passive
        connections means that the FTP client will connect to a socket opened by the server.
        Non-passive connections require the FTP client to act as a server for the data
        session and the remote FTP server to connect back to the client."""
        self.abort_flag = threading.Event()
        self.host = host
        self.port = port
        self.dispatcher = dispatcher

        if passive:
            proto = socket.getprotobyname('tcp')
            try:
                self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM, proto)
            except socket.error, strerror:
                raise FtpException(_("Error creating socket for transfer: %(err)s")
                    %{ 'err' : strerror })

            self.sock.setblocking(0)
            try:
                err = self.sock.connect_ex((host, port))
            except socket.error, strerror:
                raise FtpException(_("Error creating socket for transfer: %(err)s")
                    %{ 'err' : strerror })

            while (err == errno.EINPROGRESS or err == errno.EWOULDBLOCK) and \
                  not self.abort_flag.isSet():
                time.sleep(0.10)
                err = self.sock.getsockopt(socket.SOL_SOCKET, socket.SO_ERROR)
            if err != 0 or self.abort_flag.isSet():
                raise FtpException(_("Error creating socket for transfer: %(err)s")
                    %{ 'err' : os.strerror(err) })
            else:
                self.sock.setblocking(1)
        else:
            try:
                self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                self.sock.bind(('', 0))
                self.sock.listen(1)
                port = self.sock.getsockname()[1]
                host = socket.gethostbyname(dispatcher.getHost())
            except socket.error, strerror:
                if self.sock:
                    self.sock.close()
                raise FtpException(_("Error creating socket for transfer: %(err)s")
                    %{ 'err' : strerror })

        self.file = self.sock.makefile('rb')

    def sendPort(self, host, port):
        """Sends port information to the remote FTP session."""
        hbytes = host.split('.')
        pbytes = [ str(port / 256), str(port % 256) ]
        bytes = hbytes + pbytes

        class Handler(libftpcube.dispatcher.ErrorHandler):
            def __init__(self, transfer):
                self.transfer = transfer
            def handle(self, error, dispatcher):
                dispatcher.log(Logger.ERROR, error)
                self.transfer.abort()

        cmd = FTPResponseCommand('PORT ' + ','.join(bytes))
        err_handler = cmd.getErrorHandler()
        cmd.setErrorHandler(Handler(self))
        cmd.execute(self.dispatcher)
        cmd.setErrorHandler(err_handler)

    def finalizeNonPassiveTransfer(self):
        """Finishes setting up a non-passive transfer by blocking waiting for a connection
        from the FTP server.

        The old socket is closed and the new accepted socket is used for data transfer."""
        new_sock, sockaddr = self.sock.accept()
        self.close()
        self.sock = new_sock
        self.file = self.sock.makefile('rb')

    def getSock(self):
        """Gets the socket object for the transfer connection."""
        return self.sock

    def getFile(self):
        """Gets the file object for the transfer connection."""
        return self.file

    def close(self):
        """Closes the current open file and socket."""
        if self.file:
            self.file.close()
        if self.sock:
            self.sock.close()

    def abort(self):
        """Aborts the current transfer connection action.

        This method is thread-safe."""
        self.abort_flag.set()

class FTPTransferCommand(FTPAbstractCommand):
    """FTP control session initiate data transfer command.

    This command allows for the sending of FTP control session commands
    that initiate data transfer connections. Execution of this command
    also provides size information about the file to be transfered when
    the transfer is a download. This information is made available via
    the getSize() method post execution."""

    def __init__(self, cmd):
        FTPAbstractCommand.__init__(self)
        self.cmd = cmd
        self.size = None

    def execute(self, dispatcher):
        dispatcher.send(self.cmd)
        response = dispatcher.waitFtpResponse()
        if response[-1][0] != '1':
            raise FtpException(_("Error initiating transfer: %(resp)s")
                %{ 'resp' : response[-1] })
        if response[-1][:3] == '150':
            self.size = ftplib.parse150(response[-1])

    def getSize(self):
        """Returns the size of a file transfer download.

        If no size information is available, then the None value is returned."""
        return self.size

class FTPTransfer(FTPAbstractCommand):
    """Initiates an FTP transfer.

    This class contains all the sub-commands needed to initiate an FTP transfer.
    This command supports both passive and active transfers, and makes use of the
    FTPTransferCommand class under the covers. It also initiates an
    FTPTransferConnection for the data session. The transfer can be aborted via
    the abort() method."""

    def __init__(self, cmd, rest=None, passive=True):
        FTPAbstractCommand.__init__(self)
        self.cmd = cmd
        self.rest = rest
        self.passive = passive
        self.transfer = None
        self.lock = threading.Lock()

    def execute(self, dispatcher):
        if self.passive:
            cmd = FTPResponseCommand('PASV')
            cmd.execute(dispatcher)
            host, port = ftplib.parse227(cmd.getResponse()[-1])
            transfer_args = (host, port)
        else:
            transfer_args = ('localhost', 0, dispatcher, False)

        self.lock.acquire()
        try:
            self.transfer = apply(FTPTransferConnection, transfer_args)
        finally:
            self.lock.release()

        if self.rest is not None:
            cmd = FTPPendingCommand("REST %s" %self.rest)
            cmd.execute(dispatcher)

        cmd = FTPTransferCommand(self.cmd)
        cmd.execute(dispatcher)
        self.size = cmd.getSize()
        if not self.passive:
            self.transfer.finalizeNonPassiveTransfer()

    def abort(self):
        self.lock.acquire()
        try:
            if self.transfer:
                self.transfer.abort()
        finally:
            self.lock.release()

class FTPList(FTPTransfer):
    """Gets an FTP listing.

    This retrieves an FTP listing within a data session. This command encompasses
    both the control and data aspects to the retrieval of the listing. This command
    takes a dispatcher status object, which is updated with the latest status as the
    listing is retrieved."""

    def __init__(self, cmd, status=None, passive=True):
        FTPTransfer.__init__(self, cmd, passive=passive)

        if status is not None and not isinstance(status, libftpcube.dispatcher.DispatchStatus):
            raise FtpException(_("Invalid status object"))
        self.status = status

        self.abort_flag = threading.Event()
        self.data = [ ] # Local copy of data

    def execute(self, dispatcher):
        # Set the appropriate transfer type
        type = FTPResponseCommand('TYPE A')
        type.execute(dispatcher)

        # Now prepare for the transfer
        FTPTransfer.execute(self, dispatcher)

        self.sock = self.transfer.getSock()
        self.file = self.transfer.getFile()

        if self.status:
            self.status.start()

        while not self.abort_flag.isSet():
            line = self.file.readline()
            if not line:
                break
            if line[-2:] == CRLF:
                line = line[:-2]
            elif line[-1:] in CRLF:
                line = line[:-1]

            if self.status:
                self.status.update(len(line))
            self.data.append(line)

        self.transfer.close()
        response = dispatcher.waitFtpResponse()
        if self.status:
            self.status.setOptionalData(self.data)
            self.status.finished()

    def getData(self):
        return self.data

    def abort(self):
        FTPTransfer.abort(self)
        self.abort_flag.set()

class FTPAsciiTransfer(FTPTransfer):
    """Performs an ASCII file transfer.

    This class performs an ASCII file transfer -both upload and download. This command
    encompasses both the control and data aspects to the retrieval of the listing.
    This command takes a dispatcher status object, which is updated with the latest
    status as the file is transfered."""

    def __init__(self, cmd, local_file, download=True, status=None, passive=True):
        FTPTransfer.__init__(self, cmd, passive=passive)
        self.local_file = local_file

        if not isinstance(status, libftpcube.dispatcher.DispatchStatus):
            raise FtpException(_("Invalid status object"))
        self.status = status

        self.download = download
        self.abort_flag = threading.Event()

    def execute(self, dispatcher):
        # Set the transfer type
        type = FTPResponseCommand('TYPE A')
        type.execute(dispatcher)

        FTPTransfer.execute(self, dispatcher)

        self.sock = self.transfer.getSock()
        self.file = self.transfer.getFile()

        if self.status:
            self.status.start()

        if self.download:
            local_file = open(self.local_file, 'w')
            while not self.abort_flag.isSet():
                line = self.file.readline()
                if not line:
                    break
                if self.status:
                    self.status.update(len(line))
                local_file.write(line)
        else:
            local_file = open(self.local_file, 'r')
            while not self.abort_flag.isSet():
                line = local_file.readline()
                if not line:
                    break
                self.sock.sendall(line)
                if self.status:
                    self.status.update(len(line))

        self.transfer.close()
        dispatcher.waitFtpResponse()
        if self.status:
            self.status.finished()

    def abort(self):
        FTPTransfer.abort(self)
        self.abort_flag.set()

class FTPBinaryTransfer(FTPTransfer):
    """Performs a binary file transfer.

    This class performs a binary file transfer -both upload and download. This command
    encompasses both the control and data aspects to the retrieval of the listing.
    This command takes a dispatcher status object, which is updated with the latest
    status as the file is transfered."""

    def __init__(self, cmd, local_file, download=True, status=None, blocksize=8192,
                 rest=None, passive=True):
        FTPTransfer.__init__(self, cmd, rest=rest, passive=passive)

        if not isinstance(status, libftpcube.dispatcher.DispatchStatus):
            raise FtpException(_("Invalid status object"))
        self.status = status

        self.local_file = local_file
        self.blocksize = blocksize
        self.download = download
        self.abort_flag = threading.Event()

    def execute(self, dispatcher):
        # Set the transfer type
        type = FTPResponseCommand('TYPE I')
        type.execute(dispatcher)

        FTPTransfer.execute(self, dispatcher)

        self.sock = self.transfer.getSock()
        if self.status:
            self.status.start()

        if self.download:
            if self.rest is not None:
                local_file = open(self.local_file, 'ab')
            else:
                local_file = open(self.local_file, 'wb')

            while not self.abort_flag.isSet():
                data = self.sock.recv(self.blocksize)
                if not data:
                    break
                if self.status:
                    self.status.update(len(data))
                local_file.write(data)
        else:
            local_file = open(self.local_file, 'rb')
            if self.rest is not None:
                local_file.seek(self.rest)

            while not self.abort_flag.isSet():
                data = local_file.read(self.blocksize)
                if not data:
                    break
                self.sock.sendall(data)
                if self.status:
                    self.status.update(len(data))

        local_file.close()
        self.transfer.close()
        dispatcher.waitFtpResponse()
        if self.status:
            self.status.finished()

    def abort(self):
        FTPTransfer.abort(self)
        self.abort_flag.set()

class FTP(libftpcube.protocol.ProtocolInterface):
    """Main interface for the FTP protocol implementation.

    This class provides file transfer functionality over the FTP protocol. Classes that
    are external to the transports package should only use this class as the interface
    into FTP interactions. Each instance of this object represents an FTP session,
    including both command and data connections."""

    def __init__(self, **kwargs):
        """Initializes the FTP interface.

        This creates a new dispatcher thread and dispatch queue for outbound FTP
        commands. The default transfer mode for FTP is passive transfers.

        This method defines the following keywords:
            'host'   : The host to connect to - default is localhost.
            'port'   : The FTP port for the control session - default is 21.
            'logger' : The console/file logger to use. May be omitted.
        """
        # Process all critical keyword arguments
        try:
            self.host = kwargs['host']
        except KeyError:
            raise FtpException(_("Error opening FTP connection: no host given"))
        try:
            self.port = kwargs['port']
        except KeyError:
            raise FtpException(_("Error opening FTP connection: no port given"))
        try:
            self.logger = kwargs['logger']
        except KeyError:
            self.logger = None

        # Initialize instance variables
        self.passive = True

        # Create the dispatcher
        self.dispatcher = FTPDispatcher(**kwargs)
        self.dispatcher.start()

    def setPassive(self, passive):
        """Sets a boolean indicating whether to use passive mode for transfers."""
        self.passive = passive

    def getPassive(self):
        """Returns a boolean indicating whether to use passive mode for transfers."""
        return self.passive

    def getDispatcher(self):
        """Returns the dispatcher for this FTP session."""
        return self.dispatcher

    def destroy(self):
        """Destroys the FTP session."""
        self.dispatcher.destroy()

    def busy(self):
        """Returns True if the dispatcher is currently busy executing a command."""
        return self.dispatcher.busy()

    def addOutgoing(self, cmd, err_handler=None):
        """Adds an outgoing FTP command to the dispatch queue.

        If a handler is specified, then it is set as the error handler on the command
        object. Otherwise, a default error logger that prints to the console is used."""
        if err_handler is None:
            if self.logger is not None:
                handler = libftpcube.dispatcher.ErrorLogger(self.logger)
                cmd.setErrorHandler(handler)
        else:
            cmd.setErrorHandler(err_handler)
        self.dispatcher.dispatch(cmd)

    def initiateConnect(self, err_handler=None):
        """Initiates an outbound FTP connection."""
        connect = FTPConnect()
        self.addOutgoing(connect, err_handler)

    def getWelcomeMessage(self, err_handler=None):
        """Receives a welcome message from a newly connected FTP control session."""
        welcome = FTPGetWelcomeMessage()
        self.addOutgoing(welcome, err_handler)

    def login(self, username, password, err_handler=None):
        """Performs a login to an FTP control session.

        The specified username and password are used for the login. If the session is
        to be anonymous, then the anonymous username/password convention should be
        specified."""
        login = FTPLogin(username, password, '')
        self.addOutgoing(login, err_handler)

    def noop(self, err_handler=None):
        """Sends a NOOP command to the control session."""
        ftpcmd = FTPCommand('NOOP')
        self.addOutgoing(ftpcmd, err_handler)

    def cwd(self, dir, err_handler=None):
        """Changes the current working directory to the specified directory.

        This method provides special handling for the '.' and '..' entries. This allows
        those entries to be used like regular path entries. A '..' corresponds to a
        CDUP command and a '.' corresponds to a no-op."""
        if not dir or dir == '.':
            return
        elif dir == '..':
            cmd = 'CDUP'
        else:
            cmd = 'CWD ' + dir
        ftpcmd = FTPCommand(cmd)
        self.addOutgoing(ftpcmd, err_handler)

    def pwd(self, status=None, err_handler=None):
        """Gets the current working directory.

        The specified status object is updated with the data received for the current
        working directory."""
        cmd = 'PWD'
        ftpcmd = FTP257Command(cmd, status=status)
        self.addOutgoing(ftpcmd, err_handler)

    def delete(self, file, err_handler=None):
        """Deletes a file with the specified name in the current directory."""
        cmd = 'DELE ' + file
        ftpcmd = FTPCommand(cmd)
        self.addOutgoing(ftpcmd, err_handler)

    def rename(self, name, new_name, err_handler=None):
        """Performs a file rename for a file in the current directory."""
        cmd = 'RNFR ' + name
        ftpcmd = FTPPendingCommand(cmd)
        self.addOutgoing(ftpcmd, err_handler)
        cmd = 'RNTO ' + new_name
        ftpcmd = FTPCommand(cmd)
        self.addOutgoing(ftpcmd, err_handler)

    def mkdir(self, dir, status=None, err_handler=None):
        """Creates the specified directory.

        The specified status object is updated with the new directory path."""
        cmd = 'MKD ' + dir
        ftpcmd = FTP257Command(cmd, status=status)
        self.addOutgoing(ftpcmd, err_handler)

    def rmdir(self, dir, err_handler=None):
        """Removes the specified directory."""
        cmd = 'RMD ' + dir
        ftpcmd = FTPCommand(cmd)
        self.addOutgoing(ftpcmd, err_handler)

    def chmod(self, mode, file, err_handler=None):
        """Changes the permissions to the specified mode on the specified file.

        Permissions are in octal form, as is used on unix."""
        cmd = "SITE CHMOD %s %s" %(mode, file)
        ftpcmd = FTPCommand(cmd)
        self.addOutgoing(ftpcmd, err_handler)

    def abort(self, err_handler=None):
        """Performs an abort of the current running command.

        An abort is only performed on an abortable command. Abortable commands are
        FTPTransfer and FTPConnect objects. Aborting a connection object destroys
        the connection."""
        executing = self.dispatcher.getExecutingCommand()
        if isinstance(executing, FTPTransfer):
            self.dispatcher.resetQueue()
            executing.abort()
        elif isinstance(executing, FTPConnect):
            self.dispatcher.destroy()

    def list(self, args=None, status=None, passive=True, err_handler=None):
        """Gets a listing of the current directory.

        The supplied list of arguments are supplied to the list command, separated
        by spaces. The status object is updated with the list transfer events,
        including the amount of listing data transfered. The listing data is then
        made available within the optional data field, to be checked when the
        finished event is called. The passive argument controls whether the transfer
        is done in active or passive mode."""
        cmd = 'LIST'
        if args is not None:
            for list_arg in args:
                if list_arg:
                    cmd = cmd + ' ' + list_arg
        listcmd = FTPList(cmd, status=status, passive=passive)
        self.addOutgoing(listcmd, err_handler)

    def retrieveAscii(self, file, local_dir, status=None, passive=True, err_handler=None):
        """Performs the retrieval of an ASCII file.

        The retrieved file is stored in the specified local directory, under the supplied
        file name. The status object is updated with the file transfer events, including
        the amount of data transfered (for each line transfered). The passive
        argument controls whether the transfer is done in active or passive mode."""
        cmd = 'RETR ' + file
        local_file = os.path.join(local_dir, file)
        asciicmd = FTPAsciiTransfer(cmd, local_file, status=status, passive=passive)
        self.addOutgoing(asciicmd, err_handler)

    def uploadAscii(self, file, local_dir, status=None, passive=True, err_handler=None):
        """Performs an upload of a local ASCII file.

        The specified file in the specified local directory is uploaded in ASCII format
        to the FTP server. The status object is updated with the file transfer events,
        including the amount of data transfered (for each line transfered). The passive
        argument controls whether the transfer is done in active or passive mode."""
        cmd = 'STOR ' + file
        local_file = os.path.join(local_dir, file)
        asciicmd = FTPAsciiTransfer(cmd, local_file, download=False, status=status,
                                    passive=passive)
        self.addOutgoing(cmd, err_handler)

    def retrieveBinary(self, file, local_dir, status=None, blocksize=8192, rest=None,
                       passive=True, err_handler=None):
        """Performs a download of a binary file.

        The retrieved file is stored in the specified local directory, under the supplied
        file name. The status object is updated with the file transfer events, including
        the amount of data transfered (for each block transfered). The rest argument
        is the byte offset of the file used to resume transfers. A rest of None indicates
        to download the whole file (overwriting the local copy). The passive argument
        controls whether the transfer is done in active or passive mode."""
        cmd = 'RETR ' + file
        local_file = os.path.join(local_dir, file)
        binarycmd = FTPBinaryTransfer(cmd, local_file, status=status, blocksize=blocksize,
                                      rest=rest, passive=passive)
        self.addOutgoing(binarycmd, err_handler)

    def uploadBinary(self, file, local_dir, status=None, blocksize=8192, rest=None,
                     passive=True, err_handler=None):
        """Performs an upload of a binary file.

        The specified file in the specified local directory is uploaded in binary format
        to the FTP server. The status object is updated with the file transfer events,
        including the amount of data transfered (for each block transfered). The rest
        argument is the byte offset of the file used to resume transfers. A rest of None
        indicates that the whole file should be uploaded (overwriting the remote copy).
        The passive argument controls whether the transfer is done in active or passive
        mode."""
        cmd = 'STOR ' + file
        local_file = os.path.join(local_dir, file)
        binarycmd = FTPBinaryTransfer(cmd, local_file, download=False, status=status,
                                      blocksize=blocksize, rest=rest, passive=passive)
        self.addOutgoing(binarycmd, err_handler)
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.