Here's my work-in-progress patch FYI.
Since I think the cleanest method is for some changes in python's
mailbox library, I'm discussing that too:
<http://bugs.python.org/issue20328>
--- /usr/bin/archivemail 2011-07-10 14:57:42.000000000 +0100
+++ bin/archivemail 2014-01-29 21:00:32.000000000 +0000
@@ -180,6 +180,7 @@
archive_name = None
days_old_max = 180
date_old_max = None
+ delete_empty = False
delete_old_mail = False
dry_run = False
filter_append = None
@@ -216,13 +217,13 @@
"""
try:
opts, args = getopt.getopt(args, '?D:S:Vd:hno:F:P:qs:p:a:uv',
- ["date=", "days=", "delete", "dry-run", "help",
- "include-flagged", "no-compress", "output-dir=",
- "filter-append=", "pwfile=", "dont-mangle",
- "preserve-unread", "quiet", "size=", "suffix=",
- "prefix=", "archive-name=", "verbose",
- "debug-imap=", "version", "warn-duplicate",
- "copy", "all"])
+ ["date=", "days=", "delete", "delete-empty",
+ "dry-run", "help", "include-flagged",
+ "no-compress", "output-dir=", "filter-append=",
+ "pwfile=", "dont-mangle", "preserve-unread",
+ "quiet", "size=", "suffix=", "prefix=",
+ "archive-name=", "verbose", "debug-imap=",
+ "version", "warn-duplicate", "copy", "all"])
except getopt.error, msg:
user_error(msg)
@@ -233,6 +234,8 @@
if self.copy_old_mail:
user_error("found conflicting options --copy and --delete")
self.delete_old_mail = True
+ if o == '--delete-empty':
+ self.delete_empty = True
if o == '--include-flagged':
self.include_flagged = True
if o == '--no-compress':
@@ -1219,6 +1222,13 @@
retain.close()
vprint("no changes to mbox '%s'" % original.mbox_file_name)
retain.remove()
+
+ # XXX: jmtd: remove old/empty Maildirs
+ if options.delete_empty and len(original) == 0:
+ original.remove()
+ else:
+ pass # can't delete; not empty. complain! XXX
+
original.unlock()
original.close()
original.reset_timestamps() # Minor race here; mutt has this too.
@@ -1226,6 +1236,37 @@
stats.display()
+# XXX: eventually class method / monkey patch
+def monkeypatch_method(cls):
+ def decorator(func):
+ setattr(cls, func.__name__, func)
+ return func
+ return decorator
+
+@monkeypatch_method(mailbox.Maildir)
+def remove(self): # XXX: handle known cache/index files
+ for d in ['tmp','new','cur']:
+ os.rmdir(os.path.join(self._path,d))
+ if os.path.isfile(os.path.join(self._path,'dovecot.index.cache')):
+ os.remove("dovecot.index.cache")
+ os.rmdir(self._path)
+
+@monkeypatch_method(mailbox.MHMailbox)
+def remove(self):
+ pass
+
+# this should _not_ be necessary
+@monkeypatch_method(mailbox.MHMailbox)
+def lock(self):
+ pass
+@monkeypatch_method(mailbox.MHMailbox)
+def unlock(self):
+ pass
+
+@monkeypatch_method(mailbox.mbox)
+def remove(self):
+ os.remove(self._path)
+
def _archive_dir(mailbox_name, type):
"""Archive a 'maildir' or 'MH' style mailbox - used by archive_mailbox()"""
assert mailbox_name
@@ -1277,6 +1318,14 @@
try: os.remove(file_name)
except OSError, e:
if e.errno != errno.ENOENT: raise
+ # XXX: jmtd: remove old/empty Maildirs
+ if options.delete_empty and "maildir" == type:
+ original.lock()
+ if len(original) + len(original.list_folders()) == 0:
+ original.remove()
+ else:
+ pass # can't delete; not empty. complain! XXX
+ original.unlock()
if not options.quiet:
stats.display()
@@ -1409,6 +1458,7 @@
'+FLAGS.SILENT', '\\Deleted')
if result != 'OK': unexpected_error("Error while deleting "
"messages; server says '%s'" % response[0])
+ # XXX: jmtd: delete empty mailboxes/folders
vprint("Closing mailbox.")
imap_srv.close()
if not options.quiet: