32
32
from bzrlib.errors import (FileExists,
33
TransportNotPossible, NoSuchFile, NonRelativePath,
33
TransportNotPossible, NoSuchFile, PathNotChild,
36
36
from bzrlib.config import config_dir, ensure_config_dir_exists
181
181
mutter('failed to save bzr host keys: ' + str(e))
185
class SFTPTransportError (TransportError):
188
184
class SFTPLock(object):
189
185
"""This fakes a lock in a remote location."""
190
186
__slots__ = ['path', 'lock_path', 'lock_file', 'transport']
298
294
if (not path.startswith(self._path)):
299
295
error.append('path mismatch')
301
raise NonRelativePath('path %r is not under base URL %r: %s'
302
% (abspath, self.base, ', '.join(error)))
297
extra = ': ' + ', '.join(error)
298
raise PathNotChild(abspath, self.base, extra=extra)
303
299
pl = len(self._path)
304
300
return path[pl:].lstrip('/')
365
361
self._pump(f, fout)
367
self._translate_io_exception(e, relpath)
368
except paramiko.SSHException, x:
369
raise SFTPTransportError('Unable to write file %r' % (relpath,), x)
362
except (paramiko.SSHException, IOError), e:
363
self._translate_io_exception(e, relpath, ': unable to write')
370
364
except Exception, e:
371
365
# If we fail, try to clean up the temporary file
372
366
# before we throw the exception
392
386
self._sftp.rename(tmp_abspath, final_path)
394
self._translate_io_exception(e, relpath)
395
except paramiko.SSHException, x:
396
raise SFTPTransportError('Unable to rename into file %r' % (path,), x)
387
except (paramiko.SSHException, IOError), e:
388
self._translate_io_exception(e, relpath, ': unable to rename')
421
413
path = self._abspath(relpath)
422
414
self._sftp.mkdir(path)
424
self._translate_io_exception(e, relpath)
425
except (IOError, paramiko.SSHException), x:
426
raise SFTPTransportError('Unable to mkdir %r' % (path,), x)
415
except (paramiko.SSHException, IOError), e:
416
self._translate_io_exception(e, relpath, ': unable to mkdir')
428
def _translate_io_exception(self, e, relpath):
418
def _translate_io_exception(self, e, relpath, more_info=''):
429
419
# paramiko seems to generate detailless errors.
430
if (e.errno == errno.ENOENT or
431
e.args == ('No such file or directory',) or
432
e.args == ('No such file',)):
433
raise NoSuchFile(relpath)
434
if (e.args == ('mkdir failed',)):
435
raise FileExists(relpath)
436
# strange but true, for the paramiko server.
437
if (e.args == ('Failure',)):
438
raise FileExists(relpath)
420
self._translate_error(e, relpath, raise_generic=False)
421
if hasattr(e, 'args'):
422
if (e.args == ('No such file or directory',) or
423
e.args == ('No such file',)):
424
raise NoSuchFile(relpath, str(e) + more_info)
425
if (e.args == ('mkdir failed',)):
426
raise FileExists(relpath, str(e) + more_info)
427
# strange but true, for the paramiko server.
428
if (e.args == ('Failure',)):
429
raise FileExists(relpath, str(e) + more_info)
441
432
def append(self, relpath, f):
448
439
fout = self._sftp.file(path, 'ab')
449
440
self._pump(f, fout)
450
441
except (IOError, paramiko.SSHException), x:
451
raise SFTPTransportError('Unable to append file %r' % (path,), x)
442
raise TransportError('Unable to append file %r' % (path,), x)
453
444
def copy(self, rel_from, rel_to):
454
445
"""Copy the item at rel_from to the location at rel_to"""
482
473
except (IOError, paramiko.SSHException), x:
483
raise SFTPTransportError('Unable to copy %r to %r' % (path_from, path_to), x)
474
raise TransportError('Unable to copy %r to %r' % (path_from, path_to), x)
485
476
def copy_to(self, relpaths, other, pb=None):
486
477
"""Copy a set of entries from self into another Transport.
517
508
self._sftp.rename(path_from, path_to)
518
509
except (IOError, paramiko.SSHException), x:
519
raise SFTPTransportError('Unable to move %r to %r' % (path_from, path_to), x)
510
raise TransportError('Unable to move %r to %r' % (path_from, path_to), x)
521
512
def delete(self, relpath):
522
513
"""Delete the item at relpath"""
525
516
self._sftp.remove(path)
526
517
except (IOError, paramiko.SSHException), x:
527
raise SFTPTransportError('Unable to delete %r' % (path,), x)
518
raise TransportError('Unable to delete %r' % (path,), x)
529
520
def listable(self):
530
521
"""Return True if this store supports listing."""
540
531
return self._sftp.listdir(path)
541
532
except (IOError, paramiko.SSHException), x:
542
raise SFTPTransportError('Unable to list folder %r' % (path,), x)
533
raise TransportError('Unable to list folder %r' % (path,), x)
544
535
def stat(self, relpath):
545
536
"""Return the stat information for a file."""
548
539
return self._sftp.stat(path)
549
540
except (IOError, paramiko.SSHException), x:
550
raise SFTPTransportError('Unable to stat %r' % (path,), x)
541
raise TransportError('Unable to stat %r' % (path,), x)
552
543
def lock_read(self, relpath):
634
625
def _sftp_connect(self):
635
626
"""Connect to the remote sftp server.
636
627
After this, self._sftp should have a valid connection (or
637
we raise an SFTPTransportError 'could not connect').
628
we raise an TransportError 'could not connect').
639
630
TODO: Raise a more reasonable ConnectionFailed exception
665
656
t = paramiko.Transport((self._host, self._port))
667
658
except paramiko.SSHException:
668
raise SFTPTransportError('Unable to reach SSH host %s:%d' % (self._host, self._port))
659
raise TransportError('Unable to reach SSH host %s:%d' % (self._host, self._port))
670
661
server_key = t.get_remote_server_key()
671
662
server_key_hex = paramiko.util.hexify(server_key.get_fingerprint())
687
678
if server_key != our_server_key:
688
679
filename1 = os.path.expanduser('~/.ssh/known_hosts')
689
680
filename2 = pathjoin(config_dir(), 'ssh_host_keys')
690
raise SFTPTransportError('Host keys for %s do not match! %s != %s' % \
681
raise TransportError('Host keys for %s do not match! %s != %s' % \
691
682
(self._host, our_server_key_hex, server_key_hex),
692
683
['Try editing %s or %s' % (filename1, filename2)])
708
699
# Also, it would mess up the self.relpath() functionality
709
700
username = self._username or getpass.getuser()
711
agent = paramiko.Agent()
712
for key in agent.get_keys():
713
mutter('Trying SSH agent key %s' % paramiko.util.hexify(key.get_fingerprint()))
715
transport.auth_publickey(username, key)
717
except paramiko.SSHException, e:
702
# Paramiko tries to open a socket.AF_UNIX in order to connect
703
# to ssh-agent. That attribute doesn't exist on win32 (it does in cygwin)
704
# so we get an AttributeError exception. For now, just don't try to
705
# connect to an agent if we are on win32
706
if sys.platform != 'win32':
707
agent = paramiko.Agent()
708
for key in agent.get_keys():
709
mutter('Trying SSH agent key %s' % paramiko.util.hexify(key.get_fingerprint()))
711
transport.auth_publickey(username, key)
713
except paramiko.SSHException, e:
720
716
# okay, try finding id_rsa or id_dss? (posix only)
721
717
if self._try_pkey_auth(transport, paramiko.RSAKey, username, 'id_rsa'):
741
737
transport.auth_password(username, password)
742
738
except paramiko.SSHException:
743
raise SFTPTransportError('Unable to authenticate to SSH host as %s@%s' % \
739
raise TransportError('Unable to authenticate to SSH host as %s@%s' % \
744
740
(username, self._host))
746
742
def _try_pkey_auth(self, transport, pkey_class, username, filename):
785
781
t, msg = self._sftp._request(CMD_OPEN, path, mode, attr)
786
782
if t != CMD_HANDLE:
787
raise SFTPTransportError('Expected an SFTP handle')
783
raise TransportError('Expected an SFTP handle')
788
784
handle = msg.get_string()
789
785
return SFTPFile(self._sftp, handle, 'w', -1)
791
self._translate_io_exception(e, relpath)
792
except paramiko.SSHException, x:
793
raise SFTPTransportError('Unable to open file %r' % (path,), x)
786
except (paramiko.SSHException, IOError), e:
787
self._translate_io_exception(e, relpath, ': unable to open')