~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/transport/sftp.py

  • Committer: Martin Pool
  • Date: 2006-01-30 06:23:50 UTC
  • mfrom: (1534.1.17 integration)
  • Revision ID: mbp@sourcefrog.net-20060130062350-d6f25277ddcdfd79
[merge] robert's integration of much recent work

Show diffs side-by-side

added added

removed removed

Lines of Context:
144
144
        self.proc.wait()
145
145
 
146
146
 
 
147
class LoopbackSFTP(object):
 
148
    """Simple wrapper for a socket that pretends to be a paramiko Channel."""
 
149
 
 
150
    def __init__(self, sock):
 
151
        self.__socket = sock
 
152
 
 
153
    def send(self, data):
 
154
        return self.__socket.send(data)
 
155
 
 
156
    def recv(self, n):
 
157
        return self.__socket.recv(n)
 
158
 
 
159
    def recv_ready(self):
 
160
        return True
 
161
 
 
162
    def close(self):
 
163
        self.__socket.close()
 
164
 
 
165
 
147
166
SYSTEM_HOSTKEYS = {}
148
167
BZR_HOSTKEYS = {}
149
168
 
153
172
# X seconds. But that requires a lot more fanciness.
154
173
_connected_hosts = weakref.WeakValueDictionary()
155
174
 
 
175
 
156
176
def load_host_keys():
157
177
    """
158
178
    Load system host keys (probably doesn't work on windows) and any
170
190
        mutter('failed to load bzr host keys: ' + str(e))
171
191
        save_host_keys()
172
192
 
 
193
 
173
194
def save_host_keys():
174
195
    """
175
196
    Save "discovered" host keys in $(config)/ssh_host_keys/.
209
230
    def __del__(self):
210
231
        """Should this warn, or actually try to cleanup?"""
211
232
        if self.lock_file:
212
 
            warn("SFTPLock %r not explicitly unlocked" % (self.path,))
 
233
            warning("SFTPLock %r not explicitly unlocked" % (self.path,))
213
234
            self.unlock()
214
235
 
215
236
    def unlock(self):
224
245
            pass
225
246
 
226
247
 
