NOTE: All referenced scripts are available at https://github.com/vdanen/mutt-scripts.

I’ve been using OS X on my desktop for a number of years now and unfortunately, the availability of email clients for OS X, while numerous, all stink. I’m coming from a background where on Linux I’ve always used the mutt email client. To be honest, I’m not a big fan of any of the GUI email clients on Linux either.

I was happy with Mail.app in Panther, but with Tiger, it quickly turned into a painful experience, especially for someone who deals with thousands of emails per month and keeps an archive of each and every one of them. Mail.app simply couldn’t handle that load to my liking, so as a result I decided to go back to mutt. Of course, mutt by itself doesn’t have nearly the same nice integration with the system that something like Mail.app does. I have a few requirements for an email client that simply must be adhered to:

  • Address Book integration
  • GnuPG support
  • good threading support

Other than this, I just like “power-user” features, same as any other good geek. Needs to be fast, stable, and reliable. Too many email clients, while nice looking, fall flat here. So it was back to good-old mutt.

NOTE: The current version as of this writing is 1.5.21 and is the recommended version of mutt to use.

Installing mutt

You can install mutt via Fink, but I prefer to build it from source. The latest version as of this writing is 1.5.21. Download the tarball, compile, and install it:

$ curl -O ftp://ftp.mutt.org/mutt/devel/mutt-1.5.21.tar.gz
$ tar xvzf mutt-1.5.21.tar.gz
$ cd mutt-1.5.21

Applying the sidebar patch

At this point, I typically patch mutt with the sidebar patch; a patch that gives mutt a listing of folders on the screen, same as any GUI client. You can easily apply it as well:

$ curl -O http://lunar-linux.org/~tchan/mutt/patch-1.5.20.sidebar.20090619.txt
$ patch -p1 <patch-1.5.20.sidebar.20090619.txt

The latest official sidebar patch is for version 1.5.20 which does not apply to version 1.5.21. I’ve pulled a sidebar patch from Debian’s mutt-patched package, and it is available here: Sidebar patch taken from Debian for 1.5.21.

Compiling mutt

Then finish by compiling and installing mutt. Note that having fink setup is a good idea; you may need to install some packages via fink to satisfy build requirements.

$ ./configure --prefix=/sw --with-curses --with-regex --enable-locales-fix \
  --enable-pop --enable-imap --enable-smtp --with-sasl=/sw --enable-hcache --with-ssl --mandir=/sw/share/man
$ make
$ sudo make install

This will compile mutt with curses and regex support, with POP3 and IMAP support (and using header caching with IMAP), with SSL support, and will install it into /sw, the default location for where fink and friends live. New in 1.5.15 is SMTP support within mutt and it is fantastic. You will need to add —enable-smtp —with-sasl=/sw for that support to be available. You’ll also want to install the cyrus-sasl2-dev package from Fink to get the SASL support (required for SMTP-AUTH).

NOTE: If you find you have problems with mutt compiling, particularly if you are using SASL support (it complains it cannot find a working SASL, but you have the cyrus-sasl2-dev package from Fink installed), you may need to tell OS X to compile for 32bit explicitly (otherwise it will try to compile for both 64bit and 32bit). This can be accomplished using the following configure line instead of that noted above, which sets the appropriate CFLAG environment variable for the compiler to use:

$ CFLAGS="-m32" ./configure --prefix=/sw --with-curses --with-regex --enable-locales-fix \
  --enable-pop --enable-imap --enable-smtp --with-sasl=/sw --enable-hcache --with-ssl --mandir=/sw/share/man

(This only seems to be a problem with OS X 10.6; earlier versions should not be affected by this).

Once this is done, congratulations… the absolute easiest part is done (and you thought compiling software was difficult).

Configuring fetchmail

Although mutt can handle POP3 and IMAP accounts, using fetchmail and procmail is much easier and more flexible. You can continue to download email in the background without even having mutt open, and by using fetchmail, you can hand the mails off to procmail to sort and so all kinds of interesting things with.

Fetchmail is quite simple to configure. Create the file ~/.fetchmailrc to configure your mail accounts. For example:

poll mail.server.com protocol pop3 user "[email protected]" password "secret" mda "/usr/bin/procmail -d %T" ssl
poll mail.somewhere.com protocol imap user "jimbob" password "secret" mda "/usr/bin/procmail -d %T" ssl

