~bzr-pqm/bzr/bzr.dev

1662.1.12 by Martin Pool
Translate unknown sftp errors to PathError, no NoSuchFile
1
# Copyright (C) 2005 Robey Pointer <robey@lag.net>
2
# Copyright (C) 2005, 2006 Canonical Ltd
1185.16.87 by mbp at sourcefrog
- fix line endings in sftp transport
3
4
# This program is free software; you can redistribute it and/or modify
5
# it under the terms of the GNU General Public License as published by
6
# the Free Software Foundation; either version 2 of the License, or
7
# (at your option) any later version.
8
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
# GNU General Public License for more details.
13
14
# You should have received a copy of the GNU General Public License
15
# along with this program; if not, write to the Free Software
16
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17
18
"""Implementation of Transport over SFTP, using paramiko."""
19
1489 by Robert Collins
Make the paramiko tests pass. Nice huh.
20
import errno
1185.16.87 by mbp at sourcefrog
- fix line endings in sftp transport
21
import getpass
22
import os
1540.2.2 by Röbey Pointer
allow forcing the use of paramiko via environ var; use prefetch on paramiko >= 1.5.2
23
import random
1185.16.87 by mbp at sourcefrog
- fix line endings in sftp transport
24
import re
25
import stat
1540.2.2 by Röbey Pointer
allow forcing the use of paramiko via environ var; use prefetch on paramiko >= 1.5.2
26
import subprocess
1185.16.87 by mbp at sourcefrog
- fix line endings in sftp transport
27
import sys
1540.2.2 by Röbey Pointer
allow forcing the use of paramiko via environ var; use prefetch on paramiko >= 1.5.2
28
import time
1185.16.87 by mbp at sourcefrog
- fix line endings in sftp transport
29
import urllib
1185.48.1 by James Henstridge
Use /usr/bin/ssh if we can.
30
import urlparse
1185.49.10 by John Arbash Meinel
Use a weakref dictionary to enable re-use of a connection (for sftp).
31
import weakref
1185.16.87 by mbp at sourcefrog
- fix line endings in sftp transport
32
1530.1.7 by Robert Collins
merge integration.
33
from bzrlib.config import config_dir, ensure_config_dir_exists
1530.1.3 by Robert Collins
transport implementations now tested consistently.
34
from bzrlib.errors import (ConnectionError,
35
                           FileExists, 
1185.31.44 by John Arbash Meinel
Cleaned up Exceptions for all transports.
36
                           TransportNotPossible, NoSuchFile, PathNotChild,
1185.49.3 by John Arbash Meinel
Added a form of locking to sftp branches. Refactored _sftp_open_exclusive to take a relative path
37
                           TransportError,
1662.1.12 by Martin Pool
Translate unknown sftp errors to PathError, no NoSuchFile
38
                           LockError, 
39
                           PathError,
40
                           ParamikoNotPresent,
1530.1.3 by Robert Collins
transport implementations now tested consistently.
41
                           )
1530.1.7 by Robert Collins
merge integration.
42
from bzrlib.osutils import pathjoin, fancy_rename
1185.16.87 by mbp at sourcefrog
- fix line endings in sftp transport
43
from bzrlib.trace import mutter, warning, error
1636.1.2 by Robert Collins
More review fixen to the relpath at '/' fixes.
44
from bzrlib.transport import (
45
    register_urlparse_netloc_protocol,
46
    Server,
1707.3.4 by John Arbash Meinel
Moved most of sftp.split_url into a Transport function.
47
    split_url,
1636.1.2 by Robert Collins
More review fixen to the relpath at '/' fixes.
48
    Transport,
49
    )
1185.50.10 by John Arbash Meinel
Don't import ui_factory directly, in case it gets changed later.
50
import bzrlib.ui
1685.1.45 by John Arbash Meinel
Moved url functions into bzrlib.urlutils
51
import bzrlib.urlutils as urlutils
1185.16.87 by mbp at sourcefrog
- fix line endings in sftp transport
52
53
try:
54
    import paramiko
1185.62.24 by John Arbash Meinel
Changing the exception that sftp.py throws when it can't find paramiko, so that the test suite can handle it.
55
except ImportError, e:
56
    raise ParamikoNotPresent(e)
1185.49.2 by John Arbash Meinel
Adding a open_exclusive function since paramiko supports it, but doesn't expose it
57
else:
58
    from paramiko.sftp import (SFTP_FLAG_WRITE, SFTP_FLAG_CREATE,
59
                               SFTP_FLAG_EXCL, SFTP_FLAG_TRUNC,
60
                               CMD_HANDLE, CMD_OPEN)
61
    from paramiko.sftp_attr import SFTPAttributes
62
    from paramiko.sftp_file import SFTPFile
1185.48.1 by James Henstridge
Use /usr/bin/ssh if we can.
63
    from paramiko.sftp_client import SFTPClient
