45
47
CMD_HANDLE, CMD_OPEN)
46
48
from paramiko.sftp_attr import SFTPAttributes
47
49
from paramiko.sftp_file import SFTPFile
50
from paramiko.sftp_client import SFTPClient
52
if 'sftp' not in urlparse.uses_netloc: urlparse.uses_netloc.append('sftp')
56
def _get_ssh_vendor():
57
"""Find out what version of SSH is on the system."""
59
if _ssh_vendor is not None:
65
p = subprocess.Popen(['ssh', '-V'],
67
stdin=subprocess.PIPE,
68
stdout=subprocess.PIPE,
69
stderr=subprocess.PIPE)
70
returncode = p.returncode
71
stdout, stderr = p.communicate()
75
if 'OpenSSH' in stderr:
76
mutter('ssh implementation is OpenSSH')
77
_ssh_vendor = 'openssh'
78
elif 'SSH Secure Shell' in stderr:
79
mutter('ssh implementation is SSH Corp.')
82
if _ssh_vendor != 'none':
85
# XXX: 20051123 jamesh
86
# A check for putty's plink or lsh would go here.
88
mutter('falling back to paramiko implementation')
93
"""A socket-like object that talks to an ssh subprocess via pipes."""
94
def __init__(self, hostname, port=None, user=None):
95
vendor = _get_ssh_vendor()
96
assert vendor in ['openssh', 'ssh']
97
if vendor == 'openssh':
99
'-oForwardX11=no', '-oForwardAgent=no',
100
'-oClearAllForwardings=yes', '-oProtocol=2',
101
'-oNoHostAuthenticationForLocalhost=yes']
103
args.extend(['-p', str(port)])
105
args.extend(['-l', user])
106
args.extend(['-s', hostname, 'sftp'])
107
elif vendor == 'ssh':
110
args.extend(['-p', str(port)])
112
args.extend(['-l', user])
113
args.extend(['-s', 'sftp', hostname])
115
self.proc = subprocess.Popen(args, close_fds=True,
116
stdin=subprocess.PIPE,
117
stdout=subprocess.PIPE)
119
def send(self, data):
120
return os.write(self.proc.stdin.fileno(), data)
122
def recv(self, count):
123
return os.read(self.proc.stdout.fileno(), count)
126
self.proc.stdin.close()
127
self.proc.stdout.close()
50
132
SYSTEM_HOSTKEYS = {}
194
274
def relpath(self, abspath):
195
# FIXME: this is identical to HttpTransport -- share it
196
m = self._url_matcher.match(abspath)
198
if not path.startswith(self._path):
275
username, password, host, port, path = self._split_url(abspath)
276
if (username != self._username or host != self._host or
277
port != self._port or not path.startswith(self._path)):
199
278
raise NonRelativePath('path %r is not under base URL %r'
200
279
% (abspath, self.base))
202
return abspath[pl:].lstrip('/')
281
return path[pl:].lstrip('/')
204
283
def has(self, relpath):
435
514
def _unparse_url(self, path=None):
437
516
path = self._path
439
username = urllib.quote(self._username)
441
username += ':' + urllib.quote(self._password)
443
host += ':%d' % self._port
444
return 'sftp://%s@%s/%s' % (username, host, urllib.quote(path))
517
path = urllib.quote(path)
518
if path.startswith('/'):
519
path = '/%2F' + path[1:]
522
netloc = urllib.quote(self._host)
523
if self._username is not None:
524
netloc = '%s@%s' % (urllib.quote(self._username), netloc)
525
if self._port is not None:
526
netloc = '%s:%d' % (netloc, self._port)
528
return urlparse.urlunparse(('sftp', netloc, path, '', '', ''))
530
def _split_url(self, url):
531
if isinstance(url, unicode):
532
url = url.encode('utf-8')
533
(scheme, netloc, path, params,
534
query, fragment) = urlparse.urlparse(url, allow_fragments=False)
535
assert scheme == 'sftp'
536
username = password = host = port = None
538
username, host = netloc.split('@', 1)
540
username, password = username.split(':', 1)
541
password = urllib.unquote(password)
542
username = urllib.unquote(username)
547
host, port = host.rsplit(':', 1)
551
raise SFTPTransportError('%s: invalid port number' % port)
552
host = urllib.unquote(host)
554
path = urllib.unquote(path)
556
# the initial slash should be removed from the path, and treated
557
# as a homedir relative path (the path begins with a double slash
558
# if it is absolute).
559
# see draft-ietf-secsh-scp-sftp-ssh-uri-03.txt
560
if path.startswith('/'):
563
return (username, password, host, port, path)
446
565
def _parse_url(self, url):
447
assert url[:7] == 'sftp://'
448
m = self._url_matcher.match(url)
450
raise SFTPTransportError('Unable to parse SFTP URL %r' % (url,))
451
self._username, self._password, self._host, self._port, self._path = m.groups()
452
if self._username is None:
453
self._username = getpass.getuser()
456
# username field is 'user:pass@' in this case, and password is ':pass'
457
username_len = len(self._username) - len(self._password) - 1
458
self._username = urllib.unquote(self._username[:username_len])
459
self._password = urllib.unquote(self._password[1:])
461
self._username = urllib.unquote(self._username[:-1])
462
if self._port is None:
466
self._port = int(self._port[1:])
468
raise SFTPTransportError('%s: invalid port number' % self._port[1:])
566
(self._username, self._password,
567
self._host, self._port, self._path) = self._split_url(url)
569
def _sftp_connect(self):
570
vendor = _get_ssh_vendor()
469
571
if (self._path is None) or (self._path == ''):
472
574
# remove leading '/'
473
575
self._path = urllib.unquote(self._path[1:])
577
sock = SFTPSubprocess(self._host, self._port, self._username)
578
self._sftp = SFTPClient(sock)
580
self._paramiko_connect()
475
def _sftp_connect(self):
582
def _paramiko_connect(self):
476
583
global SYSTEM_HOSTKEYS, BZR_HOSTKEYS
481
t = paramiko.Transport((self._host, self._port))
588
t = paramiko.Transport((self._host, self._port or 22))
483
590
except paramiko.SSHException:
484
591
raise SFTPTransportError('Unable to reach SSH host %s:%d' % (self._host, self._port))