In the above example we have two mail accounts, one with a username of “[email protected]” which is a POP3 account (using SSL). The other is an IMAPS (IMAP over SSL) account with a username of “jimbob”.

Because I like getting alerts as to when I get new mail, I take advantage of Growl to alert me when fetchmail has received new mail. Instead of calling fetchmail directly via cron, I use a script (~/bin/getmail.sh) to do it, which looks like:

#!/bin/sh

if [ "$1" = "-v" ]; then
    EXTRAARG="-v"
fi

fetchmail -a -K $EXTRAARG >/dev/null 2>&1

if [ "$?" = "0" ]; then
    /usr/local/bin/growlnotify -n mutt -a Mail.app -m "New mail received" >/dev/null 2>&1
fi

This is the script that I get cron to run every 5 minutes (I use Cronnix to manage my crontab). The relevant section of the crontab looks like:

$ crontab -l
*/5     *       *       *       *       /Users/vdanen/bin/getmail.sh

As you can see, you’ll need to first install Growl and the add-on CLI program (growlnotify).

Configuring procmail

One of the most fantastic filters available is procmail. This beast will let you do pretty much anything you want to your email. Smart mailboxes and simple filters? Give me a break. This puppy puts the filtering capabilities of Mail.app to shame.

The first step is to create ~/.procmailrc; the simplest way of doing it is this:

# .procmailrc
#
DATE=`date +%Y-%m`
SHELL=/bin/sh
VERBOSE=off
PMDIR=/Users/vdanen/.procmail
LOGFILE=$PMDIR/procmail.log
# inserts a blank line between log entries
LOG=""
MAIL=/Users/vdanen/Mail

#
# delete duplicate messages first
#
:0 Wh: msgid.lock
| formail -D 8192 $PMDIR/msgid.cache

:0
$MAIL/Inbox

That essentially does no filtering, except for weeding out duplicates, and puts all of your emails into ~/Mail/Inbox. That in itself is pretty much useless… the whole point is to give us power and flexibility. There are plenty of tutorials on tweaking procmail, using Google to search will give you plenty of hits.

For myself, I prefer to break up the procmail configuration into different special files. First, I create the directory ~/.procmail. Then my default procmail configuration file is ~/.procmailrc, but for consistency I symlink ~/.procmailrc to ~/.procmail/procmailrc. Then I also have some special include configurations, namely ~/.procmail/mailinglists.rc and ~/.procmail/spam.rc. To include these files in your main procmail configuration, use the INCLUDE directive:

#
# [--SPAM--] check messages with spamassassin
#
INCLUDERC=$PMDIR/spam.rc

procmail includes those extra files directly where you include them, so be sure of your ordering. Typically I order things as follows:

  • basic procmail setup (variable definitions, etc.)
  • weed out messages I don’t want (delivery failure mails, things that my server tags as spam, etc.)
  • parse through mailinglists.rc which filters mail based on the mailing list to go to various different mailboxes
  • parse through spam.rc to get rid of spam messages locally (note, I used to run spamassassin locally with more aggressive rules than on the server, but I’m started using TMDA again, so I no longer have to run SA locally)
  • filter other messages (i.e. my *mandriva.com mails go to a Mandriva inbox, my *annvix.org mails to go to an Annvix inbox, etc.)
  • everything that hasn’t been filtered by the end goes into my general Inbox

You can look at my procmail settings here (they’re much too big to include as part of this article directly):

Setting up some nice extras

So far we have fetchmail retrieving our email, procmail filtering our email, and an unconfigured mutt to (eventually) read the email. However, there are a few niceties we can also include to make using mutt so much nicer.

MailtoMutt

The first is a little cocoa program called MailtoMutt. This little gem essentially is a “mailto:” url handler. So when you click on an email link on a web page, instead of opening Mail.app or some other GUI email client, mutt will get the focus and take that address to start a new message. Installing MailtoMutt is as simple as downloading the dmg file, mounting it, copying MailtoMutt.app to your Applications directory, and running it. To make it the default mailto: handler, you’ll need to either use RCDefaultApp or fire up Mail.app and set the default mail client to MailtoMutt.

The only downside here is that MailtoMutt launches a new Terminal window, launches mutt to send the mail. Depending on how you have Terminal configured, you may need to manually exit/close the window after it’s used. However, since I don’t usually click on too many mailto: urls, I think it’s not that big of a deal.

lbdb: The Little Brother’s Database

Another very important little application is lbdb. This program is the “glue” that will make mutt able to use the OS X Address Book. To begin, download the tarball for the latest version (0.34 as of this writing) and compile it:

