~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/transport/sftp.py

transport implementations now tested consistently.

Show diffs side-by-side

added added

removed removed

Lines of Context:
29
29
import subprocess
30
30
import weakref
31
31
 
32
 
from bzrlib.errors import (FileExists, 
 
32
from bzrlib.errors import (ConnectionError,
 
33
                           FileExists, 
33
34
                           TransportNotPossible, NoSuchFile, PathNotChild,
34
35
                           TransportError,
35
 
                           LockError)
 
36
                           LockError
 
37
                           )
36
38
from bzrlib.config import config_dir
37
39
from bzrlib.trace import mutter, warning, error
38
 
from bzrlib.transport import Transport, register_transport
 
40
from bzrlib.transport import Transport, Server, urlescape
39
41
import bzrlib.ui
40
42
 
41
43
try:
98
100
 
99
101
class SFTPSubprocess:
100
102
    """A socket-like object that talks to an ssh subprocess via pipes."""
101
 
    def __init__(self, hostname, port=None, user=None):
102
 
        vendor = _get_ssh_vendor()
 
103
    def __init__(self, hostname, vendor, port=None, user=None):
103
104
        assert vendor in ['openssh', 'ssh']
104
105
        if vendor == 'openssh':
105
106
            args = ['ssh',
213
214
            pass
214
215
 
215
216
 
 
217
 
216
218
class SFTPTransport (Transport):
217
219
    """
218
220
    Transport implementation for SFTP access.
223
225
        assert base.startswith('sftp://')
224
226
        self._parse_url(base)
225
227
        base = self._unparse_url()
 
228
        if base[-1] != '/':
 
229
            base = base + '/'
226
230
        super(SFTPTransport, self).__init__(base)
227
231
        if clone_from is None:
228
232
            self._sftp_connect()
299
303
            extra = ': ' + ', '.join(error)
300
304
            raise PathNotChild(abspath, self.base, extra=extra)
301
305
        pl = len(self._path)
302
 
        return path[pl:].lstrip('/')
 
306
        return path[pl:].strip('/')
303
307
 
304
308
    def has(self, relpath):
305
309
        """
656
660
        
657
661
        vendor = _get_ssh_vendor()
658
662
        if vendor != 'none':
659
 
            sock = SFTPSubprocess(self._host, self._port, self._username)
 
663
            sock = SFTPSubprocess(self._host, vendor, self._port,
 
664
                                  self._username)
660
665
            self._sftp = SFTPClient(sock)
661
666
        else:
662
667
            self._paramiko_connect()
736
741
        if self._try_pkey_auth(transport, paramiko.DSSKey, username, 'id_dsa'):
737
742
            return
738
743
 
739
 
 
740
744
        if self._password:
741
745
            try:
742
746
                transport.auth_password(username, self._password)
807
811
                failure_exc=FileExists)
808
812
 
809
813
 
810
 
class SFTPAbsoluteServer(object):
 
814
# ------------- server test implementation --------------
 
815
import socket
 
816
import threading
 
817
 
 
818
from bzrlib.tests.stub_sftp import StubServer, StubSFTPServer
 
819
 
 
820
STUB_SERVER_KEY = """
 
821
-----BEGIN RSA PRIVATE KEY-----
 
822
MIICWgIBAAKBgQDTj1bqB4WmayWNPB+8jVSYpZYk80Ujvj680pOTh2bORBjbIAyz
 
823
oWGW+GUjzKxTiiPvVmxFgx5wdsFvF03v34lEVVhMpouqPAYQ15N37K/ir5XY+9m/
 
824
d8ufMCkjeXsQkKqFbAlQcnWMCRnOoPHS3I4vi6hmnDDeeYTSRvfLbW0fhwIBIwKB
 
825
gBIiOqZYaoqbeD9OS9z2K9KR2atlTxGxOJPXiP4ESqP3NVScWNwyZ3NXHpyrJLa0
 
826
EbVtzsQhLn6rF+TzXnOlcipFvjsem3iYzCpuChfGQ6SovTcOjHV9z+hnpXvQ/fon
 
827
soVRZY65wKnF7IAoUwTmJS9opqgrN6kRgCd3DASAMd1bAkEA96SBVWFt/fJBNJ9H
 
828
tYnBKZGw0VeHOYmVYbvMSstssn8un+pQpUm9vlG/bp7Oxd/m+b9KWEh2xPfv6zqU
 
829
avNwHwJBANqzGZa/EpzF4J8pGti7oIAPUIDGMtfIcmqNXVMckrmzQ2vTfqtkEZsA
 
830
4rE1IERRyiJQx6EJsz21wJmGV9WJQ5kCQQDwkS0uXqVdFzgHO6S++tjmjYcxwr3g
 
831
H0CoFYSgbddOT6miqRskOQF3DZVkJT3kyuBgU2zKygz52ukQZMqxCb1fAkASvuTv
 
832
qfpH87Qq5kQhNKdbbwbmd2NxlNabazPijWuphGTdW0VfJdWfklyS2Kr+iqrs/5wV
 
833
HhathJt636Eg7oIjAkA8ht3MQ+XSl9yIJIS8gVpbPxSw5OMfw0PjVE7tBdQruiSc
 
834
nvuQES5C9BMHjF39LZiGH1iLQy7FgdHyoP+eodI7
 
835
-----END RSA PRIVATE KEY-----
 
836
"""
 
