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).

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>