From 2bfde528cd269126e54cd756e4a7c4dda3b6da6e Mon Sep 17 00:00:00 2001 From: Paul Rodger Date: Tue, 23 Apr 2002 03:01:26 +0000 Subject: [PATCH] Added the ability to archive messages older than a given absolute date with the new option '--date' and fixed a bug where archivemail would complain about messages older than 1970. --- CHANGELOG | 6 ++ Makefile | 7 +- TODO | 4 +- archivemail.py | 107 ++++++++++++++++++++++-------- archivemail.sgml | 31 ++++++++- setup.py | 2 +- test_archivemail.py | 158 ++++++++++++++++++++++++++++++++++---------- 7 files changed, 248 insertions(+), 67 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index fc42aec..643a4e6 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,4 +1,10 @@ +Version 0.4.2 - ??? + * Added the ability to archive messages older than a given absolute date + with the new option '--date'. + * Fixed a bug where archivemail would complain about messages older than + 1970. Yes, someone had a 'Date' header with 1967 :) + Version 0.4.1 - 21 April 2002 * Don't archive messages that are flagged important unless we are given the --include-flagged option. diff --git a/Makefile b/Makefile index ffb4452..f7b0a43 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ -VERSION=0.4.1 +VERSION=0.4.2 VERSION_TAG=v$(subst .,_,$(VERSION)) TARFILE=archivemail-$(VERSION).tar.gz @@ -16,6 +16,7 @@ test: clobber: clean rm -rf build dist + sdist: clobber doc cp archivemail.py archivemail fakeroot python setup.py sdist @@ -24,11 +25,11 @@ tag: cvs tag -F current cvs tag $(VERSION_TAG) -doc: archivemail.1 archivemail.html - upload: (cd dist && lftp -c 'open upload.sf.net && cd incoming && put $(TARFILE)') +doc: archivemail.1 archivemail.html + archivemail.1: archivemail.sgml nsgmls archivemail.sgml | sgmlspl docbook2man-spec.pl chmod 644 archivemail.1 diff --git a/TODO b/TODO index 31d9d73..0dc95a5 100644 --- a/TODO +++ b/TODO @@ -1,9 +1,11 @@ -Goals for next minor release (0.4.2): +Goals for next minor release (0.4.3): ------------------------------------- * Think about the best way to specify the names of archives created with possibly an --archive-name option. * Add a lot more tests (see top of test_archivemail.py) +* We need some better checking to see if we are really looking at a valid + mbox-format mailbox. Goals for next major release (0.5.0): ------------------------------------- diff --git a/archivemail.py b/archivemail.py index b2dade7..6cdbdcd 100755 --- a/archivemail.py +++ b/archivemail.py @@ -22,7 +22,7 @@ Website: http://archivemail.sourceforge.net/ """ # global administrivia -__version__ = "archivemail v0.4.1" +__version__ = "archivemail v0.4.2" __cvs_id__ = "$Id$" __copyright__ = """Copyright (C) 2002 Paul Rodger This is free software; see the source for copying conditions. There is NO @@ -133,6 +133,7 @@ class Options: """Class to store runtime options, including defaults""" archive_suffix = "_archive" days_old_max = 180 + date_old_max = None delete_old_mail = 0 dry_run = 0 include_flagged = 0 @@ -161,15 +162,16 @@ class Options: """ try: - opts, args = getopt.getopt(args, '?Vd:hno:qs:uv', - ["days=", "delete", "dry-run", "help", - "include-flagged", "no-compress", - "output-dir=", "preserve-unread", "quiet", - "suffix", "verbose", "version", - "warn-duplicate"]) + opts, args = getopt.getopt(args, '?D:Vd:hno:qs:uv', + ["date=", "days=", "delete", "dry-run", "help", + "include-flagged", "no-compress", "output-dir=", + "preserve-unread", "quiet", "suffix", "verbose", + "version", "warn-duplicate"]) except getopt.error, msg: user_error(msg) + archive_by = None + for o, a in opts: if o == '--delete': self.delete_old_mail = 1 @@ -179,7 +181,15 @@ class Options: self.no_compress = 1 if o == '--warn-duplicate': self.warn_duplicates = 1 + if o in ('-D', '--date'): + if archive_by: + user_error("you cannot specify both -d and -D options") + archive_by = "date" + self.date_old_max = self.date_argument(a) if o in ('-d', '--days'): + if archive_by: + user_error("you cannot specify both -d and -D options") + archive_by = "days" self.days_old_max = string.atoi(a) if o in ('-o', '--output-dir'): self.output_dir = a @@ -218,6 +228,27 @@ class Options: if (self.days_old_max >= 10000): user_error("argument to -d must be less than 10000") + def date_argument(self, string): + """Converts a date argument string into seconds since the epoch""" + date_formats = ( + "%Y-%m-%d", # ISO format + "%d %b %Y" , # Internet format + "%d %B %Y" , # Internet format with full month names + ) + time.accept2dyear = 0 # I'm not going to support 2-digit years + for format in date_formats: + try: + date = time.strptime(string, format) + seconds = time.mktime(date) + return seconds + except (ValueError, OverflowError): + pass + user_error("cannot parse the date argument '%s'\n" + "The date should be in ISO format (eg '2002-04-23'),\n" + "Internet format (eg '23 Apr 2002') or\n" + "Internet format with full month names (eg '23 April 2002')" % + string) + class Mbox(mailbox.UnixMailbox): """Class that allows read/write access to a 'mbox' mailbox. @@ -526,7 +557,8 @@ Moves old mail in mbox, MH or maildir-format mailboxes to an mbox-format mailbox compressed with gzip. Options are as follows: - -d, --days= archive messages older than days (default: %d) + -d, --days=NUM archive messages older than NUM days (default: %d) + -D, --date=DATE archive messages older than DATE -o, --output-dir=DIR directory to store archives (default: same as original) -s, --suffix=NAME suffix for archive filename (default: '%s') -n, --dry-run don't write to anything - just show what would be done @@ -747,34 +779,54 @@ def is_unread(message): def should_archive(message): - """Return 1 if we should archive the message, 0 otherwise""" + """Return true if we should archive the message, false otherwise""" + old = 0 time_message = guess_delivery_time(message) - old = is_too_old(time_message, options.days_old_max) - # I could probably do this in one if statement, but then I wouldn't - # understand it. - if old: - if not options.include_flagged and is_flagged(message): - return 0 - if options.preserve_unread: - if is_unread(message): - return 0 - else: - return 1 - else: - return 1 - return 0 - + if options.date_old_max == None: + old = is_older_than_days(time_message, options.days_old_max) + else: + old = is_older_than_time(time_message, options.date_old_max) -def is_too_old(time_message, max_days): - """Return true if a message is too old (and should be archived), + # I could probably do this in one if statement, but then I wouldn't + # understand it. + if not old: + return 0 + if not options.include_flagged and is_flagged(message): + return 0 + if options.preserve_unread and is_unread(message): + return 0 + return 1 + + +def is_older_than_time(time_message, max_time): + """Return true if a message is older than the specified time, false otherwise. Arguments: time_message -- the delivery date of the message measured in seconds since the epoch + max_time -- maximum time allowed for message + + """ + days_old = (max_time - time_message) / 24 / 60 / 60 + if time_message < max_time: + vprint("message is %.2f days older than the specified date" % days_old) + return 1 + vprint("message is %.2f days younger than the specified date" % \ + abs(days_old)) + return 0 + + +def is_older_than_days(time_message, max_days): + """Return true if a message is older than the specified number of days, + false otherwise. + + Arguments: + time_message -- the delivery date of the message measured in seconds + since the epoch + max_days -- maximum number of days before message is considered old """ - assert(time_message > 0) assert(max_days >= 1) time_now = time.time() @@ -1021,6 +1073,7 @@ def set_signal_handlers(): signal.signal(signal.SIGQUIT, clean_up_signal) # signal 3 signal.signal(signal.SIGTERM, clean_up_signal) # signal 15 + def clean_up(): """Delete stale files -- to be registered with atexit.register()""" vprint("cleaning up ...") diff --git a/archivemail.sgml b/archivemail.sgml index 2ad5329..c20f0cd 100644 --- a/archivemail.sgml +++ b/archivemail.sgml @@ -93,7 +93,20 @@ mailbox it is reading, creating any archive files as that user.