/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/transport/sftp.py

  • Committer: Robert Collins
  • Date: 2006-08-08 23:19:29 UTC
  • mfrom: (1884 +trunk)
  • mto: This revision was merged to the branch mainline in revision 1912.
  • Revision ID: robertc@robertcollins.net-20060808231929-4e3e298190214b3a
current status

Show diffs side-by-side

added added

removed removed

Lines of Context:
22
22
import os
23
23
import random
24
24
import re
 
25
import select
 
26
import socket
25
27
import stat
26
28
import subprocess
27
29
import sys
39
41
                           PathError,
40
42
                           ParamikoNotPresent,
41
43
                           )
42
 
from bzrlib.osutils import pathjoin, fancy_rename
 
44
from bzrlib.osutils import pathjoin, fancy_rename, getcwd
43
45
from bzrlib.trace import mutter, warning, error
44
46
from bzrlib.transport import (
45
47
    register_urlparse_netloc_protocol,
100
102
                }
101
103
 
102
104
 
103
 
# don't use prefetch unless paramiko version >= 1.5.2 (there were bugs earlier)
104
 
_default_do_prefetch = False
105
 
if getattr(paramiko, '__version_info__', (0, 0, 0)) >= (1, 5, 5):
106
 
    _default_do_prefetch = True
 
105
_paramiko_version = getattr(paramiko, '__version_info__', (0, 0, 0))
 
106
# don't use prefetch unless paramiko version >= 1.5.5 (there were bugs earlier)
 
107
_default_do_prefetch = (_paramiko_version >= (1, 5, 5))
 
108
 
 
109
# Paramiko 1.5 tries to open a socket.AF_UNIX in order to connect
 
110
# to ssh-agent. That attribute doesn't exist on win32 (it does in cygwin)
 
111
# so we get an AttributeError exception. So we will not try to
 
112
# connect to an agent if we are on win32 and using Paramiko older than 1.6
 
113
_use_ssh_agent = (sys.platform != 'win32' or _paramiko_version >= (1, 6, 0)) 
107
114
 
108
115
 
109
116
_ssh_vendor = None
302
309
            # What specific errors should we catch here?
303
310
            pass
304
311
 
 
312
 
305
313
class SFTPTransport (Transport):
306
314
    """
307
315
    Transport implementation for SFTP access.
373
381
                basepath.append(p)
374
382
 
375
383
        path = '/'.join(basepath)
 
384
        # mutter('relpath => remotepath %s => %s', relpath, path)
376
385
        return path
377
386
 
378
387
    def relpath(self, abspath):
496
505
 
497
506
    def mkdir(self, relpath, mode=None):
498
507
        """Create a directory at the given path."""
 
508
        path = self._remote_path(relpath)
499
509
        try:
500
 
            path = self._remote_path(relpath)
501
510
            # In the paramiko documentation, it says that passing a mode flag 
502
511
            # will filtered against the server umask.
503
512
            # StubSFTPServer does not do this, which would be nice, because it is
702
711
        vendor = _get_ssh_vendor()
703
712
        if vendor == 'loopback':
704
713
            sock = socket.socket()
705
 
            sock.connect((self._host, self._port))
 
714
            try:
 
715
                sock.connect((self._host, self._port))
 
716
            except socket.error, e:
 
717
                raise ConnectionError('Unable to connect to SSH host %s:%s: %s'
 
718
                                      % (self._host, self._port, e))
706
719
            self._sftp = SFTPClient(LoopbackSFTP(sock))
707
720
        elif vendor != 'none':
708
721
            sock = SFTPSubprocess(self._host, vendor, self._port,
723
736
            t.set_log_channel('bzr.paramiko')
724
737
            t.start_client()
725
738
        except paramiko.SSHException, e:
726
 
            raise ConnectionError('Unable to reach SSH host %s:%d' %
727
 
                                  (self._host, self._port), e)
 
739
            raise ConnectionError('Unable to reach SSH host %s:%s: %s' 
 
740
                                  % (self._host, self._port, e))
728
741
            
729
742
        server_key = t.get_remote_server_key()
730
743
        server_key_hex = paramiko.util.hexify(server_key.get_fingerprint())
767
780
        # Also, it would mess up the self.relpath() functionality
768
781
        username = self._username or getpass.getuser()
769
782
 
770
 
        # Paramiko tries to open a socket.AF_UNIX in order to connect
771
 
        # to ssh-agent. That attribute doesn't exist on win32 (it does in cygwin)
772
 
        # so we get an AttributeError exception. For now, just don't try to
773
 
        # connect to an agent if we are on win32
774
 
        if sys.platform != 'win32':
 
783
        if _use_ssh_agent:
775
784
            agent = paramiko.Agent()
776
785
            for key in agent.get_keys():
777
786
                mutter('Trying SSH agent key %s' % paramiko.util.hexify(key.get_fingerprint()))
844
853
        :param mode: The mode permissions bits for the new file
845
854
        """
