1
# Copyright (C) 2005, 2006, 2007 Canonical Ltd
1
# Copyright (C) 2005 Canonical Ltd
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
306
300
:param retries: Number of retries after temporary failures so far
307
301
for this operation.
309
TODO: jam 20051215 ftp as a protocol seems to support chmod, but
303
TODO: jam 20051215 ftp as a protocol seems to support chmod, but ftplib does not
312
305
abspath = self._abspath(relpath)
313
306
tmp_abspath = '%s.tmp.%.9f.%d.%d' % (abspath, time.time(),
319
312
f = self._get_FTP()
321
314
f.storbinary('STOR '+tmp_abspath, fp)
322
self._rename_and_overwrite(tmp_abspath, abspath, f)
315
f.rename(tmp_abspath, abspath)
323
316
except (ftplib.error_temp,EOFError), e:
324
317
warning("Failure during ftp PUT. Deleting temporary file.")
332
325
except ftplib.error_perm, e:
333
self._translate_perm_error(e, abspath, extra='could not store',
334
unknown_exc=errors.NoSuchFile)
326
self._translate_perm_error(e, abspath, extra='could not store')
335
327
except ftplib.error_temp, e:
336
328
if retries > _number_of_retries:
337
329
raise errors.TransportError("FTP temporary error during PUT %s. Aborting."
439
431
# to give it its own address as the 'to' location.
440
432
# So implement a fancier 'copy()'
442
def rename(self, rel_from, rel_to):
443
abs_from = self._abspath(rel_from)
444
abs_to = self._abspath(rel_to)
445
mutter("FTP rename: %s => %s", abs_from, abs_to)
447
return self._rename(abs_from, abs_to, f)
449
def _rename(self, abs_from, abs_to, f):
451
f.rename(abs_from, abs_to)
452
except ftplib.error_perm, e:
453
self._translate_perm_error(e, abs_from,
454
': unable to rename to %r' % (abs_to))
456
434
def move(self, rel_from, rel_to):
457
435
"""Move the item at rel_from to the location at rel_to"""
458
436
abs_from = self._abspath(rel_from)
461
439
mutter("FTP mv: %s => %s", abs_from, abs_to)
462
440
f = self._get_FTP()
463
self._rename_and_overwrite(abs_from, abs_to, f)
441
f.rename(abs_from, abs_to)
464
442
except ftplib.error_perm, e:
465
443
self._translate_perm_error(e, abs_from,
466
444
extra='unable to rename to %r' % (rel_to,),
467
445
unknown_exc=errors.PathError)
469
def _rename_and_overwrite(self, abs_from, abs_to, f):
470
"""Do a fancy rename on the remote server.
472
Using the implementation provided by osutils.
474
osutils.fancy_rename(abs_from, abs_to,
475
rename_func=lambda p1, p2: self._rename(p1, p2, f),
476
unlink_func=lambda p: self._delete(p, f))
478
449
def delete(self, relpath):
479
450
"""Delete the item at relpath"""
480
451
abspath = self._abspath(relpath)
482
self._delete(abspath, f)
484
def _delete(self, abspath, f):
486
453
mutter("FTP rm: %s", abspath)
487
455
f.delete(abspath)
488
456
except ftplib.error_perm, e:
489
457
self._translate_perm_error(e, abspath, 'error deleting',
490
458
unknown_exc=errors.NoSuchFile)
492
def external_url(self):
493
"""See bzrlib.transport.Transport.external_url."""
494
# FTP URL's are externally usable.
497
460
def listable(self):
498
461
"""See Transport.listable."""
586
549
"""This is used by medusa.ftp_server to log connections, etc."""
587
550
self.logs.append(message)
589
def setUp(self, vfs_server=None):
590
554
if not _have_medusa:
591
555
raise RuntimeError('Must have medusa to run the FtpServer')
593
assert vfs_server is None or isinstance(vfs_server, LocalURLServer), \
594
"FtpServer currently assumes local transport, got %s" % vfs_server
596
557
self._root = os.getcwdu()
597
558
self._ftp_server = _ftp_server(
598
559
authorizer=_test_authorizer(root=self._root),
604
565
self._port = self._ftp_server.getsockname()[1]
605
566
# Don't let it loop forever, or handle an infinite number of requests.
606
567
# In this case it will run for 100s, or 1000 requests
607
self._async_thread = threading.Thread(
608
target=FtpServer._asyncore_loop_ignore_EBADF,
568
self._async_thread = threading.Thread(target=asyncore.loop,
609
569
kwargs={'timeout':0.1, 'count':1000})
610
570
self._async_thread.setDaemon(True)
611
571
self._async_thread.start()
617
577
asyncore.close_all()
618
578
self._async_thread.join()
621
def _asyncore_loop_ignore_EBADF(*args, **kwargs):
622
"""Ignore EBADF during server shutdown.
624
We close the socket to get the server to shutdown, but this causes
625
select.select() to raise EBADF.
628
asyncore.loop(*args, **kwargs)
629
except select.error, e:
630
if e.args[0] != errno.EBADF:
634
581
_ftp_channel = None
635
582
_ftp_server = None
700
647
pfrom = self.filesystem.translate(self._renaming)
701
648
self._renaming = None
702
649
pto = self.filesystem.translate(line[1])
703
if os.path.exists(pto):
704
self.respond('550 RNTO failed: file exists')
707
651
os.rename(pfrom, pto)
708
652
except (IOError, OSError), e:
709
653
# TODO: jam 20060516 return custom responses based on
710
654
# why the command failed
711
# (bialix 20070418) str(e) on Python 2.5 @ Windows
712
# sometimes don't provide expected error message;
713
# so we obtain such message via os.strerror()
714
self.respond('550 RNTO failed: %s' % os.strerror(e.errno))
655
self.respond('550 RNTO failed: %s' % (e,))
716
657
self.respond('550 RNTO failed')
717
658
# For a test server, we will go ahead and just die
749
690
self.filesystem.mkdir (path)
750
691
self.respond ('257 MKD command successful.')
751
692
except (IOError, OSError), e:
752
# (bialix 20070418) str(e) on Python 2.5 @ Windows
753
# sometimes don't provide expected error message;
754
# so we obtain such message via os.strerror()
755
self.respond ('550 error creating directory: %s' %
756
os.strerror(e.errno))
693
self.respond ('550 error creating directory: %s' % (e,))
758
695
self.respond ('550 error creating directory.')