~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

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

  • Committer: Matt Nordhoff
  • Date: 2009-04-04 02:50:01 UTC
  • mfrom: (4253 +trunk)
  • mto: This revision was merged to the branch mainline in revision 4256.
  • Revision ID: mnordhoff@mattnordhoff.com-20090404025001-z1403k0tatmc8l91
Merge bzr.dev, fixing conflicts.

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 (
63
63
 
64
64
 
65
65
class FtpStatResult(object):
66
 
    def __init__(self, f, relpath):
 
66
 
 
67
    def __init__(self, f, abspath):
67
68
        try:
68
 
            self.st_size = f.size(relpath)
 
69
            self.st_size = f.size(abspath)
69
70
            self.st_mode = stat.S_IFREG
70
71
        except ftplib.error_perm:
71
72
            pwd = f.pwd()
72
73
            try:
73
 
                f.cwd(relpath)
 
74
                f.cwd(abspath)
74
75
                self.st_mode = stat.S_IFDIR
75
76
            finally:
76
77
                f.cwd(pwd)
145
146
                                             port=self._port)
146
147
            connection.login(user=user, passwd=password)
147
148
            connection.set_pasv(not self.is_active)
 
149
            # binary mode is the default
 
150
            connection.voidcmd('TYPE I')
148
151
        except socket.error, e:
149
152
            raise errors.SocketConnectionError(self._host, self._port,
150
153
                                               msg='Unable to connect to',
195
198
 
196
199
        if unknown_exc:
197
200
            raise unknown_exc(path, extra=extra)
198
 
        # TODO: jam 20060516 Consider re-raising the error wrapped in 
 
201
        # TODO: jam 20060516 Consider re-raising the error wrapped in
199
202
        #       something like TransportError, but this loses the traceback
200
203
        #       Also, 'sftp' has a generic 'Failure' mode, which we use failure_exc
201
204
        #       to handle. Consider doing something like that here.
401
404
 
402
405
    def _try_append(self, relpath, text, mode=None, retries=0):
403
406
        """Try repeatedly to append the given text to the file at relpath.
404
 
        
 
407
 
405
408
        This is a recursive function. On errors, it will be called until the
406
409
        number of retries is exceeded.
407
410
        """
409
412
            abspath = self._remote_path(relpath)
410
413
            mutter("FTP appe (try %d) to %s", retries, abspath)
411
414
            ftp = self._get_FTP()
412
 
            ftp.voidcmd("TYPE I")
413
415
            cmd = "APPE %s" % abspath
414
416
            conn = ftp.transfercmd(cmd)
415
417
            conn.sendall(text)
437
439
        if mode:
438
440
            try:
439
441
                mutter("FTP site chmod: setting permissions to %s on %s",
440
 
                    str(mode), self._remote_path(relpath))
 
442
                       oct(mode), self._remote_path(relpath))
441
443
                ftp = self._get_FTP()
442
444
                cmd = "SITE CHMOD %s %s" % (oct(mode),
443
445
                                            self._remote_path(relpath))
445
447
            except ftplib.error_perm, e:
446
448
                # Command probably not available on this server
447
449
                warning("FTP Could not set permissions to %s on %s. %s",
448
 
                        str(mode), self._remote_path(relpath), str(e))
 
450
                        oct(mode), self._remote_path(relpath), str(e))
449
451
 
450
452
    # TODO: jam 20060516 I believe ftp allows you to tell an ftp server
451
453
    #       to copy something to another machine. And you may be able
476
478
            self._rename_and_overwrite(abs_from, abs_to, f)
477
479
        except ftplib.error_perm, e:
478
480
            self._translate_perm_error(e, abs_from,
479
 
                extra='unable to rename to %r' % (rel_to,), 
 
481
                extra='unable to rename to %r' % (rel_to,),
480
482
                unknown_exc=errors.PathError)
481
483
 
482
484
    def _rename_and_overwrite(self, abs_from, abs_to, f):
517
519
        mutter("FTP nlst: %s", basepath)
518
520
        f = self._get_FTP()
519
521
        try:
520
 
            paths = f.nlst(basepath)
521
 
        except ftplib.error_perm, e:
522
 
            self._translate_perm_error(e, relpath, extra='error with list_dir')
523
 
        except ftplib.error_temp, e:
524
 
            # xs4all's ftp server raises a 450 temp error when listing an empty
525
 
            # directory. Check for that and just return an empty list in that
526
 
            # case. See bug #215522
527
 
            if str(e).lower().startswith('450 no files found'):
528
 
                mutter('FTP Server returned "%s" for nlst.'
529
 
                       ' Assuming it means empty directory',
530
 
                       str(e))
531
 
                return []
532
 
            raise
 
522
            try:
 
523
                paths = f.nlst(basepath)
 
524
            except ftplib.error_perm, e:
 
525
                self._translate_perm_error(e, relpath,
 
526
                                           extra='error with list_dir')
 
527
            except ftplib.error_temp, e:
 
528
                # xs4all's ftp server raises a 450 temp error when listing an
 
529
                # empty directory. Check for that and just return an empty list
 
530
                # in that case. See bug #215522
 
531
                if str(e).lower().startswith('450 no files found'):
 
532
                    mutter('FTP Server returned "%s" for nlst.'
 
533
                           ' Assuming it means empty directory',
 
534
                           str(e))
 
535
                    return []
 
536
                raise
 
537
        finally:
 
538
            # Restore binary mode as nlst switch to ascii mode to retrieve file
 
539
            # list
 
540
            f.voidcmd('TYPE I')
 
541
 
533
542
        # If FTP.nlst returns paths prefixed by relpath, strip 'em
534
543
        if paths and paths[0].startswith(basepath):
535
544
            entries = [path[len(basepath)+1:] for path in paths]
588
597
 
589
598
def get_test_permutations():
590
599
    """Return the permutations to be used in testing."""
591
 
    from bzrlib import tests
592
 
    if tests.FTPServerFeature.available():
593
 
        from bzrlib.tests import ftp_server
594
 
        return [(FtpTransport, ftp_server.FTPServer)]
595
 
    else:
596
 
        # Dummy server to have the test suite report the number of tests
597
 
        # needing that feature. We raise UnavailableFeature from methods before
598
 
        # the test server is being used. Doing so in the setUp method has bad
599
 
        # side-effects (tearDown is never called).
600
 
        class UnavailableFTPServer(object):
601
 
 
602
 
            def setUp(self, vfs_server=None):
603
 
                pass
604
 
 
605
 
            def tearDown(self):
606
 
                pass
607
 
 
608
 
            def get_url(self):
609
 
                raise tests.UnavailableFeature(tests.FTPServerFeature)
610
 
 
611
 
            def get_bogus_url(self):
612
 
                raise tests.UnavailableFeature(tests.FTPServerFeature)
613
 
 
614
 
        return [(FtpTransport, UnavailableFTPServer)]
 
600
    from bzrlib.tests import ftp_server
 
601
    return [(FtpTransport, ftp_server.FTPTestServer)]