/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/transport/ssh.py

First attempt to merge .dev and resolve the conflicts (but tests are 
failing)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
# Copyright (C) 2005 Robey Pointer <robey@lag.net>
2
 
# Copyright (C) 2005, 2006 Canonical Ltd
 
2
# Copyright (C) 2005, 2006, 2007 Canonical Ltd
3
3
#
4
4
# This program is free software; you can redistribute it and/or modify
5
5
# it under the terms of the GNU General Public License as published by
24
24
import subprocess
25
25
import sys
26
26
 
27
 
from bzrlib.config import config_dir, ensure_config_dir_exists
28
 
from bzrlib.errors import (ConnectionError,
29
 
                           ParamikoNotPresent,
30
 
                           TransportError,
31
 
                           )
32
 
 
33
 
from bzrlib.osutils import pathjoin
34
 
from bzrlib.trace import mutter, warning
35
 
import bzrlib.ui
 
27
from bzrlib import (
 
28
    config,
 
29
    errors,
 
30
    osutils,
 
31
    trace,
 
32
    ui,
 
33
    )
36
34
 
37
35
try:
38
36
    import paramiko
39
37
except ImportError, e:
40
 
    raise ParamikoNotPresent(e)
 
38
    # If we have an ssh subprocess, we don't strictly need paramiko for all ssh
 
39
    # access
 
40
    paramiko = None
41
41
else:
42
42
    from paramiko.sftp_client import SFTPClient
43
43
 
54
54
# connect to an agent if we are on win32 and using Paramiko older than 1.6
55
55
_use_ssh_agent = (sys.platform != 'win32' or _paramiko_version >= (1, 6, 0))
56
56
 
57
 
_ssh_vendors = {}
58
 
 
59
 
def register_ssh_vendor(name, vendor):
60
 
    """Register SSH vendor."""
61
 
    _ssh_vendors[name] = vendor
62
 
 
63
 
    
64
 
_ssh_vendor = None
65
 
def _get_ssh_vendor():
66
 
    """Find out what version of SSH is on the system."""
67
 
    global _ssh_vendor
68
 
    if _ssh_vendor is not None:
69
 
        return _ssh_vendor
70
 
 
71
 
    if 'BZR_SSH' in os.environ:
72
 
        vendor_name = os.environ['BZR_SSH']
 
57
 
 
58
class SSHVendorManager(object):
 
59
    """Manager for manage SSH vendors."""
 
60
 
 
61
    # Note, although at first sign the class interface seems similar to
 
62
    # bzrlib.registry.Registry it is not possible/convenient to directly use
 
63
    # the Registry because the class just has "get()" interface instead of the
 
64
    # Registry's "get(key)".
 
65
 
 
66
    def __init__(self):
 
67
        self._ssh_vendors = {}
 
68
        self._cached_ssh_vendor = None
 
69
        self._default_ssh_vendor = None
 
70
 
 
71
    def register_default_vendor(self, vendor):
 
72
        """Register default SSH vendor."""
 
73
        self._default_ssh_vendor = vendor
 
74
 
 
75
    def register_vendor(self, name, vendor):
 
76
        """Register new SSH vendor by name."""
 
77
        self._ssh_vendors[name] = vendor
 
78
 
 
79
    def clear_cache(self):
 
80
        """Clear previously cached lookup result."""
 
81
        self._cached_ssh_vendor = None
 
82
 
 
83
    def _get_vendor_by_environment(self, environment=None):
 
84
        """Return the vendor or None based on BZR_SSH environment variable.
 
85
 
 
86
        :raises UnknownSSH: if the BZR_SSH environment variable contains
 
87
                            unknown vendor name
 
88
        """
 
89
        if environment is None:
 
90
            environment = os.environ
 
91
        if 'BZR_SSH' in environment:
 
92
            vendor_name = environment['BZR_SSH']
 
93
            try:
 
94
                vendor = self._ssh_vendors[vendor_name]
 
95
            except KeyError:
 
96
                raise errors.UnknownSSH(vendor_name)
 
97
            return vendor
 
98
        return None
 
99
 
 
100
    def _get_ssh_version_string(self, args):
 
101
        """Return SSH version string from the subprocess."""
73
102
        try:
74
 
            _ssh_vendor = _ssh_vendors[vendor_name]
75
 
        except KeyError:
76
 
            raise UnknownSSH(vendor_name)
77
 
        return _ssh_vendor
78
 
 
79
 
    try:
80
 
        p = subprocess.Popen(['ssh', '-V'],
81
 
                             stdin=subprocess.PIPE,
82
 
                             stdout=subprocess.PIPE,
83
 
                             stderr=subprocess.PIPE,
84
 
                             **os_specific_subprocess_params())
85
 
        returncode = p.returncode
86
 
        stdout, stderr = p.communicate()
87
 
    except OSError:
88
 
        returncode = -1
89
 
        stdout = stderr = ''
90
 
    if 'OpenSSH' in stderr:
91
 
        mutter('ssh implementation is OpenSSH')
92
 
        _ssh_vendor = OpenSSHSubprocessVendor()
93
 
    elif 'SSH Secure Shell' in stderr:
94
 
        mutter('ssh implementation is SSH Corp.')
95
 
        _ssh_vendor = SSHCorpSubprocessVendor()
96
 
 
97
 
    if _ssh_vendor is not None:
98
 
        return _ssh_vendor
99
 
 
100
 
    # XXX: 20051123 jamesh
101
 
    # A check for putty's plink or lsh would go here.
102
 
 
103
 
    mutter('falling back to paramiko implementation')
104
 
    _ssh_vendor = ssh.ParamikoVendor()
105
 
    return _ssh_vendor
106
 
 
 
103
            p = subprocess.Popen(args,
 
104
                                 stdout=subprocess.PIPE,
 
105
                                 stderr=subprocess.PIPE,
 
106
                                 **os_specific_subprocess_params())
 
107
            stdout, stderr = p.communicate()
 
108
        except OSError:
 
109
            stdout = stderr = ''
 
110
        return stdout + stderr
 
111
 
 
112
    def _get_vendor_by_version_string(self, version, args):
 
113
        """Return the vendor or None based on output from the subprocess.
 
114
 
 
115
        :param version: The output of 'ssh -V' like command.
 
116
        :param args: Command line that was run.
 
117
        """
 
118
        vendor = None
 
119
        if 'OpenSSH' in version:
 
120
            trace.mutter('ssh implementation is OpenSSH')
 
121
            vendor = OpenSSHSubprocessVendor()
 
122
        elif 'SSH Secure Shell' in version:
 
123
            trace.mutter('ssh implementation is SSH Corp.')
 
124
            vendor = SSHCorpSubprocessVendor()
 
125
        elif 'plink' in version and args[0] == 'plink':
 
126
            # Checking if "plink" was the executed argument as Windows
 
127
            # sometimes reports 'ssh -V' incorrectly with 'plink' in it's
 
128
            # version.  See https://bugs.launchpad.net/bzr/+bug/107155
 
129
            trace.mutter("ssh implementation is Putty's plink.")
 
130
            vendor = PLinkSubprocessVendor()
 
131
        return vendor
 
132
 
 
133
    def _get_vendor_by_inspection(self):
 
134
        """Return the vendor or None by checking for known SSH implementations."""
 
135
        for args in (['ssh', '-V'], ['plink', '-V']):
 
136
            version = self._get_ssh_version_string(args)
 
137
            vendor = self._get_vendor_by_version_string(version, args)
 
138
            if vendor is not None:
 
139
                return vendor
 
140
        return None
 
141
 
 
142
    def get_vendor(self, environment=None):
 
143
        """Find out what version of SSH is on the system.
 
144
 
 
145
        :raises SSHVendorNotFound: if no any SSH vendor is found
 
146
        :raises UnknownSSH: if the BZR_SSH environment variable contains
 
147
                            unknown vendor name
 
148
        """
 
149
        if self._cached_ssh_vendor is None:
 
150
            vendor = self._get_vendor_by_environment(environment)
 
151
            if vendor is None:
 
152
                vendor = self._get_vendor_by_inspection()
 
153
                if vendor is None:
 
154
                    trace.mutter('falling back to default implementation')
 
155
                    vendor = self._default_ssh_vendor
 
156
                    if vendor is None:
 
157
                        raise errors.SSHVendorNotFound()
 
158
            self._cached_ssh_vendor = vendor
 
159
        return self._cached_ssh_vendor
 
160
 
 
161
_ssh_vendor_manager = SSHVendorManager()
 
162
_get_ssh_vendor = _ssh_vendor_manager.get_vendor
 
