[Twisted-Python] Conch SFTP client help

Brad Milne brad.milne at devx.runthered.com
Tue Feb 8 20:45:31 MST 2011


Nevermind. I was missing 'self.dataReceived =
self.interface_handler.dataReceived' in the __init__ of SSHSession
and self.dataReceived = self._client.dataReceived in the __init__ of
SftpClient. Maybe I need to reduce the abstraction a bit here, hmm.

Well hopefully this helps someone sometime....


On 9 February 2011 10:00, Brad Milne <brad.milne at devx.runthered.com> wrote:

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



-- 
Brad Milne | Run The Red | *brad.milne at devx.runthered.com*
-------------- next part --------------
An HTML attachment was scrubbed...
URL: </pipermail/twisted-python/attachments/20110209/531636b3/attachment.html>


More information about the Twisted-Python mailing list