~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/transport/ftp.py

  • Committer: John Arbash Meinel
  • Date: 2007-04-28 15:04:17 UTC
  • mfrom: (2466 +trunk)
  • mto: This revision was merged to the branch mainline in revision 2566.
  • Revision ID: john@arbash-meinel.com-20070428150417-trp3pi0pzd411pu4
[merge] bzr.dev 2466

Show diffs side-by-side

added added

removed removed

Lines of Context:
29
29
import errno
30
30
import ftplib
31
31
import os
 
32
import os.path
32
33
import urllib
33
34
import urlparse
 
35
import select
34
36
import stat
35
37
import threading
36
38
import time
39
41
 
40
42
from bzrlib import (
41
43
    errors,
 
44
    osutils,
42
45
    urlutils,
43
46
    )
44
47
from bzrlib.trace import mutter, warning
47
50
    split_url,
48
51
    Transport,
49
52
    )
 
53
from bzrlib.transport.local import LocalURLServer
50
54
import bzrlib.ui
51
55
 
52
56
_have_medusa = False
301
305
        :param retries: Number of retries after temporary failures so far
302
306
                        for this operation.
303
307
 
304
 
        TODO: jam 20051215 ftp as a protocol seems to support chmod, but ftplib does not
 
308
        TODO: jam 20051215 ftp as a protocol seems to support chmod, but
 
309
        ftplib does not
305
310
        """
306
311
        abspath = self._abspath(relpath)
307
312
        tmp_abspath = '%s.tmp.%.9f.%d.%d' % (abspath, time.time(),
313
318
            f = self._get_FTP()
314
319
            try:
315
320
                f.storbinary('STOR '+tmp_abspath, fp)
316
 
                f.rename(tmp_abspath, abspath)
 
321
                self._rename_and_overwrite(tmp_abspath, abspath, f)
317
322
            except (ftplib.error_temp,EOFError), e:
318
323
                warning("Failure during ftp PUT. Deleting temporary file.")
319
324
                try:
432
437
    #       to give it its own address as the 'to' location.
433
438
    #       So implement a fancier 'copy()'
434
439
 
 
440
    def rename(self, rel_from, rel_to):
 
441
        abs_from = self._abspath(rel_from)
 
442
        abs_to = self._abspath(rel_to)
 
443
        mutter("FTP rename: %s => %s", abs_from, abs_to)
 
444
        f = self._get_FTP()
 
445
        return self._rename(abs_from, abs_to, f)
 
446
 
 
447
    def _rename(self, abs_from, abs_to, f):
 
448
        try:
 
449
            f.rename(abs_from, abs_to)
 
450
        except ftplib.error_perm, e:
 
451
            self._translate_perm_error(e, abs_from,
 
452
                ': unable to rename to %r' % (abs_to))
 
453
 
435
454
    def move(self, rel_from, rel_to):
436
455
        """Move the item at rel_from to the location at rel_to"""
437
456
        abs_from = self._abspath(rel_from)
439
458
        try:
440
459
            mutter("FTP mv: %s => %s", abs_from, abs_to)
441
460
            f = self._get_FTP()
442
 
            f.rename(abs_from, abs_to)
 
461
            self._rename_and_overwrite(abs_from, abs_to, f)
443
462
        except ftplib.error_perm, e:
444
463
            self._translate_perm_error(e, abs_from,
445
464
                extra='unable to rename to %r' % (rel_to,), 
446
465
                unknown_exc=errors.PathError)
447
466
 
448
 
    rename = move
 
467
    def _rename_and_overwrite(self, abs_from, abs_to, f):
 
468
        """Do a fancy rename on the remote server.
 
469
 
 
470
        Using the implementation provided by osutils.
 