163
register_default_ssh_vendor = _ssh_vendor_manager.register_default_vendor
 
164
register_ssh_vendor = _ssh_vendor_manager.register_vendor
107
165
 
108
166
 
109
167
def _ignore_sigint():
112
170
    # <https://launchpad.net/products/bzr/+bug/41433/+index>
113
171
    import signal
114
172
    signal.signal(signal.SIGINT, signal.SIG_IGN)
115
 
    
116
 
 
117
 
 
118
 
class LoopbackSFTP(object):
 
173
 
 
174
 
 
175
class SocketAsChannelAdapter(object):
119
176
    """Simple wrapper for a socket that pretends to be a paramiko Channel."""
120
177
 
121
178
    def __init__(self, sock):
122
179
        self.__socket = sock
123
 
 
 
180
 
 
181
    def get_name(self):
 
182
        return "bzr SocketAsChannelAdapter"
 
183
    
124
184
    def send(self, data):
125
185
        return self.__socket.send(data)
126
186
 
127
187
    def recv(self, n):
128
 
        return self.__socket.recv(n)
 
188
        try:
 
189
            return self.__socket.recv(n)
 
190
        except socket.error, e:
 
191
            if e.args[0] in (errno.EPIPE, errno.ECONNRESET, errno.ECONNABORTED,
 
192
                             errno.EBADF):
 
193
                # Connection has closed.  Paramiko expects an empty string in
 
194
                # this case, not an exception.
 
195
                return ''
 
196
            raise
129
197
 
130
198
    def recv_ready(self):
 
199
        # TODO: jam 20051215 this function is necessary to support the
 
200
        # pipelined() function. In reality, it probably should use
 
201
        # poll() or select() to actually return if there is data
 
202
        # available, otherwise we probably don't get any benefit
131
203
        return True
132
204
 
133
205
    def close(self):
136
208
 
137
209
class SSHVendor(object):
138
210
    """Abstract base class for SSH vendor implementations."""
139
 
    
 
211
 
140
212
    def connect_sftp(self, username, password, host, port):
141
213
        """Make an SSH connection, and return an SFTPClient.
142
214
        
153
225
        raise NotImplementedError(self.connect_sftp)
154
226
 
155
227
    def connect_ssh(self, username, password, host, port, command):
156
 
        """Make an SSH connection, and return a pipe-like object.
 
228
        """Make an SSH connection.
157
229
        
158
 
        (This is currently unused, it's just here to indicate future directions
159
 
        for this code.)
 
230
        :returns: something with a `close` method, and a `get_filelike_channels`
 
231
            method that returns a pair of (read, write) filelike objects.
160
232
        """
161
233
        raise NotImplementedError(self.connect_ssh)
162
 
        
 
234
 
 
235
    def _raise_connection_error(self, host, port=None, orig_error=None,
 
236
                                msg='Unable to connect to SSH host'):
 
237
        """Raise a SocketConnectionError with properly formatted host.
 
238
 
 
239
        This just unifies all the locations that try to raise ConnectionError,
 
240
        so that they format things properly.
 
241
        """
 
242
        raise errors.SocketConnectionError(host=host, port=port, msg=msg,
 
243
                                           orig_error=orig_error)
 
244
 
163
245
 
164
246
class LoopbackVendor(SSHVendor):
165
247
    """SSH "vendor" that connects over a plain TCP socket, not SSH."""
166
 
    
 
248
 
167
249
    def connect_sftp(self, username, password, host, port):
168
250
        sock = socket.socket()
169
251
        try:
170
252
            sock.connect((host, port))
171
253
        except socket.error, e:
172
 
            raise ConnectionError('Unable to connect to SSH host %s:%s: %s'
173
 
                                  % (host, port, e))
174
 
        return SFTPClient(LoopbackSFTP(sock))
 
254
            self._raise_connection_error(host, port=port, orig_error=e)
 
255
        return SFTPClient(SocketAsChannelAdapter(sock))
175
256
 
176
257
register_ssh_vendor('loopback', LoopbackVendor())
177
258
 
178
259
 
 
260
class _ParamikoSSHConnection(object):
 
261
    def __init__(self, channel):
 
262
        self.channel = channel
 
263
 
 
264
    def get_filelike_channels(self):
 
265
        return self.channel.makefile('rb'), self.channel.makefile('wb')
 
266
 
 
267
    def close(self):
 
268
        return self.channel.close()
 
