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
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!
Andrew McCombe said this on January 23, 2009 at 12:03 pm
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.
David said this on May 12, 2009 at 9:38 pm
Great post, thanks a lot!
Aurélien Gâteau said this on September 1, 2009 at 1:20 pm
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
Poor Yoirck said this on February 16, 2010 at 8:54 pm
Cool! Do you mind if I add it to the post?
moiristo said this on February 20, 2010 at 1:18 pm
Pretty helpful. I was finding it difficult to delete the emails on the server. this post helped.
Senthil Kumaran said this on September 21, 2010 at 7:59 am
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.
Oyster said this on July 25, 2012 at 3:39 am
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)
denk_mal said this on July 3, 2013 at 11:44 am
Thank you for the post. This is very helpful information.
Joe Chin said this on July 18, 2014 at 2:42 pm
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
Bernie said this on December 21, 2014 at 7:12 pm
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.
Christopher Bowns said this on February 6, 2015 at 8:38 pm