[Twisted-Python] Conch SFTP client help

Brad Milne brad.milne at devx.runthered.com
Tue Feb 8 14:00:05 MST 2011


Hi all

I've looked around for SFTP client doco, but it's come up pretty thin on the
ground. I've seen http://twistedmatrix.com/trac/wiki/TwistedConch and
everything under it. With it I've been able to connect via SSH to a server
and run a command (*a la* the 'cat' example from tutorial page and from
sshsimpleclient.py<http://twistedmatrix.com/documents/current/conch/examples/sshsimpleclient.py>
).

On the other hand, plenty of posts recommend checking out cftp.py in
scripts/. That uses the FileTransferClient, and appears to be more along the
lines of what is needed for a persistent SFTP connection and file transfers.

What seems to be happening with the below is that the connection is being
created OK, but I haven't been able to utilise the _remoteGlob method (taken
from cftp.py). It simply hangs.

My sandbox code is below.

Any help greatly appreciated.
Brad

<code>
import os
from twisted.python import failure
from twisted.internet import reactor, protocol, defer
from twisted.conch.ssh import transport, connection, userauth, channel,
common, filetransfer
import logging
from twisted.python import log
import sys

class SimpleTransport(transport.SSHClientTransport):
    def verifyHostKey(self, hostKey, fingerprint):
        print 'host key fingerprint: %s' % fingerprint
        return defer.succeed(1)

    def connectionSecure(self):
        '''
        called when the encryption is set up and other services can be run
        '''
        self.requestService(SimpleUserAuth(USERNAME,SimpleConnection()))

class SimpleConnection(connection.SSHConnection):
    def serviceStarted(self):
        self.openChannel(SSHSession())

class SimpleUserAuth(userauth.SSHUserAuthClient):
    def getPassword(self):
        return defer.succeed(PASS)

class SSHSession(channel.SSHChannel):
    '''
    Things to do within the active SSH session.
    '''
    name = 'session'

    def __init__(self, interface_handler=None, *args, **kwargs):
        channel.SSHChannel.__init__(self, *args, **kwargs)
        self.interface_handler = interface_handler or SftpClient(self)

    def channelOpen(self, ignoredData):
        self.data = ''
        # Create an sftp connection (stays open)
        d = self.conn.sendRequest(self, 'subsystem', common.NS('sftp'),
wantReply=1)
        d.addCallback(self._cbSubsystem)

    def _cbSubsystem(self, result):
        self.interface_handler.getDirectoryContents('/tmp')

    def closeReceived(self):
        logging.info('remote side closed %s' % self)
        self.conn.sendClose(self)
        reactor.stop()

class SftpClient(object):

    def __init__(self, transport, *args, **kwargs):
        super(SftpClient, self).__init__(*args, **kwargs)
        self.transport = transport
        self._client = filetransfer.FileTransferClient()

    @property
    def client(self):
        if not self._client.connected:
            self._client.makeConnection(self.transport)
            logging.debug("Made 'connection' with transport class")
        return self._client

    def getDirectoryContents(self, path):
        d = self._remoteGlob(path)

        def gotit(files):
            print "Got %s: %s" % (type(files), files)
        d.addCallback(gotit)
        return d

    # Accessory methods.
    # These are stolen from twisted.conch.scripts.cftp.py. We can't
    # import that module as it contains unix-dependent imports.
    def _remoteGlob(self, fullPath):
        logging.debug('looking up %s' % fullPath)
        head, tail = os.path.split(fullPath)
        if '*' in tail or '?' in tail:
            glob = 1
        else:
            glob = 0
        if tail and not glob: # could be file or directory
            # try directory first
            logging.debug("Opening dir")
            d = self.client.openDirectory(fullPath)
            d.addCallback(self._cbOpenList, '')
            d.addErrback(self._ebNotADirectory, head, tail)
        else:
            d = self.client.openDirectory(head)
            d.addCallback(self._cbOpenList, tail)
        return d

    def _cbOpenList(self, directory, glob):
        logging.debug("Got dir")
        files = []
        d = directory.read()
        d.addBoth(self._cbReadFile, files, directory, glob)
        return d

    def _ebNotADirectory(self, reason, path, glob):
        logging.debug("Not a dir")
        d = self.client.openDirectory(path)
        d.addCallback(self._cbOpenList, glob)
        return d

    def _cbReadFile(self, files, l, directory, glob):
        logging.debug("Reading file")
        if not isinstance(files, failure.Failure):
            if glob:
                raise NotImplementedError("don't have fnmatch available to
use on Windows so have commented this bit out")
#                l.extend([f for f in files if fnmatch.fnmatch(f[0], glob)])
            else:
                l.extend(files)
            d = directory.read()
            d.addBoth(self._cbReadFile, l, directory, glob)
            return d
        else:
            reason = files
            reason.trap(EOFError)
            directory.close()
            return l

if __name__=='__main__':
    protocol.ClientCreator(reactor, SimpleTransport).connectTCP(HOST, 22)
    log.startLogging(sys.stdout, setStdout=0)
    reactor.run()

</code>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: </pipermail/twisted-python/attachments/20110209/af455516/attachment.html>


More information about the Twisted-Python mailing list