269
 
 
270
 
179
271
class ParamikoVendor(SSHVendor):
180
272
    """Vendor that uses paramiko."""
181
273
 
182
 
    def connect_sftp(self, username, password, host, port):
 
274
    def _connect(self, username, password, host, port):
183
275
        global SYSTEM_HOSTKEYS, BZR_HOSTKEYS
184
 
        
 
276
 
185
277
        load_host_keys()
186
278
 
187
279
        try:
189
281
            t.set_log_channel('bzr.paramiko')
190
282
            t.start_client()
191
283
        except (paramiko.SSHException, socket.error), e:
192
 
            raise ConnectionError('Unable to reach SSH host %s:%s: %s' 
193
 
                                  % (host, port, e))
194
 
            
 
284
            self._raise_connection_error(host, port=port, orig_error=e)
 
285
 
195
286
        server_key = t.get_remote_server_key()
196
287
        server_key_hex = paramiko.util.hexify(server_key.get_fingerprint())
197
288
        keytype = server_key.get_name()
198
289
        if host in SYSTEM_HOSTKEYS and keytype in SYSTEM_HOSTKEYS[host]:
199
290
            our_server_key = SYSTEM_HOSTKEYS[host][keytype]
200
 
            our_server_key_hex = paramiko.util.hexify(our_server_key.get_fingerprint())
 
291
            our_server_key_hex = paramiko.util.hexify(
 
292
                our_server_key.get_fingerprint())
201
293
        elif host in BZR_HOSTKEYS and keytype in BZR_HOSTKEYS[host]:
202
294
            our_server_key = BZR_HOSTKEYS[host][keytype]
203
 
            our_server_key_hex = paramiko.util.hexify(our_server_key.get_fingerprint())
 
295
            our_server_key_hex = paramiko.util.hexify(
 
296
                our_server_key.get_fingerprint())
204
297
        else:
205
 
            warning('Adding %s host key for %s: %s' % (keytype, host, server_key_hex))
206
 
            if host not in BZR_HOSTKEYS:
207
 
                BZR_HOSTKEYS[host] = {}
208
 
            BZR_HOSTKEYS[host][keytype] = server_key
 
298
            trace.warning('Adding %s host key for %s: %s'
 
299
                          % (keytype, host, server_key_hex))
 
300
            add = getattr(BZR_HOSTKEYS, 'add', None)
 
301
            if add is not None: # paramiko >= 1.X.X
 
302
                BZR_HOSTKEYS.add(host, keytype, server_key)
 
303
            else:
 
304
                BZR_HOSTKEYS.setdefault(host, {})[keytype] = server_key
209
305
            our_server_key = server_key
210
 
            our_server_key_hex = paramiko.util.hexify(our_server_key.get_fingerprint())
 
306
            our_server_key_hex = paramiko.util.hexify(
 
307
                our_server_key.get_fingerprint())
211
308
            save_host_keys()
212
309
        if server_key != our_server_key:
213
310
            filename1 = os.path.expanduser('~/.ssh/known_hosts')
214
 
            filename2 = pathjoin(config_dir(), 'ssh_host_keys')