$ curl -O http://www.spinnaker.de/debian/lbdb_0.34.tar.gz
$ tar xvzf lbdb_0.34.tar.gz
$ cd lbdb-0.34
$ ./configure --prefix=/sw --mandir=/sw/share/man --with-gpg --libdir=/sw/lib/lbdb
$ make
$ sudo make install

This will configure lbdb with GnuPG support and install it into /sw, the same place that mutt itself lives. You will need to create ~/.lbdbrc which is the configuration file for lbdb. It configures what lookup methods to use with lbdbrc (the mutt alias file and OS X Address Book, plus whatever else you want to be able to lookup (you could use LDAP, for instance)), and some other preferences.

To give it a quick test, just execute:

$ lbdbq

You should see everything that is currently stored in your OS X Address Book, printed out as:

[email protected]       Joe Person   (AddressBook)

This allows you to address emails to any user in your OS X Address Book using mutt’s lookup completion (query_command). More on that in the mutt configuration later.

urlview

urlview is another handy program that extracts URLs from text files and allows you to define mappings to them. urlview can be installed via Fink:

$ sudo fink install urlview

This little program takes the input of a file (such as an email message) and prints out links to everything, which you can then navigate around, highlight, and press enter to open them.

To have these links open in your default web browser, create the file ~/.urlview with the contents:

COMMAND open %s

This then uses the standard open command to open the URL, which will in turn call your default web browser to open the URL (Safari, Chrome, Firefox… whatever you have defined as your default web browser).

Mairix

Mairix is a great mail searching tool, and very easy to implement in mutt. First, install it via Fink:

$ sudo fink install mairix

Create the ~/.mairixrc to create your configuration for Mairix (sample below), and the ~/.mutt/msearch.sh script that does the actual searching (sample below). This script will delete any existing results folders, update the index, and then execute the search.

To integrate this with mutt, edit your ~/.mutt/macros file and add:

macro index "\em"    "<shell-escape>$HOME/.mutt/msearch.sh " "Run a Mairix search"
macro index "\ef"    "<change-folder-readonly>=mairix-search\n" "Switch to Mairix search results"

macro pager "\em"    "<shell-escape>$HOME/.mutt/msearch.sh " "Run a Mairix search"
macro pager "\ef"    "<change-folder-readonly>=mairix-search\n" "Switch to Mairix search results"

This will bind the search command to ESC+m (or ALT-m) and then create a change-folder command to go to the search results folder (ESC+f or ALT-f).

Finally, edit ~/.mutt/mailboxes and add:

mailboxes =mairix-search

I put that at the top of the list, so it’s always first.

Now you can hit ALT-m and provide your search criteria (like f:[email protected] to find all messages from [email protected] in all the indexed mailboxes), then hit ALT-f to go to the search results folder.

mailcap

Like urlview, using mailcap is important so we can open attachments directly from mutt and have them open using our default applications automatically. To do this, create ~/.mailcap add entries similar to:

# Images
image/jpeg; /Users/vdanen/.mutt/view_attachment.sh %s
image/pjpeg; /Users/vdanen/.mutt/view_attachment.sh %s
image/png; /Users/vdanen/.mutt/view_attachment.sh %s
image/gif; /Users/vdanen/.mutt/view_attachment.sh %s

# PDF
application/pdf; /Users/vdanen/.mutt/view_attachment.sh %s pdf

# HTML
# -- always use lynx; we prefer the commandline
text/html; elinks -force-html %s

Here’s the ~/.mailcap file that I use, and the ~/.mutt/view_attachment.sh script to actually open the files (thanks to Eric Gebhart who wrote the script and made it available)

Essentially, this calls “open” to open the file, which will use your default application (so if TextEdit is the default handler for MS Word files, if you open a Word attachment, it will open in TextEdit).

Setting up SMTP Support

New in mutt 1.5.15 is SMTP support within mutt itself. Older versions will need to use the sendmail interface, which requires a properly configured postfix or exim (or some other MTA) server. With mutt 1.5.15 and later, you no longer need to configure postfix or exim as noted below (although those sections remain because some people may prefer the flexibility that using a full-blown MTA provides).

If you compiled mutt with SMTP and SASL support, you have full support for SMTP-AUTH; if you did not compile with SASL support, you can only do straight SMTP negotiation with no authentication. If you didn’t compile with SMTP support, well, you’ll need to use the sendmail interface or recompile.

