Skip to content

Instantly share code, notes, and snippets.

@tehlers
Created April 7, 2017 18:38
Show Gist options
  • Save tehlers/8b49ccbbd5c461217bed30cbea429161 to your computer and use it in GitHub Desktop.
Save tehlers/8b49ccbbd5c461217bed30cbea429161 to your computer and use it in GitHub Desktop.
Delete old mails from a directory with folders in the maildir format.
#! /usr/bin/env python
# -*- coding: utf-8 -*-
"""Delete old mails from a directory with folders in the maildir format.
This script is intended to be used on a directory that contains mails managed by offlineimap.
Since offlineimap only provides the means to download mails of a maximal age but doesn't remove
them once they have been downloaded and became outdated, this script fills the void and removes
any mail exceeding a given age.
Use '--help' to get a detailed explanation of the available options.
The non standard module 'dateutil' is used to parse the mail headers. You have to install
it, if you haven't done so already.
"""
import argparse
from datetime import datetime, timedelta
import dateutil.parser
import logging
import mailbox
import os
def main():
"""Delete mails according to the given arguments"""
args = parse_args()
configure_logging( args.verbose, args.quiet )
for mail_folder in os.listdir( args.maildirs ):
path = os.path.join( args.maildirs, mail_folder )
logging.debug( "Checking messages in %s" % path )
maildir = mailbox.Maildir( path )
keys_of_old_mails = []
try:
for mail_key in maildir.iterkeys():
mail = maildir[ mail_key ]
date = dateutil.parser.parse( mail["date"] )
oldest_date = datetime.now( date.tzinfo ) - timedelta( days = args.max_age )
if ( date < oldest_date ):
log_message = "Marking '%s' received on '%s' for deletion" % ( mail["subject"], mail["date"] )
keys_of_old_mails.append( mail_key )
else:
log_message = "Keeping '%s' received on '%s'" % ( mail["subject"], mail["date"] )
logging.debug( log_message )
if ( len( keys_of_old_mails ) ):
logging.info( "Deleting %d mails from %s" % ( len( keys_of_old_mails ), mail_folder ) )
if ( not args.dry_run ):
maildir.lock()
try:
for mail_key in keys_of_old_mails:
maildir.remove( mail_key )
finally:
maildir.flush()
maildir.unlock()
except ( FileNotFoundError, NotADirectoryError ):
logging.warn( "'%s' is not a valid maildir" % path )
except ( PermissionError ):
logging.warn( "'%s' is not readable" % path )
def parse_args():
"""Evaluate the arguments that have been passed on the command line."""
parser = argparse.ArgumentParser( description = "Delete old mails from a directory with folders in the maildir format", formatter_class = argparse.RawDescriptionHelpFormatter )
parser.add_argument( "--dry-run", help = "run without deletion of mails", action = "store_true" )
parser.add_argument( "--max-age", type = int, help = "maximum age of mails before deletion in days (default: %(default)s)", default = 365 )
parser.add_argument( "--maildirs", help = "directory containing maildir folders (default: %(default)s)", default = os.path.join( os.path.expanduser( "~" ), "mail" ) )
parser.add_argument( "--quiet", help = "don't print any status information", action = "store_true" )
parser.add_argument( "--verbose", help = "enable additional debug output", action = "store_true" )
return parser.parse_args()
def configure_logging( verbose, quiet ):
"""Set the log level according to the given preferences.
'--quiet' takes precedence over '--verbose'.
"""
stdout_handler = logging.StreamHandler()
stdout_handler.setFormatter( logging.Formatter( "%(message)s" ) )
if ( quiet ):
level = logging.WARN
elif ( verbose ):
level = logging.DEBUG
else:
level = logging.INFO
stdout_handler.setLevel( level )
logging.getLogger().setLevel( level )
logging.getLogger().addHandler( stdout_handler )
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment