Hi all
<div><br></div><div>I've looked around for SFTP client doco, but it's come up pretty thin on the ground. I've seen <a href="http://twistedmatrix.com/trac/wiki/TwistedConch" target="_blank">http://twistedmatrix.com/trac/wiki/TwistedConch</a> and everything under it. With it I've been able to connect via SSH to a server and run a command (<i>a la</i> the 'cat' example from tutorial page and from <a href="http://twistedmatrix.com/documents/current/conch/examples/sshsimpleclient.py" shape="rect" target="_blank">sshsimpleclient.py</a>).</div>
<div><br></div><div>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.</div>
<div><br></div><div>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.</div><div><br>
</div><div>My sandbox code is below.</div><div><br></div><div>Any help greatly appreciated.</div><div>Brad</div><div><br></div><div><code></div><div><div>import os</div><div>from twisted.python import failure</div></div>
<div><div>from twisted.internet import reactor, protocol, defer</div><div>from twisted.conch.ssh import transport, connection, userauth, channel, common, filetransfer</div><div>import logging</div><div>from twisted.python import log</div>
<div>import sys</div></div><div><br></div><div><div>class SimpleTransport(transport.SSHClientTransport):</div><div> def verifyHostKey(self, hostKey, fingerprint):</div><div> print 'host key fingerprint: %s' % fingerprint</div>
<div> return defer.succeed(1) </div><div><br></div><div> def connectionSecure(self):</div><div> '''</div><div> called when the encryption is set up and other services can be run</div><div>
'''</div><div> self.requestService(SimpleUserAuth(USERNAME,SimpleConnection()))</div><div><br></div><div>class SimpleConnection(connection.SSHConnection):</div><div> def serviceStarted(self):</div>
<div> self.openChannel(SSHSession())</div><div> </div><div>class SimpleUserAuth(userauth.SSHUserAuthClient):</div><div> def getPassword(self):</div><div> return defer.succeed(PASS)</div></div><div>
<br></div><div><div>class SSHSession(channel.SSHChannel):</div><div> '''</div><div> Things to do within the active SSH session.</div><div> '''</div><div> name = 'session'</div><div>
</div><div> def __init__(self, interface_handler=None, *args, **kwargs):</div><div> channel.SSHChannel.__init__(self, *args, **kwargs)</div><div> self.interface_handler = interface_handler or SftpClient(self)</div>
<div> </div><div> def channelOpen(self, ignoredData):</div><div> self.data = ''</div><div> # Create an sftp connection (stays open)</div><div> d = self.conn.sendRequest(self, 'subsystem', common.NS('sftp'), wantReply=1)</div>
<div> d.addCallback(self._cbSubsystem)</div><div> </div><div> def _cbSubsystem(self, result):</div><div> self.interface_handler.getDirectoryContents('/tmp')</div><div> </div><div> def closeReceived(self):</div>
<div> <a href="http://logging.info">logging.info</a>('remote side closed %s' % self)</div><div> self.conn.sendClose(self)</div><div> reactor.stop()</div><div><br></div><div><div>class SftpClient(object):</div>
<div> </div><div> def __init__(self, transport, *args, **kwargs):</div><div> super(SftpClient, self).__init__(*args, **kwargs)</div><div> self.transport = transport</div><div> self._client = filetransfer.FileTransferClient()</div>
<div><br></div><div> @property</div><div> def client(self):</div><div> if not self._client.connected:</div><div> self._client.makeConnection(self.transport)</div><div> logging.debug("Made 'connection' with transport class")</div>
<div> return self._client</div><div> </div><div> def getDirectoryContents(self, path):</div><div> d = self._remoteGlob(path)</div><div> </div><div> def gotit(files):</div><div> print "Got %s: %s" % (type(files), files)</div>
<div> d.addCallback(gotit)</div><div> return d</div><div> </div><div> # Accessory methods.</div><div> # These are stolen from <a href="http://twisted.conch.scripts.cftp.py">twisted.conch.scripts.cftp.py</a>. We can't</div>
<div> # import that module as it contains unix-dependent imports.</div><div> def _remoteGlob(self, fullPath):</div><div> logging.debug('looking up %s' % fullPath)</div><div> head, tail = os.path.split(fullPath)</div>
<div> if '*' in tail or '?' in tail:</div><div> glob = 1</div><div> else:</div><div> glob = 0</div><div> if tail and not glob: # could be file or directory</div><div>
# try directory first</div><div> logging.debug("Opening dir")</div><div> d = self.client.openDirectory(fullPath)</div><div> d.addCallback(self._cbOpenList, '')</div>
<div> d.addErrback(self._ebNotADirectory, head, tail)</div><div> else:</div><div> d = self.client.openDirectory(head)</div><div> d.addCallback(self._cbOpenList, tail)</div><div> return d</div>
<div><br></div><div> def _cbOpenList(self, directory, glob):</div><div> logging.debug("Got dir")</div><div> files = []</div><div> d = directory.read()</div><div> d.addBoth(self._cbReadFile, files, directory, glob)</div>
<div> return d</div><div><br></div><div> def _ebNotADirectory(self, reason, path, glob):</div><div> logging.debug("Not a dir")</div><div> d = self.client.openDirectory(path)</div><div> d.addCallback(self._cbOpenList, glob)</div>
<div> return d</div><div><br></div><div> def _cbReadFile(self, files, l, directory, glob):</div><div> logging.debug("Reading file")</div><div> if not isinstance(files, failure.Failure):</div>
<div> if glob:</div><div> raise NotImplementedError("don't have fnmatch available to use on Windows so have commented this bit out")</div><div># l.extend([f for f in files if fnmatch.fnmatch(f[0], glob)])</div>
<div> else:</div><div> l.extend(files)</div><div> d = directory.read()</div><div> d.addBoth(self._cbReadFile, l, directory, glob)</div><div> return d</div><div> else:</div>
<div> reason = files</div><div> reason.trap(EOFError)</div><div> directory.close()</div><div> return l</div></div><div> </div><div>if __name__=='__main__':</div><div>
protocol.ClientCreator(reactor, SimpleTransport).connectTCP(HOST, 22)</div><div> log.startLogging(sys.stdout, setStdout=0)</div><div> reactor.run()</div></div><div><br></div><div></code></div>