Configuring SMTP support is very simple. Edit ~/.muttrc and add:

set smtp_url="smtp://mail.myisp.com"

This will send all mail via SMTP to mail.myisp.com (presumably your ISP’s SMTP server). If you need to use SMTP authentication, this can also be easily accomplished using:

set smtp_url="smtp://[email protected]:[email protected]"
set smtp_authenticators="cram-md5"

The smtp_url command defines the url to use, and is of the form “smtp[s]://[user[:pass]@]host[:port]/”, so in the above example “[email protected]” is the username to use, “secret” is the password, and “smtp.myhost.com” is the SMTP server to connect to. Using smtps:// instead of smtp:// to use SMTP over SSL.

The smtp_authenticators command is only available if SASL support has been compiled in, and can consist of one or more of “cram-md5”, “digest-md5”, or “gssapi”. If left unconfigured, mutt will attempt to use the most secure to the least secure in order.

With these two commands enabled, you no longer need to set the “sendmail” command, nor do you need to have a locally-running SMTP server for mutt to relay mail through. However, if you prefer, you can setup postfix on OS X as a mail delivery relay in the next section.

Configure postfix

By default, OS X uses postfix for local mail delivery. You can install another MTA, such as exim, if you like, but postfix works fine. It does need a little bit of configuration because mutt will send mail via the local MTA, so it’s the MTA’s job to do the final delivery. Unless you have a static IP that’s resolvable on the internet, chances are any mails you send out will be rejected, so you need to tell postfix to forward mails on to your ISP’s mail server, for it to do the final delivery of.

The easiest way to do this is to edit /etc/postfix/main.cf. Now, how you do this will largely depend on your ISP’s SMTP server’s configuration. I relay through my own remote SMTP server, and it uses TLS and authentication. The configuration you need to take may be different.

Adding the following to /etc/postfix/main.cf did the trick here:

myhostname = odin.annvix.ca
mydomain = annvix.ca
myorigin = $mydomain
inet_interfaces = all
mynetworks = 127.0.0.0/8, 192.168.55.101

# make postfix relay all mail through our main SMTP server
smtp_use_tls = yes
smtp_tls_CAfile = /etc/postfix/cacert.pem
# cache for 1 day
##smtp_tls_session_cache_database = sdbm:/etc/postfix/smtp_scache
##smtp_tls_session_cache_timeout = 86400s
# don't use strict checking on the hostname match to CommonName in the cert
smtp_tls_enforce_peername = no
smtp_sasl_auth_enable = yes
smtp_sasl_password_maps = hash:/etc/postfix/saslpass
smtp_sasl_security_options = noplaintext, noanonymous
smtp_sasl_tls_security_options = noanonymous
relayhost = mail.myserver.com
myorigin = annvix.org

This is all added to the end of the file. The only thing here that isn’t from my real configuration file is the relayhost setting. Everything else is exactly as I have it configured.

The next step is to create the /etc/postfix/saslpass file, which would contain:

mail.myserver.com  [email protected]:secret

In the above, “mail.myserver.com” is the same entry as the relayhost setting. The “[email protected]” is the address with which to authenticate, and “secret” is, of course, the password to authenticate with. Then, convert the file into dbm format for postfix to use:

# postmap /etc/postfix/saslpass
# chmod 0600 /etc/postfix/saslpass.db
# rm /etc/postfix/saslpass

You can elect to keep the saslpass file around, if you like, just make sure to give it 0600 permissions as well so no one can snoop at it since it does store your password in cleartext.

Because, by default, postfix isn’t always running, there is no need to reload the postfix service.

To do a quick test to make sure it’s working, execute:

$ echo "this is a test"|mail -s test [email protected]
$ sudo tail /var/log/mail.log
Feb 28 16:36:05 odin postfix/master[15284]: daemon started -- version 2.1.5
Feb 28 16:36:06 odin postfix/pickup[15285]: CE0A04E29CE: uid=1001 from=<vdanen>
Feb 28 16:36:06 odin postfix/cleanup[15286]: CE0A04E29CE: message-id=<[email protected]>
Feb 28 16:36:06 odin postfix/qmgr[15288]: CE0A04E29CE: from=<[email protected]>, size=311, nrcpt=1 (queue active)
Feb 28 16:36:15 odin postfix/smtp[15289]: CE0A04E29CE: to=<[email protected]>, relay=mail.myserver.com[1.2.3.4], delay=10, status=sent (250 OK id=1HMYM7-0005cY-Eb)
Feb 28 16:36:15 odin postfix/qmgr[15288]: CE0A04E29CE: removed
Feb 28 16:37:05 odin postfix/master[15284]: master exit time has arrived

If you see something similar to the above, you’ll know it worked (yes, the above was obfuscated a bit).

On a side note, if you don’t mind parting with a few dollars, you can also use PostfixEnabler on OS X to do this for you. I haven’t registered this but played with it on a friend’s system who has and it seems fairly nice, although you will want to chmod a few files in /etc/postfix, in particualr the sasl_passwd file is mode 0644 and it’s the file that contains the cleartext password. Other than that it seems like a pretty straightforward program.

Using exim instead of postfix

While postfix might do the job ok, I prefer something like Exim which is, in my opinion, a much better MTA. So this, entirely optional, step is to use exim instead of postfix.

Create the exim system account

To begin, you need to create some system accounts for exim to use:

$ sudo niutil -create . /users/exim
$ sudo niutil -createprop . /users/exim uid 90
$ sudo niutil -createprop . /users/exim gid 20
$ sudo niutil -createprop . /users/exim passwd '*'
$ sudo niutil -createprop . /users/exim realname 'Exim'
$ id exim
uid=90(exim) gid=20(staff) groups=20(staff)

The above creates a dedicated system user “exim” to use.

Compile and install exim

Next, download exim and prepare to compile it:

$ curl -O ftp://ftp.csx.cam.ac.uk/pub/software/email/exim/exim4/exim-4.66.tar.gz
$ tar xvzf exim-4.66.tar.gz
$ cd exim-4.66
$ cp src/EDITME Local/Makefile
$ mate Local/Makefile

The last command opens the Makefile in TextMate (replace “mate” with “vim” or “bbedit” or whatever you prefer to edit text files with). The following lines are important to change to the settings noted:

BIN_DIRECTORY=/sw/bin
CONFIGURE_FILE=/sw/etc/exim.conf
EXIM_USER=exim
SPOOL_DIRECTORY=/sw/var/spool/exim

#EXIM_MONITOR=eximon.bin

AUTH_CRAM_MD5=yes
AUTH_PLAINTEXT=yes

SUPPORT_TLS=yes
TLS_LIBS=-L/usr/include/openssl -lssl -lcrypto
TLS_INCLUDE=-I/usr/include/openssl

LOG_FILE_PATH=syslog

CHOWN_COMMAND=/usr/sbin/chown

Once this is done (and note the above commenting out the EXIM_MONITOR) entry), you can compile exim using:

$ make
$ sudo make install