846
855
        path = self._sftp._adjust_cwd(abspath)
 
856
        # mutter('sftp abspath %s => %s', abspath, path)
847
857
        attr = SFTPAttributes()
848
858
        if mode is not None:
849
859
            attr.st_mode = mode
883
893
nvuQES5C9BMHjF39LZiGH1iLQy7FgdHyoP+eodI7
884
894
-----END RSA PRIVATE KEY-----
885
895
"""
886
 
    
887
 
 
888
 
class SingleListener(threading.Thread):
 
896
 
 
897
 
 
898
class SocketListener(threading.Thread):
889
899
 
890
900
    def __init__(self, callback):
891
901
        threading.Thread.__init__(self)
895
905
        self._socket.bind(('localhost', 0))
896
906
        self._socket.listen(1)
897
907
        self.port = self._socket.getsockname()[1]
898
 
        self.stop_event = threading.Event()
899
 
 
900
 
    def run(self):
901
 
        s, _ = self._socket.accept()
902
 
        # now close the listen socket
903
 
        self._socket.close()
904
 
        try:
905
 
            self._callback(s, self.stop_event)
906
 
        except socket.error:
907
 
            pass #Ignore socket errors
908
 
        except Exception, x:
909
 
            # probably a failed test
910
 
            warning('Exception from within unit test server thread: %r' % x)
 
908
        self._stop_event = threading.Event()
911
909
 
912
910
    def stop(self):
913
 
        self.stop_event.set()
 
911
        # called from outside this thread
 
912
        self._stop_event.set()
914
913
        # use a timeout here, because if the test fails, the server thread may
915
914
        # never notice the stop_event.
916
915
        self.join(5.0)
 
916
        self._socket.close()
 
917
 
 
918
    def run(self):
 
919
        while True:
 
920
            readable, writable_unused, exception_unused = \
 
921
                select.select([self._socket], [], [], 0.1)
 
922
            if self._stop_event.isSet():
 
923
                return
 
924
            if len(readable) == 0:
 
925
                continue
 
926
            try:
 
927
                s, addr_unused = self._socket.accept()
 
928
                # because the loopback socket is inline, and transports are
 
929
                # never explicitly closed, best to launch a new thread.
 
930
                threading.Thread(target=self._callback, args=(s,)).start()
 
931
            except socket.error, x:
 
932
                sys.excepthook(*sys.exc_info())
 
933
                warning('Socket error during accept() within unit test server'
 
934
                        ' thread: %r' % x)
 
935
            except Exception, x:
 
936
                # probably a failed test; unit test thread will log the
 
937
                # failure/error
 
938
                sys.excepthook(*sys.exc_info())
 
939
                warning('Exception from within unit test server thread: %r' % 
 
940
                        x)
917
941
 
918
942
 
919
943
class SFTPServer(Server):
937
961
        """StubServer uses this to log when a new server is created."""
938
962
        self.logs.append(message)
939
963
 
940
 
    def _run_server(self, s, stop_event):
 
964
    def _run_server(self, s):
941
965
        ssh_server = paramiko.Transport(s)
942
 
        key_file = os.path.join(self._homedir, 'test_rsa.key')
943
 
        file(key_file, 'w').write(STUB_SERVER_KEY)
 
966
        key_file = pathjoin(self._homedir, 'test_rsa.key')
 
967
        f = open(key_file, 'w')
 
968
        f.write(STUB_SERVER_KEY)
 
969
        f.close()
944
970
        host_key = paramiko.RSAKey.from_private_key_file(key_file)
945
971
        ssh_server.add_server_key(host_key)
946
972
        server = StubServer(self)
950
976
        event = threading.Event()
951
977
        ssh_server.start_server(event, server)
952
978
        event.wait(5.0)
953
 
        stop_event.wait(30.0)
954
979
    
955
980
    def setUp(self):
956
981
        global _ssh_vendor
957
982
        self._original_vendor = _ssh_vendor
958
983
        _ssh_vendor = self._vendor
959
 
        self._homedir = os.getcwd()
 
984
        if sys.platform == 'win32':
 
985
            # Win32 needs to use the UNICODE api
 
986
            self._homedir = getcwd()
 
987
        else:
 
988
            # But Linux SFTP servers should just deal in bytestreams
 
989
            self._homedir = os.getcwd()
960
990
        if self._server_homedir is None:
961
991
            self._server_homedir = self._homedir
962
992
        self._root = '/'
963
 
        # FIXME WINDOWS: _root should be _server_homedir[0]:/
964
 
        self._listener = SingleListener(self._run_server)
 
993
        if sys.platform == 'win32':
 
994
            self._root = ''
 
995
        self._listener = SocketListener(self._run_server)
965
996
        self._listener.setDaemon(True)
966
997
        self._listener.start()
967
998
 
971
1002
        self._listener.stop()
972
1003
        _ssh_vendor = self._original_vendor
973
1004
 
 
1005
    def get_bogus_url(self):
 
1006
        """See bzrlib.transport.Server.get_bogus_url."""
 
1007
        # this is chosen to try to prevent trouble with proxies, wierd dns,
 
1008
        # etc
 
1009
        return 'sftp://127.0.0.1:1/'
 
1010
 
 
1011
 
974
1012
 
975
1013
class SFTPFullAbsoluteServer(SFTPServer):
976
1014
    """A test server for sftp transports, using absolute urls and ssh."""
987
1025
        super(SFTPServerWithoutSSH, self).__init__()
988
1026
        self._vendor = 'loopback'
989
1027
 
990
 
    def _run_server(self, sock, stop_event):
 
1028
    def _run_server(self, sock):
991
1029
        class FakeChannel(object):
992
1030
            def get_transport(self):
993
1031
                return self
1002
1040
 
1003
1041
        server = paramiko.SFTPServer(FakeChannel(), 'sftp', StubServer(self), StubSFTPServer,
1004
1042
                                     root=self._root, home=self._server_homedir)
1005
 
        server.start_subsystem('sftp', None, sock)
 
1043
        try:
 
1044
            server.start_subsystem('sftp', None, sock)
 
1045
        except socket.error, e:
 
1046
            if (len(e.args) > 0) and (e.args[0] == errno.EPIPE):
 
1047
                # it's okay for the client to disconnect abruptly
 
1048
                # (bug in paramiko 1.6: it should absorb this exception)
 
1049
                pass
 
1050
            else:
 
1051
                raise
 
1052
        except Exception, e:
 
1053
            import sys; sys.stderr.write('\nEXCEPTION %r\n\n' % e.__class__)
1006
1054
        server.finish_subsystem()
1007
1055
 
1008
1056
 
1011
1059
 
1012
1060
    def get_url(self):
1013
1061
        """See bzrlib.transport.Server.get_url."""
1014
 
        return self._get_sftp_url(urlutils.escape(self._homedir[1:]))
 
1062
        if sys.platform == 'win32':
 
1063
            return self._get_sftp_url(urlutils.escape(self._homedir))
 
1064
        else:
 
1065
            return self._get_sftp_url(urlutils.escape(self._homedir[1:]))
1015
1066
 
1016
1067
 
1017
1068
class SFTPHomeDirServer(SFTPServerWithoutSSH):