/ root / pages / mutt2.html

You're using an old link! - Thankfully, you no longer need to specify a nonstandard port (8080) to access my site. You could've used the more standard: http://pbrisbin.com/pages/mutt2.html.

Using 2 IMAP accounts in Mutt through Offlineimap


Mutt can be really great with multiple accounts, but it's not exactly intuitive to setup. Here I'll document how I access my GMail and GMX email accounts together in one mutt instance.

Theory

Three tools are in use here:

  1. One to sync your IMAP accounts with local Maildir folders
  2. One to send mail via the appropriate smtp server
  3. Finally, one to interact with the synced folders, compose messages, and call the send application as needed

All of my live config files can always be found with my dotfiles; there are many changes, additions, and better ways of doing things compared to what's printed below.

Offlineimap

Offlineimap handles multiple accounts very well. Here's my ~/.offlineimaprc set up to sync my GMail and GMX accounts into two local Maildir folders:

  [general]
  # NOTE: cronjob calls the quiet UI with -u
  ui = TTY.TTYUI
  accounts = GMail,GMX

  [Account GMail]
  localrepository = Gmail-Local
  remoterepository = Gmail-Remote

  [Account GMX]
  localrepository = GMX-Local
  remoterepository = GMX-Remote

  [Repository Gmail-Local]
  type = Maildir
  localfolders = ~/Mail/GMail

  [Repository GMX-Local]
  type = Maildir
  localfolders = ~/Mail/GMX

  [Repository Gmail-Remote]
  type = Gmail 
  remoteuser = username@gmail.com
  remotepass = gmailpassword
  realdelete = no
  nametrans = lambda folder: re.sub('.*Spam$', 'spam', re.sub('.*Drafts$', 'drafts', re.sub('.*Sent Mail$', 'sent', re.sub('.*Starred$', 'flagged', re.sub('.*Trash$', 'trash', re.sub('.*All Mail$', 'archive', folder))))))

  [Repository GMX-Remote]
  type = IMAP
  remotehost = imap.gmx.com
  remoteport = 143
  remoteuser = username@gmx.com
  remotepass = gmxpassword

  # vim: ft=cfg tw=0

Take note of the nametrans for gmail. My line there will change folder names during the sync so IMAP://[Gmail].Archive becomes ~/Mail/GMail/archive and similar for all other folders. You can choose not to use this line if you don't mind the default folder names, but you'll have to be careful to use the right folder names in your ~/.mutt/muttrc.

Offlineimap is written in python, and any valid python can actually be used in that nametrans. In fact, my live offlineimaprc has a more compact version that uses a couple of other function calls to accomplish essentially the same translation.

The ui setting is also useful in that when you test or sync with offlineimap from a command prompt, you can view the output, but in our cronjob we can call -u Noninteractive.Quiet explicitly to suppress it; best of both worlds.