NOTE: There is a problem with setting exim as the default MTA for the entire system, which I didn’t realize immediately. If you set /usr/sbin/sendmail as a symlink to /sw/bin/exim, when you do a Repair Disk Permissions via Disk Utility, the suid bit on exim-4.66-1 gets removed. Disk Utility stupidly follows symlinks and since /usr/sbin/sendmail isn’t suid, it follows the symlink to the resulting binary (in our case, /sw/bin/exim-4.66-1 and removes the suid bit. This will break exim!

The solution is to make /sw/bin/sendmail a symlink to /sw/bin/exim which, in turn, is a symlink to the real exim binary. Leave /usr/sbin/sendmail as-is. Realistically, the only thing that should require sendmail is mutt itself, so you do need to make mutt aware of the new location. This can be done by setting the sendmail keyword:

set sendmail="/sw/bin/sendmail"

This way, mutt will use the symlink we created, and Disk Utility will never remove the suid bit from the real exim binary because it won’t know about it.

Note that /sw/bin/exim is actually a symlink to /sw/bin/exim-4.66-1; however the advantage of symlinking to the non-versioned symlink is that you can upgrade exim without ever changing the sendmail symlinks as the “make install” for exim will update /sw/bin/exim to point to /sw/bin/exim-[version]-[build].

Telling OS X to start exim at boot

You can have exim start at boot, or you can simply use it to push mails as it receives them. The better idea is to have exim up and running so that it’s queue-runner can work automatically. If you don’t want other systems to be able to use the SMTP server on your laptop/workstation, either firewall off port 25 or tell exim to listen to the localhost only (by setting local_interfaces in the configuration file).

Create the file /Library/LaunchDaemons/exim.plist with the following contents:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
   <key>Label</key>
   <string>com.exim.exim</string>
   <key>ProgramArguments</key>
   <array>
       <string>/sw/bin/exim</string>
       <string>-bd</string>
       <string>-q30m</string>
   </array>
   <key>RunAtLoad</key>
   <true/>
   <key>ServiceDescription</key>
   <string>This plist launches the exim server at startup.</string>
</dict>
</plist>

You need to be root to create and save that file. Once this is done, exim will start at boot. You will also need to disable postfix at boot by editing /System/Library/LaunchDaemons/org.postfix.master.plist and adding:

    <key>Disabled</key>
    <true/>

to the plist, immediately after “<dict>” and before “<key>Label</key>”. Once these changes have been made, you will need to reboot for them to take effect.

Configure exim

The exim configuration file is /sw/etc/exim.conf unless you used different paths than those above. This file is extremely well-commented which makes it exceptionally huge. You can begin by using the following configuration file instead and build from there:

# exim configuration for OS X as a smtp-auth client
#
# to test the configuration before sending a HUP to exim use:
#   /sw/bin/exim -C /sw/etc/exim.conf -bV

# MAIN CONFIGURATION

domainlist local_domains        = @
domainlist relay_to_domains     =
hostlist   relay_from_hosts = 127.0.0.1

local_interfaces = 127.0.0.1

acl_smtp_rcpt = acl_check_rcpt
acl_smtp_data = acl_check_data

never_users = root
# combine this with mutt's "set envelope_from" and your Return-Path will get set
# to whatever mutt sets it to
untrusted_set_sender = *

# ACL CONFIGURATION
begin acl

acl_check_rcpt:
  accept  hosts = :

  accept

acl_check_data:
  accept

# ROUTERS CONFIGURATION
begin routers

send_upstream:
  driver = manualroute
  transport = remote_smtp
  route_list = !+local_domains mail.domain.com

# TRANSPORTS CONFIGURATION
begin transports

remote_smtp:
  driver = smtp
  # comment hosts_require_auth and hosts_try_auth if you don't use auth
  hosts_require_auth = *
  hosts_try_auth = *

# RETRY CONFIGURATION
begin retry

# Address or Domain    Error       Retries
# -----------------    -----       -------

*                      *           F,2h,15m; G,16h,1h,1.5; F,4d,6h

# REWRITE CONFIGURATION
begin rewrite

# AUTHENTICATION CONFIGURATION
begin authenticators

# comment all of this if you don't need to use auth
fixed_cram:
  driver = cram_md5
  public_name = CRAM-MD5
  client_name = user
  client_secret = secret

This is an extremely basic exim.conf file, but it suits the purpose. It isn’t meant to be used for anything other than relaying mail from this host to the smarthost (mail.domain.com as seen in the send_upstream router above). About the only things you need to configure here are the username and password in the fixed_cram authenticator and the smarthost domain name (or IP address) in the send_upstream router.

The above will also only accept mail from the local machine; it binds to port 25 on 127.0.0.1 and will accept mail from that, and also locally via /usr/sbin/sendmail; otherwise it does not accept mail from any other system (which is important considering how loose the security on it is considering it will take any mail, unauthenticated, and send it on, authenticated, to the smarthost).

If your upstream SMTP server does not require authentication to relay mail, be sure to comment out all of the auth-related options (as noted in the config above).

Once you’ve made those changes and rebooted, exim should be running and working nicely. It logs via syslog, so you can see what it’s doing by looking at /var/log/mail.log.

Configuring Terminal’s Meta Key Support

Because mutt runs in the terminal, there are a few little things that should be done to make it work a little nicer. For instance, the sidebar patch, by default, suggests using CTRL-O, CTRL-P, and CTRL-N to navigate the sidebar but I found that CTRL-O doesn’t work at all. The solution was to use meta keys to navigate, but by default this won’t work.

To bind escape keys to the meta key, you need to configure mutt to allow it first:

set meta_key=yes

This will make escape commands accessible via the meta key as well (typically the ALT key). So, for instance, “\ep” will work with ESC+P and ALT-P. But Terminal doesn’t support/use the meta (ALT) key by default.

To do this, you need to enter Terminal’s Window Settings, then select Keyboard. Make sure the “Use option key as meta key” checkbox is enabled. On a Mac keyboard, the ALT key is the OPTION key, so this will properly map the meta key strokes, allowing us to define “\ep” as ALT-P (aka OPTION-P or META-P).

Now you can configure the sidebar options like this:

bind index \ep  sidebar-prev
bind index \en  sidebar-next
bind index \eo  sidebar-open

At this point, you can use ALT-P to move up the sidebar, ALT-N to move down it, and ALT-O to open the highlighted mailbox. This also allows you to create a whole host of keystrokes for other commands that you would otherwise have to use the ESC key to access.

Configuring mutt

Now comes the fun part… configuring mutt itself. Mutt itself can be a bit of a beast to setup the first time around. The first thing to do is to create the configuration directory where all of the mutt files will live: ~/.mutt. The main configuration file is ~/.mutt/muttrc.

I prefer a modular approach to one huge monolithic configuration file and mutt lets us do that with various source options in the configuration file which you’ll see. Most of this is largely personal preference and the configuration files are included to be perused. There are a few important things that need to be set to make all of this work well.

The following are my mutt configuration files which were largely inspired from using mutt on Linux for years, and other tips I’ve picked up along the way. They’re not overly pretty, but it’s what I use and it’s functional:

The only files omitted from the above list are the ~/.mutt/signature* and ~/.mutt/aliases files (they should be pretty self-explanatory).

Sourcing external configuration files

To source an external configuration file, use:

source ~/.mutt/gpg.rc                   # Use GPG
source ~/.mutt/auto_views               # Define auto_views.
source ~/.mutt/aliases                  # Load in my aliases.
source ~/.mutt/subscriptions            # Define the list of subscribed mailing lists.
source ~/.mutt/mailboxes                # Define the list of folders that receive mail.
source ~/.mutt/headers                  # Configure header display.
source ~/.mutt/folder-hooks             # Define folder-hooks.
source ~/.mutt/save-hooks               # Define save-hooks.
source ~/.mutt/fcc-hooks                # Define fcc-hooks.
source ~/.mutt/message-hooks            # Define message hooks.
source ~/.mutt/bindings                 # Define key bindings.
source ~/.mutt/macros                   # Define macros.
source ~/.mutt/colours                  # Define colours.
source ~/.mutt/sidebar                  # Define sidebar support (requires sidebar patch)

Each of these are separate configuration files that get included by mutt when it starts up. This makes configuration much easier by keeping things separated.

muttrc options

The ~/.mutt/muttrc configuration file is the main one. There are a few things that should be set:

set envelope_from

This is really important if you’re using exim, as it will set the Return-Path properly and other envelope information. If you are using exim, be sure to keep untrusted_set_sender = * set so that exim doesn’t rewrite the Return-Path.

set query_command="/sw/bin/lbdbq '%s'"

This will use lbdbq (part of lbdb which we configured already) to do queries for looking up email addresses.

set mailcap_path="~/.mailcap"

Where the mailcap definition file is, which we’ve already configured.

Special Keybindings

If you want to use special function keys in mutt, there are a few steps you must first take. These involve disassociating the function keys with the rest of the OS X system and Expose.

By default, F9 through F11 are reserved for Expose, while F12 brings up Dashboard. These can be changed by going into System Preferences and the Expose & Spaces preference pane. Here you can either re-map the functions you want to a different key, or set them to “-” to disable them. Typically I active Expose via extra mouse buttons, so disabling them entirely here is fine (also note the new Expose features in OS X 10.6 make it less necessary to have all these mapped unless you absolutely want to activate via the keyboard).

For the F1 through F8 function keys, go to the Keyboard preference pane and select the “Use all F1, F2, etc. keys as standard function keys”. This will prevent the system from using F1 to dim the screen (on an Apple keyboard); instead you must use Fn-F1 to do it. This leaves the F1 key free to use.

To use the function keys in mutt, edit the file that contains your key bindings and use something similar to:

bind index <f10>        sidebar-prev

This will bind the F10 key to the “sidebar-prev” command in the index.

Editor settings

set editor="vim +12"

This tells mutt to use vim to edit emails. This works fine for me, but some people like GUI editors. If you want that, you could do something like:

set editor="/Users/vdanen/bin/mate_mutt"

with the contents of ~/bin/mate_mutt being:

#!/bin/sh
#
# wrapper script to edit emails with textmate

mate="/usr/local/bin/mate"
args="-w -l 12"

mailfile="${1}"

pushd ~/tmp >/dev/null 2>&1
    mv -f ${mailfile} ${mailfile}.mail
    ${mate} ${args} ${mailfile}.mail
    mv -f ${mailfile}.mail ${mailfile}
popd >/dev/null 2>&1

This wrapper will open TextMate to edit emails, tell the calling app to wait for TextMate to close the file, and position it at line 12. You need to do this so you can use the Mail bundle (essentially we just move the file to one named *.mail so TextMate recognizes it as a “mail” file, then move it back when we’re done).

The only problem I have with this is that TextMate doesn’t do hard-wrapping, so you can’t tell it to use column 72 or something as a hard-wrap; you’d have to manually reformat your mails before sending them or write a snippet to do that (I haven’t gotten far enough in TextMate to know how to do that yet).

Alternatively, TextMate’s mail bundle will identify a text file as being of the mail type (with the Mail bundle installed) if it starts with the “From:” line. With a little bit of configuration changes, this is easy.

The appropriate settings of your .muttrc would be:

set editor            ="mate -w -l 8"
set edit_headers      = yes
# Don't display certain headers
ignore *
unignore From To Cc Subject Date Reply-To
# order headers
unhdr_order *
hdr_order From: To: Cc: Date: Subject:

This directly sets the editor the the mate command and re-orders the headers to ensure the “From:” header is first. With this method, there is no need for an external file to call the editor.

Of course, with a little creativity, you could use the mate_mutt to differentiate between a local/GUI connection and over ssh. In other words, depending on the environment, it would use vim (if remote) and TextMate (if local).

Print settings

set print_command="/Users/vdanen/bin/mutt_print"          # use a wrapper script for printing (opens in Preview to print)

This sets the print_command option for printing messages. The contents of ~/bin/mutt_print would be:

 #!/bin/sh
pfile="${1}"
pdir="${HOME}/tmp/mutt_print"

if [ ! -d ${pdir} ]; then
    mkdir -p ${pdir}
fi

# delete any old temporary files first
rm -f ${pdir}/*_mutt.pdf

tmpfile="`mktemp ${pdir}/mutt_XXXXXXXX`"
mv -f ${tmpfile} ${tmpfile}.pdf
enscript --font=Times-Roman10 --pretty-print ${1} -o - 2>/dev/null | pstopdf -i -o ${tmpfile}.pdf
open ${tmpfile}.pdf

This script uses enscript to make a pretty output to a postscript file, which is then converted to a PDF via pstopdf, which is finally opened in Preview so you can then print or save the PDF. It will also remove any existing PDF’s in the temporary directory prior to creating the new one, so you don’t ever really have to worry about cleanup.

Final Notes and Resources

A lot of this can be used to configure mutt on Linux or any other operating system as well. lbdb works great and can use LDAP, the Evolution address book, and so on. Everything with the exception of the urlview configuration and MailtoMutt, and maybe a few other bits, are fairly generic.

The following resources may be use/interest as well:

Finally, I’m always interested in tweaking mutt more, so this article will likely be updated on a semi-regular basis. If those of you reading this have better ways of doing things (or more unique ways of doing things), I would love to hear about it. In particular, I haven’t yet gotten the function keys, (F1, etc.) to work in mutt, and would love to be able to do that (well, they probably work fine in mutt, it’s Terminal that would be the problem). Finally, anyone getting TextMate to work in a sane manner with mutt (so as to not have to manually wrap lines), please let me know how you did it!

You can reach me via email.

Muttscreen

Revision History

  • 02/22/2011 - note that urlview and mairix are available via Fink now, use mutt 1.5.21 as a base, note problems compiling on 10.6, and link to recent sidebar patch
  • 08/30/2009 - added special function key bindings section courtesy of Tommaso Leonardi and alternative TextMate invocation courtesy of Peter D. Cowan
  • 08/05/2009 - added info on printing emails to PDF for printing
  • 01/19/2009 - link to Tommaso’s blog post about using mutt with multiple SMTP servers, and an auto-detection script (i.e. useful for VPN usage, etc.)
  • 09/06/2007 - links to GTD with mutt info and an updated X-Label edit patch for mutt 1.5.16
  • 04/30/2007 - changes due to mutt 1.5.15 (smtp support!!)
  • 04/08/2007 - add setup of Mairix
  • 03/21/2007 - note the use of —enable-hcache for imap header caching and link to the Woodnotes guide
  • 03/12/2007 - added section on enabling meta keys in Terminal
  • 03/10/2007 - updated mutt configs and added notes on exim’s suid bit getting stripped by Disk Utility if symlinked via /usr/sbin/sendmail
  • 03/04/2007 - finished initial article