Sorting IMAP Mail with Imapfilter

The situation is as follows: I have multiple IMAP mail accounts on multiple servers and access these mailboxes from multiple computers using different e-mail clients. Per mailbox, I have multiple folders that contain messages regarding a certain subject. To be sure that new mail appears in the folder it belongs to, I have set up message filters on every e-mail client. The trouble is, for every new folder I had to alter the filters on every client.

As a solution, I left one computer always running with Thunderbird active, but since I already have a linux server running (terminal access only), I was thinking whether there was something available that was able to sort my messages, which I can alter from the command line and doesn’t involve a graphical frontend.

Imapfilter did the job for me. Although I don’t have any experience in Lua, in which it is written, I managed to successfully sort my mail. You can install it from source, but there is also a Debian package available from the apt (at least, from repository unstable). Below I’ll describe how I configured imapfilter, since there is not much information around on this (there are however some small examples described in a sample config file).

After install, first run imapfilter as a normal user. This will create the directory ~/.imapfilter with appropriate permissions. Enter that directory and create a new file named config.lua. In this file, you should first define some global options, then your IMAP accounts, followed by the filters (rules) that should be applied to the messages in your mailboxes.

The global options are defined in a lua table variable named options. A table in lua is much like a hash in Ruby. I have defined the following options:

  options.timeout = 120
  options.subscribe = true

The meaning of these options is described on the imapfilter homepage.
Definitions of IMAP accounts look like this:

account1 = IMAP {
	   	server = 'imap.example.com',
		username = 'user',
		password = 'pass',
		ssl = 'ssl3'
	}

account2 = IMAP {
	   	server = 'imap2.example.com',
		username = 'user',
		password = 'pass'
	}

As you can see, imapfilter also supports SSL (you need to have OpenSSL installed though). You can define all your IMAP accounts like this.

Next, I define the sorting rules. Note that I mostly move messages from the inbox of an account to a folder of the same account. This is however not necessary, it is also possible to transfer messages from one account to the other. Example 1:

   msgs = account1.INBOX:contain_from('MediaPortal')
   account1.INBOX:move_messages(account1.MediaPortal, msgs)

The first example is simple: find all messages in the INBOX in which the sender contains the name MediaPortal and move those messages to the MediaPortal folder.
Now, a more complex example:

 msgs = 	account2.INBOX:contain_from('Cron') +
		account2.INBOX:contain_from('Log') +
		account2.INBOX:contain_subject('yum')
 account2.INBOX:mark_seen(msgs)
 account2.INBOX:move_messages(account2['Maildir/Admin Notifications'], msgs)

What you need to know here is that the ‘+’ operator means ‘or’. So, I put all messages that are from ‘Cron’, ‘Log’ or have the subject ‘yum’ in the variable msgs, I mark all those messages as read and finally move them to the ‘Admin Notifications folder’. As you can see, this account uses the Maildir directory.

You can debug your configuration by running imapfilter -d or imapfilter -v. To run these rules frequently, I finally had to run this code in daemon mode. To achieve this, I had to enclose all the above code in a function and pass that function in the become_daemon() function:

-- global options

function sortMail()
   -- IMAP account definitions
   -- rules
end

become_daemon(300, sortMail)

Note that I had to include the account definitions in the function; it seems the accounts are also initialized when defined and it wouldn’t work anymore when these definitions were outside the function. The above example will run imapfilter as a daemon and execute the sortMail function every 300 seconds.

UPDATE 03-02-2010: Sometimes, an error may occur while processing the messages, causing imapfilter to terminate. To solve this, I created a function that wraps the sortMail function and ensures errors are ignored:

function sort_mail_and_ignore_errors()
        pcall(sort_mail())
end

daemon_mode(300, sort_mail_and_ignore_errors)

Some final remarks that may save you some time:

  • There are problems selecting/searching messages when you’re running an Exchange server. I tried several things to get it working properly, but nothing worked. This could be dependent on the configuration or version of the Exchange server however.
  • Be sure you add the IMAP definitions to the function called in daemon mode
  • if you don’t know exactly which folders you have or should define, you can add some debugging code to find out:
-- Inbox information
account1.INBOX:check_status()

-- Get and print available mailboxes and folders
mailboxes, folders = account1:list_all()
table.foreach(mailboxes,print)
table.foreach(folders,print)
  • Want to filter out spam? This can be done like this:
