Richard Jones' Log: OS X Mail.app rules to sieve filters

Tue, 01 Feb 2011

These days I have a snazzy phone with an awesome IMAP client. A problem I have though is the large volume of email my work address gets. I have had my laptop Mail.app rules sorting those messages to sensible folders, but that approach is flawed when my laptop isn't active. I've moved to using sieve on the IMAP server and to do that I wrote the following simple script to convert my Mail.app rules to sieve commands. I didn't even realise the standard library had a plist module until I googled it :-)

import plistlib

p = plistlib.readPlist('/Users/sekrit/Library/Mail/MessageRules.plist')

print '''
require ["fileinto", "reject"];
'''
stmt = 'if'
def handle(header, expression, mbox):
    global stmt
    if header in ('To', 'Cc', 'From'):
        print stmt, 'address :contains ["%s"]  "%s" { fileinto "INBOX.%s"; }' % (header, expression, mbox)
    elif header == 'Subject':
        print stmt, 'header :matches "Subject" ["*%s*"] { fileinto "INBOX.%s"; }' % (expression, mbox)
    else:
        raise ValueError(header)
    stmt = 'elsif'

for rule in p['rules']:
    if 'Mailbox' not in rule: continue
    mbox = rule['Mailbox'].split('/')[-1].split('.')[0]
    print '#', rule['RuleName'], '->', mbox
    for criteria in rule['Criteria']:
        if criteria['Header'] == 'AnyRecipient':
            handle('To', criteria['Expression'], mbox)
            handle('Cc', criteria['Expression'], mbox)
        else:
            handle(criteria['Header'], criteria['Expression'], mbox)

print '''
else {
     keep;
}
'''

ps. I realise my sieve-fu is probably really quite poor :-)