64
1636.1.1 by Robert Collins
Fix calling relpath() and abspath() on transports at their root.
65
1636.1.2 by Robert Collins
More review fixen to the relpath at '/' fixes.
66
register_urlparse_netloc_protocol('sftp')
1636.1.1 by Robert Collins
Fix calling relpath() and abspath() on transports at their root.
67
1185.48.1 by James Henstridge
Use /usr/bin/ssh if we can.
68
1684.2.1 by Martin Pool
Run ssh subprocess with SIGINT ignored, but in the same process group. (Malone #40508, #5987)
69
def _ignore_sigint():
1684.2.2 by Martin Pool
doc
70
    # TODO: This should possibly ignore SIGHUP as well, but bzr currently
71
    # doesn't handle it itself.
72
    # <https://launchpad.net/products/bzr/+bug/41433/+index>
1684.2.1 by Martin Pool
Run ssh subprocess with SIGINT ignored, but in the same process group. (Malone #40508, #5987)
73
    import signal
74
    signal.signal(signal.SIGINT, signal.SIG_IGN)
75
    
76
1666.1.12 by Robert Collins
Change SFTP subprocess handling so that Ctrl-C does not cause stale locks in SFTP accessed repositories.
77
def os_specific_subprocess_params():
78
    """Get O/S specific subprocess parameters."""
79
    if sys.platform == 'win32':
80
        # setting the process group and closing fds is not supported on 
81
        # win32
82
        return {}
83
    else:
1684.2.1 by Martin Pool
Run ssh subprocess with SIGINT ignored, but in the same process group. (Malone #40508, #5987)
84
        # We close fds other than the pipes as the child process does not need 
85
        # them to be open.
86
        #
87
        # We also set the child process to ignore SIGINT.  Normally the signal
88
        # would be sent to every process in the foreground process group, but
89
        # this causes it to be seen only by bzr and not by ssh.  Python will
90
        # generate a KeyboardInterrupt in bzr, and we will then have a chance
91
        # to release locks or do other cleanup over ssh before the connection
92
        # goes away.  
93
        # <https://launchpad.net/products/bzr/+bug/5987>
94
        #
95
        # Running it in a separate process group is not good because then it
96
        # can't get non-echoed input of a password or passphrase.
97
        # <https://launchpad.net/products/bzr/+bug/40508>
98
        return {'preexec_fn': _ignore_sigint,
1666.1.12 by Robert Collins
Change SFTP subprocess handling so that Ctrl-C does not cause stale locks in SFTP accessed repositories.
99
                'close_fds': True,
100
                }
101
102
1540.2.3 by Robey Pointer
move the paramiko version check up to the top and use getattr instead of hasattr. use BZR_SSH instead of BZR_USE_PARAMIKO, to allow overriding the ssh vendor check in a general way.
103
# don't use prefetch unless paramiko version >= 1.5.2 (there were bugs earlier)
104
_default_do_prefetch = False
1666.1.6 by Robert Collins
Make knit the default format.
105
if getattr(paramiko, '__version_info__', (0, 0, 0)) >= (1, 5, 5):
1540.2.3 by Robey Pointer
move the paramiko version check up to the top and use getattr instead of hasattr. use BZR_SSH instead of BZR_USE_PARAMIKO, to allow overriding the ssh vendor check in a general way.
106
    _default_do_prefetch = True
107
1185.48.1 by James Henstridge
Use /usr/bin/ssh if we can.
108
109
_ssh_vendor = None
110
def _get_ssh_vendor():
111
    """Find out what version of SSH is on the system."""
1185.48.2 by James Henstridge
Fix some bugs so it actually works
112
    global _ssh_vendor
113
    if _ssh_vendor is not None:
114
        return _ssh_vendor
1185.48.1 by James Henstridge
Use /usr/bin/ssh if we can.
115
1185.48.2 by James Henstridge
Fix some bugs so it actually works
116
    _ssh_vendor = 'none'
1185.48.1 by James Henstridge
Use /usr/bin/ssh if we can.
117
1540.2.3 by Robey Pointer
move the paramiko version check up to the top and use getattr instead of hasattr. use BZR_SSH instead of BZR_USE_PARAMIKO, to allow overriding the ssh vendor check in a general way.
118
    if 'BZR_SSH' in os.environ:
119
        _ssh_vendor = os.environ['BZR_SSH']
120
        if _ssh_vendor == 'paramiko':
121
            _ssh_vendor = 'none'
122
        return _ssh_vendor
123
1185.48.1 by James Henstridge
Use /usr/bin/ssh if we can.
124
    try:
125
        p = subprocess.Popen(['ssh', '-V'],
126
                             stdin=subprocess.PIPE,
127
                             stdout=subprocess.PIPE,
1666.1.12 by Robert Collins
Change SFTP subprocess handling so that Ctrl-C does not cause stale locks in SFTP accessed repositories.
128
                             stderr=subprocess.PIPE,
129
                             **os_specific_subprocess_params())
1185.48.1 by James Henstridge
Use /usr/bin/ssh if we can.
130
        returncode = p.returncode
131
        stdout, stderr = p.communicate()
132
    except OSError:
133
        returncode = -1
134
        stdout = stderr = ''
135
    if 'OpenSSH' in stderr:
1185.48.2 by James Henstridge
Fix some bugs so it actually works
136
        mutter('ssh implementation is OpenSSH')
137
        _ssh_vendor = 'openssh'
1185.48.1 by James Henstridge
Use /usr/bin/ssh if we can.
138
    elif 'SSH Secure Shell' in stderr:
1185.48.2 by James Henstridge
Fix some bugs so it actually works
139
        mutter('ssh implementation is SSH Corp.')
140
        _ssh_vendor = 'ssh'
1185.48.1 by James Henstridge
Use /usr/bin/ssh if we can.
141
1185.48.2 by James Henstridge
Fix some bugs so it actually works
142
    if _ssh_vendor != 'none':
143
        return _ssh_vendor
1185.48.1 by James Henstridge
Use /usr/bin/ssh if we can.
144
145
    # XXX: 20051123 jamesh
146
    # A check for putty's plink or lsh would go here.
147
1185.48.2 by James Henstridge
Fix some bugs so it actually works
148
    mutter('falling back to paramiko implementation')
149
    return _ssh_vendor
1185.48.1 by James Henstridge
Use /usr/bin/ssh if we can.
150
151
152
class SFTPSubprocess:
153
    """A socket-like object that talks to an ssh subprocess via pipes."""
1530.1.3 by Robert Collins
transport implementations now tested consistently.
154
    def __init__(self, hostname, vendor, port=None, user=None):
1185.48.1 by James Henstridge
Use /usr/bin/ssh if we can.
155
        assert vendor in ['openssh', 'ssh']
156
        if vendor == 'openssh':
157
            args = ['ssh',
158
                    '-oForwardX11=no', '-oForwardAgent=no',
159
                    '-oClearAllForwardings=yes', '-oProtocol=2',
160
                    '-oNoHostAuthenticationForLocalhost=yes']
161
            if port is not None:
162
                args.extend(['-p', str(port)])
163
            if user is not None:
164
                args.extend(['-l', user])
165
            args.extend(['-s', hostname, 'sftp'])
166
        elif vendor == 'ssh':
167
            args = ['ssh', '-x']
168
            if port is not None:
169
                args.extend(['-p', str(port)])
170
            if user is not None:
171
                args.extend(['-l', user])
172
            args.extend(['-s', 'sftp', hostname])
173
1666.1.12 by Robert Collins
Change SFTP subprocess handling so that Ctrl-C does not cause stale locks in SFTP accessed repositories.
174
        self.proc = subprocess.Popen(args,
1185.48.1 by James Henstridge
Use /usr/bin/ssh if we can.
175
                                     stdin=subprocess.PIPE,
1666.1.12 by Robert Collins
Change SFTP subprocess handling so that Ctrl-C does not cause stale locks in SFTP accessed repositories.
176
                                     stdout=subprocess.PIPE,
177
                                     **os_specific_subprocess_params())
1185.48.1 by James Henstridge
Use /usr/bin/ssh if we can.
178
179
    def send(self, data):
180
        return os.write(self.proc.stdin.fileno(), data)
181
1185.58.2 by John Arbash Meinel
Added mode to the appropriate transport functions, and tests to make sure they work.
182
    def recv_ready(self):
183
        # TODO: jam 20051215 this function is necessary to support the
184
        # pipelined() function. In reality, it probably should use
185
        # poll() or select() to actually return if there is data
186
        # available, otherwise we probably don't get any benefit
187
        return True
188
1185.48.1 by James Henstridge
Use /usr/bin/ssh if we can.
189
    def recv(self, count):
190
        return os.read(self.proc.stdout.fileno(), count)
191
192
    def close(self):
193
        self.proc.stdin.close()
194
        self.proc.stdout.close()
195
        self.proc.wait()
196
1185.16.87 by mbp at sourcefrog
- fix line endings in sftp transport
197
1547.1.1 by Robey Pointer
modify the sftp unit tests to perform sftp over a direct localhost socket instead of over an actual ssh2 transport
198
class LoopbackSFTP(object):
199
    """Simple wrapper for a socket that pretends to be a paramiko Channel."""
200
201
    def __init__(self, sock):
202
        self.__socket = sock
203
 
204
    def send(self, data):
205
        return self.__socket.send(data)
206
207
    def recv(self, n):
208
        return self.__socket.recv(n)
209
1547.1.3 by Robey Pointer
spell warning correctly :) and move recv_ready close to recv for readability
210
    def recv_ready(self):
211
        return True
212
1547.1.1 by Robey Pointer
modify the sftp unit tests to perform sftp over a direct localhost socket instead of over an actual ssh2 transport
213
    def close(self):
214
        self.__socket.close()
215
216
1185.16.87 by mbp at sourcefrog
- fix line endings in sftp transport
217
SYSTEM_HOSTKEYS = {}
218
BZR_HOSTKEYS = {}
219
1185.49.10 by John Arbash Meinel
Use a weakref dictionary to enable re-use of a connection (for sftp).
220
# This is a weakref dictionary, so that we can reuse connections
221
# that are still active. Long term, it might be nice to have some
222
# sort of expiration policy, such as disconnect if inactive for
223
# X seconds. But that requires a lot more fanciness.
224
_connected_hosts = weakref.WeakValueDictionary()
225
1607.1.13 by Robert Collins
Add a clear_connection_cache to the SFTP transport and use it in fixing the bound branch test speed performance problem which was cause by the server thread timing out - it was blocked on a read and closing the client side causes the server to unblock and exit.
226
def clear_connection_cache():
227
    """Remove all hosts from the SFTP connection cache.
228
229
    Primarily useful for test cases wanting to force garbage collection.
230
    """
1607.1.16 by Robert Collins
Review feedback on sftp.clear_connection_cache
231
    _connected_hosts.clear()
1607.1.13 by Robert Collins
Add a clear_connection_cache to the SFTP transport and use it in fixing the bound branch test speed performance problem which was cause by the server thread timing out - it was blocked on a read and closing the client side causes the server to unblock and exit.
232
1547.1.1 by Robey Pointer
modify the sftp unit tests to perform sftp over a direct localhost socket instead of over an actual ssh2 transport
233
1185.16.87 by mbp at sourcefrog
- fix line endings in sftp transport
234
def load_host_keys():
235
    """
236
    Load system host keys (probably doesn't work on windows) and any
237
    "discovered" keys from previous sessions.
238
    """
239
    global SYSTEM_HOSTKEYS, BZR_HOSTKEYS
240
    try:
241
        SYSTEM_HOSTKEYS = paramiko.util.load_host_keys(os.path.expanduser('~/.ssh/known_hosts'))
242
    except Exception, e:
243
        mutter('failed to load system host keys: ' + str(e))
1185.31.33 by John Arbash Meinel
A couple more path.join statements needed changing.
244
    bzr_hostkey_path = pathjoin(config_dir(), 'ssh_host_keys')
1185.16.87 by mbp at sourcefrog
- fix line endings in sftp transport
245
    try:
246
        BZR_HOSTKEYS = paramiko.util.load_host_keys(bzr_hostkey_path)
247
    except Exception, e:
248
        mutter('failed to load bzr host keys: ' + str(e))
249
        save_host_keys()
250
1547.1.1 by Robey Pointer
modify the sftp unit tests to perform sftp over a direct localhost socket instead of over an actual ssh2 transport
251
1185.16.87 by mbp at sourcefrog
- fix line endings in sftp transport
252
def save_host_keys():
253
    """
254
    Save "discovered" host keys in $(config)/ssh_host_keys/.
255
    """
256
    global SYSTEM_HOSTKEYS, BZR_HOSTKEYS
1185.31.33 by John Arbash Meinel
A couple more path.join statements needed changing.
257
    bzr_hostkey_path = pathjoin(config_dir(), 'ssh_host_keys')
1185.31.43 by John Arbash Meinel
Reintroduced ensure_config_dir_exists() for sftp
258
    ensure_config_dir_exists()
259
1185.16.87 by mbp at sourcefrog
- fix line endings in sftp transport
260
    try:
261
        f = open(bzr_hostkey_path, 'w')
262
        f.write('# SSH host keys collected by bzr\n')
263
        for hostname, keys in BZR_HOSTKEYS.iteritems():
264
            for keytype, key in keys.iteritems():
265
                f.write('%s %s %s\n' % (hostname, keytype, key.get_base64()))
266
        f.close()
267
    except IOError, e:
268
        mutter('failed to save bzr host keys: ' + str(e))
269
270
1185.49.3 by John Arbash Meinel
Added a form of locking to sftp branches. Refactored _sftp_open_exclusive to take a relative path
271
class SFTPLock(object):
272
    """This fakes a lock in a remote location."""
273
    __slots__ = ['path', 'lock_path', 'lock_file', 'transport']
274
    def __init__(self, path, transport):
275
        assert isinstance(transport, SFTPTransport)
276
277
        self.lock_file = None
278
        self.path = path
279
        self.lock_path = path + '.write-lock'
280
        self.transport = transport
281
        try:
1530.1.7 by Robert Collins
merge integration.
282
            # RBC 20060103 FIXME should we be using private methods here ?
283
            abspath = transport._remote_path(self.lock_path)
1185.58.2 by John Arbash Meinel
Added mode to the appropriate transport functions, and tests to make sure they work.
284
            self.lock_file = transport._sftp_open_exclusive(abspath)
1185.49.3 by John Arbash Meinel
Added a form of locking to sftp branches. Refactored _sftp_open_exclusive to take a relative path
285
        except FileExists:
286
            raise LockError('File %r already locked' % (self.path,))
287
288
    def __del__(self):
289
        """Should this warn, or actually try to cleanup?"""
290
        if self.lock_file:
1547.1.3 by Robey Pointer
spell warning correctly :) and move recv_ready close to recv for readability
291
            warning("SFTPLock %r not explicitly unlocked" % (self.path,))
1185.49.3 by John Arbash Meinel
Added a form of locking to sftp branches. Refactored _sftp_open_exclusive to take a relative path
292
            self.unlock()
293
294
    def unlock(self):
295
        if not self.lock_file:
296
            return
297
        self.lock_file.close()
298
        self.lock_file = None
299
        try:
300
            self.transport.delete(self.lock_path)
301
        except (NoSuchFile,):
302
            # What specific errors should we catch here?
303
            pass
1185.16.87 by mbp at sourcefrog
- fix line endings in sftp transport
304
305
class SFTPTransport (Transport):
306
    """
307
    Transport implementation for SFTP access.
308
    """
1540.2.3 by Robey Pointer
move the paramiko version check up to the top and use getattr instead of hasattr. use BZR_SSH instead of BZR_USE_PARAMIKO, to allow overriding the ssh vendor check in a general way.
309
    _do_prefetch = _default_do_prefetch
1185.16.87 by mbp at sourcefrog
- fix line endings in sftp transport
310
311
    def __init__(self, base, clone_from=None):
312
        assert base.startswith('sftp://')
1185.49.6 by John Arbash Meinel
Fixed the double rename, to rename the safety in case of problem.
313
        self._parse_url(base)
314
        base = self._unparse_url()
1530.1.3 by Robert Collins
transport implementations now tested consistently.
315
        if base[-1] != '/':
1636.1.1 by Robert Collins
Fix calling relpath() and abspath() on transports at their root.
316
            base += '/'
1185.16.87 by mbp at sourcefrog
- fix line endings in sftp transport
317
        super(SFTPTransport, self).__init__(base)
318
        if clone_from is None:
319
            self._sftp_connect()
320
        else:
321
            # use the same ssh connection, etc
322
            self._sftp = clone_from._sftp
323
        # super saves 'self.base'
324
    
325
    def should_cache(self):
326
        """
327
        Return True if the data pulled across should be cached locally.
328
        """
329
        return True
330
331
    def clone(self, offset=None):
332
        """
333
        Return a new SFTPTransport with root at self.base + offset.
334
        We share the same SFTP session between such transports, because it's
335
        fairly expensive to set them up.
336
        """
337
        if offset is None:
338
            return SFTPTransport(self.base, self)
339
        else:
340
            return SFTPTransport(self.abspath(offset), self)
341
342
    def abspath(self, relpath):
343
        """
344
        Return the full url to the given relative path.
345
        
346
        @param relpath: the relative path or path components
347
        @type relpath: str or list
348
        """
1524.1.1 by Robert Collins
Test sftp with relative, absolute-in-homedir and absolute-not-in-homedir
349
        return self._unparse_url(self._remote_path(relpath))
1185.16.87 by mbp at sourcefrog
- fix line endings in sftp transport
350
    
1524.1.1 by Robert Collins
Test sftp with relative, absolute-in-homedir and absolute-not-in-homedir
351
    def _remote_path(self, relpath):
352
        """Return the path to be passed along the sftp protocol for relpath.
353
        
354
        relpath is a urlencoded string.
355
        """
1185.16.87 by mbp at sourcefrog
- fix line endings in sftp transport
356
        # FIXME: share the common code across transports
357
        assert isinstance(relpath, basestring)
1685.1.45 by John Arbash Meinel
Moved url functions into bzrlib.urlutils
358
        relpath = urlutils.unescape(relpath).split('/')
1185.16.87 by mbp at sourcefrog
- fix line endings in sftp transport
359
        basepath = self._path.split('/')
360
        if len(basepath) > 0 and basepath[-1] == '':
361
            basepath = basepath[:-1]
362
363
        for p in relpath:
364
            if p == '..':
365
                if len(basepath) == 0:
366
                    # In most filesystems, a request for the parent
367
                    # of root, just returns root.
368
                    continue
369
                basepath.pop()
370
            elif p == '.':
371
                continue # No-op
372
            else:
373
                basepath.append(p)
374
375
        path = '/'.join(basepath)
376
        return path
377
378
    def relpath(self, abspath):
1185.48.8 by James Henstridge
More URL handling fixes
379
        username, password, host, port, path = self._split_url(abspath)
1185.49.23 by John Arbash Meinel
bugreport from Matthieu Moy: relpath was failing, but throwing an unhelpful exception.
380
        error = []
381
        if (username != self._username):
382
            error.append('username mismatch')
383
        if (host != self._host):
384
            error.append('host mismatch')
385
        if (port != self._port):
386
            error.append('port mismatch')
387
        if (not path.startswith(self._path)):
388
            error.append('path mismatch')
389
        if error:
1185.31.44 by John Arbash Meinel
Cleaned up Exceptions for all transports.
390
            extra = ': ' + ', '.join(error)
391
            raise PathNotChild(abspath, self.base, extra=extra)
1185.48.8 by James Henstridge
More URL handling fixes
392
        pl = len(self._path)
1530.1.3 by Robert Collins
transport implementations now tested consistently.
393
        return path[pl:].strip('/')
1185.16.87 by mbp at sourcefrog
- fix line endings in sftp transport
394
395
    def has(self, relpath):
396
        """
397
        Does the target location exist?
398
        """
399
        try:
1524.1.1 by Robert Collins
Test sftp with relative, absolute-in-homedir and absolute-not-in-homedir
400
            self._sftp.stat(self._remote_path(relpath))
1185.16.87 by mbp at sourcefrog
- fix line endings in sftp transport
401
            return True
402
        except IOError:
403
            return False
404
1540.3.2 by Martin Pool
Remove obsolete (and no-op) `decode` parameter to `Transport.get`.
405
    def get(self, relpath):
1185.16.87 by mbp at sourcefrog
- fix line endings in sftp transport
406
        """
407
        Get the file at the given relative path.
408
409
        :param relpath: The relative path to the file
410
        """
411
        try:
1524.1.1 by Robert Collins
Test sftp with relative, absolute-in-homedir and absolute-not-in-homedir
412
            path = self._remote_path(relpath)
1185.31.51 by John Arbash Meinel
Setting binary flags for sftp.
413
            f = self._sftp.file(path, mode='rb')
1540.2.3 by Robey Pointer
move the paramiko version check up to the top and use getattr instead of hasattr. use BZR_SSH instead of BZR_USE_PARAMIKO, to allow overriding the ssh vendor check in a general way.
414
            if self._do_prefetch and (getattr(f, 'prefetch', None) is not None):
1185.40.1 by Robey Pointer
prefetch files under paramiko 1.5.1 for improved speed
415
                f.prefetch()
416
            return f
1185.50.13 by John Arbash Meinel
Expanded the Transport test suite. Including delete, copy, move, etc. Updated SftpTransport to conform.
417
        except (IOError, paramiko.SSHException), e:
418
            self._translate_io_exception(e, path, ': error retrieving')
1185.16.87 by mbp at sourcefrog
- fix line endings in sftp transport
419
420
    def get_partial(self, relpath, start, length=None):
421
        """
422
        Get just part of a file.
423
424
        :param relpath: Path to the file, relative to base
425
        :param start: The starting position to read from
426
        :param length: The length to read. A length of None indicates
427
                       read to the end of the file.
428
        :return: A file-like object containing at least the specified bytes.
429
                 Some implementations may return objects which can be read
430
                 past this length, but this is not guaranteed.
431
        """
1185.49.2 by John Arbash Meinel
Adding a open_exclusive function since paramiko supports it, but doesn't expose it
432
        # TODO: implement get_partial_multi to help with knit support
1185.16.87 by mbp at sourcefrog
- fix line endings in sftp transport
433
        f = self.get(relpath)
434
        f.seek(start)
1185.49.16 by John Arbash Meinel
Disabling prefetch
435
        if self._do_prefetch and hasattr(f, 'prefetch'):
1185.40.1 by Robey Pointer
prefetch files under paramiko 1.5.1 for improved speed
436
            f.prefetch()
1185.16.87 by mbp at sourcefrog
- fix line endings in sftp transport
437
        return f
438
1185.58.2 by John Arbash Meinel
Added mode to the appropriate transport functions, and tests to make sure they work.
439
    def put(self, relpath, f, mode=None):
1185.16.87 by mbp at sourcefrog
- fix line endings in sftp transport
440
        """
441
        Copy the file-like or string object into the location.
442
443
        :param relpath: Location to put the contents, relative to base.
444
        :param f:       File-like or string object.
1185.58.2 by John Arbash Meinel
Added mode to the appropriate transport functions, and tests to make sure they work.
445
        :param mode: The final mode for the file
1185.16.87 by mbp at sourcefrog
- fix line endings in sftp transport
446
        """
1524.1.1 by Robert Collins
Test sftp with relative, absolute-in-homedir and absolute-not-in-homedir
447
        final_path = self._remote_path(relpath)
1185.58.2 by John Arbash Meinel
Added mode to the appropriate transport functions, and tests to make sure they work.
448
        self._put(final_path, f, mode=mode)
449
450
    def _put(self, abspath, f, mode=None):
451
        """Helper function so both put() and copy_abspaths can reuse the code"""
452
        tmp_abspath = '%s.tmp.%.9f.%d.%d' % (abspath, time.time(),
1185.49.3 by John Arbash Meinel
Added a form of locking to sftp branches. Refactored _sftp_open_exclusive to take a relative path
453
                        os.getpid(), random.randint(0,0x7FFFFFFF))
1185.58.2 by John Arbash Meinel
Added mode to the appropriate transport functions, and tests to make sure they work.
454
        fout = self._sftp_open_exclusive(tmp_abspath, mode=mode)
1185.31.47 by John Arbash Meinel
Added a fancy footwork rename to osutils, made SftpTransport use it.
455
        closed = False
1185.41.6 by Robey Pointer
modified version of john's patch to add atomic put and locking to the sftp transport
456
        try:
1185.49.1 by John Arbash Meinel
Updating SftpTransport.put() so that it is atomic
457
            try:
1185.58.2 by John Arbash Meinel
Added mode to the appropriate transport functions, and tests to make sure they work.
458
                fout.set_pipelined(True)
1185.49.1 by John Arbash Meinel
Updating SftpTransport.put() so that it is atomic
459
                self._pump(f, fout)
1185.31.47 by John Arbash Meinel
Added a fancy footwork rename to osutils, made SftpTransport use it.
460
            except (IOError, paramiko.SSHException), e:
461
                self._translate_io_exception(e, tmp_abspath)
1185.50.20 by John Arbash Meinel
merge permissions branch, also fixup tests so they are lined up with bzr.dev to help prevent conflicts.
462
            if mode is not None:
463
                self._sftp.chmod(tmp_abspath, mode)
1185.31.47 by John Arbash Meinel
Added a fancy footwork rename to osutils, made SftpTransport use it.
464
            fout.close()
465
            closed = True
1553.5.13 by Martin Pool
New Transport.rename that mustn't overwrite
466
            self._rename_and_overwrite(tmp_abspath, abspath)
1185.49.1 by John Arbash Meinel
Updating SftpTransport.put() so that it is atomic
467
        except Exception, e:
468
            # If we fail, try to clean up the temporary file
469
            # before we throw the exception
470
            # but don't let another exception mess things up
1185.50.20 by John Arbash Meinel
merge permissions branch, also fixup tests so they are lined up with bzr.dev to help prevent conflicts.
471
            # Write out the traceback, because otherwise
472
            # the catch and throw destroys it
1185.58.2 by John Arbash Meinel
Added mode to the appropriate transport functions, and tests to make sure they work.
473
            import traceback
474
            mutter(traceback.format_exc())
1185.49.1 by John Arbash Meinel
Updating SftpTransport.put() so that it is atomic
475
            try:
1185.31.47 by John Arbash Meinel
Added a fancy footwork rename to osutils, made SftpTransport use it.
476
                if not closed:
477
                    fout.close()
1185.49.3 by John Arbash Meinel
Added a form of locking to sftp branches. Refactored _sftp_open_exclusive to take a relative path
478
                self._sftp.remove(tmp_abspath)
1185.49.1 by John Arbash Meinel
Updating SftpTransport.put() so that it is atomic
479
            except:
1532 by Robert Collins
Merge in John Meinels integration branch.
480
                # raise the saved except
481
                raise e
482
            # raise the original with its traceback if we can.
483
            raise
1185.16.87 by mbp at sourcefrog
- fix line endings in sftp transport
484
485
    def iter_files_recursive(self):
486
        """Walk the relative paths of all files in this transport."""
487
        queue = list(self.list_dir('.'))
488
        while queue:
1185.12.96 by Aaron Bentley
Merge from mpool
489
            relpath = urllib.quote(queue.pop(0))
1185.16.87 by mbp at sourcefrog
- fix line endings in sftp transport
490
            st = self.stat(relpath)
491
            if stat.S_ISDIR(st.st_mode):
492
                for i, basename in enumerate(self.list_dir(relpath)):
493
                    queue.insert(i, relpath+'/'+basename)
494
            else:
495
                yield relpath
496
1185.58.2 by John Arbash Meinel
Added mode to the appropriate transport functions, and tests to make sure they work.
497
    def mkdir(self, relpath, mode=None):
1185.16.87 by mbp at sourcefrog
- fix line endings in sftp transport
498
        """Create a directory at the given path."""
499
        try:
1524.1.1 by Robert Collins
Test sftp with relative, absolute-in-homedir and absolute-not-in-homedir
500
            path = self._remote_path(relpath)
1185.58.2 by John Arbash Meinel
Added mode to the appropriate transport functions, and tests to make sure they work.
501
            # In the paramiko documentation, it says that passing a mode flag 
502
            # will filtered against the server umask.
503
            # StubSFTPServer does not do this, which would be nice, because it is
504
            # what we really want :)
505
            # However, real servers do use umask, so we really should do it that way
1185.16.87 by mbp at sourcefrog
- fix line endings in sftp transport
506
            self._sftp.mkdir(path)
1185.58.2 by John Arbash Meinel
Added mode to the appropriate transport functions, and tests to make sure they work.
507
            if mode is not None:
508
                self._sftp.chmod(path, mode=mode)
1185.31.44 by John Arbash Meinel
Cleaned up Exceptions for all transports.
509
        except (paramiko.SSHException, IOError), e:
1185.58.4 by John Arbash Meinel
Added permission checking to Branch, and propogated that change into the stores.
510
            self._translate_io_exception(e, path, ': unable to mkdir',
1185.50.13 by John Arbash Meinel
Expanded the Transport test suite. Including delete, copy, move, etc. Updated SftpTransport to conform.
511
                failure_exc=FileExists)
512
1662.1.12 by Martin Pool
Translate unknown sftp errors to PathError, no NoSuchFile
513
    def _translate_io_exception(self, e, path, more_info='', 
514
                                failure_exc=PathError):
1185.50.13 by John Arbash Meinel
Expanded the Transport test suite. Including delete, copy, move, etc. Updated SftpTransport to conform.
515
        """Translate a paramiko or IOError into a friendlier exception.
516
517
        :param e: The original exception
518
        :param path: The path in question when the error is raised
519
        :param more_info: Extra information that can be included,
520
                          such as what was going on
521
        :param failure_exc: Paramiko has the super fun ability to raise completely
522
                           opaque errors that just set "e.args = ('Failure',)" with
523
                           no more information.
1662.1.12 by Martin Pool
Translate unknown sftp errors to PathError, no NoSuchFile
524
                           If this parameter is set, it defines the exception 
525
                           to raise in these cases.
1185.50.13 by John Arbash Meinel
Expanded the Transport test suite. Including delete, copy, move, etc. Updated SftpTransport to conform.
526
        """
1489 by Robert Collins
Make the paramiko tests pass. Nice huh.
527
        # paramiko seems to generate detailless errors.
1185.50.13 by John Arbash Meinel
Expanded the Transport test suite. Including delete, copy, move, etc. Updated SftpTransport to conform.
528
        self._translate_error(e, path, raise_generic=False)
1185.31.44 by John Arbash Meinel
Cleaned up Exceptions for all transports.
529
        if hasattr(e, 'args'):
530
            if (e.args == ('No such file or directory',) or
531
                e.args == ('No such file',)):
1185.50.13 by John Arbash Meinel
Expanded the Transport test suite. Including delete, copy, move, etc. Updated SftpTransport to conform.
532
                raise NoSuchFile(path, str(e) + more_info)
1185.31.44 by John Arbash Meinel
Cleaned up Exceptions for all transports.
533
            if (e.args == ('mkdir failed',)):
1185.50.13 by John Arbash Meinel
Expanded the Transport test suite. Including delete, copy, move, etc. Updated SftpTransport to conform.
534
                raise FileExists(path, str(e) + more_info)
1185.31.44 by John Arbash Meinel
Cleaned up Exceptions for all transports.
535
            # strange but true, for the paramiko server.
536
            if (e.args == ('Failure',)):
1185.50.13 by John Arbash Meinel
Expanded the Transport test suite. Including delete, copy, move, etc. Updated SftpTransport to conform.
537
                raise failure_exc(path, str(e) + more_info)
1185.58.2 by John Arbash Meinel
Added mode to the appropriate transport functions, and tests to make sure they work.
538
            mutter('Raising exception with args %s', e.args)
539
        if hasattr(e, 'errno'):
540
            mutter('Raising exception with errno %s', e.errno)
1185.31.44 by John Arbash Meinel
Cleaned up Exceptions for all transports.
541
        raise e
1489 by Robert Collins
Make the paramiko tests pass. Nice huh.
542
1666.1.6 by Robert Collins
Make knit the default format.
543
    def append(self, relpath, f, mode=None):
1185.16.87 by mbp at sourcefrog
- fix line endings in sftp transport
544
        """
545
        Append the text in the file-like object into the final
546
        location.
547
        """
548
        try:
1524.1.1 by Robert Collins
Test sftp with relative, absolute-in-homedir and absolute-not-in-homedir
549
            path = self._remote_path(relpath)
1185.16.87 by mbp at sourcefrog
- fix line endings in sftp transport
550
            fout = self._sftp.file(path, 'ab')
1666.1.6 by Robert Collins
Make knit the default format.
551
            if mode is not None:
552
                self._sftp.chmod(path, mode)
1563.2.3 by Robert Collins
Change the return signature of transport.append and append_multi to return the length of the pre-append content.
553
            result = fout.tell()
1185.16.87 by mbp at sourcefrog
- fix line endings in sftp transport
554
            self._pump(f, fout)
1563.2.3 by Robert Collins
Change the return signature of transport.append and append_multi to return the length of the pre-append content.
555
            return result
1185.50.13 by John Arbash Meinel
Expanded the Transport test suite. Including delete, copy, move, etc. Updated SftpTransport to conform.
556
        except (IOError, paramiko.SSHException), e:
557
            self._translate_io_exception(e, relpath, ': unable to append')
1185.16.87 by mbp at sourcefrog
- fix line endings in sftp transport
558
1553.5.13 by Martin Pool
New Transport.rename that mustn't overwrite
559
    def rename(self, rel_from, rel_to):
560
        """Rename without special overwriting"""
561
        try:
562
            self._sftp.rename(self._remote_path(rel_from),
563
                              self._remote_path(rel_to))
564
        except (IOError, paramiko.SSHException), e:
565
            self._translate_io_exception(e, rel_from,
566
                    ': unable to rename to %r' % (rel_to))
567
568
    def _rename_and_overwrite(self, abs_from, abs_to):
1185.31.47 by John Arbash Meinel
Added a fancy footwork rename to osutils, made SftpTransport use it.
569
        """Do a fancy rename on the remote server.
570
        
571
        Using the implementation provided by osutils.
572
        """
573
        try:
574
            fancy_rename(abs_from, abs_to,
575
                    rename_func=self._sftp.rename,
576
                    unlink_func=self._sftp.remove)
577
        except (IOError, paramiko.SSHException), e:
578
            self._translate_io_exception(e, abs_from, ': unable to rename to %r' % (abs_to))
579
1185.16.87 by mbp at sourcefrog
- fix line endings in sftp transport
580
    def move(self, rel_from, rel_to):
581
        """Move the item at rel_from to the location at rel_to"""
1524.1.1 by Robert Collins
Test sftp with relative, absolute-in-homedir and absolute-not-in-homedir
582
        path_from = self._remote_path(rel_from)
583
        path_to = self._remote_path(rel_to)
1553.5.13 by Martin Pool
New Transport.rename that mustn't overwrite
584
        self._rename_and_overwrite(path_from, path_to)
1185.16.87 by mbp at sourcefrog
- fix line endings in sftp transport
585
586
    def delete(self, relpath):
587
        """Delete the item at relpath"""
1524.1.1 by Robert Collins
Test sftp with relative, absolute-in-homedir and absolute-not-in-homedir
588
        path = self._remote_path(relpath)
1185.16.87 by mbp at sourcefrog
- fix line endings in sftp transport
589
        try:
590
            self._sftp.remove(path)
1185.50.13 by John Arbash Meinel
Expanded the Transport test suite. Including delete, copy, move, etc. Updated SftpTransport to conform.
591
        except (IOError, paramiko.SSHException), e:
592
            self._translate_io_exception(e, path, ': unable to delete')
1185.16.87 by mbp at sourcefrog
- fix line endings in sftp transport
593
            
594
    def listable(self):
595
        """Return True if this store supports listing."""
596
        return True
597
598
    def list_dir(self, relpath):
599
        """
600
        Return a list of all files at the given location.
601
        """
602
        # does anything actually use this?
1524.1.1 by Robert Collins
Test sftp with relative, absolute-in-homedir and absolute-not-in-homedir
603
        path = self._remote_path(relpath)
1185.16.87 by mbp at sourcefrog
- fix line endings in sftp transport
604
        try:
605
            return self._sftp.listdir(path)
1185.50.13 by John Arbash Meinel
Expanded the Transport test suite. Including delete, copy, move, etc. Updated SftpTransport to conform.
606
        except (IOError, paramiko.SSHException), e:
607
            self._translate_io_exception(e, path, ': failed to list_dir')
1185.16.87 by mbp at sourcefrog
- fix line endings in sftp transport
608
1534.4.15 by Robert Collins
Remove shutil dependency in upgrade - create a delete_tree method for transports.
609
    def rmdir(self, relpath):
610
        """See Transport.rmdir."""
611
        path = self._remote_path(relpath)
612
        try:
613
            return self._sftp.rmdir(path)
614
        except (IOError, paramiko.SSHException), e:
615
            self._translate_io_exception(e, path, ': failed to rmdir')
616
1185.16.87 by mbp at sourcefrog
- fix line endings in sftp transport
617
    def stat(self, relpath):
618
        """Return the stat information for a file."""
1524.1.1 by Robert Collins
Test sftp with relative, absolute-in-homedir and absolute-not-in-homedir
619
        path = self._remote_path(relpath)
1185.16.87 by mbp at sourcefrog
- fix line endings in sftp transport
620
        try:
621
            return self._sftp.stat(path)
1185.50.13 by John Arbash Meinel
Expanded the Transport test suite. Including delete, copy, move, etc. Updated SftpTransport to conform.
622
        except (IOError, paramiko.SSHException), e:
623
            self._translate_io_exception(e, path, ': unable to stat')
1185.16.87 by mbp at sourcefrog
- fix line endings in sftp transport
624
625
    def lock_read(self, relpath):
626
        """
627
        Lock the given file for shared (read) access.
1185.49.3 by John Arbash Meinel
Added a form of locking to sftp branches. Refactored _sftp_open_exclusive to take a relative path
628
        :return: A lock object, which has an unlock() member function
1185.16.87 by mbp at sourcefrog
- fix line endings in sftp transport
629
        """
630
        # FIXME: there should be something clever i can do here...
631
        class BogusLock(object):
632
            def __init__(self, path):
633
                self.path = path
634
            def unlock(self):
635
                pass
636
        return BogusLock(relpath)
637
638
    def lock_write(self, relpath):
639
        """
640
        Lock the given file for exclusive (write) access.
641
        WARNING: many transports do not support this, so trying avoid using it
642
1185.49.3 by John Arbash Meinel
Added a form of locking to sftp branches. Refactored _sftp_open_exclusive to take a relative path
643
        :return: A lock object, which has an unlock() member function
1185.16.87 by mbp at sourcefrog
- fix line endings in sftp transport
644
        """
1185.49.2 by John Arbash Meinel
Adding a open_exclusive function since paramiko supports it, but doesn't expose it
645
        # This is a little bit bogus, but basically, we create a file
646
        # which should not already exist, and if it does, we assume
647
        # that there is a lock, and if it doesn't, the we assume
648
        # that we have taken the lock.
1185.49.3 by John Arbash Meinel
Added a form of locking to sftp branches. Refactored _sftp_open_exclusive to take a relative path
649
        return SFTPLock(relpath, self)
1185.16.87 by mbp at sourcefrog
- fix line endings in sftp transport
650
651
    def _unparse_url(self, path=None):
652
        if path is None:
1185.48.5 by James Henstridge
Change SFTP url parsing back to treat the path in sftp://host/path as
653
            path = self._path
654
        path = urllib.quote(path)
1534.1.8 by Robert Collins
Update SFTP Urls as per mailing list thread.
655
        # handle homedir paths
656
        if not path.startswith('/'):
657
            path = "/~/" + path
1185.48.1 by James Henstridge
Use /usr/bin/ssh if we can.
658
        netloc = urllib.quote(self._host)
659
        if self._username is not None:
660
            netloc = '%s@%s' % (urllib.quote(self._username), netloc)
1185.49.23 by John Arbash Meinel
bugreport from Matthieu Moy: relpath was failing, but throwing an unhelpful exception.
661
        if self._port is not None:
1185.48.1 by James Henstridge
Use /usr/bin/ssh if we can.
662
            netloc = '%s:%d' % (netloc, self._port)
663
        return urlparse.urlunparse(('sftp', netloc, path, '', '', ''))
1185.16.87 by mbp at sourcefrog
- fix line endings in sftp transport
664
1185.48.8 by James Henstridge
More URL handling fixes
665
    def _split_url(self, url):
1707.3.4 by John Arbash Meinel
Moved most of sftp.split_url into a Transport function.
666
        (scheme, username, password, host, port, path) = split_url(url)
1185.48.1 by James Henstridge
Use /usr/bin/ssh if we can.
667
        assert scheme == 'sftp'
1185.48.5 by James Henstridge
Change SFTP url parsing back to treat the path in sftp://host/path as
668
669
        # the initial slash should be removed from the path, and treated
670
        # as a homedir relative path (the path begins with a double slash
671
        # if it is absolute).
672
        # see draft-ietf-secsh-scp-sftp-ssh-uri-03.txt
1534.1.8 by Robert Collins
Update SFTP Urls as per mailing list thread.
673
        # RBC 20060118 we are not using this as its too user hostile. instead
674
        # we are following lftp and using /~/foo to mean '~/foo'.
675
        # handle homedir paths
676
        if path.startswith('/~/'):
677
            path = path[3:]
678
        elif path == '/~':
679
            path = ''
1185.48.8 by James Henstridge
More URL handling fixes
680
        return (username, password, host, port, path)
681
682
    def _parse_url(self, url):
683
        (self._username, self._password,
684
         self._host, self._port, self._path) = self._split_url(url)
1185.16.87 by mbp at sourcefrog
- fix line endings in sftp transport
685
686
    def _sftp_connect(self):
1185.49.14 by John Arbash Meinel
[merge] bzr.dev
687
        """Connect to the remote sftp server.
688
        After this, self._sftp should have a valid connection (or
1185.31.44 by John Arbash Meinel
Cleaned up Exceptions for all transports.
689
        we raise an TransportError 'could not connect').
1185.49.14 by John Arbash Meinel
[merge] bzr.dev
690
691
        TODO: Raise a more reasonable ConnectionFailed exception
692
        """
693
        global _connected_hosts
1185.49.10 by John Arbash Meinel
Use a weakref dictionary to enable re-use of a connection (for sftp).
694
695
        idx = (self._host, self._port, self._username)
696
        try:
697
            self._sftp = _connected_hosts[idx]
698
            return
699
        except KeyError:
700
            pass
1185.16.87 by mbp at sourcefrog
- fix line endings in sftp transport
701
        
1185.48.1 by James Henstridge
Use /usr/bin/ssh if we can.
702
        vendor = _get_ssh_vendor()
1547.1.1 by Robey Pointer
modify the sftp unit tests to perform sftp over a direct localhost socket instead of over an actual ssh2 transport
703
        if vendor == 'loopback':
704
            sock = socket.socket()
705
            sock.connect((self._host, self._port))
706
            self._sftp = SFTPClient(LoopbackSFTP(sock))
707
        elif vendor != 'none':
1530.1.3 by Robert Collins
transport implementations now tested consistently.
708
            sock = SFTPSubprocess(self._host, vendor, self._port,
709
                                  self._username)
1185.48.1 by James Henstridge
Use /usr/bin/ssh if we can.
710
            self._sftp = SFTPClient(sock)
711
        else:
712
            self._paramiko_connect()
713
1185.49.14 by John Arbash Meinel
[merge] bzr.dev
714
        _connected_hosts[idx] = self._sftp
715
1185.48.1 by James Henstridge
Use /usr/bin/ssh if we can.
716
    def _paramiko_connect(self):
1185.16.87 by mbp at sourcefrog
- fix line endings in sftp transport
717
        global SYSTEM_HOSTKEYS, BZR_HOSTKEYS
718
        
719
        load_host_keys()
1185.48.1 by James Henstridge
Use /usr/bin/ssh if we can.
720
1185.16.87 by mbp at sourcefrog
- fix line endings in sftp transport
721
        try:
1185.49.27 by John Arbash Meinel
James Henstridge confirmed that sftp.py needs self._port or 22
722
            t = paramiko.Transport((self._host, self._port or 22))
1547.1.2 by Robey Pointer
ask paramiko to log into the bzr logging tree during unit tests, and log a warning when the server thread exits due to exception
723
            t.set_log_channel('bzr.paramiko')
1185.16.87 by mbp at sourcefrog
- fix line endings in sftp transport
724
            t.start_client()
1185.50.13 by John Arbash Meinel
Expanded the Transport test suite. Including delete, copy, move, etc. Updated SftpTransport to conform.
725
        except paramiko.SSHException, e:
726
            raise ConnectionError('Unable to reach SSH host %s:%d' %
727
                                  (self._host, self._port), e)
1185.16.87 by mbp at sourcefrog
- fix line endings in sftp transport
728
            
729
        server_key = t.get_remote_server_key()
730
        server_key_hex = paramiko.util.hexify(server_key.get_fingerprint())
731
        keytype = server_key.get_name()
732
        if SYSTEM_HOSTKEYS.has_key(self._host) and SYSTEM_HOSTKEYS[self._host].has_key(keytype):
733
            our_server_key = SYSTEM_HOSTKEYS[self._host][keytype]
734
            our_server_key_hex = paramiko.util.hexify(our_server_key.get_fingerprint())
735
        elif BZR_HOSTKEYS.has_key(self._host) and BZR_HOSTKEYS[self._host].has_key(keytype):
736
            our_server_key = BZR_HOSTKEYS[self._host][keytype]
737
            our_server_key_hex = paramiko.util.hexify(our_server_key.get_fingerprint())
738
        else:
739
            warning('Adding %s host key for %s: %s' % (keytype, self._host, server_key_hex))
740
            if not BZR_HOSTKEYS.has_key(self._host):
741
                BZR_HOSTKEYS[self._host] = {}
742
            BZR_HOSTKEYS[self._host][keytype] = server_key
743
            our_server_key = server_key
744
            our_server_key_hex = paramiko.util.hexify(our_server_key.get_fingerprint())
745
            save_host_keys()
746
        if server_key != our_server_key:
747
            filename1 = os.path.expanduser('~/.ssh/known_hosts')
1185.31.33 by John Arbash Meinel
A couple more path.join statements needed changing.
748
            filename2 = pathjoin(config_dir(), 'ssh_host_keys')
1185.31.44 by John Arbash Meinel
Cleaned up Exceptions for all transports.
749
            raise TransportError('Host keys for %s do not match!  %s != %s' % \
1185.16.87 by mbp at sourcefrog
- fix line endings in sftp transport
750
                (self._host, our_server_key_hex, server_key_hex),
751
                ['Try editing %s or %s' % (filename1, filename2)])
752
1185.49.22 by John Arbash Meinel
Added get_password to the UIFactory, using it inside of sftp.py
753
        self._sftp_auth(t)
1185.16.87 by mbp at sourcefrog
- fix line endings in sftp transport
754
        
755
        try:
756
            self._sftp = t.open_sftp_client()
1185.50.13 by John Arbash Meinel
Expanded the Transport test suite. Including delete, copy, move, etc. Updated SftpTransport to conform.
757
        except paramiko.SSHException, e:
758
            raise ConnectionError('Unable to start sftp client %s:%d' %
759
                                  (self._host, self._port), e)
1185.16.87 by mbp at sourcefrog
- fix line endings in sftp transport
760
1185.49.22 by John Arbash Meinel
Added get_password to the UIFactory, using it inside of sftp.py
761
    def _sftp_auth(self, transport):
762
        # paramiko requires a username, but it might be none if nothing was supplied
763
        # use the local username, just in case.
764
        # We don't override self._username, because if we aren't using paramiko,
765
        # the username might be specified in ~/.ssh/config and we don't want to
766
        # force it to something else
767
        # Also, it would mess up the self.relpath() functionality
768
        username = self._username or getpass.getuser()
769
1185.31.44 by John Arbash Meinel
Cleaned up Exceptions for all transports.
770
        # Paramiko tries to open a socket.AF_UNIX in order to connect
771
        # to ssh-agent. That attribute doesn't exist on win32 (it does in cygwin)
772
        # so we get an AttributeError exception. For now, just don't try to
773
        # connect to an agent if we are on win32
774
        if sys.platform != 'win32':
775
            agent = paramiko.Agent()
776
            for key in agent.get_keys():
777
                mutter('Trying SSH agent key %s' % paramiko.util.hexify(key.get_fingerprint()))
778
                try:
779
                    transport.auth_publickey(username, key)
780
                    return
781
                except paramiko.SSHException, e:
782
                    pass
1185.16.87 by mbp at sourcefrog
- fix line endings in sftp transport
783
        
784
        # okay, try finding id_rsa or id_dss?  (posix only)
1185.49.22 by John Arbash Meinel
Added get_password to the UIFactory, using it inside of sftp.py
785
        if self._try_pkey_auth(transport, paramiko.RSAKey, username, 'id_rsa'):
786
            return
787
        if self._try_pkey_auth(transport, paramiko.DSSKey, username, 'id_dsa'):
788
            return
789
1185.27.1 by Jelmer Vernooij
Parse passwords in sftp URLs (sftp://foo:bar@localhost/).
790
        if self._password:
791
            try:
1185.49.22 by John Arbash Meinel
Added get_password to the UIFactory, using it inside of sftp.py
792
                transport.auth_password(username, self._password)
1185.27.1 by Jelmer Vernooij
Parse passwords in sftp URLs (sftp://foo:bar@localhost/).
793
                return
794
            except paramiko.SSHException, e:
795
                pass
796
1185.49.22 by John Arbash Meinel
Added get_password to the UIFactory, using it inside of sftp.py
797
            # FIXME: Don't keep a password held in memory if you can help it
798
            #self._password = None
799
1185.16.87 by mbp at sourcefrog
- fix line endings in sftp transport
800
        # give up and ask for a password
1185.50.10 by John Arbash Meinel
Don't import ui_factory directly, in case it gets changed later.
801
        password = bzrlib.ui.ui_factory.get_password(
802
                prompt='SSH %(user)s@%(host)s password',
803
                user=username, host=self._host)
1185.16.87 by mbp at sourcefrog
- fix line endings in sftp transport
804
        try:
1185.49.22 by John Arbash Meinel
Added get_password to the UIFactory, using it inside of sftp.py
805
            transport.auth_password(username, password)
1185.50.13 by John Arbash Meinel
Expanded the Transport test suite. Including delete, copy, move, etc. Updated SftpTransport to conform.
806
        except paramiko.SSHException, e:
807
            raise ConnectionError('Unable to authenticate to SSH host as %s@%s' %
808
                                  (username, self._host), e)
1185.16.87 by mbp at sourcefrog
- fix line endings in sftp transport
809
1185.49.22 by John Arbash Meinel
Added get_password to the UIFactory, using it inside of sftp.py
810
    def _try_pkey_auth(self, transport, pkey_class, username, filename):
1185.16.87 by mbp at sourcefrog
- fix line endings in sftp transport
811
        filename = os.path.expanduser('~/.ssh/' + filename)
812
        try:
813
            key = pkey_class.from_private_key_file(filename)
1185.49.22 by John Arbash Meinel
Added get_password to the UIFactory, using it inside of sftp.py
814
            transport.auth_publickey(username, key)
1185.16.87 by mbp at sourcefrog
- fix line endings in sftp transport
815
            return True
816
        except paramiko.PasswordRequiredException:
1185.50.10 by John Arbash Meinel
Don't import ui_factory directly, in case it gets changed later.
817
            password = bzrlib.ui.ui_factory.get_password(
818
                    prompt='SSH %(filename)s password',
819
                    filename=filename)
1185.16.87 by mbp at sourcefrog
- fix line endings in sftp transport
820
            try:
821
                key = pkey_class.from_private_key_file(filename, password)
1185.49.22 by John Arbash Meinel
Added get_password to the UIFactory, using it inside of sftp.py
822
                transport.auth_publickey(username, key)
1185.16.87 by mbp at sourcefrog
- fix line endings in sftp transport
823
                return True
824
            except paramiko.SSHException:
825
                mutter('SSH authentication via %s key failed.' % (os.path.basename(filename),))
826
        except paramiko.SSHException:
827
            mutter('SSH authentication via %s key failed.' % (os.path.basename(filename),))
828
        except IOError:
829
            pass
830
        return False
1185.49.2 by John Arbash Meinel
Adding a open_exclusive function since paramiko supports it, but doesn't expose it
831
1185.58.2 by John Arbash Meinel
Added mode to the appropriate transport functions, and tests to make sure they work.
832
    def _sftp_open_exclusive(self, abspath, mode=None):
1185.49.2 by John Arbash Meinel
Adding a open_exclusive function since paramiko supports it, but doesn't expose it
833
        """Open a remote path exclusively.
834
835
        SFTP supports O_EXCL (SFTP_FLAG_EXCL), which fails if
836
        the file already exists. However it does not expose this
837
        at the higher level of SFTPClient.open(), so we have to
838
        sneak away with it.
839
840
        WARNING: This breaks the SFTPClient abstraction, so it
841
        could easily break against an updated version of paramiko.
842
1185.58.2 by John Arbash Meinel
Added mode to the appropriate transport functions, and tests to make sure they work.
843
        :param abspath: The remote absolute path where the file should be opened
844
        :param mode: The mode permissions bits for the new file
1185.49.2 by John Arbash Meinel
Adding a open_exclusive function since paramiko supports it, but doesn't expose it
845
        """
1185.58.2 by John Arbash Meinel
Added mode to the appropriate transport functions, and tests to make sure they work.
846
        path = self._sftp._adjust_cwd(abspath)
1185.49.2 by John Arbash Meinel
Adding a open_exclusive function since paramiko supports it, but doesn't expose it
847
        attr = SFTPAttributes()
1185.58.2 by John Arbash Meinel
Added mode to the appropriate transport functions, and tests to make sure they work.
848
        if mode is not None:
849
            attr.st_mode = mode
850
        omode = (SFTP_FLAG_WRITE | SFTP_FLAG_CREATE 
1185.49.2 by John Arbash Meinel
Adding a open_exclusive function since paramiko supports it, but doesn't expose it
851
                | SFTP_FLAG_TRUNC | SFTP_FLAG_EXCL)
852
        try:
1185.58.2 by John Arbash Meinel
Added mode to the appropriate transport functions, and tests to make sure they work.
853
            t, msg = self._sftp._request(CMD_OPEN, path, omode, attr)
1185.49.2 by John Arbash Meinel
Adding a open_exclusive function since paramiko supports it, but doesn't expose it
854
            if t != CMD_HANDLE:
1185.31.44 by John Arbash Meinel
Cleaned up Exceptions for all transports.
855
                raise TransportError('Expected an SFTP handle')
1185.49.2 by John Arbash Meinel
Adding a open_exclusive function since paramiko supports it, but doesn't expose it
856
            handle = msg.get_string()
1185.31.51 by John Arbash Meinel
Setting binary flags for sftp.
857
            return SFTPFile(self._sftp, handle, 'wb', -1)
1185.31.44 by John Arbash Meinel
Cleaned up Exceptions for all transports.
858
        except (paramiko.SSHException, IOError), e:
1185.58.2 by John Arbash Meinel
Added mode to the appropriate transport functions, and tests to make sure they work.
859
            self._translate_io_exception(e, abspath, ': unable to open',
1185.50.13 by John Arbash Meinel
Expanded the Transport test suite. Including delete, copy, move, etc. Updated SftpTransport to conform.
860
                failure_exc=FileExists)
1185.49.2 by John Arbash Meinel
Adding a open_exclusive function since paramiko supports it, but doesn't expose it
861
1530.1.1 by Robert Collins
Minimal infrastructure to test TransportTestProviderAdapter.
862
1530.1.3 by Robert Collins
transport implementations now tested consistently.
863
# ------------- server test implementation --------------
864
import socket
865
import threading
866
867
from bzrlib.tests.stub_sftp import StubServer, StubSFTPServer
868
869
STUB_SERVER_KEY = """
870
-----BEGIN RSA PRIVATE KEY-----
871
MIICWgIBAAKBgQDTj1bqB4WmayWNPB+8jVSYpZYk80Ujvj680pOTh2bORBjbIAyz
872
oWGW+GUjzKxTiiPvVmxFgx5wdsFvF03v34lEVVhMpouqPAYQ15N37K/ir5XY+9m/
873
d8ufMCkjeXsQkKqFbAlQcnWMCRnOoPHS3I4vi6hmnDDeeYTSRvfLbW0fhwIBIwKB
874
gBIiOqZYaoqbeD9OS9z2K9KR2atlTxGxOJPXiP4ESqP3NVScWNwyZ3NXHpyrJLa0
875
EbVtzsQhLn6rF+TzXnOlcipFvjsem3iYzCpuChfGQ6SovTcOjHV9z+hnpXvQ/fon
876
soVRZY65wKnF7IAoUwTmJS9opqgrN6kRgCd3DASAMd1bAkEA96SBVWFt/fJBNJ9H
877
tYnBKZGw0VeHOYmVYbvMSstssn8un+pQpUm9vlG/bp7Oxd/m+b9KWEh2xPfv6zqU
878
avNwHwJBANqzGZa/EpzF4J8pGti7oIAPUIDGMtfIcmqNXVMckrmzQ2vTfqtkEZsA
879
4rE1IERRyiJQx6EJsz21wJmGV9WJQ5kCQQDwkS0uXqVdFzgHO6S++tjmjYcxwr3g
880
H0CoFYSgbddOT6miqRskOQF3DZVkJT3kyuBgU2zKygz52ukQZMqxCb1fAkASvuTv
881
qfpH87Qq5kQhNKdbbwbmd2NxlNabazPijWuphGTdW0VfJdWfklyS2Kr+iqrs/5wV
882
HhathJt636Eg7oIjAkA8ht3MQ+XSl9yIJIS8gVpbPxSw5OMfw0PjVE7tBdQruiSc
883
nvuQES5C9BMHjF39LZiGH1iLQy7FgdHyoP+eodI7
884
-----END RSA PRIVATE KEY-----
885
"""
886
    
887
888
class SingleListener(threading.Thread):
889
890
    def __init__(self, callback):
891
        threading.Thread.__init__(self)
892
        self._callback = callback
893
        self._socket = socket.socket()
894
        self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
895
        self._socket.bind(('localhost', 0))
896
        self._socket.listen(1)
897
        self.port = self._socket.getsockname()[1]
898
        self.stop_event = threading.Event()
899
900
    def run(self):
901
        s, _ = self._socket.accept()
902
        # now close the listen socket
903
        self._socket.close()
1547.1.1 by Robey Pointer
modify the sftp unit tests to perform sftp over a direct localhost socket instead of over an actual ssh2 transport
904
        try:
905
            self._callback(s, self.stop_event)
1185.50.56 by John Arbash Meinel
[patch] Robey Pointer: squelch the socket.error messages.
906
        except socket.error:
907
            pass #Ignore socket errors
1547.1.1 by Robey Pointer
modify the sftp unit tests to perform sftp over a direct localhost socket instead of over an actual ssh2 transport
908
        except Exception, x:
1547.1.2 by Robey Pointer
ask paramiko to log into the bzr logging tree during unit tests, and log a warning when the server thread exits due to exception
909
            # probably a failed test
1547.1.7 by Robey Pointer
correct another misspelling of warning
910
            warning('Exception from within unit test server thread: %r' % x)
1547.1.1 by Robey Pointer
modify the sftp unit tests to perform sftp over a direct localhost socket instead of over an actual ssh2 transport
911
1530.1.3 by Robert Collins
transport implementations now tested consistently.
912
    def stop(self):
913
        self.stop_event.set()
1547.1.1 by Robey Pointer
modify the sftp unit tests to perform sftp over a direct localhost socket instead of over an actual ssh2 transport
914
        # use a timeout here, because if the test fails, the server thread may
915
        # never notice the stop_event.
916
        self.join(5.0)
917
918
1530.1.3 by Robert Collins
transport implementations now tested consistently.
919
class SFTPServer(Server):
920
    """Common code for SFTP server facilities."""
921
922
    def __init__(self):
923
        self._original_vendor = None
924
        self._homedir = None
1530.1.8 by Robert Collins
More NEWS, move sibling sftp tests into new framework, nuke legacy local transport tests.
925
        self._server_homedir = None
1530.1.3 by Robert Collins
transport implementations now tested consistently.
926
        self._listener = None
927
        self._root = None
1547.1.1 by Robey Pointer
modify the sftp unit tests to perform sftp over a direct localhost socket instead of over an actual ssh2 transport
928
        self._vendor = 'none'
1530.1.6 by Robert Collins
Trim duplicate sftp tests.
929
        # sftp server logs
930
        self.logs = []
1530.1.3 by Robert Collins
transport implementations now tested consistently.
931
1547.1.1 by Robey Pointer
modify the sftp unit tests to perform sftp over a direct localhost socket instead of over an actual ssh2 transport
932
    def _get_sftp_url(self, path):
933
        """Calculate an sftp url to this server for path."""
934
        return 'sftp://foo:bar@localhost:%d/%s' % (self._listener.port, path)
935
1530.1.6 by Robert Collins
Trim duplicate sftp tests.
936
    def log(self, message):
1547.1.1 by Robey Pointer
modify the sftp unit tests to perform sftp over a direct localhost socket instead of over an actual ssh2 transport
937
        """StubServer uses this to log when a new server is created."""
1530.1.6 by Robert Collins
Trim duplicate sftp tests.
938
        self.logs.append(message)
1530.1.3 by Robert Collins
transport implementations now tested consistently.
939
940
    def _run_server(self, s, stop_event):
941
        ssh_server = paramiko.Transport(s)
942
        key_file = os.path.join(self._homedir, 'test_rsa.key')
943
        file(key_file, 'w').write(STUB_SERVER_KEY)
944
        host_key = paramiko.RSAKey.from_private_key_file(key_file)
945
        ssh_server.add_server_key(host_key)
946
        server = StubServer(self)
947
        ssh_server.set_subsystem_handler('sftp', paramiko.SFTPServer,
948
                                         StubSFTPServer, root=self._root,
1530.1.8 by Robert Collins
More NEWS, move sibling sftp tests into new framework, nuke legacy local transport tests.
949
                                         home=self._server_homedir)
1530.1.3 by Robert Collins
transport implementations now tested consistently.
950
        event = threading.Event()
951
        ssh_server.start_server(event, server)
952
        event.wait(5.0)
953
        stop_event.wait(30.0)
1547.1.1 by Robey Pointer
modify the sftp unit tests to perform sftp over a direct localhost socket instead of over an actual ssh2 transport
954
    
1530.1.3 by Robert Collins
transport implementations now tested consistently.
955
    def setUp(self):
956
        global _ssh_vendor
957
        self._original_vendor = _ssh_vendor
1547.1.1 by Robey Pointer
modify the sftp unit tests to perform sftp over a direct localhost socket instead of over an actual ssh2 transport
958
        _ssh_vendor = self._vendor
1685.1.72 by Wouter van Heyst
StubSFTPServer should use bytestreams rather than unicode
959
        self._homedir = os.getcwd()
1530.1.8 by Robert Collins
More NEWS, move sibling sftp tests into new framework, nuke legacy local transport tests.
960
        if self._server_homedir is None:
961
            self._server_homedir = self._homedir
1530.1.3 by Robert Collins
transport implementations now tested consistently.
962
        self._root = '/'
1530.1.8 by Robert Collins
More NEWS, move sibling sftp tests into new framework, nuke legacy local transport tests.
963
        # FIXME WINDOWS: _root should be _server_homedir[0]:/
1530.1.3 by Robert Collins
transport implementations now tested consistently.
964
        self._listener = SingleListener(self._run_server)
965
        self._listener.setDaemon(True)
966
        self._listener.start()
967
968
    def tearDown(self):
969
        """See bzrlib.transport.Server.tearDown."""
970
        global _ssh_vendor
971
        self._listener.stop()
972
        _ssh_vendor = self._original_vendor
973
974
1534.4.50 by Robert Collins
Got the bzrdir api straightened out, plenty of refactoring to use it pending, but the api is up and running.
975
class SFTPFullAbsoluteServer(SFTPServer):
976
    """A test server for sftp transports, using absolute urls and ssh."""
977
978
    def get_url(self):
979
        """See bzrlib.transport.Server.get_url."""
1685.1.45 by John Arbash Meinel
Moved url functions into bzrlib.urlutils
980
        return self._get_sftp_url(urlutils.escape(self._homedir[1:]))
1534.4.50 by Robert Collins
Got the bzrdir api straightened out, plenty of refactoring to use it pending, but the api is up and running.
981
982
1547.1.1 by Robey Pointer
modify the sftp unit tests to perform sftp over a direct localhost socket instead of over an actual ssh2 transport
983
class SFTPServerWithoutSSH(SFTPServer):
1534.4.50 by Robert Collins
Got the bzrdir api straightened out, plenty of refactoring to use it pending, but the api is up and running.
984
    """An SFTP server that uses a simple TCP socket pair rather than SSH."""
1547.1.1 by Robey Pointer
modify the sftp unit tests to perform sftp over a direct localhost socket instead of over an actual ssh2 transport
985
986
    def __init__(self):
987
        super(SFTPServerWithoutSSH, self).__init__()
988
        self._vendor = 'loopback'
989
990
    def _run_server(self, sock, stop_event):
991
        class FakeChannel(object):
992
            def get_transport(self):
993
                return self
994
            def get_log_channel(self):
995
                return 'paramiko'
996
            def get_name(self):
997
                return '1'
998
            def get_hexdump(self):
999
                return False
1711.2.1 by John Arbash Meinel
[patch] Marien Zwart: paramiko-1.6 compatibility fix
1000
            def close(self):
1001
                pass
1547.1.1 by Robey Pointer
modify the sftp unit tests to perform sftp over a direct localhost socket instead of over an actual ssh2 transport
1002
1003
        server = paramiko.SFTPServer(FakeChannel(), 'sftp', StubServer(self), StubSFTPServer,
1004
                                     root=self._root, home=self._server_homedir)
1005
        server.start_subsystem('sftp', None, sock)
1006
        server.finish_subsystem()
1007
1008
1009
class SFTPAbsoluteServer(SFTPServerWithoutSSH):
1530.1.1 by Robert Collins
Minimal infrastructure to test TransportTestProviderAdapter.
1010
    """A test server for sftp transports, using absolute urls."""
1011
1530.1.3 by Robert Collins
transport implementations now tested consistently.
1012
    def get_url(self):
1013
        """See bzrlib.transport.Server.get_url."""
1685.1.45 by John Arbash Meinel
Moved url functions into bzrlib.urlutils
1014
        return self._get_sftp_url(urlutils.escape(self._homedir[1:]))
1530.1.3 by Robert Collins
transport implementations now tested consistently.
1015
1016
1547.1.1 by Robey Pointer
modify the sftp unit tests to perform sftp over a direct localhost socket instead of over an actual ssh2 transport
1017
class SFTPHomeDirServer(SFTPServerWithoutSSH):
1530.1.1 by Robert Collins
Minimal infrastructure to test TransportTestProviderAdapter.
1018
    """A test server for sftp transports, using homedir relative urls."""
1530.1.3 by Robert Collins
transport implementations now tested consistently.
1019
1020
    def get_url(self):
1021
        """See bzrlib.transport.Server.get_url."""
1534.1.8 by Robert Collins
Update SFTP Urls as per mailing list thread.
1022
        return self._get_sftp_url("~/")
1530.1.8 by Robert Collins
More NEWS, move sibling sftp tests into new framework, nuke legacy local transport tests.
1023
1024
1025
class SFTPSiblingAbsoluteServer(SFTPAbsoluteServer):
1026
    """A test servere for sftp transports, using absolute urls to non-home."""
1027
1028
    def setUp(self):
1029
        self._server_homedir = '/dev/noone/runs/tests/here'
1030
        super(SFTPSiblingAbsoluteServer, self).setUp()
1530.1.11 by Robert Collins
Push the transport permutations list into each transport module allowing for automatic testing of new modules that are registered as transports.
1031
1032
1033
def get_test_permutations():
1034
    """Return the permutations to be used in testing."""
1035
    return [(SFTPTransport, SFTPAbsoluteServer),
1036
            (SFTPTransport, SFTPHomeDirServer),
1037
            (SFTPTransport, SFTPSiblingAbsoluteServer),
1038
            ]