Mutt: Multiple Gmail IMAP Setup

NOTE: Read the August 10, 2011 update below for a more secure ~/.muttrc regarding passwords!
NOTE: Read the December 22, 2011 update below for a secure and ultra-simple way to handle passwords!
NOTE: Keep in mind that the “my_” prefix in “my_gpass1” is mandatory! (See January 3, 2012 embedded update.)

Since Alpine is no longer maintained, I decided to just move on to Mutt. Apparently Mutt has a large user base, is actively developed, and is used by friendly programmers like Greg Kroah-Hartman (Linux Kernel core dev; skip video to 25:44). Although I need to take care of two separate Gmail accounts through IMAP, I got it working in part to the nice guides here. In particular, I used this guide and this one. However, the configuration from the second link contains a couple typos that are just wrong (no equal sign after imap_user, and no semicolons between unset commands). I figured things out by trial and error and over 8 hours of googling, tweaking, and fiddling (granted, my problem was very unique). But finally, I got things working (can log in, view emails, etc.; however, I haven’t tested out everything — I suspect there needs to be some more settings to let Mutt know how to handle multiple accounts properly (setting the correct From/To areas for forwarding/replying), but let us focus on the basics for now.)

Before I get started, I’m using Mutt 1.5.21, and it was compiled with these options (all decided by Arch Linux’s Mutt maintainer; see here):

./configure --prefix=/usr --sysconfdir=/etc \
  --enable-pop --enable-imap --enable-smtp \
  --with-sasl --with-ssl=/usr --without-idn \
  --enable-hcache --enable-pgp --enable-inodesort \
   --enable-compressed --with-regex \
   --enable-gpgme --with-slang=/usr

The great thing is that Mutt comes with smtp support built-in this way (with the –enable-smtp flag), so you don’t have to rely on external mail-sending programs like msmtp.

Now, here is my ~/.muttrc:

#-----------#
# Passwords #
#-----------#
set my_tmpsecret=`gpg2 -o ~/.sec/.tmp -d ~/.sec/pass.gpg`
set my_gpass1=`awk '/Gmail1/ {print $2}' ~/.sec/.tmp`
set my_gpass2=`awk '/Gmail2/ {print $2}' ~/.sec/.tmp`
set my_del=`rm -f ~/.sec/.tmp`

#---------------#
# Account Hooks #
#---------------#
account-hook . "unset imap_user; unset imap_pass; unset tunnel" # unset first!
account-hook        "imaps://user1@imap.gmail.com/" "\
    set imap_user   = user1@gmail.com \
        imap_pass   = $my_gpass1"
account-hook        "imaps://user2@imap.gmail.com/" "\
    set imap_user   = user2@gmail.com \
        imap_pass   = $my_gpass2"

#-------------------------------------#
# Folders, mailboxes and folder hooks #
#-------------------------------------#
# Setup for user1:
set folder          = imaps://user1@imap.gmail.com/
mailboxes           = +INBOX =[Gmail]/Drafts =[Gmail]/'Sent Mail' =[Gmail]/Spam =[Gmail]/Trash
set spoolfile       = +INBOX
folder-hook         imaps://user1@imap.gmail.com/ "\
    set folder      = imaps://user1@imap.gmail.com/ \
        spoolfile   = +INBOX \
        postponed   = +[Gmail]/Drafts \
        record      = +[Gmail]/'Sent Mail' \
        from        = 'My Real Name <user1@gmail.com> ' \
        realname    = 'My Real Name' \
        smtp_url    = smtps://user1@smtp.gmail.com \
        smtp_pass   = $my_gpass1"

# Setup for user2:
set folder          = imaps://user2@imap.gmail.com/
mailboxes           = +INBOX =[Gmail]/Drafts =[Gmail]/'Sent Mail' =[Gmail]/Spam =[Gmail]/Trash
set spoolfile       = +INBOX
folder-hook         imaps://user2@imap.gmail.com/ "\
    set folder      = imaps://user2@imap.gmail.com/ \
        spoolfile   = +INBOX \
        postponed   = +[Gmail]/Drafts \
        record      = +[Gmail]/'Sent Mail' \
        from        = 'My Real Name <user2@gmail.com> ' \
        realname    = 'My Real Name' \
        smtp_url    = smtps://user2@smtp.gmail.com \
        smtp_pass   = $my_gpass2"