837
    
 
838
 
 
839
class SingleListener(threading.Thread):
 
840
 
 
841
    def __init__(self, callback):
 
842
        threading.Thread.__init__(self)
 
843
        self._callback = callback
 
844
        self._socket = socket.socket()
 
845
        self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
 
846
        self._socket.bind(('localhost', 0))
 
847
        self._socket.listen(1)
 
848
        self.port = self._socket.getsockname()[1]
 
849
        self.stop_event = threading.Event()
 
850
 
 
851
    def run(self):
 
852
        s, _ = self._socket.accept()
 
853
        # now close the listen socket
 
854
        self._socket.close()
 
855
        self._callback(s, self.stop_event)
 
856
    
 
857
    def stop(self):
 
858
        self.stop_event.set()
 
859
        # We should consider waiting for the other thread
 
860
        # to stop, because otherwise we get spurious
 
861
        #   bzr: ERROR: Socket exception: Connection reset by peer (54)
 
862
        # because the test suite finishes before the thread has a chance
 
863
        # to close. (Especially when only running a few tests)
 
864
        
 
865
        
 
866
class SFTPServer(Server):
 
867
    """Common code for SFTP server facilities."""
 
868
 
 
869
    def _get_sftp_url(self, path):
 
870
        """Calculate a sftp url to this server for path."""
 
871
        return 'sftp://foo:bar@localhost:%d/%s' % (self._listener.port, path)
 
872
 
 
873
    def __init__(self):
 
874
        self._original_vendor = None
 
875
        self._homedir = None
 
876
        self._listener = None
 
877
        self._root = None
 
878
 
 
879
    def log(self, *args, **kwargs):
 
880
        """What to do here? do we need this? Its for the StubServer.."""
 
881
 
 
882
    def _run_server(self, s, stop_event):
 
883
        ssh_server = paramiko.Transport(s)
 
884
        key_file = os.path.join(self._homedir, 'test_rsa.key')
 
885
        file(key_file, 'w').write(STUB_SERVER_KEY)
 
886
        host_key = paramiko.RSAKey.from_private_key_file(key_file)
 
887
        ssh_server.add_server_key(host_key)
 
888
        server = StubServer(self)
 
889
        ssh_server.set_subsystem_handler('sftp', paramiko.SFTPServer,
 
890
                                         StubSFTPServer, root=self._root,
 
891
                                         home=self._homedir)
 
892
        event = threading.Event()
 
893
        ssh_server.start_server(event, server)
 
894
        event.wait(5.0)
 
895
        stop_event.wait(30.0)
 
896
 
 
897
    def setUp(self):
 
898
        """See bzrlib.transport.Server.setUp."""
 
899
        # XXX: 20051124 jamesh
 
900
        # The tests currently pop up a password prompt when an external ssh
 
901
        # is used.  This forces the use of the paramiko implementation.
 
902
        global _ssh_vendor
 
903
        self._original_vendor = _ssh_vendor
 
904
        _ssh_vendor = 'none'
 
905
        self._homedir = os.getcwdu()
 
906
        self._root = '/'
 
907
        # FIXME WINDOWS: _root should be _homedir[0]:/
 
908
        self._listener = SingleListener(self._run_server)
 
909
        self._listener.setDaemon(True)
 
910
        self._listener.start()
 
911
 
 
912
    def tearDown(self):
 
913
        """See bzrlib.transport.Server.tearDown."""
 
914
        global _ssh_vendor
 
915
        self._listener.stop()
 
916
        _ssh_vendor = self._original_vendor
 
917
 
 
918
 
 
919
class SFTPAbsoluteServer(SFTPServer):
811
920
    """A test server for sftp transports, using absolute urls."""
812
921
 
813
 
 
814
 
class SFTPHomeDirServer(object):
 
922
    def get_url(self):
 
923
        """See bzrlib.transport.Server.get_url."""
 
924
        return self._get_sftp_url("%%2f%s" % 
 
925
                urlescape(self._homedir[1:]))
 
926
 
 
927
 
 
928
class SFTPHomeDirServer(SFTPServer):
815
929
    """A test server for sftp transports, using homedir relative urls."""
 
930
 
 
931
    def get_url(self):
 
932
        """See bzrlib.transport.Server.get_url."""
 
933
        return self._get_sftp_url("")