-- Select all messages marked as spam       
msgs = account1.INBOX:match_header('^.+MailScanner.*Check: [Ss]pam.*$')
-- Throw them away 
account1.INBOX:delete_messages(msgs)

Fore more information and examples, see also the imapfilter homepage: http://imapfilter.hellug.gr

~ by moiristo on November 18, 2008.

11 Responses to “Sorting IMAP Mail with Imapfilter”

  1. Fantastic. Been trying to find a solution to the multiple clients, multiple filters problem for a while and this is the only decent tutorial on imapfilter that I could find.

    Thanks!

  2. Excellent. I have had the exact same issue, and had been using Thunderbird to sort messages. Unfortunately, it had been crashing a lot.

    I had one problem. For some reason “contain_from” would just refuse to work for some reason. (Our main IT group runs an exchange server. :-P) I ended up just using contain_field(‘From’,”MyString”) to get it to work.

  3. Great post, thanks a lot!

  4. Thanks! With the information gleaned from your post, I wrote an imapfilter recipe to migrate all mailboxes from one imap account to another:

    http://www.ynform.org/w/Pub/ImapfilterMigrate

  5. Cool! Do you mind if I add it to the post?

  6. Pretty helpful. I was finding it difficult to delete the emails on the server. this post helped.

  7. Thank you for this tutorial, it has made my day! I’ve been looking for an easy server-side way to filter my mail for years.

  8. Nice article.
    for those that only checking one folder enter_idle() seems to be better than calling sort_mail() on a regular timebase. For multiple accounts and/or multiple folders to check you could use separate configfiles (imapfilter -c ) or you have to fork inside the config file (parallel processing)

  9. Thank you for the post. This is very helpful information.

  10. One thing I couldn’t seem to find on any blogs on this was a decent systemctl start/stop script for this so I put one together. It’s really for one-person linux machine where you set the USER and CONFIG_FILE in /etc/sysconfig/imapfilter and it handles the start and stop of the daemon. It assumes the config file is using become_daemon() so that it’s running as a daemon.

    Feel free to add to your post:

    #!/bin/sh
    #
    # /etc/init.d/imapfilter
    # Subsystem file for imapfilter… mail filterer
    #
    # NOTE: This script depends on your config running as daemon using:
    # become_daemon()
    #

    #
    # chkconfig: 2345 98 02
    # description: Filter email using imapfilter and lua config file
    # processname: imapfilter
    # config: /etc/sysconfig/imapfilter
    # pidfile: /var/run/imapfilter.pid

    # source function library
    . /etc/rc.d/init.d/functions

    # pull in sysconfig settings
    # in sysconfig, set USER and CONFIG_FILE
    [ -f /etc/sysconfig/imapfilter ] && . /etc/sysconfig/imapfilter

    RETVAL=0
    prog=”imapfilter”
    exec=/usr/local/bin/imapfilter
    lockfile=/var/lock/subsys/$prog
    pidfile=/var/run/imapfilter.pid

    start() {
    echo -n $”Starting $prog:”
    daemon –user ${USER} $exec -c ${CONFIG_FILE}
    RETVAL=$?
    echo
    [ “$RETVAL” = 0 ] && touch $lockfile && /sbin/pidof $exec > $pidfile
    echo
    }

    stop() {
    echo -n $”Stopping $prog:”
    killproc $prog -TERM
    RETVAL=$?
    echo
    [ “$RETVAL” = 0 ] && rm -f $lockfile $pidfile
    echo
    }

    case “$1″ in
    start)
    start
    ;;
    stop)
    stop
    ;;
    restart)
    stop
    start
    ;;
    reload)
    stop
    start
    ;;
    condrestart)
    if [ -f /var/lock/subsys/$prog ] ; then
    stop
    # avoid race
    sleep 3
    start
    fi
    ;;
    status)
    status $prog
    RETVAL=$?
    ;;
    *)
    echo $”Usage: $0 {start|stop|restart|reload|condrestart|status}”
    RETVAL=1
    esac
    exit $RETVAL

  11. A quick note for debugging issues with become_daemon: it takes two optional arguments which disable its chdir and turn off its detachment of stdin/stdout. “become_daemon (, , true, true)” will disable both of those things.

Leave a comment