32
32
from bzrlib.errors import (FileExists,
33
TransportNotPossible, NoSuchFile, NonRelativePath,
33
TransportNotPossible, NoSuchFile, PathNotChild,
36
36
from bzrlib.config import config_dir
37
37
from bzrlib.trace import mutter, warning, error
38
38
from bzrlib.transport import Transport, register_transport
39
from bzrlib.ui import ui_factory
297
293
if (not path.startswith(self._path)):
298
294
error.append('path mismatch')
300
raise NonRelativePath('path %r is not under base URL %r: %s'
301
% (abspath, self.base, ', '.join(error)))
296
extra = ': ' + ', '.join(error)
297
raise PathNotChild(abspath, self.base, extra=extra)
302
298
pl = len(self._path)
303
299
return path[pl:].lstrip('/')
324
320
if self._do_prefetch and hasattr(f, 'prefetch'):
327
except (IOError, paramiko.SSHException), x:
328
raise NoSuchFile('Error retrieving %s: %s' % (path, str(x)), x)
323
except (IOError, paramiko.SSHException), e:
324
self._translate_io_exception(e, path, ': error retrieving')
330
326
def get_partial(self, relpath, start, length=None):
364
360
self._pump(f, fout)
366
self._translate_io_exception(e, relpath)
367
except paramiko.SSHException, x:
368
raise SFTPTransportError('Unable to write file %r' % (relpath,), x)
361
except (paramiko.SSHException, IOError), e:
362
self._translate_io_exception(e, relpath, ': unable to write')
369
363
except Exception, e:
370
364
# If we fail, try to clean up the temporary file
371
365
# before we throw the exception
391
385
self._sftp.rename(tmp_abspath, final_path)
393
self._translate_io_exception(e, relpath)
394
except paramiko.SSHException, x:
395
raise SFTPTransportError('Unable to rename into file %r' % (path,), x)
386
except (paramiko.SSHException, IOError), e:
387
self._translate_io_exception(e, relpath, ': unable to rename')
420
412
path = self._abspath(relpath)
421
413
self._sftp.mkdir(path)
423
self._translate_io_exception(e, relpath)
424
except (IOError, paramiko.SSHException), x:
425
raise SFTPTransportError('Unable to mkdir %r' % (path,), x)
427
def _translate_io_exception(self, e, relpath):
414
except (paramiko.SSHException, IOError), e:
415
self._translate_io_exception(e, relpath, ': unable to mkdir',
416
failure_exc=FileExists)
418
def _translate_io_exception(self, e, path, more_info='', failure_exc=NoSuchFile):
419
"""Translate a paramiko or IOError into a friendlier exception.
421
:param e: The original exception
422
:param path: The path in question when the error is raised
423
:param more_info: Extra information that can be included,
424
such as what was going on
425
:param failure_exc: Paramiko has the super fun ability to raise completely
426
opaque errors that just set "e.args = ('Failure',)" with
428
This sometimes means FileExists, but it also sometimes
428
431
# paramiko seems to generate detailless errors.
429
if (e.errno == errno.ENOENT or
430
e.args == ('No such file or directory',) or
431
e.args == ('No such file',)):
432
raise NoSuchFile(relpath)
433
if (e.args == ('mkdir failed',)):
434
raise FileExists(relpath)
435
# strange but true, for the paramiko server.
436
if (e.args == ('Failure',)):
437
raise FileExists(relpath)
432
self._translate_error(e, path, raise_generic=False)
433
if hasattr(e, 'args'):
434
if (e.args == ('No such file or directory',) or
435
e.args == ('No such file',)):
436
raise NoSuchFile(path, str(e) + more_info)
437
if (e.args == ('mkdir failed',)):
438
raise FileExists(path, str(e) + more_info)
439
# strange but true, for the paramiko server.
440
if (e.args == ('Failure',)):
441
raise failure_exc(path, str(e) + more_info)
440
444
def append(self, relpath, f):
446
450
path = self._abspath(relpath)
447
451
fout = self._sftp.file(path, 'ab')
448
452
self._pump(f, fout)
449
except (IOError, paramiko.SSHException), x:
450
raise SFTPTransportError('Unable to append file %r' % (path,), x)
453
except (IOError, paramiko.SSHException), e:
454
self._translate_io_exception(e, relpath, ': unable to append')
452
456
def copy(self, rel_from, rel_to):
453
457
"""Copy the item at rel_from to the location at rel_to"""
481
except (IOError, paramiko.SSHException), x:
482
raise SFTPTransportError('Unable to copy %r to %r' % (path_from, path_to), x)
485
except (IOError, paramiko.SSHException), e:
486
self._translate_io_exception(e, path_from, ': unable copy to: %r' % path_to)
484
488
def copy_to(self, relpaths, other, pb=None):
485
489
"""Copy a set of entries from self into another Transport.
514
518
path_to = self._abspath(rel_to)
516
520
self._sftp.rename(path_from, path_to)
517
except (IOError, paramiko.SSHException), x:
518
raise SFTPTransportError('Unable to move %r to %r' % (path_from, path_to), x)
521
except (IOError, paramiko.SSHException), e:
522
self._translate_io_exception(e, path_from, ': unable to move to: %r' % path_to)
520
524
def delete(self, relpath):
521
525
"""Delete the item at relpath"""
522
526
path = self._abspath(relpath)
524
528
self._sftp.remove(path)
525
except (IOError, paramiko.SSHException), x:
526
raise SFTPTransportError('Unable to delete %r' % (path,), x)
529
except (IOError, paramiko.SSHException), e:
530
self._translate_io_exception(e, path, ': unable to delete')
528
532
def listable(self):
529
533
"""Return True if this store supports listing."""
537
541
path = self._abspath(relpath)
539
543
return self._sftp.listdir(path)
540
except (IOError, paramiko.SSHException), x:
541
raise SFTPTransportError('Unable to list folder %r' % (path,), x)
544
except (IOError, paramiko.SSHException), e:
545
self._translate_io_exception(e, path, ': failed to list_dir')
543
547
def stat(self, relpath):
544
548
"""Return the stat information for a file."""
545
549
path = self._abspath(relpath)
547
551
return self._sftp.stat(path)
548
except (IOError, paramiko.SSHException), x:
549
raise SFTPTransportError('Unable to stat %r' % (path,), x)
552
except (IOError, paramiko.SSHException), e:
553
self._translate_io_exception(e, path, ': unable to stat')
551
555
def lock_read(self, relpath):
664
t = paramiko.Transport((self._host, self._port))
669
t = paramiko.Transport((self._host, self._port or 22))
666
except paramiko.SSHException:
667
raise SFTPTransportError('Unable to reach SSH host %s:%d' % (self._host, self._port))
671
except paramiko.SSHException, e:
672
raise ConnectionError('Unable to reach SSH host %s:%d' %
673
(self._host, self._port), e)
669
675
server_key = t.get_remote_server_key()
670
676
server_key_hex = paramiko.util.hexify(server_key.get_fingerprint())
686
692
if server_key != our_server_key:
687
693
filename1 = os.path.expanduser('~/.ssh/known_hosts')
688
694
filename2 = os.path.join(config_dir(), 'ssh_host_keys')
689
raise SFTPTransportError('Host keys for %s do not match! %s != %s' % \
695
raise TransportError('Host keys for %s do not match! %s != %s' % \
690
696
(self._host, our_server_key_hex, server_key_hex),
691
697
['Try editing %s or %s' % (filename1, filename2)])
696
702
self._sftp = t.open_sftp_client()
697
except paramiko.SSHException:
698
raise BzrError('Unable to find path %s on SFTP server %s' % \
699
(self._path, self._host))
703
except paramiko.SSHException, e:
704
raise ConnectionError('Unable to start sftp client %s:%d' %
705
(self._host, self._port), e)
701
707
def _sftp_auth(self, transport):
702
708
# paramiko requires a username, but it might be none if nothing was supplied
707
713
# Also, it would mess up the self.relpath() functionality
708
714
username = self._username or getpass.getuser()
710
agent = paramiko.Agent()
711
for key in agent.get_keys():
712
mutter('Trying SSH agent key %s' % paramiko.util.hexify(key.get_fingerprint()))
714
transport.auth_publickey(username, key)
716
except paramiko.SSHException, e:
716
# Paramiko tries to open a socket.AF_UNIX in order to connect
717
# to ssh-agent. That attribute doesn't exist on win32 (it does in cygwin)
718
# so we get an AttributeError exception. For now, just don't try to
719
# connect to an agent if we are on win32
720
if sys.platform != 'win32':
721
agent = paramiko.Agent()
722
for key in agent.get_keys():
723
mutter('Trying SSH agent key %s' % paramiko.util.hexify(key.get_fingerprint()))
725
transport.auth_publickey(username, key)
727
except paramiko.SSHException, e:
719
730
# okay, try finding id_rsa or id_dss? (posix only)
720
731
if self._try_pkey_auth(transport, paramiko.RSAKey, username, 'id_rsa'):
734
745
#self._password = None
736
747
# give up and ask for a password
737
password = ui_factory.get_password(prompt='SSH %(user)s@%(host)s password',
738
user=username, host=self._host)
748
password = bzrlib.ui.ui_factory.get_password(
749
prompt='SSH %(user)s@%(host)s password',
750
user=username, host=self._host)
740
752
transport.auth_password(username, password)
741
except paramiko.SSHException:
742
raise SFTPTransportError('Unable to authenticate to SSH host as %s@%s' % \
743
(username, self._host))
753
except paramiko.SSHException, e:
754
raise ConnectionError('Unable to authenticate to SSH host as %s@%s' %
755
(username, self._host), e)
745
757
def _try_pkey_auth(self, transport, pkey_class, username, filename):
746
758
filename = os.path.expanduser('~/.ssh/' + filename)
749
761
transport.auth_publickey(username, key)
751
763
except paramiko.PasswordRequiredException:
752
password = ui_factory.get_password(prompt='SSH %(filename)s password',
764
password = bzrlib.ui.ui_factory.get_password(
765
prompt='SSH %(filename)s password',
755
768
key = pkey_class.from_private_key_file(filename, password)
756
769
transport.auth_publickey(username, key)
784
797
t, msg = self._sftp._request(CMD_OPEN, path, mode, attr)
785
798
if t != CMD_HANDLE:
786
raise SFTPTransportError('Expected an SFTP handle')
799
raise TransportError('Expected an SFTP handle')
787
800
handle = msg.get_string()
788
801
return SFTPFile(self._sftp, handle, 'w', -1)
790
self._translate_io_exception(e, relpath)
791
except paramiko.SSHException, x:
792
raise SFTPTransportError('Unable to open file %r' % (path,), x)
802
except (paramiko.SSHException, IOError), e:
803
self._translate_io_exception(e, relpath, ': unable to open',
804
failure_exc=FileExists)