diff --git a/archivemail b/archivemail index 26b9aca..6124d2a 100755 --- a/archivemail +++ b/archivemail @@ -180,6 +180,8 @@ class Options: archive_name = None days_old_max = 180 date_old_max = None + days_old_min = 10950 + date_old_min = None delete_old_mail = False dry_run = False filter_append = None @@ -215,8 +217,8 @@ class Options: """ try: - opts, args = getopt.getopt(args, '?D:S:Vd:hno:F:P:qs:p:a:uv', - ["date=", "days=", "delete", "dry-run", "help", + opts, args = getopt.getopt(args, '?D:M:S:Vd:m:hno:F:P:qs:p:a:uv', + ["date=", "days=", "mindate=", "mindays=", "delete", "dry-run", "help", "include-flagged", "no-compress", "output-dir=", "filter-append=", "pwfile=", "dont-mangle", "preserve-unread", "quiet", "size=", "suffix=", @@ -226,7 +228,8 @@ class Options: except getopt.error, msg: user_error(msg) - archive_by = None + archive_by_max = None + archive_by_min = None for o, a in opts: if o == '--delete': @@ -240,15 +243,25 @@ class Options: if o == '--warn-duplicate': self.warn_duplicates = True if o in ('-D', '--date'): - if archive_by: + if archive_by_max: user_error("you cannot specify both -d and -D options") - archive_by = "date" + archive_by_max = "date" self.date_old_max = self.date_argument(a) if o in ('-d', '--days'): - if archive_by: + if archive_by_max: user_error("you cannot specify both -d and -D options") - archive_by = "days" + archive_by_max = "days" self.days_old_max = string.atoi(a) + if o in ('-M', '--mindate'): + if archive_by_min: + user_error("you cannot specify both -m and -M options") + archive_by_min = "date" + self.date_old_min = self.date_argument(a) + if o in ('-m', '--mindays'): + if archive_by_min: + user_error("you cannot specify both -m and -M options") + archive_by_min = "days" + self.days_old_min = string.atoi(a) if o in ('-o', '--output-dir'): self.output_dir = os.path.expanduser(a) if o in ('-P', '--pwfile'): @@ -295,8 +308,12 @@ class Options: check_sane_destdir(self.output_dir) if self.days_old_max < 0: user_error("--days argument must be positive") - if self.days_old_max >= 10000: - user_error("--days argument must be less than 10000") + if self.days_old_max >= 100000: + user_error("--days argument must be less than 100000") + if self.days_old_min < 0: + user_error("--mindays argument must be positive") + if self.days_old_max >= 100000: + user_error("--mindays argument must be less than 100000") if self.min_size is not None and self.min_size < 1: user_error("--size argument must be greater than zero") if self.quiet and self.verbose: @@ -653,6 +670,8 @@ mailbox compressed with gzip. Options are as follows: -d, --days=NUM archive messages older than NUM days (default: %d) -D, --date=DATE archive messages older than DATE + -m, --mindays=NUM archive messages newer than NUM days (default: %d) + -N, --mindate=DATE archive messages newer than DATE -o, --output-dir=DIR directory to store archives (default: same as original) -P, --pwfile=FILE file to read imap password from (default: None) -F, --filter-append=STRING append arbitrary string to the IMAP filter string @@ -686,7 +705,7 @@ To archive IMAP mailboxes, format your mailbox argument like this: (substitute 'imap' with 'imaps' for an SSL connection) Website: http://archivemail.sourceforge.net/ """ % \ - (options.script_name, options.days_old_max, options.archive_suffix, + (options.script_name, options.days_old_max, options.days_old_min, options.archive_suffix, options.script_name, options.days_old_max) args = options.parse_args(args, usage) @@ -997,11 +1016,17 @@ def should_archive(message): old = is_older_than_days(time_message, options.days_old_max) else: old = is_older_than_time(time_message, options.date_old_max) + if options.date_old_min == None: + new = is_younger_than_days(time_message, options.days_old_min) + else: + new = is_younger_than_time(time_message, options.date_old_min) # I could probably do this in one if statement, but then I wouldn't # understand it. if not old: return False + if not new: + return False if not options.include_flagged and is_flagged(message): return False if options.min_size and is_smaller(message, options.min_size): @@ -1050,6 +1075,42 @@ def is_older_than_days(time_message, max_days): return True return False +def is_younger_than_time(time_message, min_time): + """Return true if a message is newer than the specified time, + false otherwise. + + Arguments: + time_message -- the delivery date of the message measured in seconds + since the epoch + min_time -- minimum time allowed for message + + """ + days_old = (min_time - time_message) / 24 / 60 / 60 + if time_message >= min_time: + vprint("message is %.2f days newer than the specified date" % days_old) + return True + vprint("message is %.2f days older than the specified date" % \ + abs(days_old)) + return False + + +def is_younger_than_days(time_message, min_days): + """Return true if a message is newer than the specified number of days, + false otherwise. + + Arguments: + time_message -- the delivery date of the message measured in seconds + since the epoch + min_days -- minimum number of days before message is considered old + """ + time_now = time.time() + secs_old_min = (min_days * 24 * 60 * 60) + days_old = (time_now - time_message) / 24 / 60 / 60 + vprint("message is %.2f days old" % days_old) + if ((time_message + secs_old_min) >= time_now): + return True + return False + def build_imap_filter(): """Return an imap filter string""" @@ -1057,12 +1118,21 @@ def build_imap_filter(): if options.date_old_max == None: time_now = time.time() secs_old_max = (options.days_old_max * 24 * 60 * 60) - time_old = time.gmtime(time_now - secs_old_max) + time_old = time.localtime(time_now - secs_old_max) else: - time_old = time.gmtime(options.date_old_max) + time_old = time.localtime(options.date_old_max) time_str = time.strftime('%d-%b-%Y', time_old) imap_filter.append("BEFORE %s" % time_str) + if options.date_old_min == None: + time_now = time.time() + secs_old_min = (options.days_old_min * 24 * 60 * 60) + time_old = time.localtime(time_now - secs_old_min) + else: + time_old = time.localtime(options.date_old_min) + time_str = time.strftime('%d-%b-%Y', time_old) + imap_filter.append("SINCE %s" % time_str) + if not options.include_flagged: imap_filter.append("UNFLAGGED") if options.min_size: diff --git a/test_archivemail b/test_archivemail index 4f90519..4256743 100755 --- a/test_archivemail +++ b/test_archivemail @@ -544,6 +544,47 @@ class TestIsTooOld(unittest.TestCase): assert not archivemail.is_older_than_days(time_message=time_msg, max_days=1) +########## archivemail.is_younger_than_days() unit testing ################# + +class TestIsTooYoung(unittest.TestCase): + def testVeryNotYoung(self): + """with min_days=360, should be false for these dates > 1 year""" + ctime = time.time() + for years in range(1, 10): + time_msg = ctime - (years * 365 * 24 * 60 * 60) + assert not archivemail.is_younger_than_days(time_message=time_msg, + min_days=360) + + def testNotYoung(self): + """with min_days=14, should be false for these dates > 14 days""" + ctime = time.time() + for days in range(14, 360): + time_msg = ctime - (days * 24 * 60 * 60) + assert not archivemail.is_younger_than_days(time_message=time_msg, + min_days=14) + + def testJustNotYoung(self): + """with min_days=1, should be false for these dates >= 1 day""" + ctime = time.time() + for minutes in range(0, 61): + time_msg = ctime - (25 * 60 * 60) + (minutes * 60) + assert not archivemail.is_younger_than_days(time_message=time_msg, + min_days=1) + + def testYoung(self): + """with min_days=9, should be true for these dates < 9 days""" + for days in range(0, 9): + time_msg = time.time() - (days * 24 * 60 * 60) + assert archivemail.is_younger_than_days(time_message=time_msg, + min_days=9) + + def testJustYoung(self): + """with min_days=1, should be true for these hours <= 1 day""" + for minutes in range(0, 60): + time_msg = time.time() - (23 * 60 * 60) - (minutes * 60) + assert archivemail.is_younger_than_days(time_message=time_msg, + min_days=1) + ########## archivemail.parse_imap_url() unit testing ################# class TestParseIMAPUrl(unittest.TestCase):