227
 
 
228
248
class SFTPTransport (Transport):
229
249
    """
230
250
    Transport implementation for SFTP access.
673
693
            pass
674
694
        
675
695
        vendor = _get_ssh_vendor()
676
 
        if vendor != 'none':
 
696
        if vendor == 'loopback':
 
697
            sock = socket.socket()
 
698
            sock.connect((self._host, self._port))
 
699
            self._sftp = SFTPClient(LoopbackSFTP(sock))
 
700
        elif vendor != 'none':
677
701
            sock = SFTPSubprocess(self._host, vendor, self._port,
678
702
                                  self._username)
679
703
            self._sftp = SFTPClient(sock)
689
713
 
690
714
        try:
691
715
            t = paramiko.Transport((self._host, self._port or 22))
 
716
            t.set_log_channel('bzr.paramiko')
692
717
            t.start_client()
693
718
        except paramiko.SSHException, e:
694
719
            raise ConnectionError('Unable to reach SSH host %s:%d' %
869
894
        s, _ = self._socket.accept()
870
895
        # now close the listen socket
871
896
        self._socket.close()
872
 
        self._callback(s, self.stop_event)
873
 
    
 
897
        try:
 
898
            self._callback(s, self.stop_event)
 
899
        except socket.error:
 
900
            pass #Ignore socket errors
 
901
        except Exception, x:
 
902
            # probably a failed test
 
903
            warning('Exception from within unit test server thread: %r' % x)
 
904
 
874
905
    def stop(self):
875
906
        self.stop_event.set()
876
 
        # We should consider waiting for the other thread
877
 
        # to stop, because otherwise we get spurious
878
 
        #   bzr: ERROR: Socket exception: Connection reset by peer (54)
879
 
        # because the test suite finishes before the thread has a chance
880
 
        # to close. (Especially when only running a few tests)
881
 
        
882
 
        
 
907
        # use a timeout here, because if the test fails, the server thread may
 
908
        # never notice the stop_event.
 
909
        self.join(5.0)
 
910
 
 
911
 
883
912
class SFTPServer(Server):
884
913
    """Common code for SFTP server facilities."""
885
914
 
886
 
    def _get_sftp_url(self, path):
887
 
        """Calculate a sftp url to this server for path."""
888
 
        return 'sftp://foo:bar@localhost:%d/%s' % (self._listener.port, path)
889
 
 
890
915
    def __init__(self):
891
916
        self._original_vendor = None
892
917
        self._homedir = None
893
918
        self._server_homedir = None
894
919
        self._listener = None
895
920
        self._root = None
 
921
        self._vendor = 'none'
896
922
        # sftp server logs
897
923
        self.logs = []
898
924
 
 
925
    def _get_sftp_url(self, path):
 
926
        """Calculate an sftp url to this server for path."""
 
927
        return 'sftp://foo:bar@localhost:%d/%s' % (self._listener.port, path)
 
928
 
899
929
    def log(self, message):
900
 
        """What to do here? do we need this? Its for the StubServer.."""
 
930
        """StubServer uses this to log when a new server is created."""
901
931
        self.logs.append(message)
902
932
 
903
933
    def _run_server(self, s, stop_event):
914
944
        ssh_server.start_server(event, server)
915
945
        event.wait(5.0)
916
946
        stop_event.wait(30.0)
917
 
 
 
947
    
918
948
    def setUp(self):
919
 
        """See bzrlib.transport.Server.setUp."""
920
 
        # XXX: 20051124 jamesh
921
 
        # The tests currently pop up a password prompt when an external ssh
922
 
        # is used.  This forces the use of the paramiko implementation.
923
949
        global _ssh_vendor
924
950
        self._original_vendor = _ssh_vendor
925
 
        _ssh_vendor = 'none'
 
951
        _ssh_vendor = self._vendor
926
952
        self._homedir = os.getcwdu()
927
953
        if self._server_homedir is None:
928
954
            self._server_homedir = self._homedir
939
965
        _ssh_vendor = self._original_vendor
940
966
 
941
967
 
942
 
class SFTPAbsoluteServer(SFTPServer):
 
968
class SFTPServerWithoutSSH(SFTPServer):
 
969
    """
 
970
    Common code for an SFTP server over a clear TCP loopback socket,
 
971
    instead of over an SSH secured socket.
 
972
    """
 
973
 
 
974
    def __init__(self):
 
975
        super(SFTPServerWithoutSSH, self).__init__()
 
976
        self._vendor = 'loopback'
 
977
 
 
978
    def _run_server(self, sock, stop_event):
 
979
        class FakeChannel(object):
 
980
            def get_transport(self):
 
981
                return self
 
982
            def get_log_channel(self):
 
983
                return 'paramiko'
 
984
            def get_name(self):
 
985
                return '1'
 
986
            def get_hexdump(self):
 
987
                return False
 
988
 
 
989
        server = paramiko.SFTPServer(FakeChannel(), 'sftp', StubServer(self), StubSFTPServer,
 
990
                                     root=self._root, home=self._server_homedir)
 
991
        server.start_subsystem('sftp', None, sock)
 
992
        server.finish_subsystem()
 
993
 
 
994
 
 
995
class SFTPAbsoluteServer(SFTPServerWithoutSSH):
943
996
    """A test server for sftp transports, using absolute urls."""
944
997
 
945
998
    def get_url(self):
947
1000
        return self._get_sftp_url(urlescape(self._homedir[1:]))
948
1001
 
949
1002
 
950
 
class SFTPHomeDirServer(SFTPServer):
 
1003
class SFTPHomeDirServer(SFTPServerWithoutSSH):
951
1004
    """A test server for sftp transports, using homedir relative urls."""
952
1005
 
953
1006
    def get_url(self):