215
 
            raise TransportError('Host keys for %s do not match!  %s != %s' % \
 
311
            filename2 = osutils.pathjoin(config.config_dir(), 'ssh_host_keys')
 
312
            raise errors.TransportError(
 
313
                'Host keys for %s do not match!  %s != %s' %
216
314
                (host, our_server_key_hex, server_key_hex),
217
315
                ['Try editing %s or %s' % (filename1, filename2)])
218
316
 
219
 
        _paramiko_auth(username, password, host, t)
220
 
        
221
 
        try:
222
 
            sftp = t.open_sftp_client()
223
 
        except paramiko.SSHException, e:
224
 
            raise ConnectionError('Unable to start sftp client %s:%d' %
225
 
                                  (host, port), e)
226
 
        return sftp
227
 
 
228
 
register_ssh_vendor('paramiko', ParamikoVendor())
 
317
        _paramiko_auth(username, password, host, port, t)
 
318
        return t
 
319
 
 
320
    def connect_sftp(self, username, password, host, port):
 
321
        t = self._connect(username, password, host, port)
 
322
        try:
 
323
            return t.open_sftp_client()
 
324
        except paramiko.SSHException, e:
 
325
            self._raise_connection_error(host, port=port, orig_error=e,
 
326
                                         msg='Unable to start sftp client')
 
327
 
 
328
    def connect_ssh(self, username, password, host, port, command):
 
329
        t = self._connect(username, password, host, port)
 
330
        try:
 
331
            channel = t.open_session()
 
332
            cmdline = ' '.join(command)
 
333
            channel.exec_command(cmdline)
 
334
            return _ParamikoSSHConnection(channel)
 
335
        except paramiko.SSHException, e:
 
336
            self._raise_connection_error(host, port=port, orig_error=e,
 
337
                                         msg='Unable to invoke remote bzr')
 
338
 
 
339
if paramiko is not None:
 
340
    vendor = ParamikoVendor()
 
341
    register_ssh_vendor('paramiko', vendor)
 
342
    register_ssh_vendor('none', vendor)
 
343
    register_default_ssh_vendor(vendor)
 
344
    _sftp_connection_errors = (EOFError, paramiko.SSHException)
 
345
    del vendor
 
346
else:
 
347
    _sftp_connection_errors = (EOFError,)
229
348
 
230
349
 
231
350
class SubprocessVendor(SSHVendor):
232
351
    """Abstract base class for vendors that use pipes to a subprocess."""
233
 
    
 
352
 
 
353
    def _connect(self, argv):
 
354
        proc = subprocess.Popen(argv,
 
355
                                stdin=subprocess.PIPE,
 
356
                                stdout=subprocess.PIPE,
 
357
                                **os_specific_subprocess_params())
 
358
        return SSHSubprocess(proc)
 
359
 
234
360
    def connect_sftp(self, username, password, host, port):
235
361
        try:
236
362
            argv = self._get_vendor_specific_argv(username, host, port,
237
363
                                                  subsystem='sftp')
238
 
            proc = subprocess.Popen(argv,
239
 
                                    stdin=subprocess.PIPE,
240
 
                                    stdout=subprocess.PIPE,
241
 
                                    **os_specific_subprocess_params())
242
 
            sock = SSHSubprocess(proc)
243
 
            return SFTPClient(sock)
244
 
        except (EOFError, paramiko.SSHException), e:
245
 
            raise ConnectionError('Unable to connect to SSH host %s:%s: %s'
246
 
                                  % (host, port, e))
247
 
        except (OSError, IOError), e:
248
 
            # If the machine is fast enough, ssh can actually exit
249
 
            # before we try and send it the sftp request, which
250
 
            # raises a Broken Pipe
251
 
            if e.errno not in (errno.EPIPE,):
252
 
                raise
253
 
            raise ConnectionError('Unable to connect to SSH host %s:%s: %s'
254
 
                                  % (host, port, e))
 
364
            sock = self._connect(argv)
 
365
            return SFTPClient(SocketAsChannelAdapter(sock))
 
366
        except _sftp_connection_errors, e:
 
367
            self._raise_connection_error(host, port=port, orig_error=e)
 
368
        except (OSError, IOError), e:
 
369
            # If the machine is fast enough, ssh can actually exit
 
370
            # before we try and send it the sftp request, which
 
371
            # raises a Broken Pipe
 
372
            if e.errno not in (errno.EPIPE,):
 
373
                raise
 
374
            self._raise_connection_error(host, port=port, orig_error=e)
 
375
 
 
376
    def connect_ssh(self, username, password, host, port, command):
 
377
        try:
 
378
            argv = self._get_vendor_specific_argv(username, host, port,
 
379
                                                  command=command)
 
380
            return self._connect(argv)
 
381
        except (EOFError), e:
 
382
            self._raise_connection_error(host, port=port, orig_error=e)
 
383
        except (OSError, IOError), e:
 
384
            # If the machine is fast enough, ssh can actually exit
 
385
            # before we try and send it the sftp request, which
 
386
            # raises a Broken Pipe
 
387
            if e.errno not in (errno.EPIPE,):
 
388
                raise
 
389
            self._raise_connection_error(host, port=port, orig_error=e)
255
390
 
256
391
    def _get_vendor_specific_argv(self, username, host, port, subsystem=None,
257
392
                                  command=None):
261
396
        """
262
397
        raise NotImplementedError(self._get_vendor_specific_argv)
263
398
 
264
 
register_ssh_vendor('none', ParamikoVendor())
265
 
 
266
399
 
267
400
class OpenSSHSubprocessVendor(SubprocessVendor):
268
401
    """SSH vendor that uses the 'ssh' executable from OpenSSH."""
269
 
    
 
402
 
270
403
    def _get_vendor_specific_argv(self, username, host, port, subsystem=None,
271
404
                                  command=None):
272
 
        assert subsystem is not None or command is not None, (
273
 
            'Must specify a command or subsystem')
274
 
        if subsystem is not None:
275
 
            assert command is None, (
276
 
                'subsystem and command are mutually exclusive')
277
405
        args = ['ssh',
278
406
                '-oForwardX11=no', '-oForwardAgent=no',
279
407
                '-oClearAllForwardings=yes', '-oProtocol=2',
296
424
 
297
425
    def _get_vendor_specific_argv(self, username, host, port, subsystem=None,
298
426
                                  command=None):
299
 
        assert subsystem is not None or command is not None, (
300
 
            'Must specify a command or subsystem')
301
 
        if subsystem is not None:
302
 
            assert command is None, (
303
 
                'subsystem and command are mutually exclusive')
304
427
        args = ['ssh', '-x']
305
428
        if port is not None:
306
429
            args.extend(['-p', str(port)])
311
434
        else:
312
435
            args.extend([host] + command)
313
436
        return args
314
 
    
 
437
 
315
438
register_ssh_vendor('ssh', SSHCorpSubprocessVendor())
316
439
 
317
440
 
318
 
def _paramiko_auth(username, password, host, paramiko_transport):
 
441
class PLinkSubprocessVendor(SubprocessVendor):
 
442
    """SSH vendor that uses the 'plink' executable from Putty."""
 
443
 
 
444
    def _get_vendor_specific_argv(self, username, host, port, subsystem=None,
 
445
                                  command=None):
 
446
        args = ['plink', '-x', '-a', '-ssh', '-2', '-batch']
 
447
        if port is not None:
 
448
            args.extend(['-P', str(port)])
 
449
        if username is not None:
 
450
            args.extend(['-l', username])
 
451
        if subsystem is not None:
 
452
            args.extend(['-s', host, subsystem])
 
453
        else:
 
454
            args.extend([host] + command)
 
455
        return args
 
456
 
 
457
register_ssh_vendor('plink', PLinkSubprocessVendor())
 
458
 
 
459
 
 
460
def _paramiko_auth(username, password, host, port, paramiko_transport):
319
461
    # paramiko requires a username, but it might be none if nothing was supplied
320
462
    # use the local username, just in case.
321
463
    # We don't override username, because if we aren't using paramiko,
322
464
    # the username might be specified in ~/.ssh/config and we don't want to
323
465
    # force it to something else
324
466
    # Also, it would mess up the self.relpath() functionality
325
 
    username = username or getpass.getuser()
 
467
    auth = config.AuthenticationConfig()
 
468
    if username is None:
 
469
        username = auth.get_user('ssh', host, port=port)
 
470
        if username is None:
 
471
            # Default to local user
 
472
            username = getpass.getuser()
326
473
 
327
474
    if _use_ssh_agent:
328
475
        agent = paramiko.Agent()
329
476
        for key in agent.get_keys():
330
 
            mutter('Trying SSH agent key %s' % paramiko.util.hexify(key.get_fingerprint()))
 
477
            trace.mutter('Trying SSH agent key %s'
 
478
                         % paramiko.util.hexify(key.get_fingerprint()))
331
479
            try:
332
480
                paramiko_transport.auth_publickey(username, key)
333
481
                return
334
482
            except paramiko.SSHException, e:
335
483
                pass
336
 
    
 
484
 
337
485
    # okay, try finding id_rsa or id_dss?  (posix only)
338
486
    if _try_pkey_auth(paramiko_transport, paramiko.RSAKey, username, 'id_rsa'):
339
487
        return
348
496
            pass
349
497
 
350
498
    # give up and ask for a password
351
 
    password = bzrlib.ui.ui_factory.get_password(
352
 
            prompt='SSH %(user)s@%(host)s password',
353
 
            user=username, host=host)
 
499
    password = auth.get_password('ssh', host, username, port=port)
354
500
    try:
355
501
        paramiko_transport.auth_password(username, password)
356
502
    except paramiko.SSHException, e:
357
 
        raise ConnectionError('Unable to authenticate to SSH host as %s@%s' %
358
 
                              (username, host), e)
 
503
        raise errors.ConnectionError(
 
504
            'Unable to authenticate to SSH host as %s@%s' % (username, host), e)
359
505
 
360
506
 
361
507
def _try_pkey_auth(paramiko_transport, pkey_class, username, filename):
365
511
        paramiko_transport.auth_publickey(username, key)
366
512
        return True
367
513
    except paramiko.PasswordRequiredException:
368
 
        password = bzrlib.ui.ui_factory.get_password(
369
 
                prompt='SSH %(filename)s password',
370
 
                filename=filename)
 
514
        password = ui.ui_factory.get_password(
 
515
            prompt='SSH %(filename)s password', filename=filename)
371
516
        try:
372
517
            key = pkey_class.from_private_key_file(filename, password)
373
518
            paramiko_transport.auth_publickey(username, key)
374
519
            return True
375
520
        except paramiko.SSHException:
376
 
            mutter('SSH authentication via %s key failed.' % (os.path.basename(filename),))
 
521
            trace.mutter('SSH authentication via %s key failed.'
 
522
                         % (os.path.basename(filename),))
377
523
    except paramiko.SSHException:
378
 
        mutter('SSH authentication via %s key failed.' % (os.path.basename(filename),))
 
524
        trace.mutter('SSH authentication via %s key failed.'
 
525
                     % (os.path.basename(filename),))
379
526
    except IOError:
380
527
        pass
381
528
    return False
388
535
    """
389
536
    global SYSTEM_HOSTKEYS, BZR_HOSTKEYS
390
537
    try:
391
 
        SYSTEM_HOSTKEYS = paramiko.util.load_host_keys(os.path.expanduser('~/.ssh/known_hosts'))
392
 
    except Exception, e:
393
 
        mutter('failed to load system host keys: ' + str(e))
394
 
    bzr_hostkey_path = pathjoin(config_dir(), 'ssh_host_keys')
 
538
        SYSTEM_HOSTKEYS = paramiko.util.load_host_keys(
 
539
            os.path.expanduser('~/.ssh/known_hosts'))
 
540
    except IOError, e:
 
541
        trace.mutter('failed to load system host keys: ' + str(e))
 
542
    bzr_hostkey_path = osutils.pathjoin(config.config_dir(), 'ssh_host_keys')
395
543
    try:
396
544
        BZR_HOSTKEYS = paramiko.util.load_host_keys(bzr_hostkey_path)
397
 
    except Exception, e:
398
 
        mutter('failed to load bzr host keys: ' + str(e))
 
545
    except IOError, e:
 
546
        trace.mutter('failed to load bzr host keys: ' + str(e))
399
547
        save_host_keys()
400
548
 
401
549
 
404
552
    Save "discovered" host keys in $(config)/ssh_host_keys/.
405
553
    """
406
554
    global SYSTEM_HOSTKEYS, BZR_HOSTKEYS
407
 
    bzr_hostkey_path = pathjoin(config_dir(), 'ssh_host_keys')
408
 
    ensure_config_dir_exists()
 
555
    bzr_hostkey_path = osutils.pathjoin(config.config_dir(), 'ssh_host_keys')
 
556
    config.ensure_config_dir_exists()
409
557
 
410
558
    try:
411
559
        f = open(bzr_hostkey_path, 'w')
415
563
                f.write('%s %s %s\n' % (hostname, keytype, key.get_base64()))
416
564
        f.close()
417
565
    except IOError, e:
418
 
        mutter('failed to save bzr host keys: ' + str(e))
 
566
        trace.mutter('failed to save bzr host keys: ' + str(e))
419
567
 
420
568
 
421
569
def os_specific_subprocess_params():
453
601
    def send(self, data):
454
602
        return os.write(self.proc.stdin.fileno(), data)
455
603
 
456
 
    def recv_ready(self):
457
 
        # TODO: jam 20051215 this function is necessary to support the
458
 
        # pipelined() function. In reality, it probably should use
459
 
        # poll() or select() to actually return if there is data
460
 
        # available, otherwise we probably don't get any benefit
461
 
        return True
462
 
 
463
604
    def recv(self, count):
464
605
        return os.read(self.proc.stdout.fileno(), count)
465
606
 
468
609
        self.proc.stdout.close()
469
610
        self.proc.wait()
470
611
 
 
612
    def get_filelike_channels(self):
 
613
        return (self.proc.stdout, self.proc.stdin)
471
614