"""
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 libftpcube.messages
import libftpcube.utils
import paramiko
import wx
import errno
import exceptions
import os
import socket
import sys
import threading
import time
class SFtpException(libftpcube.protocol.ProtocolException):
"""Base exception for SFTP communications."""
def __init__(self, args=None):
libftpcube.protocol.ProtocolException.__init__(self, args)
class SFTPDispatcher(libftpcube.dispatcher.Dispatcher):
"""SFTP command dispatcher.
This class extends the base dispatcher to allow for dispatching of SFTP
command objects. Unless otherwise noted, the extension methods should be
executed within the dispatcher thread. The SFTP dispatcher provides the
low level handling of SSH communications to the SFTP server. The extension
methods allow commands to wait on the dispatcher for received messages
from the import SFTP 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 dipatcher capabilities without worrying about thread safety
issues.
"""
def __init__(self, **kwargs):
"""Creates an SFTP dispatcher instance.
This initializes the 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 SSH port to connect to - default is 22.
'host_keys' : The path to the SSH host keys file - default
searches in well known places.
"""
libftpcube.dispatcher.Dispatcher.__init__(self, **kwargs)
self.sock = None
self.transport = None
self.sftp_client = None
try:
self.host = kwargs['host']
except KeyError:
self.host = 'localhost'
try:
self.port = kwargs['port']
except KeyError:
self.port = 22
try:
self.host_key_path = kwargs['host_keys']
except KeyError:
self.host_key_path = self.getDefaultHostKeyPath()
self.host_keys = self.initializeHostKeys()
def getDefaultHostKeyPath(self):
"""Returns the full default path to the host key file."""
path = None
if sys.platform == 'win32':
path = os.path.join('.ssh', 'known_hosts')
path = os.path.join(os.environ['USERPROFILE'], path)
elif sys.platform == 'posix':
path = os.sep.join([ '~', '.ssh', 'known_hosts' ])
path = os.path.expanduser(path)
else:
path = './known_hosts'
return path
def getHostKeyPath(self):
"""Returns the host key path."""
return self.host_key_path
def initializeHostKeys(self):
"""Initializes the host keys file using the supplied host key path and returns a
host key object.
This checks to see if the host keys file exists. Otherwise, it creates the containing
directory so the host key file can be saved in the future."""
host_keys = None
host_key_path = self.getHostKeyPath()
if not os.path.exists(host_key_path) or not os.path.isfile(host_key_path):
base_dir = os.path.dirname(host_key_path)
if not os.path.exists(base_dir):
try:
os.makedirs(base_dir)
except OSError, strerror:
raise SFtpException, sterror
elif not os.path.isdir(base_dir):
raise SFtpException, \
_("Path must be directory to create host keys file: %(path)s") \
%{ 'path' : base_dir }
host_keys = paramiko.HostKeys()
else:
try:
host_keys = paramiko.HostKeys(host_key_path)
except Exception, strerror:
raise SFtpException, _("Error initializing host key file: %(f)s") \
%{ 'f' : strerror }
return host_keys
def connect(self):
"""Opens a socket connection to the SSH server.
This method only establishes the connection and starts the SFTP client negotation.
This does not authenticate the user, which must be performed in a separate step.
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 SFtpException.
The establishment of the connect 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 SFtpException(_("Error opening SFTP 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 SFtpException(_("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 SFtpException(_("Error connecting to host %(h)s: %(err)s")
%{ 'h' : self.host, 'err' : os.strerror(err) })
self.log(Logger.LOCAL, _("Opening SSH transport and negotiating keys."))
self.sock.setblocking(1)
try:
self.transport = paramiko.Transport(self.sock)
self.transport.start_client()
key = self.transport.get_remote_server_key()
except Exception, strerror:
self.closeTransport()
raise SFtpException(_("Error opening SSH transport: %(err)s")
%{ 'err' : strerror })
ret = None
if not self.host_keys.has_key(self.host) or \
not self.host_keys[self.host].has_key(key.get_name()):
ret = libftpcube.messages.displayMessageDialog(libftpcube.utils.getMainWindow(),
_("Ftpcube - SFTP Unknown Host Key"),
_("WARNING: Unknown host key for %(host)s. Proceed?")
%{ 'host' : self.host }, style=wx.YES_NO)
elif self.host_keys[self.host][key.get_name()] != key:
ret = libftpcube.messages.displayMessageDialog(libftpcube.utils.getMainWindow(),
_("Ftpcube - SFTP Host Key Changed"),
_("WARNING: Host key has changed for %(host)s. Proceed?")
%{ 'host' : self.host }, style=wx.YES_NO)
# Process the outcome of the popup if one was made
if ret == wx.ID_NO:
self.closeTransport()
raise SFtpException(_("Error opening SSH transport: Host key rejected."))
elif ret == wx.ID_YES:
self.host_keys.add(self.host, key.get_name(), key)
try:
self.host_keys.save(self.getHostKeyPath())
except IOError, strerror:
self.closeTransport()
raise SFtpException(_("Error writing host key file: %(err)s")
%{ 'err' : strerror })
self.log(Logger.REMOTE, _("SSH channel successfully negotiated."))
def getHost(self):
"""Returns the host that this SFTP session is connected to."""
return self.host
def getPort(self):
"""Returns the port that this SFTP session is connected to."""
return self.port
def getTransport(self):
"""Returns the underlying SSH transport for the SFTP session."""
return self.transport
def getSFTPClient(self):
"""Returns the SFTP client object."""
return self.sftp_client
def closeTransport(self):
"""Closes the SSH transport connection."""
if self.transport is not None:
self.transport.close()
self.transport = None
self.closeSocket()
def closeSocket(self):
"""Closes the socket connection."""
if self.sock is not None:
self.sock.close()
self.sock = None
def openSFTPSession(self):
"""Opens an SFTP session.
This method should be called once authentication has finished. It gets the current
wording directory and sets up the SFTP client so paramiko keeps track of relative
directories."""
self.log(Logger.LOCAL, _("Opening SFTP session."))
try:
self.sftp_client = paramiko.SFTPClient.from_transport(self.transport)
self.log(Logger.LOCAL, _("Getting remote directory."))
real_cwd = self.sftp_client.normalize('.')
self.log(Logger.REMOTE, _("Remote directory: %(dir)s") %{ 'dir' : real_cwd })
self.sftp_client.chdir(real_cwd)
except Exception, strerror:
raise SFtpException(_("Error creating SFTP client session: %(err)s")
%{ 'err' : strerror })
self.log(Logger.LOCAL, _("SFTP session opened."))
def run(self):
"""Overrides the dispatcher execute method to clean up the SFTP session after the
thread has terminated."""
try:
libftpcube.dispatcher.Dispatcher.run(self)
finally:
try:
self.quit()
except Exception, strerror:
pass
def quit(self):
"""Quits the current session."""
if self.sftp_client is not None:
self.sftp_client.close()
self.closeTransport()
class SFTPAbstractCommand(libftpcube.dispatcher.DispatcherCommand):
"""Abstract base class for all SFTP commands."""
REMOTE_SEP = '/'
class SFTPConnect(SFTPAbstractCommand):
"""SFTP connection command.
This command opens up the SSH connection to the remote server. After execution of this
command, crypto keys will have been negotiated, but the user will remain unauthenticated.
The connection can be aborted by destroying the dispatcher instance while this
connection is outstanding."""
def execute(self, dispatcher):
dispatcher.connect()
class SFTPLogin(SFTPAbstractCommand):
"""SFTP Login command.
Completes the authentication of the user over the SSH connection. This then finalizes
the SFTP client session. If an error occurs during log-in, then an SFtpException will
be raised."""
def __init__(self, user, pw):
SFTPAbstractCommand.__init__(self)
self.user = user
self.pw = pw
def execute(self, dispatcher):
dispatcher.log(Logger.LOCAL, _("Logging in: USER %(user)s, PASS %(pass)s")
%{ 'user' : self.user, 'pass' : '*' * len(self.pw) })
transport = dispatcher.getTransport()
try:
transport.auth_password(self.user, self.pw)
if not transport.is_authenticated():
raise _("Invalid username and password")
dispatcher.log(Logger.REMOTE, _("Login successful."))
dispatcher.openSFTPSession()
except Exception, strerror:
raise SFtpException(_("Error logging in to SSH connection: %(err)s")
%{ 'err' : strerror })
class SFTPNOOP(SFTPAbstractCommand):
"""SFTP NOOP command.
This sends some ignored junk data that can be used to keep alive the connection."""
def execute(self, dispatcher):
dispatcher.log(Logger.LOCAL, _("NOOP"))
transport = dispatcher.getTransport()
if transport is None:
return
transport.send_ignore()
dispatcher.log(Logger.REMOTE, _("NOOP OK"))
class SFTPCwd(SFTPAbstractCommand):
"""SFTP change working directory command."""
def __init__(self, dir):
SFTPAbstractCommand.__init__(self)
self.dir = dir
def execute(self, dispatcher):
dispatcher.log(Logger.LOCAL, _("Changing reference directory: %(dir)s")
%{ 'dir' : self.dir })
sftp_client = dispatcher.getSFTPClient()
if sftp_client is None or self.dir == '.':
return
sftp_client.chdir(self.dir)
class SFTPPwd(SFTPAbstractCommand):
"""SFTP get working directory command."""
def __init__(self, status=None):
SFTPAbstractCommand.__init__(self)
self.status = status
def execute(self, dispatcher):
sftp_client = dispatcher.getSFTPClient()
if sftp_client is None:
return
cwd = sftp_client.getcwd()
dispatcher.log(Logger.LOCAL, _("Getting current reference directory: %(dir)s")
%{ 'dir' : cwd })
if self.status:
self.status.setOptionalData(cwd)
self.status.finished()
class SFTPDelete(SFTPAbstractCommand):
"""SFTP delete comand."""
def __init__(self, file):
SFTPAbstractCommand.__init__(self)
self.file = file
def execute(self, dispatcher):
dispatcher.log(Logger.LOCAL, _("Deleting file: %(file)s") %{ 'file' : self.file })
sftp_client = dispatcher.getSFTPClient()
if sftp_client is None:
return
sftp_client.remove(self.file)
dispatcher.log(Logger.REMOTE, _("File successfuly removed."))
class SFTPRename(SFTPAbstractCommand):
"""SFTP rename command."""
def __init__(self, old_name, new_name):
SFTPAbstractCommand.__init__(self)
self.old_name = old_name
self.new_name = new_name
def execute(self, dispatcher):
dispatcher.log(Logger.LOCAL, _("Renaming file: %(old)s to %(new)s")
%{ 'old' : self.old_name, 'new' : self.new_name })
sftp_client = dispatcher.getSFTPClient()
if sftp_client is None:
return
sftp_client.rename(self.old_name, self.new_name)
dispatcher.log(Logger.REMOVE, _("File successfully renamed."))
class SFTPMkdir(SFTPAbstractCommand):
"""SFTP make directory command."""
def __init__(self, dir, status=None):
SFTPAbstractCommand.__init__(self)
self.dir = dir
self.status = status
def execute(self, dispatcher):
dispatcher.log(Logger.LOCAL, _("Creating new directory: %(dir)s")
%{ 'dir' : self.dir })
sftp_client = dispatcher.getSFTPClient()
if sftp_client is None:
return
sftp_client.mkdir(self.dir)
if self.status:
path = self.REMOTE_SEP.join([ sftp_client.getcwd(), self.dir ])
self.status.setOptionalData(path)
self.status.finished()
dispatcher.log(Logger.REMOTE, _("Directory successfully created."))
class SFTPRmdir(SFTPAbstractCommand):
"""SFTP remove directory command."""
def __init__(self, dir):
SFTPAbstractCommand.__init__(self)
self.dir = dir
def execute(self, dispatcher):
dispatcher.log(Logger.LOCAL, _("Removing directory: %(dir)s") %{ 'dir' : self.dir })
sftp_client = dispatcher.getSFTPClient()
if sftp_client is None:
return
sftp_client.rmdir(self.dir)
dispatcher.log(Logger.REMOTE, _("Directory successfully removed."))
class SFTPChmod(SFTPAbstractCommand):
"""SFTP change permissions."""
def __init__(self, file, mode):
SFTPAbstractCommand.__init__(self)
self.file = file
self.mode = mode
def execute(self, dispatcher):
dispatcher.log(Logger.LOCAL, _("Changing permissions for %(file)s to %(mode)s")
%{ 'file' : self.file, 'mode' : self.mode })
sftp_client = dispatcher.getSFTPClient()
if sftp_client is None:
return
sftp_client.chmod(self.file, self.mode)
dispatcher.log(Logger.REMOTE, _("Permissions set successfully."))
class SFTPList(SFTPAbstractCommand):
"""Gets an SFTP listing.
This retries an SFTP listing within an SFTP session. This command takes a
dispatcher status object, which is updated with the latest status as the
listing is retrieved."""
def __init__(self, status=None):
SFTPAbstractCommand.__init__(self)
if status is not None and not isinstance(status, libftpcube.dispatcher.DispatchStatus):
raise SFtpException(_("Invalid status object"))
self.status = status
self.data = [ ]
def execute(self, dispatcher):
dispatcher.log(Logger.LOCAL, _("Getting listing for current directory."))
sftp_client = dispatcher.getSFTPClient()
if sftp_client is None:
return
if self.status:
self.status.start()
self.data = sftp_client.listdir_attr()
# Paramiko converts to unix ls -l style when calling str()
self.data = [ str(x) for x in self.data if x ]
if self.status:
self.status.setOptionalData(self.data)
self.status.finished()
dispatcher.log(Logger.REMOTE, _("Listing sent."))
def getData(self):
return self.data
class SFTPTransfer(SFTPAbstractCommand):
"""Base class for SFTP file transfers.
This base class performs common file transfer function."""
def __init__(self, remote_file, local_file, download=True, status=None):
self.remote_file = remote_file
self.local_file = local_file
self.download = download
self.abort_flag = threading.Event()
if not isinstance(status, libftpcube.dispatcher.DispatchStatus):
raise SFtpException(_("Invalid status object"))
self.status = status
def abort(self):
"""Aborts the current executing transfer."""
self.abort_flag.set()
class SFTPAsciiTransfer(SFTPTransfer):
"""Performs an ASCII file transfer.
This class performs an ASCII file transfer - both upload and download. This
command takes a dispatcher status object, which is updated with the latest
status as the file is transfered."""
MODES = { True : 'r', False : 'w'}
def __init__(self, remote_file, local_file, download=True, status=None):
SFTPTransfer.__init__(self, remote_file, local_file, download=download,
status=status)
def execute(self, dispatcher):
sftp_client = dispatcher.getSFTPClient()
if sftp_client is None:
return
if self.status:
self.status.start()
self.file = sftp_client.file(self.remote_file, self.MODES[self.download])
local_file = open(self.local_file, self.MODES[not self.download])
try:
if self.download:
dispatcher.log(Logger.LOCAL, _("Initiating ASCII download: %(remote)s")
%{ 'remote' : self.remote_file })
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)
dispatcher.log(Logger.REMOTE, _("Transfer of %(file)s completed.")
%{ 'file' : self.remote_file })
else:
dispatcher.log(Logger.LOCAL, _("Initiating ASCII upload: %(remote)s")
%{ 'remote' : self.remote_file })
while not self.abort_flag.isSet():
line = local_file.readline()
if not line:
break
self.file.write(line)
if self.status:
self.status.update(len(line))
dispatcher.log(Logger.REMOTE, _("Transfer of %(file)s completed.")
%{ 'file' : self.remote_file })
finally:
if self.file:
self.file.close()
if local_file:
local_file.close()
if self.status:
self.status.finished()
class SFTPBinaryTransfer(SFTPTransfer):
"""Performs a binary file transfer.
This class performs a binary file transfer - both upload and download. This
command takes a dispatcher status object, which is updated with the latest
status as the file is transfered."""
def __init__(self, remote_file, local_file, download=True, status=None,
blocksize=8192, rest=None):
SFTPTransfer.__init__(self, remote_file, local_file, download=download,
status=status)
self.blocksize = blocksize
self.rest = rest
def execute(self, dispatcher):
sftp_client = dispatcher.getSFTPClient()
if sftp_client is None:
return
self.file = None
local_file = None
try:
if self.download:
dispatcher.log(Logger.LOCAL, _("Initiating binary download: %(remote)s")
%{ 'remote' : self.remote_file })
self.file = sftp_client.file(self.remote_file, 'r')
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.file.read(self.blocksize)
if not data:
break
if self.status:
self.status.update(len(data))
local_file.write(data)
dispatcher.log(Logger.REMOTE, _("Transfer of %(file)s completed.")
%{ 'file' : self.remote_file })
else:
dispatcher.log(Logger.LOCAL, _("Initiating binary upload: %(remote)s")
%{ 'remote' : self.remote_file })
local_file = open(self.local_file, 'rb')
self.file = sftp_client.file(self.remote_file, 'wb')
if self.rest is not None:
self.file.seek(self.rest)
while not self.abort_flag.isSet():
data = local_file.read(self.blocksize)
if not data:
break
self.file.write(data)
if self.status:
self.status.update(len(data))
dispatcher.log(Logger.REMOTE, _("Transfer of %(file)s completed.")
%{ 'file' : self.remote_file })
finally:
if self.file:
self.file.close()
if local_file:
local_file.close()
if self.status:
self.status.finished()
class SFTP(libftpcube.protocol.ProtocolInterface):
"""Main interface for the SFTP protocol implementation.
This class provides file transfer functionality over the SFTP protocol. Classes that
are external to the transports package should only use this class as the interface
into SFTP interactions. Each instance of this object represents an SFTP session,
including both command and data connections."""
def __init__(self, **kwargs):
"""Initializes the SFTP interface.
This creates a new dispatcher thread and dispatch queue for outbound SFTP
commands.
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.
'host_keys' : The path to the SSH host keys file - default
searches in well known places.
"""
try:
self.host = kwargs['host']
except KeyError:
raise SFtpException(_("Error opening SFTP connection: no host given"))
try:
self.port = kwargs['port']
except KeyError:
raise SFtpException(_("Error opening SFTP connection: no port given"))
try:
self.logger = kwargs['logger']
except KeyError:
self.logger = None
self.dispatcher = SFTPDispatcher(**kwargs)
self.dispatcher.start()
def setPassive(self, passive):
"""This has no meaning for the SFTP."""
pass
def getPassive(self):
"""Always returns True."""
return True
def getDispatcher(self):
"""Returns the dispatcher for this SFTP session."""
return self.dispatcher
def destroy(self):
"""Destroys the SFTP 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 SFTP 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 SFTP connection."""
connect = SFTPConnect()
self.addOutgoing(connect, err_handler)
def getWelcomeMessage(self, err_handler=None):
"""Empty implementation.
This command has no meaning in SFTP."""
pass
def login(self, username, password, err_handler=None):
"""Performs a login to the SSH session.
The specified username and password are used for the login. As a result of the
login, an SFTP client session is opened."""
login = SFTPLogin(username, password)
self.addOutgoing(login, err_handler)
def noop(self, err_handler=None):
"""Sends a NOOP command to the SFTP session."""
noop = SFTPNOOP()
self.addOutgoing(noop, err_handler)
def cwd(self, dir, err_handler=None):
"""Changes the current working directory to the specified directory."""
cwdcmd = SFTPCwd(dir)
self.addOutgoing(cwdcmd, 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."""
pwdcmd = SFTPPwd(status=status)
self.addOutgoing(pwdcmd, err_handler)
def delete(self, file, err_handler=None):
delcmd = SFTPDelete(file)
self.addOutgoing(delcmd, err_handler)
def rename(self, name, new_name, err_handler=None):
"""Performs a file rename for a file in the current directory."""
recmd = SFTPRename(name, new_name)
self.addOutgoing(recmd, 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."""
mkcmd = SFTPMkdir(dir, status=status)
self.addOutgoing(mkcmd, err_handler)
def rmdir(self, dir, err_handler=None):
"""Removes the specified directory."""
rmcmd = SFTPRmdir(dir)
self.addOutgoing(rmcmd, 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."""
chmodcmd = SFTPChmod(file, mode)
self.addOutgoing(chmodcmd, err_handler)
def abort(self, err_handler=None):
"""Performs an abort of the current running command if possible."""
executing = self.dispatcher.getExecutingCommand()
if isinstance(executing, SFTPTransfer):
executing.abort()
self.dispatcher.resetQueue()
if isinstance(executing, SFTPConnect):
self.dispatcher.destroy()
def list(self, args=None, status=None, passive=True, err_handler=None):
"""Gets a listing of the current directory.
This implementation ignores the 'args' and 'passive' arguments."""
listcmd = SFTPList(status=status)
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)."""
local_file = os.path.join(local_dir, file)
asciicmd = SFTPAsciiTransfer(file, local_file, status=status)
self.addOutgoing(asciicmd, err_handler)
def uploadAscii(self, file, local_dir, status=None, passive=None, 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)."""
local_file = os.path.join(local_dir, file)
asciicmd = SFTPAsciiTransfer(file, local_file, download=False, status=status)
self.addOutgoing(ascicmd, 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)."""
local_file = os.path.join(local_dir, file)
binarycmd = SFTPBinaryTransfer(file, local_file, status=status, blocksize=blocksize,
rest=rest)
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).
"""
local_file = os.path.join(local_dir, file)
binarycmd = SFTPBinaryTransfer(file, local_file, download=False, status=status,
blocksize=blocksize, rest=rest)
self.addOutgoing(binarycmd, err_handler)
|