You can test your setup by running offlineimap -o to sync things once. It could take a while, but once done, you should have a nice folder structure like this:

  Mail/
  |-- GMX
  |   |-- Archive
  |   |-- Drafts
  |   |-- INBOX
  |   |-- Sent
  |   |-- Spam
  |   `-- Trash
  `-- GMail
      |-- INBOX
      |-- archive
      |-- drafts
      |-- flagged
      |-- sent
      |-- spam
      `-- trash

Offlineimap is kind of buggy for me, if I use its built-in refresh mechanism I find it'll often hang or quit and I'll be left with an unsynced mailbox. Therefore, I cronned a [re]start script to take care of it.

Once you're sure things are syncing fine, create a script with these contents:

#!/bin/sh

PID=`pgrep offlineimap`

[[ -n "$PID" ]] && exit 1

offlineimap -o -u Noninteractive.Quiet &>/dev/null &

exit 0

Make it executable via chmod +x /path/to/mailrun.sh and set up a cron job to run it every 3 minutes:

  crontab -e

  # add this:
  */3 * * * * /path/to/mailrun.sh

You'll likely have to restart cron for the changes to take effect.

This script checks if offlineimap is already running; there's two ways one could handle it if a PID is found: one, offlineimap is still syncing since 3 minutes and we should just exit and let it finish (this is the method I've landed on). Or two, offlineimap hasn't completed its sync in 3 minutes so it must be frozen, we should kill it and resync. The choice is yours, just change && exit 1 to && kill $PID.

Msmtp

Msmtp also handles multiple accounts very elegantly. Here's my ~/.msmtprc set up to handle my gmail and gmx accounts:

  # msmtp config file

  account gmail 
  host smtp.gmail.com
  port 587
  protocol smtp
  auth on
  from username@gmail.com
  user username@gmail.com
  password gmailpassword
  tls on
  tls_nocertcheck

  account gmx
  host mail.gmx.com
  port 587
  protocol smtp
  auth on
  from username@gmx.com
  user username@gmx.com
  password gmxpassword
  tls on
  tls_nocertcheck

  account default : gmail

Now we can simply call msmtp -a gmail or msmtp -a gmx to use whichever account we want (default is gmail for me).

You should do a quick sanity check here that you can send some mail:

  echo "Your howto is awesome, thanks" | msmtp -a gmail praise@pbrisbin.com
  echo "Your howto sucks balls, doucher..." | msmtp -a gmx spam@pbrisbin.com

Just make sure you get no errors with either account.

Mutt

Now we bring it all together in mutt. I'm gonna show you pretty much my full muttrc, not all of it is needed (but I like it) so feel free to experiment on your own version.

I prefer to separate my config out into some logical pieces. So we need to setup some support files, a few of which I'm not going to go into because they're totally dependant on you, just ensure that these exist if you plan on blindly copy/pasting large portions of my config:

  # files
  ~/.mutt/certificates    # touch an empty file
  ~/.mutt/colors.muttrc   # define colors in muttrc syntax
  ~/.mutt/mailcap         # touch an empty file (go google if you want to utilize this)
  ~/.mutt/alias           # touch an empty file
  ~/.mutt/sig             # type a signature that'll be added to emails

  # mkdir all these
  ~/.mutt/temp
  ~/.mutt/cache/bodies
  ~/.mutt/cache/headers

There is also a file I keep at ~/.mutt/sidebar.muttrc, with all of the mutt-ng specific options in it, I do this so I can quickly comment the source line in ~/.mutt/muttrc if I take that config to a machine running vanilla mutt.

My live config loads this file only if mutt -h signifies that the sidebar patch is applied.

Now, the two supporting config files we need to pay attention to: These will be setup per account, with overriding settings. That way, we can define a folder-hook so that when we're viewing mail in ~/Mail/GMail/* we have all of our gmail specific account settings loaded, and if we're in ~/Mail/GMX/* we have all of our gmx settings loaded. These are things like our from address, where to store sent messages, where to put deleted messages, etc. Pretty cool, huh? I also put a quick color setting in each of these files so when we're in gmail the statusbar is green, but when we're in gmx, it's cyan. I find this visual cue really useful.

So, setup two files, one at ~/.mutt/gmail.muttrc:

  ###
  # gmail specific options
  ###

  color status green default

  set from      = "username@gmail.com"
  set sendmail  = "/usr/bin/msmtp -a gmail"
  set mbox      = "+GMail/archive"
  unset record
  set postponed = "+GMail/drafts"

  macro index E "<change-folder>+GMail/archive<enter><limit>~B " "search everything"
  macro index D "<save-message>+GMail/trash<enter>"              "move message to the trash"
  macro index S "<save-message>+GMail/spam<enter>"               "mark message as spam"

And one at ~/.mutt/gmx.muttrc:

  ###
  # gmx specific options
  ###

  color status cyan default

  set from      = "username@gmx.com"
  set sendmail  = "/usr/bin/msmtp -a gmx"
  set mbox      = "+GMX/Archive"
  set record    = "+GMX/Sent"
  set postponed = "+GMX/Drafts"

  macro index E "<change-folder>+GMX/Archive<enter><limit>~B " "search everything"
  macro index D "<save-message>+GMX/Trash<enter>"              "move message to the trash"
  macro index S "<save-message>+GMX/Spam<enter>"               "mark message as spam"

Now the most important file of all: ~/.mutt/muttrc. Here is my complete muttrc, commented so you can tell what things do; I'm not explaining anything beyond the folder-hook. This means when we enter a folder, we load the settings specific to that account as defined above. This means there's no need to change accounts per say, just navigate to a folder and any account specific settings will automatically be in place while you're in that folder.Go man muttrc if you'd like more info on all the other settings.

  ###
  # ~/.mutt/muttrc
  ###

  # directories and commands
  set alias_file          = ~/.mutt/alias                         # alias file
  set header_cache        = ~/.mutt/cache/headers                 # where to store headers        
  set message_cachedir    = ~/.mutt/cache/bodies                  # where to store bodies
  set certificate_file    = ~/.mutt/certificates                  # where to store certs
  set mailcap_path        = ~/.mutt/mailcap                       # entrys for filetypes
  set signature           = ~/.mutt/sig                           # my signature file
  set tmpdir              = ~/.mutt/temp                          # where to keep temp files
  set editor              = "/usr/bin/vim +/^$"                   # use vim and skip to first blank line
  set ispell              = "/usr/bin/aspell -e -c"               # use aspell as ispell
  set print_command       = /usr/bin/enscript                     # print with enscript
  set query_command       = "/usr/bin/mutt_vc_query '%s'"         # query rolo's vCards

  # main options
  set mbox_type           = Maildir                               # mailbox type
  set folder              = ~/Mail                                # mailbox location
  set spoolfile           = "+GMail/INBOX"                        # GMail is default inbox
  set timeout             = 3                                     # idle time before scanning
  set mail_check          = 0                                     # minimum time between scans
  set sort_alias          = alias                                 # sort alias file by alias
  set reverse_alias                                               # show names from alias file in index
  unset move                                                      # gmail does that
  set delete                                                      # don't ask, just do
  unset confirmappend                                             # don't ask, just do!
  set quit                                                        # don't ask, just do!!
  unset mark_old                                                  # read/new is good enough for me
  set beep_new                                                    # bell on new mails
  set pipe_decode                                                 # strip headers and eval mimes when piping
  set thorough_search                                             # strip headers and eval mimes before searching

  # index options
  set sort                = threads                               # like gmail 
  set sort_aux            = reverse-last-date-received            # like gmail
  set uncollapse_jump                                             # don't collapse on an unread message 
  set sort_re                                                     # thread based on regex
  set reply_regexp        = "^(([Rr][Ee]?(\[[0-9]+\])?: *)?(\[[^]]+\] *)?)*"

  # pager options
  set pager_index_lines   = 10                                    # number of index lines to show 
  set pager_context       = 5                                     # number of context lines to show
  set pager_stop                                                  # don't go to next message automatically
  set menu_scroll                                                 # scroll in menus 
  set smart_wrap                                                  # don't split words
  set tilde                                                       # show tildes like in vim
  unset markers                                                   # no ugly plus signs
  auto_view text/html                                             # view html automatically
  alternative_order text/plain text/enriched text/html            # save html for last
  set quote_regexp        = "^( {0,4}[>|:#%]| {0,4}[a-z0-9]+[>|]+)+"

  # formats
  set date_format         = "%m/%d/%y at %I:%M%P"
  set index_format        = "%3C [%Z] %D %-15.15F %s"
  set status_format       = "-%r- %v ---[ Folder: %f, Msgs: %m%?n? New: %n?%?d? Del: %d?%?t? Tag: %t?%?l? %l? ]%?p?---- .: %p waiting to send :. ?--%>- (%P) ---"
  set alias_format        = "%4n %t %-20a  %r"

  # composing mail
  set realname            = "Patrick Brisbin"                     # who am i?
  set envelope_from                                               # which from?
  set sig_dashes                                                  # dashes before my sig... sweet
  set edit_headers                                                # show headers when composing
  set fast_reply                                                  # skip to compose when replying
  set sendmail_wait       = -1                                    # don't wait for sending... to complete
  set askcc                                                       # ask for CC:
  set fcc_attach                                                  # save attachments with the body
  unset mime_forward                                              # forward attachments as part of body
  set forward_format      = "Fwd: %s"                             # format for subject when forwarding
  set forward_decode                                              # decode when forwarding
  set attribution         = "On %d, %n wrote:"                    # set the attribution
  set reply_to                                                    # reply to Reply to: field
  set reverse_name                                                # reply as whomever it was to
  set include                                                     # include message in replies
  set forward_quote                                               # include message in forwards

  # headers to show
  ignore *                                                        # ignore all headers
  unignore from: to: cc: date: subject:                           # show only these
  hdr_order from: to: cc: date: subject:                          # and in this order

  # boxes
  mailboxes +GMail/INBOX +GMail/archive +GMail/sent +GMail/drafts +GMail/spam +GMail/trash
  mailboxes +GMX/INBOX +GMX/Archive +GMX/Sent +GMX/Drafts +GMX/Spam +GMX/Trash

  # always sourced
  source $alias_file                                              # required for functionality
  source ~/.mutt/colors.muttrc                                    # source colors file
  source ~/.mutt/sidebar.muttrc                                   # any muttng options are here
  source ~/.mutt/gmail.muttrc                                     # source gmail as default

  # account specific sources
  folder-hook GMail/* source ~/.mutt/gmail.muttrc
  folder-hook GMX/*   source ~/.mutt/gmx.muttrc

  # these just give me headaches
  bind index,pager \#     noop
  bind index i            noop

  # bindings
  bind pager i            exit
  bind pager /            search
  bind pager <up>         previous-line
  bind pager <down>       next-line
  bind pager k            previous-line
  bind pager j            next-line
  bind pager gg           top
  bind pager G            bottom
  bind index gg           first-entry
  bind index G            last-entry
  bind pager K            previous-undeleted
  bind pager J            next-undeleted
  bind index K            previous-unread
  bind index J            next-unread
  bind index,pager R      group-reply

  # macros
  macro index \Cr "<tag-prefix><clear-flag>N" "mark tagged messages as read"
  macro index B   "<limit>~b "                "search message bodies"
  macro index I   "<change-folder>!<enter>"   "go to Inbox"

  # save a decoded copy in ~
  macro index P   "<pipe-message>cat > ~/"    "save message as"

  # quick-sync ~/Mail immediately with offlineimap
  macro index Z   "<shell-escape>/usr/bin/offlineimap -q -o<enter>" "sync IMAP"

Now just send some mails back and forth between the two accounts to verify that the folder-hooks are setting the from address and such correctly.

Vim Tweaks

I also have the following tweaks in ~/.vimrc to help with my composing.

  " Some tricks for mutt
  " F1 through F3 re-wraps paragraphs in useful ways
  augroup MUTT
    au BufRead ~/.mutt/temp/mutt* set spell " <-- vim 7 required
    au BufRead ~/.mutt/temp/mutt* nmap  <F1>  gqap
    au BufRead ~/.mutt/temp/mutt* nmap  <F2>  gqqj
    au BufRead ~/.mutt/temp/mutt* nmap  <F3>  kgqj
    au BufRead ~/.mutt/temp/mutt* map!  <F1>  <ESC>gqapi
    au BufRead ~/.mutt/temp/mutt* map!  <F2>  <ESC>gqqji
    au BufRead ~/.mutt/temp/mutt* map!  <F3>  <ESC>kgqji
  augroup END

Well that's it. Two IMAP accounts accessed easily, intuitively, and efficiently in your favorite email program.

Comments

on Tue, 13 Jul 2010 10:29:09 -0400, jagadeesh Malakannavar wrote:

Instead of
macro index Z "/usr/bin/offlineimap -q -o" "sync IMAP"

macro index Z "~/mailrun.sh" "sync IMAP"

can be usedul?

on Thu, 15 Jul 2010 07:39:40 -0400, Chris Goodman wrote:

Somebody keeps breaking into my yahoo and gmail accounts.Could you tie in some sort of security that changed colors red if someone that wasnt you was invading?

on Fri, 16 Jul 2010 13:23:53 -0400, me wrote:

That's in interesting issue Chris. How might I (or mutt) know if someone were "invading" your account?

on Fri, 16 Jul 2010 13:26:29 -0400, me wrote:

Yes jagadeesh, that would be usefull.

For me however, I prefer to actually see all the output when I force a refresh from within mutt. Kind of like a progress bar when you do a manual sync in other mail clients.

If I were to use my mailrun script in this case, It would immediately send the offlineimap command to background, send me back to mutt, and I'd wait patiently as messages arrived (or didn't) in my listing.

Of course, to each their own.

on Thu, 29 Jul 2010 07:00:11 -0400, Richard wrote:

Your offlineimap config prompted me to discard my way of doing it. I had been using a local dovecot server but it was slow and unreliable. Moved to pure maildir now with gnus and it works like a treat. Rather than translate folder names though I apply a label "myaccount-inbox" to all incoming email in gmail - this you can then subscribe to as it appears as its own unique maildir - the label name contains a unqiue token (myaccount in this example) to enable something like gnus to set the correct posting style for that account.

on Sun, 08 Aug 2010 09:19:35 -0400, Eclectric Sheep wrote:

Any chance msmtp/offlineimap could send the request for a password to mutt? I hate leaving that kind of thing in a file with its only security being 'chmod 600'

on Sun, 08 Aug 2010 18:44:52 -0400, me wrote:

I think that you can get offlineimap to ask for a password by omitting it from the config file. If so, then I'd setup mutt to never auto-sync; in stead, use my macro (or varation thereof) to manually sync, during which time offlineimap should prompt you.

As for msmtp, you'd have to test -- I know error messages will come through to mutt when you try to send an email so maybe the password prompt will too?

Good luck.

on Sun, 08 Aug 2010 20:26:50 -0400, Eclectric Sheep wrote:

Yea I got offlineimap working, I'm just working on msmtp now. Oh well, I don't mind storing them in the files that much I guess since I've got multiuser working!

One more question though; I made some slight adjustments for my files and Sent messags don't appear to be being recorded. I've got my eye on .muttrc where it says 'unset record'. When I use 'set record = +//Sent' it gives me an error...I'm a bit confused here: they aren't being saved to either the server or ~/Mail. If it makes things easier, I've uploaded my dotfiles to github:

http://github.com/eclectricsheep/dotfiles/tree/master/email_files

on Sun, 08 Aug 2010 20:29:49 -0400, Eclectric Sheep wrote:

Looks like this message system take out stuff in greater than/less than signs.

'set record = +//Sent'
in the above message is supposed to be:
'set record = +/USERNAME/Sent'

on Mon, 09 Aug 2010 02:12:10 -0400, Eclectric Sheep wrote:

Got everything working great! It came down to some issues I had with cron.

Thanks for your time. =)

on Thu, 12 Aug 2010 17:15:59 -0400, Huntly Cameron wrote:

Thanks for this mate! Worked like a charm! :)

on Tue, 07 Sep 2010 05:55:14 -0400, Kim Callis wrote:

Everything is working smoothly, with the exception of the colors. I use terminator as my default terminal, and even with me not adding colors.muttrc, it still looks bad in the frames.

Any way to make the colors become bearable?

on Tue, 07 Sep 2010 05:58:41 -0400, Kim Callis wrote:

Also, one lame question... On the index page, how does one increase the separation between the send and the subject. I would rather see the name of the sender as opposed to the full subject.

on Tue, 07 Sep 2010 10:29:44 -0400, me wrote:

Re: the colors, the default colors are pretty ugly IMO, you should play with them to some degree if you want it to look better.

Re: the index spacing, man muttrc and play with the index_format setting.

on Wed, 08 Sep 2010 03:47:51 -0400, Kim Callis wrote:

Thanks for the pointer about index_format... With a gazillion switches for mutt, it is hard to keep track. I am still somewhat bothered my the garbage that is appearing on my status line.

--- Mutt 1.5.20 ─┴───...

I have tried adding charset, etc, and I still can't get rid of the status line being clear...

on Wed, 08 Sep 2010 03:50:12 -0400, Kim Callis wrote:

Anyone making use of the mutt.el mode under emacs. Yes, dammit, I said it... EMACS!!! :) I have tried to make use of the mutt mode as well as the post mode, and it still isn't making since to me!

on Wed, 08 Sep 2010 14:53:32 -0400, me wrote:

Kim, take a look at status_format. On my system, they are unicode line drawing characters which I think look quite good. You can get unicode working in your terminal or replace them with a normal '-'. Or... just remove the option and let mutt use its default.





pbrisbin dot com 2010