Posts Tagged ‘lua’

imapfilter and magical archives

Thursday, September 14th, 2006

After figuring out how to filter my mail I set about figuring out howto use imapfilter to archive old mail in my INBOX. This is actuallyquite simple.

oldmail = {    'old',    'not_since ' .. date_before(7)}results = match(myAccount, 'INBOX', oldmail)move(myAccount, 'INBOX', myAccount, 'INBOX-archive', results)

First off this code matches any old mail (read, seen, whatever) thatarrived more than 7 days ago. Then it moves this mail off to the sideto a folder called ‘INBOX-archive’. It’s put off to the side becauseof some deficiencies with mutt. But I don’t sync -archive foldersanyway so it’s not a big deal, it’s just there if I ever need it.Of course, this still lets the mail in my local folders for all mymailing lists (which constitute most of my mail) pile up. Thesolution? Well I’m subscribed to all the mailing list mailboxes, andit turns out that imapfilter gives me a really nice list of all mysubscribed mailboxes.

print("Archiving mail recieved before " .. date_before(7) .. ".")subscriptions = lsub(myAccount, '')for n in subscriptionsdo   print('Archiving in ' .. subscriptions[n])   mailbox = subscriptions[n]   results = match(myAccount, mailbox, oldmail)   move(myAccount, mailbox, myAccount, mailbox .. ‘-archive’, results)end

Of course this doesn’t work. Atleast not out of the box on imapfilter1.2.2. At one point the following essentially occurs

prefix = NULL;/* read a bunch of stuff off of the network */strlen(prefix);EXC_BAD_ACCESS

Apparently imapfilter doesn’t exactly know how to do deal with myimap server, and prefix stays NULL, causing a not very nice Bus Error.The below patch fixes the problem, though not in the nicest waypossible. It merely does what the original author did and sprinklternary operators liberally.

--- old-namespace.c     2006-09-13 22:41:20.000000000 -0700+++ namespace.c 2006-09-13 22:42:15.000000000 -0700@@ -60,8 +60,8 @@        buffer_reset(&nbuf);        o = 0;-       if (!strncasecmp(mbox, prefix, strlen(prefix)))-               o = strlen(prefix);+       if (!strncasecmp(mbox, prefix, strlen((prefix ? prefix : ""))))+          o = strlen((prefix ? prefix : ""));        n = snprintf(nbuf.data, nbuf.size + 1, "%s", mbox + o);        if (n > (int)nbuf.size) {

I’ll probably submit this patch to the upstream, or at the very leastnotify them of the bug. imapfilter is a pretty handy little app. Sohandy that it got me to actualy debug C code for the first time inyears.

Technorati Tags: , ,

imapfilter and mailinglists with a list-id header

Wednesday, September 13th, 2006

When I switched to my minimalist mail environment (which takes noless than 4 command lines to read my mail) I suddenly found myselfwith a filtering problem. First some background on my environmentI read my mail in mutt. It doesn’t suck, very much. It alsosatisfies my absolute biggest requirement in a mail reader which isthe exact same behavior on Linux as on OS X. Being able to read mymail over SSH isn’t a requirement it’s just a perk. However I do_not_ use mutt’s built in IMAP support, to actually download mymail I use offlineimap to sync my remote mailboxes with a localMaildir format directory. So now I have all my email in~/Mail/dreid.org/INBOX but having all my mail in one place is notreally an optimal solution, and so this brings me to my filteringproblem. I’d used procmail in the past, but frankly at this pointafter so many years of staring at nice clean python code for avariety of purposes I’d rather eat my own excrement than writeanother .procmailrc. Luckily for me there is a nice little toolcalled imapfilter. Written in C but scriptable in Lua so installedit then immediately set about writing a filter for my mailinglists.

The typical List-Id header

My Lua code handles the two most common List-Id headers that I’veseen, and works across all the mailing lists I’mcurrently subscribed to.First there is the domain only header:

    List-Id: baypiggies.python.org

Then there is the Description header:

    List-Id: Twisted Python Discussion     

In my case I wanted to use the first segment of the domain as themailbox name. I use a very simple algorithm that makes thefollowing assumptions:

  • Periods only appear in the domain name
  • Left Angle brackets only appear around the domain name
  • There are no spaces in the domain name

Given the 5 or 6 mailing lists I’m subscribed to and that they allfollow these rules, the obvious deficiencies in the code don’t bother meall that much, however your mileage may vary.

The Code

So without further ado here is the code so you too can be on yourway to mailing list filtering goodness

function parseListId(header)   baseheader = string.sub(header,                           string.find(header, ':')+1, nil)   destname = ''   for i=1,string.len(baseheader)   do      c = string.sub(baseheader, i, i)      if c == '<'      then         -- reset destname on any < brackets         destname = ''      elseif c == '.'      then         -- stop on the first .         return destname      elseif c ~= ' '      then         -- add any non-space character to the destination name         destname = destname .. c      end   endend

The rest of the config.lua for mailing lists

mailinglist = {   'header "list-id" ""'}results = match(myAccount, 'INBOX', mailinglist)listids = fetchfields(myAccount, 'INBOX', {'list-id'}, results) or {}mailboxes = {}for message, header in pairs(listids)do   mailbox = parseListId(header)   if not mailboxes[mailbox]   then      mailboxes[mailbox] = {}   end   table.insert(mailboxes[mailbox], message)endfor mailbox, messages in pairs(mailboxes)do   move(myAccount, ‘INBOX’, myAccount, mailbox, messages)end

Posting this I can see some room for improvement, I could cache andcompare the actual unparsed header (it shouldn’t vary from message tomessage) which would reduce the number of calls to parseListId (I’mnot sure it’s slow enough to worry about though.So now you have what I think is a pretty decent solution, eventuallyimapfilter will get run by a cronjob on my server (cron seems to notactually work on OS X tiger, which is another blog post all together.)Then I’ll only have to run 3 commands to check my mail. Next timeI’ll teach you how to create archives for all subscribed mailboxes.Which is a ridiculously easy bit of lua though I had to patchimapfilter to not get an EXC_BAD_ACCESS when reading folder lists frommy server.

Technorati Tags: , ,