471
        """
 
472
        osutils.fancy_rename(abs_from, abs_to,
 
473
            rename_func=lambda p1, p2: self._rename(p1, p2, f),
 
474
            unlink_func=lambda p: self._delete(p, f))
449
475
 
450
476
    def delete(self, relpath):
451
477
        """Delete the item at relpath"""
452
478
        abspath = self._abspath(relpath)
 
479
        f = self._get_FTP()
 
480
        self._delete(abspath, f)
 
481
 
 
482
    def _delete(self, abspath, f):
453
483
        try:
454
484
            mutter("FTP rm: %s", abspath)
455
 
            f = self._get_FTP()
456
485
            f.delete(abspath)
457
486
        except ftplib.error_perm, e:
458
487
            self._translate_perm_error(e, abspath, 'error deleting',
550
579
        """This is used by medusa.ftp_server to log connections, etc."""
551
580
        self.logs.append(message)
552
581
 
553
 
    def setUp(self):
554
 
 
 
582
    def setUp(self, vfs_server=None):
555
583
        if not _have_medusa:
556
584
            raise RuntimeError('Must have medusa to run the FtpServer')
557
585
 
 
586
        assert vfs_server is None or isinstance(vfs_server, LocalURLServer), \
 
587
            "FtpServer currently assumes local transport, got %s" % vfs_server
 
588
 
558
589
        self._root = os.getcwdu()
559
590
        self._ftp_server = _ftp_server(
560
591
            authorizer=_test_authorizer(root=self._root),
566
597
        self._port = self._ftp_server.getsockname()[1]
567
598
        # Don't let it loop forever, or handle an infinite number of requests.
568
599
        # In this case it will run for 100s, or 1000 requests
569
 
        self._async_thread = threading.Thread(target=asyncore.loop,
 
600
        self._async_thread = threading.Thread(
 
601
                target=FtpServer._asyncore_loop_ignore_EBADF,
570
602
                kwargs={'timeout':0.1, 'count':1000})
571
603
        self._async_thread.setDaemon(True)
572
604
        self._async_thread.start()
578
610
        asyncore.close_all()
579
611
        self._async_thread.join()
580
612
 
 
613
    @staticmethod
 
614
    def _asyncore_loop_ignore_EBADF(*args, **kwargs):
 
615
        """Ignore EBADF during server shutdown.
 
616
 
 
617
        We close the socket to get the server to shutdown, but this causes
 
618
        select.select() to raise EBADF.
 
619
        """
 
620
        try:
 
621
            asyncore.loop(*args, **kwargs)
 
622
        except select.error, e:
 
623
            if e.args[0] != errno.EBADF:
 
624
                raise
 
625
 
581
626
 
582
627
_ftp_channel = None
583
628
_ftp_server = None
648
693
            pfrom = self.filesystem.translate(self._renaming)
649
694
            self._renaming = None
650
695
            pto = self.filesystem.translate(line[1])
 
696
            if os.path.exists(pto):
 
697
                self.respond('550 RNTO failed: file exists')
 
698
                return
651
699
            try:
652
700
                os.rename(pfrom, pto)
653
701
            except (IOError, OSError), e:
654
702
                # TODO: jam 20060516 return custom responses based on
655
703
                #       why the command failed
656
 
                self.respond('550 RNTO failed: %s' % (e,))
 
704
                # (bialix 20070418) str(e) on Python 2.5 @ Windows
 
705
                # sometimes don't provide expected error message;
 
706
                # so we obtain such message via os.strerror()
 
707
                self.respond('550 RNTO failed: %s' % os.strerror(e.errno))
657
708
            except:
658
709
                self.respond('550 RNTO failed')
659
710
                # For a test server, we will go ahead and just die
691
742
                    self.filesystem.mkdir (path)
692
743
                    self.respond ('257 MKD command successful.')
693
744
                except (IOError, OSError), e:
694
 
                    self.respond ('550 error creating directory: %s' % (e,))
 
745
                    # (bialix 20070418) str(e) on Python 2.5 @ Windows
 
746
                    # sometimes don't provide expected error message;
 
747
                    # so we obtain such message via os.strerror()
 
748
                    self.respond ('550 error creating directory: %s' %
 
749
                                  os.strerror(e.errno))
695
750
                except:
696
751
                    self.respond ('550 error creating directory.')
697
752