#--------#
# Macros #
#--------#
macro index <F1> "y12<return><return>" # jump to mailbox number 12 (user1 inbox)
macro index <F2> "y6<return><return>"  # jump to mailbox number 6 (user2 inbox)
#-----------------------#
# Gmail-specific macros #
#-----------------------#
# to delete more than 1 message, just mark them with "t" key and then do "d" on them
macro index d ";s+[Gmail]/Trash<enter><enter>" "Move to Gmail's Trash"
# delete message, but from pager (opened email)
macro pager d "s+[Gmail]/Trash<enter><enter>"  "Move to Gmail's Trash"
# undelete messages
macro index u ";s+INBOX<enter><enter>"         "Move to Gmail's INBOX"
macro pager u "s+INBOX<enter><enter>"          "Move to Gmail's INBOX"

#-------------------------#
# Misc. optional settings #
#-------------------------#
# Check for mail every minute for current IMAP mailbox every 1 min
set timeout         = 60
# Check for new mail in ALL mailboxes every 2 min
set mail_check      = 120
# keep imap connection alive by polling intermittently (time in seconds)
set imap_keepalive  = 300
# allow mutt to open new imap connection automatically
unset imap_passive
# store message headers locally to speed things up
# (the ~/.mutt folder MUST exist! Arch does not create it by default)
set header_cache    = ~/.mutt/hcache
# sort mail by threads
set sort            = threads
# and sort threads by date
set sort_aux        = last-date-received

A couple things that are not so obvious:
The passwords are stored in a file called pass.gpg like this:

Gmail1 user1-password
Gmail2 user2-password

However, it’s encrypted. So you have to decrypt it every time you want to access it. This way, you don’t store your passwords in plaintext! The only drawback is that you have to manually type in your GnuPG password every time you start Mutt, but hey, you only have to type in one password to get the passwords of all your multiple accounts! So it’s not that bad. You can see that the passwords are fed to the variables $my_gpass1 and $my_gpass2. (UPDATE January 3, 2012: Mallik has kindly pointed out in the comments that the “my_” prefix is actually a mutt-specific way of defining custom variables! You must use this syntax — unless you want to get bitten by mysterious “unknown variable” error messages!) One HUGE caveat here: if your password contains a dollar sign ($), say “$quarepant$” then you have to escape it like this:

\\\$quarepant\\\$

in the pass.gpg file. Yes, those are three backslashes for each instance of “$”. This little-known fact caused me hours of pain, and if it weren’t for my familiarity with escape sequences and shell variables and such, I would never have figured it out. Now, I don’t know what other characters have to be escaped, but here’s a way to figure it out: change your Gmail password to contain 1 weird-character like “#”, then manually type in the password like “set my_gpass1 = #blahblah” and see if using $my_gpass1 works. If not, keep adding backslashes (“\#”, then “\\#”, etc.) until it does work. Repeat this procedure for all the other punctuation characters, if necessary. Then change your Gmail password back to its original form, and use the knowledge you gained to put in the correctly-escaped password inside your pass.gpg file.

You can see that I use awk to get the correct region of text from pass.gpg. If you don’t like awk (I just copied the guide from here), you can use these equivalent lines instead (stdin redirection, grep, and cut):

...
set my_gpass1=`<~/.sec/.tmp grep Gmail1 | cut -d " " -f 2`
set my_gpass2=`<~/.sec/.tmp grep Gmail2 | cut -d " " -f 2`
...

The macros 1 and 2 are just there to let you quickly switch between the two accounts. The number 6 and 12 happen to be the two INBOX folders on my two Gmail accounts. To check which numbers are right for you, press “y” to list all folders, with their corresponding numbers on the left.

As for the other macros, these are only required because of Gmail’s funky (nonstandard) way of handling IMAP email. Without these macros, if you just press “d” to delete an email, Gmail will NOT send it to the Trash folder, but to the “All Mail” folder. This behavior is strange, because if you use the web browser interface on Gmail, clicking on the “Delete” button moves that email to the Trash folder. I guess Google wants you to use their web interface, or something. Anyway, the macros in here for delete and undelete make Gmail’s IMAP behave the same way as the Gmail’s web interface. The only drawback is that you get a harmless error message “Mailbox was externally modified. Flags may be wrong.” on the bottom every time you use them. Anyway, I’ve tested these macros, and they work for me as expected.

The optional settings at the bottom are pretty self-explanatory, and aren’t required. The most important one in there is the header_cache value. This is definitely required if you have thousands of emails in your inboxes, because otherwise Mutt will fetch all the email headers for all emails every time it starts up.

Here are some not-so-obvious shortcut keys from the “index” menu (i.e., the list of emails, not when you’re looking at a single email (that’s called “pager”; see my macros above)) to get you started:

c   (change folder inside the current active account; press ? to bring up a menu)
y   (look at all of your accounts and folders)
*   (go to the very last (newest) email)
TAB (go to the next unread email)

The other shortcut keys that are displayed on the top line will get get you going. To customize colors in Mutt, you can start with this 256-color template (just paste it into your .muttrc):

# Source: http://trovao.droplinegnome.org/stuff/dotmuttrc
# Screenshot: http://trovao.droplinegnome.org/stuff/mutt-zenburnt.png
#
# This is a zenburn-based muttrc color scheme that is not (even by far)
# complete. There's no copyright involved. Do whatever you want with it.
# Just be aware that I won't be held responsible if the current color-scheme
# explodes your mutt.

# general-doesn't-fit stuff
color normal     color188 color237
color error      color115 color236
color markers    color142 color238
color tilde      color108 color237
color status     color144 color234

# index stuff
color indicator  color108 color236
color tree       color109 color237
color index      color188 color237 ~A
color index      color188 color237 ~N
color index      color188 color237 ~O
color index      color174 color237 ~F
color index      color174 color237 ~D

# header stuff
color hdrdefault color223 color237
color header     color223 color237 "^Subject"

# gpg stuff
color body       color188 color237 "^gpg: Good signature.*"
color body       color115 color236 "^gpg: BAD signature.*"
color body       color174 color237 "^gpg: Can't check signature.*"
color body       color174 color237 "^-----BEGIN PGP SIGNED MESSAGE-----"
color body       color174 color237 "^-----BEGIN PGP SIGNATURE-----"
color body       color174 color237 "^-----END PGP SIGNED MESSAGE-----"
color body       color174 color237 "^-----END PGP SIGNATURE-----"
color body       color174 color237 "^Version: GnuPG.*"
color body       color174 color237 "^Comment: .*"

# url, email and web stuff
color body       color174 color237 "(finger|ftp|http|https|news|telnet)://[^ >]*"
color body       color174 color237 "<URL:[^ ]*>"
color body       color174 color237 "www\\.[-.a-z0-9]+\\.[a-z][a-z][a-z]?([-_./~a-z0-9]+)?"
color body       color174 color237 "mailto: *[^ ]+\(\\i?subject=[^ ]+\)?"
color body       color174 color237 "[-a-z_0-9.%$]+@[-a-z_0-9.]+\\.[-a-z][-a-z]+"

# misc body stuff
color attachment color174 color237 #Add-ons to the message
color signature  color223 color237

# quote levels
color quoted     color108 color237
color quoted1    color116 color237
color quoted2    color247 color237
color quoted3    color108 color237
color quoted4    color116 color237
color quoted5    color247 color237
color quoted6    color108 color237
color quoted7    color116 color237
color quoted8    color247 color237
color quoted9    color108 color237

Make sure your terminal emulator supports 256 colors. (On Arch, you can use the rxvt-unicode package.)

