~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/transport/sftp.py

Merged Martin

Show diffs side-by-side

added added

removed removed

Lines of Context:
16
16
 
17
17
"""Implementation of Transport over SFTP, using paramiko."""
18
18
 
 
19
import errno
19
20
import getpass
20
21
import os
21
22
import re
23
24
import sys
24
25
import urllib
25
26
 
26
 
from bzrlib.errors import TransportNotPossible, NoSuchFile, NonRelativePath, TransportError
 
27
from bzrlib.errors import (FileExists, 
 
28
                           TransportNotPossible, NoSuchFile, NonRelativePath,
 
29
                           TransportError)
27
30
from bzrlib.config import config_dir
28
31
from bzrlib.trace import mutter, warning, error
29
32
from bzrlib.transport import Transport, register_transport
84
87
    Transport implementation for SFTP access.
85
88
    """
86
89
 
87
 
    _url_matcher = re.compile(r'^sftp://([^@]*@)?(.*?)(:\d+)?(/.*)?$')
 
90
    _url_matcher = re.compile(r'^sftp://([^:@]*(:[^@]*)?@)?(.*?)(:\d+)?(/.*)?$')
88
91
    
89
92
    def __init__(self, base, clone_from=None):
90
93
        assert base.startswith('sftp://')
145
148
                basepath.append(p)
146
149
 
147
150
        path = '/'.join(basepath)
148
 
        if path[0] != '/':
 
151
        if len(path) and path[0] != '/':
149
152
            path = '/' + path
150
153
        return path
151
154
 
206
209
        try:
207
210
            path = self._abspath(relpath)
208
211
            fout = self._sftp.file(path, 'wb')
 
212
        except IOError, e:
 
213
            self._translate_io_exception(e, relpath)
209
214
        except (IOError, paramiko.SSHException), x:
210
215
            raise SFTPTransportError('Unable to write file %r' % (path,), x)
211
216
        try:
230
235
        try:
231
236
            path = self._abspath(relpath)
232
237
            self._sftp.mkdir(path)
 
238
        except IOError, e:
 
239
            self._translate_io_exception(e, relpath)
233
240
        except (IOError, paramiko.SSHException), x:
234
241
            raise SFTPTransportError('Unable to mkdir %r' % (path,), x)
235
242
 
 
243
    def _translate_io_exception(self, e, relpath):
 
244
        # paramiko seems to generate detailless errors.
 
245
        if (e.errno == errno.ENOENT or
 
246
            e.args == ('No such file or directory',) or
 
247
            e.args == ('No such file',)):
 
248
            raise NoSuchFile(relpath)
 
249
        if (e.args == ('mkdir failed',)):
 
250
            raise FileExists(relpath)
 
251
        # strange but true, for the paramiko server.
 
252
        if (e.args == ('Failure',)):
 
253
            raise FileExists(relpath)
 
254
        raise
 
255
 
236
256
    def append(self, relpath, f):
237
257
        """
238
258
        Append the text in the file-like object into the final
344
364
        m = self._url_matcher.match(url)
345
365
        if m is None:
346
366
            raise SFTPTransportError('Unable to parse SFTP URL %r' % (url,))
347
 
        self._username, self._host, self._port, self._path = m.groups()
 
367
        self._username, self._password, self._host, self._port, self._path = m.groups()
348
368
        if self._username is None:
349
369
            self._username = getpass.getuser()
350
370
        else:
351
371
            self._username = self._username[:-1]
 
372
        if self._password:
 
373
            self._password = self._password[1:]
 
374
            self._username = self._username[len(self._password)+1:]
352
375
        if self._port is None:
353
376
            self._port = 22
354
377
        else:
415
438
        if self._try_pkey_auth(transport, paramiko.DSSKey, 'id_dsa'):
416
439
            return
417
440
 
 
441
        if self._password:
 
442
            try:
 
443
                transport.auth_password(self._username, self._password)
 
444
                return
 
445
            except paramiko.SSHException, e:
 
446
                pass
 
447
 
418
448
        # give up and ask for a password
419
449
        password = getpass.getpass('SSH %s@%s password: ' % (self._username, self._host))
420
450
        try: