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
474
493
except (IOError, paramiko.SSHException), e:
475
494
self._translate_io_exception(e, relpath, ': unable to append')
477
def copy(self, rel_from, rel_to):
478
"""Copy the item at rel_from to the location at rel_to"""
479
path_from = self._remote_path(rel_from)
480
path_to = self._remote_path(rel_to)
481
self._copy_abspaths(path_from, path_to)
483
def _copy_abspaths(self, path_from, path_to, mode=None):
484
"""Copy files given an absolute path
486
:param path_from: Path on remote server to read
487
:param path_to: Path on remote server to write
490
TODO: Should the destination location be atomically created?
491
This has not been specified
492
TODO: This should use some sort of remote copy, rather than
493
pulling the data locally, and then writing it remotely
496
fin = self._sftp.file(path_from, 'rb')
498
self._put(path_to, fin, mode=mode)
501
except (IOError, paramiko.SSHException), e:
502
self._translate_io_exception(e, path_from, ': unable copy to: %r' % path_to)
504
def copy_to(self, relpaths, other, mode=None, pb=None):
505
"""Copy a set of entries from self into another Transport.
507
:param relpaths: A list/generator of entries to be copied.
509
if isinstance(other, SFTPTransport) and other._sftp is self._sftp:
510
# Both from & to are on the same remote filesystem
511
# We can use a remote copy, instead of pulling locally, and pushing
513
total = self._get_total(relpaths)
515
for path in relpaths:
516
path_from = self._remote_path(relpath)
517
path_to = other._remote_path(relpath)
518
self._update_pb(pb, 'copy-to', count, total)
519
self._copy_abspaths(path_from, path_to, mode=mode)
523
return super(SFTPTransport, self).copy_to(relpaths, other, mode=mode, pb=pb)
525
496
def _rename(self, abs_from, abs_to):
526
497
"""Do a fancy rename on the remote server.
563
534
except (IOError, paramiko.SSHException), e:
564
535
self._translate_io_exception(e, path, ': failed to list_dir')
537
def rmdir(self, relpath):
538
"""See Transport.rmdir."""
539
path = self._remote_path(relpath)
541
return self._sftp.rmdir(path)
542
except (IOError, paramiko.SSHException), e:
543
self._translate_io_exception(e, path, ': failed to rmdir')
566
545
def stat(self, relpath):
567
546
"""Return the stat information for a file."""
568
547
path = self._remote_path(relpath)
597
576
# that we have taken the lock.
598
577
return SFTPLock(relpath, self)
601
579
def _unparse_url(self, path=None):
603
581
path = self._path
604
582
path = urllib.quote(path)
605
if path.startswith('/'):
606
path = '/%2F' + path[1:]
583
# handle homedir paths
584
if not path.startswith('/'):
609
586
netloc = urllib.quote(self._host)
610
587
if self._username is not None:
611
588
netloc = '%s@%s' % (urllib.quote(self._username), netloc)
645
622
# as a homedir relative path (the path begins with a double slash
646
623
# if it is absolute).
647
624
# see draft-ietf-secsh-scp-sftp-ssh-uri-03.txt
648
if path.startswith('/'):
625
# RBC 20060118 we are not using this as its too user hostile. instead
626
# we are following lftp and using /~/foo to mean '~/foo'.
627
# handle homedir paths
628
if path.startswith('/~/'):
651
632
return (username, password, host, port, path)
653
634
def _parse_url(self, url):
673
654
vendor = _get_ssh_vendor()
655
if vendor == 'loopback':
656
sock = socket.socket()
657
sock.connect((self._host, self._port))
658
self._sftp = SFTPClient(LoopbackSFTP(sock))
659
elif vendor != 'none':
675
660
sock = SFTPSubprocess(self._host, vendor, self._port,
677
662
self._sftp = SFTPClient(sock)
867
853
s, _ = self._socket.accept()
868
854
# now close the listen socket
869
855
self._socket.close()
870
self._callback(s, self.stop_event)
857
self._callback(s, self.stop_event)
859
pass #Ignore socket errors
861
# probably a failed test
862
warning('Exception from within unit test server thread: %r' % x)
873
865
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)
866
# use a timeout here, because if the test fails, the server thread may
867
# never notice the stop_event.
881
871
class SFTPServer(Server):
882
872
"""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
874
def __init__(self):
889
875
self._original_vendor = None
890
876
self._homedir = None
891
877
self._server_homedir = None
892
878
self._listener = None
893
879
self._root = None
880
self._vendor = 'none'
894
881
# sftp server logs
884
def _get_sftp_url(self, path):
885
"""Calculate an sftp url to this server for path."""
886
return 'sftp://foo:bar@localhost:%d/%s' % (self._listener.port, path)
897
888
def log(self, message):
898
"""What to do here? do we need this? Its for the StubServer.."""
889
"""StubServer uses this to log when a new server is created."""
899
890
self.logs.append(message)
901
892
def _run_server(self, s, stop_event):
912
903
ssh_server.start_server(event, server)
914
905
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
908
global _ssh_vendor
922
909
self._original_vendor = _ssh_vendor
910
_ssh_vendor = self._vendor
924
911
self._homedir = os.getcwdu()
925
912
if self._server_homedir is None:
926
913
self._server_homedir = self._homedir
937
924
_ssh_vendor = self._original_vendor
940
class SFTPAbsoluteServer(SFTPServer):
927
class SFTPServerWithoutSSH(SFTPServer):
929
Common code for an SFTP server over a clear TCP loopback socket,
930
instead of over an SSH secured socket.
934
super(SFTPServerWithoutSSH, self).__init__()
935
self._vendor = 'loopback'
937
def _run_server(self, sock, stop_event):
938
class FakeChannel(object):
939
def get_transport(self):
941
def get_log_channel(self):
945
def get_hexdump(self):
948
server = paramiko.SFTPServer(FakeChannel(), 'sftp', StubServer(self), StubSFTPServer,
949
root=self._root, home=self._server_homedir)
950
server.start_subsystem('sftp', None, sock)
951
server.finish_subsystem()
954
class SFTPAbsoluteServer(SFTPServerWithoutSSH):
941
955
"""A test server for sftp transports, using absolute urls."""
943
957
def get_url(self):
944
958
"""See bzrlib.transport.Server.get_url."""
945
return self._get_sftp_url("%%2f%s" %
946
urlescape(self._homedir[1:]))
949
class SFTPHomeDirServer(SFTPServer):
959
return self._get_sftp_url(urlescape(self._homedir[1:]))
962
class SFTPHomeDirServer(SFTPServerWithoutSSH):
950
963
"""A test server for sftp transports, using homedir relative urls."""
952
965
def get_url(self):
953
966
"""See bzrlib.transport.Server.get_url."""
954
return self._get_sftp_url("")
967
return self._get_sftp_url("~/")
957
970
class SFTPSiblingAbsoluteServer(SFTPAbsoluteServer):