UPDATE April 22, 2011: Tiny update regarding rxvt-unicode (it has 256 colors by default since a few months ago).

UPDATE August 10, 2011: I did some password changes the other day, and through trial and error, figured out which special (i.e., punctuation) characters you need to escape with backslashes for your Gmail passwords in pass.gpg as discussed in this post. Here is a short list of them:

    ~ ` # $ \ ; ' "

I’m quite confident that this covers all the single-character cases (e.g., “foo~bar” or “a#b`c”). The overall theme here is to prevent the shell from expanding on certain special characters (e.g., the backtick ‘`’ is used to define a shell command region, which we must escape here). Some characters, like the exclamation mark ‘!’ probably need to be escaped if you have two of them in a row (i.e., \\\!! instead of !!) since ‘!!’ is usually expanded by most shells to refer to the last entered command. Again, it’s all very shell-specific, and because (1) I don’t really know which shell mutt uses internally, and (2) I don’t really know all that much about all the nooks and crannies of shell expansion in general, I am unable to create a definitive list as to which characters you must escape.

Another, nontrivial note I would like to add is that I realized that storing the just-decrypted pass.gpg file (file ~/.sec/tmp above) is still not very secure, because the file is a regular file and the contents will still be there after removing the file. That is, a clever individual could easily run some generic “undelete” program to recover the passwords after gaining control of your machine. I found a very easy, simple solution to get around this problem of deciding where to store the decrypted temporary file: use a ram disk partition! This way, when you power off the machine, any traces of your temporarily decrypted pass.gpg file will be lost.

All it takes is a one-liner /etc/fstab entry:

none    /mnt/r0    tmpfs    rw,size=4K    0    0

The size limit of 4K is the absolute minimum (I tried 1K, for 1KiB, but it set it to 4KiB regardless — probably due to a filesystem limitation). You can use the following ~/.muttrc portion to decrypt safely and securely:

#-----------#
# Passwords #
#-----------#
set my_tmpsecret=`gpg2 -o /mnt/r0/.tmp -d ~/.sec/pass.gpg`
set my_gpass1=`awk '/Gmail1/ {print $2}' /mnt/r0/.tmp`
set my_gpass2=`awk '/Gmail2/ {print $2}' /mnt/r0/.tmp`
set my_del=`shred -zun25 /mnt/r0/.tmp`

Easy, and secure. Just for fun and extra security, we use the shred utility to really make sure that the decrypted file does get erased in case (1) the machine has a long uptime (crackers, loss of control of the machine should someone forcefully prevent you from turning off the machine, etc.) or (2) the machine’s RAM somehow remembers the file contents even after a reboot. As a wise man once said, “It is better to bow too much than to bow not enough.” So it is with security measures.

UPDATE December 22, 2011: Erwin from the comments gave me a tip about a simpler way to handle passwords. It is so simple that I am utterly embarassed about my previous approach using /mnt/r0 and all that, and I’ve already changed my setup to use this method! Apparently, mutt comes with the source command, which simply reads in chunks of mutt commands through standard input (STDIN). So, the idea is instead of storing just the passwords in an encrypted file, store the entire commands into a file and encrypt it. Then, you can just source it on decryption.

~/.sec/pass.gpg

set my_gpass1="my super secret password"
set my_gpass2="my other super secret password"

Then, you can source the above upon decryption in its entirety, like this:

source "gpg --textmode -d ~/.sec/pass.gpg |"

The unusual and welcome benefit to this approach, apart from its simplicity, is that you don’t need to change how your password was quoted/escaped from the previous approach! I only found out the hard way after trying to remove the various backslashes, thinking that I needed to change the quotation level. You just use the same password strings as before, and you’re set! And also, the double quotes used for the source command is not a typo: mutt will see that the last character is a pipe ‘|’ character and interpret it as a command, not a static string.

Many thanks to Erwin for pointing this out to me.

UPDATE April 16, 2012: Typo and grammar fixes.

Advertisements