~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/transport/ftp/__init__.py

  • Committer: Andrew Bennetts
  • Date: 2009-07-27 05:35:00 UTC
  • mfrom: (4570 +trunk)
  • mto: (4634.6.29 2.0)
  • mto: This revision was merged to the branch mainline in revision 4680.
  • Revision ID: andrew.bennetts@canonical.com-20090727053500-q76zsn2dx33jhmj5
Merge bzr.dev.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006, 2007 Canonical Ltd
 
1
# Copyright (C) 2005, 2006, 2007, 2008, 2009 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
12
12
#
13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
 
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
"""Implementation of Transport over ftp.
17
17
 
18
18
Written by Daniel Silverstone <dsilvers@digital-scurf.org> with serious
31
31
import os
32
32
import os.path
33
33
import urlparse
 
34
import random
34
35
import socket
35
36
import stat
36
37
import time
37
 
import random
38
38
from warnings import warn
39
39
 
40
40
from bzrlib import (
99
99
            self.is_active = True
100
100
        else:
101
101
            self.is_active = False
 
102
        
 
103
        # Most modern FTP servers support the APPE command. If ours doesn't, we (re)set this flag accordingly later.
 
104
        self._has_append = True
102
105
 
103
106
    def _get_FTP(self):
104
107
        """Return the ftplib.FTP instance for this object."""
129
132
 
130
133
        auth = config.AuthenticationConfig()
131
134
        if user is None:
132
 
            user = auth.get_user('ftp', self._host, port=self._port)
133
 
            if user is None:
134
 
                # Default to local user
135
 
                user = getpass.getuser()
136
 
 
 
135
            user = auth.get_user('ftp', self._host, port=self._port,
 
136
                                 default=getpass.getuser())
137
137
        mutter("Constructing FTP instance against %r" %
138
138
               ((self._host, self._port, user, '********',
139
139
                self.is_active),))
390
390
        """Append the text in the file-like object into the final
391
391
        location.
392
392
        """
 
393
        text = f.read()
 
394
        
393
395
        abspath = self._remote_path(relpath)
394
396
        if self.has(relpath):
395
397
            ftp = self._get_FTP()
397
399
        else:
398
400
            result = 0
399
401
 
400
 
        mutter("FTP appe to %s", abspath)
401
 
        self._try_append(relpath, f.read(), mode)
 
402
        if self._has_append:
 
403
            mutter("FTP appe to %s", abspath)
 
404
            self._try_append(relpath, text, mode)
 
405
        else:
 
406
            self._fallback_append(relpath, text, mode)
402
407
 
403
408
        return result
404
409
 
419
424
            self._setmode(relpath, mode)
420
425
            ftp.getresp()
421
426
        except ftplib.error_perm, e:
422
 
            self._translate_perm_error(e, abspath, extra='error appending',
423
 
                unknown_exc=errors.NoSuchFile)
 
427
            # Check whether the command is not supported (reply code 502)
 
428
            if str(e).startswith('502 '):
 
429
                warning("FTP server does not support file appending natively. " \
 
430
                    "Performance may be severely degraded! (%s)", e)
 
431
                self._has_append = False
 
432
                self._fallback_append(relpath, text, mode)
 
433
            else:
 
434
                self._translate_perm_error(e, abspath, extra='error appending',
 
435
                    unknown_exc=errors.NoSuchFile)
 
436
            
424
437
        except ftplib.error_temp, e:
425
438
            if retries > _number_of_retries:
426
439
                raise errors.TransportError("FTP temporary error during APPEND %s." \
430
443
                self._reconnect()
431
444
                self._try_append(relpath, text, mode, retries+1)
432
445
 
 
446
    def _fallback_append(self, relpath, text, mode = None):
 
447
        remote = self.get(relpath)
 
448
        remote.seek(0, 2)
 
449
        remote.write(text)
 
450
        remote.seek(0, 0)
 
451
        return self.put_file(relpath, remote, mode)
 
452
 
433
453
    def _setmode(self, relpath, mode):
434
454
        """Set permissions on a path.
435
455
 
439
459
        if mode:
440
460
            try:
441
461
                mutter("FTP site chmod: setting permissions to %s on %s",
442
 
                    str(mode), self._remote_path(relpath))
 
462
                       oct(mode), self._remote_path(relpath))
443
463
                ftp = self._get_FTP()
444
464
                cmd = "SITE CHMOD %s %s" % (oct(mode),
445
465
                                            self._remote_path(relpath))
447
467
            except ftplib.error_perm, e:
448
468
                # Command probably not available on this server
449
469
                warning("FTP Could not set permissions to %s on %s. %s",
450
 
                        str(mode), self._remote_path(relpath), str(e))
 
470
                        oct(mode), self._remote_path(relpath), str(e))
451
471
 
452
472
    # TODO: jam 20060516 I believe ftp allows you to tell an ftp server
453
473
    #       to copy something to another machine. And you may be able
597
617
 
598
618
def get_test_permutations():
599
619
    """Return the permutations to be used in testing."""
600
 
    from bzrlib import tests
601
 
    if tests.FTPServerFeature.available():
602
 
        from bzrlib.tests import ftp_server
603
 
        return [(FtpTransport, ftp_server.FTPServer)]
604
 
    else:
605
 
        # Dummy server to have the test suite report the number of tests
606
 
        # needing that feature. We raise UnavailableFeature from methods before
607
 
        # the test server is being used. Doing so in the setUp method has bad
608
 
        # side-effects (tearDown is never called).
609
 
        class UnavailableFTPServer(object):
610
 
 
611
 
            def setUp(self, vfs_server=None):
612
 
                pass
613
 
 
614
 
            def tearDown(self):
615
 
                pass
616
 
 
617
 
            def get_url(self):
618
 
                raise tests.UnavailableFeature(tests.FTPServerFeature)
619
 
 
620
 
            def get_bogus_url(self):
621
 
                raise tests.UnavailableFeature(tests.FTPServerFeature)
622
 
 
623
 
        return [(FtpTransport, UnavailableFTPServer)]
 
620
    from bzrlib.tests import ftp_server
 
621
    return [(FtpTransport, ftp_server.FTPTestServer)]