~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/transport/ssh.py

  • Committer: Dmitry Vasiliev
  • Date: 2007-01-05 15:44:16 UTC
  • mto: (2327.1.1 jam-integration)
  • mto: This revision was merged to the branch mainline in revision 2328.
  • Revision ID: dima@hlabs.spb.ru-20070105154416-z0s48q431s86rm5j
Added support for Putty's SSH implementation

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
30
30
                           SocketConnectionError,
31
31
                           TransportError,
32
32
                           UnknownSSH,
 
33
                           SSHVendorNotFound,
33
34
                           )
34
35
 
35
36
from bzrlib.osutils import pathjoin
58
59
# connect to an agent if we are on win32 and using Paramiko older than 1.6
59
60
_use_ssh_agent = (sys.platform != 'win32' or _paramiko_version >= (1, 6, 0))
60
61
 
61
 
_ssh_vendors = {}
62
 
 
63
 
def register_ssh_vendor(name, vendor):
64
 
    """Register SSH vendor."""
65
 
    _ssh_vendors[name] = vendor
66
 
 
67
 
    
68
 
_ssh_vendor = None
69
 
def _get_ssh_vendor():
70
 
    """Find out what version of SSH is on the system."""
71
 
    global _ssh_vendor
72
 
    if _ssh_vendor is not None:
73
 
        return _ssh_vendor
74
 
 
75
 
    if 'BZR_SSH' in os.environ:
76
 
        vendor_name = os.environ['BZR_SSH']
 
62
 
 
63
class SSHVendorManager(object):
 
64
    """Manager for manage SSH vendors."""
 
65
 
 
66
    def __init__(self):
 
67
        self._ssh_vendors = {}
 
68
        self.ssh_vendor = None
 
69
 
 
70
    def register_vendor(self, name, vendor):
 
71
        """Register new SSH vendor."""
 
72
        self._ssh_vendors[name] = vendor
 
73
 
 
74
    def _get_vendor_by_environment(self, environment=None):
 
75
        if environment is None:
 
76
            environment = os.environ
 
77
        if 'BZR_SSH' in environment:
 
78
            vendor_name = environment['BZR_SSH']
 
79
            try:
 
80
                vendor = self._ssh_vendors[vendor_name]
 
81
            except KeyError:
 
82
                raise UnknownSSH(vendor_name)
 
83
            return vendor
 
84
        return None
 
85
 
 
86
    def _get_ssh_version_string(self, args):
77
87
        try:
78
 
            _ssh_vendor = _ssh_vendors[vendor_name]
79
 
        except KeyError:
80
 
            raise UnknownSSH(vendor_name)
81
 
        return _ssh_vendor
82
 
 
83
 
    try:
84
 
        p = subprocess.Popen(['ssh', '-V'],
85
 
                             stdin=subprocess.PIPE,
86
 
                             stdout=subprocess.PIPE,
87
 
                             stderr=subprocess.PIPE,
88
 
                             **os_specific_subprocess_params())
89
 
        returncode = p.returncode
90
 
        stdout, stderr = p.communicate()
91
 
    except OSError:
92
 
        returncode = -1
93
 
        stdout = stderr = ''
94
 
    if 'OpenSSH' in stderr:
95
 
        mutter('ssh implementation is OpenSSH')
96
 
        _ssh_vendor = OpenSSHSubprocessVendor()
97
 
    elif 'SSH Secure Shell' in stderr:
98
 
        mutter('ssh implementation is SSH Corp.')
99
 
        _ssh_vendor = SSHCorpSubprocessVendor()
100
 
 
101
 
    if _ssh_vendor is not None:
102
 
        return _ssh_vendor
103
 
 
104
 
    # XXX: 20051123 jamesh
105
 
    # A check for putty's plink or lsh would go here.
106
 
 
107
 
    mutter('falling back to paramiko implementation')
108
 
    _ssh_vendor = ParamikoVendor()
109
 
    return _ssh_vendor
 
88
            p = subprocess.Popen(args,
 
89
                                 stdout=subprocess.PIPE,
 
90
                                 stderr=subprocess.PIPE,
 
91
                                 **os_specific_subprocess_params())
 
92
            stdout, stderr = p.communicate()
 
93
        except OSError:
 
94
            stdout = stderr = ''
 
95
        return stdout + stderr
 
96
 
 
97
    def _get_vendor_by_version_string(self, version):
 
98
        vendor = None
 
99
        if 'OpenSSH' in version:
 
100
            mutter('ssh implementation is OpenSSH')
 
101
            vendor = OpenSSHSubprocessVendor()
 
102
        elif 'SSH Secure Shell' in version:
 
103
            mutter('ssh implementation is SSH Corp.')
 
104
            vendor = SSHCorpSubprocessVendor()
 
105
        elif 'plink' in version:
 
106
            mutter("ssh implementation is Putty's plink.")
 
107
            vendor = PLinkSubprocessVendor()
 
108
        return vendor
 
109
 
 
110
    def _get_vendor_by_inspection(self):
 
111
        for args in [['ssh', '-V'], ['plink', '-V']]:
 
112
            version = self._get_ssh_version_string(args)
 
113
            vendor = self._get_vendor_by_version_string(version)
 
114
            if vendor is not None:
 
115
                return vendor
 
116
        return None
 
117
 
 
118
    def get_vendor(self, environment=None):
 
119
        """Find out what version of SSH is on the system."""
 
120
        if self.ssh_vendor is None:
 
121
            vendor = self._get_vendor_by_environment(environment)
 
122
            if vendor is None:
 
123
                vendor = self._get_vendor_by_inspection()
 
124
                if vendor is None:
 
125
                    mutter('falling back to default implementation')
 
126
                    vendor = self._ssh_vendors.get('default', None)
 
127
                    if vendor is None:
 
128
                        raise SSHVendorNotFound()
 
129
            self.ssh_vendor = vendor
 
130
        return self.ssh_vendor
 
131
 
 
132
_ssh_vendor_manager = SSHVendorManager()
 
133
_get_ssh_vendor = _ssh_vendor_manager.get_vendor
 
134
register_ssh_vendor = _ssh_vendor_manager.register_vendor
110
135
 
111
136
 
112
137
def _ignore_sigint():
115
140
    # <https://launchpad.net/products/bzr/+bug/41433/+index>
116
141
    import signal
117
142
    signal.signal(signal.SIGINT, signal.SIG_IGN)
118
 
    
119
143
 
120
144
 
121
145
class LoopbackSFTP(object):
263
287
                                         msg='Unable to invoke remote bzr')
264
288
 
265
289
if paramiko is not None:
266
 
    register_ssh_vendor('paramiko', ParamikoVendor())
 
290
    vendor = ParamikoVendor()
 
291
    register_ssh_vendor('paramiko', vendor)
 
292
    register_ssh_vendor('none', vendor)
 
293
    register_ssh_vendor('default', vendor)
 
294
    del vendor
267
295
 
268
296
 
269
297
class SubprocessVendor(SSHVendor):
315
343
        """
316
344
        raise NotImplementedError(self._get_vendor_specific_argv)
317
345
 
318
 
register_ssh_vendor('none', ParamikoVendor())
319
 
 
320
346
 
321
347
class OpenSSHSubprocessVendor(SubprocessVendor):
322
348
    """SSH vendor that uses the 'ssh' executable from OpenSSH."""
365
391
        else:
366
392
            args.extend([host] + command)
367
393
        return args
368
 
    
 
394
 
369
395
register_ssh_vendor('ssh', SSHCorpSubprocessVendor())
370
396
 
371
397
 
 
398
class PLinkSubprocessVendor(SubprocessVendor):
 
399
    """SSH vendor that uses the 'plink' executable from Putty."""
 
400
 
 
401
    def _get_vendor_specific_argv(self, username, host, port, subsystem=None,
 
402
                                  command=None):
 
403
        assert subsystem is not None or command is not None, (
 
404
            'Must specify a command or subsystem')
 
405
        if subsystem is not None:
 
406
            assert command is None, (
 
407
                'subsystem and command are mutually exclusive')
 
408
        args = ['plink', '-x', '-a', '-ssh', '-2']
 
409
        if port is not None:
 
410
            args.extend(['-P', str(port)])
 
411
        if username is not None:
 
412
            args.extend(['-l', username])
 
413
        if subsystem is not None:
 
414
            args.extend(['-s', subsystem, host])
 
415
        else:
 
416
            args.extend([host] + command)
 
417
        return args
 
418
 
 
419
register_ssh_vendor('plink', PLinkSubprocessVendor())
 
420
 
 
421
 
372
422
def _paramiko_auth(username, password, host, paramiko_transport):
373
423
    # paramiko requires a username, but it might be none if nothing was supplied
374
424
    # use the local username, just in case.