35
35
TransportNotPossible, NoSuchFile, PathNotChild,
37
LockError, ParamikoNotPresent
39
39
from bzrlib.osutils import pathjoin, fancy_rename
40
40
from bzrlib.trace import mutter, warning, error
47
error('The SFTP transport requires paramiko.')
46
except ImportError, e:
47
raise ParamikoNotPresent(e)
50
49
from paramiko.sftp import (SFTP_FLAG_WRITE, SFTP_FLAG_CREATE,
51
50
SFTP_FLAG_EXCL, SFTP_FLAG_TRUNC,
146
class LoopbackSFTP(object):
147
"""Simple wrapper for a socket that pretends to be a paramiko Channel."""
149
def __init__(self, sock):
152
def send(self, data):
153
return self.__socket.send(data)
156
return self.__socket.recv(n)
158
def recv_ready(self):
162
self.__socket.close()
147
165
SYSTEM_HOSTKEYS = {}
148
166
BZR_HOSTKEYS = {}
209
229
def __del__(self):
210
230
"""Should this warn, or actually try to cleanup?"""
211
231
if self.lock_file:
212
warn("SFTPLock %r not explicitly unlocked" % (self.path,))
232
warning("SFTPLock %r not explicitly unlocked" % (self.path,))
215
235
def unlock(self):
597
616
# that we have taken the lock.
598
617
return SFTPLock(relpath, self)
601
619
def _unparse_url(self, path=None):
603
621
path = self._path
604
622
path = urllib.quote(path)
605
if path.startswith('/'):
606
path = '/%2F' + path[1:]
623
# handle homedir paths
624
if not path.startswith('/'):
609
626
netloc = urllib.quote(self._host)
610
627
if self._username is not None:
611
628
netloc = '%s@%s' % (urllib.quote(self._username), netloc)
645
662
# as a homedir relative path (the path begins with a double slash
646
663
# if it is absolute).
647
664
# see draft-ietf-secsh-scp-sftp-ssh-uri-03.txt
648
if path.startswith('/'):
665
# RBC 20060118 we are not using this as its too user hostile. instead
666
# we are following lftp and using /~/foo to mean '~/foo'.
667
# handle homedir paths
668
if path.startswith('/~/'):
651
672
return (username, password, host, port, path)
653
674
def _parse_url(self, url):
673
694
vendor = _get_ssh_vendor()
695
if vendor == 'loopback':
696
sock = socket.socket()
697
sock.connect((self._host, self._port))
698
self._sftp = SFTPClient(LoopbackSFTP(sock))
699
elif vendor != 'none':
675
700
sock = SFTPSubprocess(self._host, vendor, self._port,
677
702
self._sftp = SFTPClient(sock)
689
714
t = paramiko.Transport((self._host, self._port or 22))
715
t.set_log_channel('bzr.paramiko')
691
717
except paramiko.SSHException, e:
692
718
raise ConnectionError('Unable to reach SSH host %s:%d' %
867
893
s, _ = self._socket.accept()
868
894
# now close the listen socket
869
895
self._socket.close()
870
self._callback(s, self.stop_event)
897
self._callback(s, self.stop_event)
899
pass #Ignore socket errors
901
# probably a failed test
902
warning('Exception from within unit test server thread: %r' % x)
873
905
self.stop_event.set()
874
# We should consider waiting for the other thread
875
# to stop, because otherwise we get spurious
876
# bzr: ERROR: Socket exception: Connection reset by peer (54)
877
# because the test suite finishes before the thread has a chance
878
# to close. (Especially when only running a few tests)
906
# use a timeout here, because if the test fails, the server thread may
907
# never notice the stop_event.
881
911
class SFTPServer(Server):
882
912
"""Common code for SFTP server facilities."""
884
def _get_sftp_url(self, path):
885
"""Calculate a sftp url to this server for path."""
886
return 'sftp://foo:bar@localhost:%d/%s' % (self._listener.port, path)
888
914
def __init__(self):
889
915
self._original_vendor = None
890
916
self._homedir = None
891
917
self._server_homedir = None
892
918
self._listener = None
893
919
self._root = None
920
self._vendor = 'none'
894
921
# sftp server logs
924
def _get_sftp_url(self, path):
925
"""Calculate an sftp url to this server for path."""
926
return 'sftp://foo:bar@localhost:%d/%s' % (self._listener.port, path)
897
928
def log(self, message):
898
"""What to do here? do we need this? Its for the StubServer.."""
929
"""StubServer uses this to log when a new server is created."""
899
930
self.logs.append(message)
901
932
def _run_server(self, s, stop_event):
912
943
ssh_server.start_server(event, server)
914
945
stop_event.wait(30.0)
917
"""See bzrlib.transport.Server.setUp."""
918
# XXX: 20051124 jamesh
919
# The tests currently pop up a password prompt when an external ssh
920
# is used. This forces the use of the paramiko implementation.
921
948
global _ssh_vendor
922
949
self._original_vendor = _ssh_vendor
950
_ssh_vendor = self._vendor
924
951
self._homedir = os.getcwdu()
925
952
if self._server_homedir is None:
926
953
self._server_homedir = self._homedir
937
964
_ssh_vendor = self._original_vendor
940
class SFTPAbsoluteServer(SFTPServer):
967
class SFTPServerWithoutSSH(SFTPServer):
969
Common code for an SFTP server over a clear TCP loopback socket,
970
instead of over an SSH secured socket.
974
super(SFTPServerWithoutSSH, self).__init__()
975
self._vendor = 'loopback'
977
def _run_server(self, sock, stop_event):
978
class FakeChannel(object):
979
def get_transport(self):
981
def get_log_channel(self):
985
def get_hexdump(self):
988
server = paramiko.SFTPServer(FakeChannel(), 'sftp', StubServer(self), StubSFTPServer,
989
root=self._root, home=self._server_homedir)
990
server.start_subsystem('sftp', None, sock)
991
server.finish_subsystem()
994
class SFTPAbsoluteServer(SFTPServerWithoutSSH):
941
995
"""A test server for sftp transports, using absolute urls."""
943
997
def get_url(self):
944
998
"""See bzrlib.transport.Server.get_url."""
945
return self._get_sftp_url("%%2f%s" %
946
urlescape(self._homedir[1:]))
949
class SFTPHomeDirServer(SFTPServer):
999
return self._get_sftp_url(urlescape(self._homedir[1:]))
1002
class SFTPHomeDirServer(SFTPServerWithoutSSH):
950
1003
"""A test server for sftp transports, using homedir relative urls."""
952
1005
def get_url(self):
953
1006
"""See bzrlib.transport.Server.get_url."""
954
return self._get_sftp_url("")
1007
return self._get_sftp_url("~/")
957
1010
class SFTPSiblingAbsoluteServer(SFTPAbsoluteServer):