sigabrt
mutt, procmail, maildir and indexing, oh my.

As a long-time *nix user, I’ve used mutt as my email client for more than a decade (I switched from elm, because it had a y2k issue; yes, really!), reading mail stored locally via fetchmail(1) and procmail(1). I initially set things up this way back in the days when network connectivity was not nearly as ubiquitous as it is now, and it was useful to continue to read and compose emails while offline.

For a long time, I stored my mail folders in mbox format because that was easiest. I did (and still do) some quirky monthly mailboxes for email lists (like LKML) that got too much mail, as mutt takes a long time to open an mbox with upwards of 20,000 messages (about two months worth of LKML traffic, give or take). I saw recommendations from others to switch to MailDir format, but my initial experiments showed that mutt handled large MailDir mailboxes even more slowly, so I stuck with mbox.

However, I finally got around to setting up an automated backup scheme with rsnapshot(1), essentially a wrapper around rsync(1). It sets things up so that files that don’t change between backup runs are hardlinked together, significantly saving space in the backup archive. In such a scheme, mbox files are a bad idea, because each time the mail folder changes, an additional copy of the mailbox is stored on the archive media. MailDir, with each message stored as an individual file, is much friendlier to this scheme, as most of the messages in the folder won’t change. There is a downside, I use more storage locally, but the space savings on my archive media is significant enough that I converted many of my mail folders.

As you might imagine, solving the performance issue when opening a large folder was pretty important. The issue was the mutt was opening each of the MailDir message files to get the header information for displaying the folder contents. Obviously, an index would be useful, and mutt can use one. All one needs to do is add something like

set header_cache="~/Mail/.cache/"

to your .muttrc, and like magic, opening mail folders is much quicker.

One quirk did arise when I made this conversion; when mutt displays a mail folder, it can show you the number of lines in each message, giving an indication of the size of each message. Unfortunately, when viewing my MailDir folders, all the messages were showing a size of 0. The issue is that mutt relies on the Lines: header to be set which most clients don’t set, and thus procmail couldn’t add the field to the header index. The solution, of course, is more procmail magic. The mutt MailDir wiki page helpfully has the following procmailrc snippet:

# MAILDIR LINES
# Add Lines header for mutt with Maildirs.
# Mutt shows lines based on Lines header.
# We have to do this manually for Maildir
# delivered messages.
:0 BWfh
* H ?? !^Lines:
* -1^0
*  1^1 ^.*$
| formail -A "Lines: $="

which calculates the number of lines in the message body and adds it to the message’s headers (using formail). Once that’s done, procmail adds the Lines: header to the index, and mutt stops reporting all messages as being 0 sized. Hooray!