TITLE: Updated Mutt config, multiple accounts
DATE: 2018-12-15
AUTHOR: John L. Godlee
====================================================================


In a long transition away from Google, I made another email
account, on an email system that values privacy and doesn't sell
information to advertisers. That means that for the moment, I now
have two email accounts. Simultaneously, I thought I would give
(Neo)Mutt another go as my email client, along with Offlineimap
(grabbing mail), notmuch/alot (indexing mail), msmtp (sending
email), pass (managing passwords), vim (writing mail), launchd
(scheduling mail sync), and w3m (parsing HTML email).

Offlineimap

This is my current .offlineimaprc:

   general
   accounts = riseup, gmail
   pythonfile = ~/.offlineimap_pass.py
   fsync = true

   Account riseup
   localrepository = riseup-local
   remoterepository = riseup-remote

   Repository riseup-local
   type = Maildir
   localfolders = ~/.mail/riseup

   Repository riseup-remote
   type = IMAP
   ssl = true
   sslcacertfile = /usr/local/etc/openssl/cert.pem
   remotehost = mail.riseup.net
   remoteuser = <EMAIL ADDRESS>
   remotepasseval = GetPassRiseup()

   Account gmail
   localrepository = gmail-local
   remoterepository = gmail-remote

   Repository gmail-local
   type = Maildir
   localfolders = ~/.mail/gmail
   nametrans = lambda folder: re.sub('drafts', '[Google
Mail].Drafts',
       re.sub('sent', '[Google Mail].Sent Mail',
       re.sub('starred', '[Google Mail].Starred',
       re.sub('trash', '[Google Mail].Bin', folder))))

   Repository gmail-remote
   maxconnections = 3
   type = Gmail
   ssl = true
   sslcacertfile = /usr/local/etc/openssl/cert.pem
   remoteuser = <EMAIL ADDRESS>
   remotepasseval = GetPassGmail()
   realdelete = no
   nametrans = lambda folder: re.sub('.*Drafts$', 'drafts',
       re.sub('.*Sent Mail$', 'sent',
       re.sub('.*Starred$', 'starred',
       re.sub('.*Bin$', 'trash', folder))))

   folderfilter = lambda folder: folder not in [
       '[Google Mail]/Important',
       '[Google Mail]/Spam',
       '[Google Mail]/Chats',
       '[Google Mail]/All Mail',
       ]
   createfolders = True


   postsynchook = notmuch new --quiet

accounts = riseup, gmail tells offlineimap that I have two accounts.

The password for each account, defined by remotepasseval is mapped
to two python functions, found in pythonfile =
~/.offlineimap_pass.py. See below in the pass section for what the
pythonfile contains.

The rest I think is self explanatory, except for the name
translations. This takes the IMAP name for certain default gmail,
like [Google Mail].Starred, and turns them into names that are easy
to read in the Neomutt sidebar, like starred. It's also necessary
to change these names back in the remote section of the config file.

Lastly, the postsynhook calls notmuch to recompile it's database,
checking for new mail. I use notmuch with alot for searching emails
sometimes. At some point it might be nice to see if I can run alot
directly from mutt.

To get offlineimap to run every few minutes, to check if I have new
mail, I use launchd, which is the successor to cron on macOS. I
keep a script in ~/Library/LaunchAgents/ that looks like this:

   <?xml version="1.0" encoding="UTF-8"?>
   <plist version="1.0">
     <dict>
       <key>EnvironmentVariables</key>
       <dict>
         <key>PATH</key>

<string>/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin</string>
       </dict>
       <key>KeepAlive</key>
       <false/>
       <key>Label</key>
       <string>homebrew.mxcl.offlineimap</string>
       <key>ProgramArguments</key>
       <array>

<string>/usr/local/opt/offlineimap/bin/offlineimap</string>
         <string>-q</string>
         <string>-u</string>
         <string>basic</string>
       </array>
       <key>StartInterval</key>
       <integer>300</integer>
       <key>RunAtLoad</key>
       <true />
       <key>StandardErrorPath</key>
       <string>/dev/null</string>
       <key>StandardOutPath</key>
       <string>/dev/null</string>
     </dict>
   </plist>

It runs offlineimap in quiet mode (-q, -u basic), every 300
seconds, and whenever the computer wakes from sleep or is powered
on (RunAtLoad), it also pipes error outputs to /dev/null.

pass

This is the python file I use to call pass to get the passwords for
each of my email accounts. Basically it just runs pass on the
appropriate entry, then takes the output and grabs the appropriate
line:

   #!/usr/bin/env python2

   from subprocess import check_output

   def GetPassGmail():
       return check_output("/usr/local/bin/pass email/gmail",
shell=True).splitlines()[0]

   def GetPassRiseup():
       return check_output("/usr/local/bin/pass email/riseup",
shell=True).splitlines()[1]

Mutt

Here is my Mutt config, which I keep in ~/.mutt/muttrc:

   # Source other files
   source ~/.mutt/mutt_colours
   source ~/.mutt/aliases

   # Basic settings
   set realname = "John Godlee"
   set folder = "~/.mail"

   # Text editor
   # Use vim with minimal mail writing vimrc
   set editor = "vim -u ~/.vimrc_alpine"
   set charset = "utf-8"
   unset record

   # Ability to change headers manually in text editor
   set edit_headers = yes
   set autoedit = yes

   # Pager View Options
   set pager_index_lines = 0  # number of index lines to show
   set pager_context = 3      # number of context lines to show
   set pager_stop             # don't go to next message
automatically
   set menu_scroll            # scroll in menus
   set tilde                  # show tildes like in vim
   unset markers              # no ugly plus signs
   set mark_old = no          # Don't add Old flags, just keep N
   set mailcap_path = ~/.mutt/mailcap
   auto_view text/html

   # Status bar
   set status_chars  = " *%A"
   set status_format = "───[Folder: %f]───[%r%m
messages%?n? (%n new)?%?d? (%d to delete)?%?t? (%t
tagged)?]───%>─%?p?( %p postponed )?───"

   # Index
   set date_format = "%b-%d"
   set index_format = "[%Z] %X %M %-20.20F -- %s %* %[%Y_%m_%d] -
%[%H:%M]"
   set sort = threads                         # like gmail
   set sort_aux = 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
   set pager_index_lines = 8  # number of index lines to show
   set pager_context = 3      # number of context lines to show
   set pager_stop             # don't go to next message
automatically
   set menu_scroll            # scroll in menus
   set tilde                  # show tildes like in vim
   unset markers              # no ugly plus signs
   alternative_order text/plain text/enriched text/html

   # Sidebar
   set sidebar_visible = yes
   set sidebar_format = "%B %* [%N]%S"

   ## Don't abbreviate folders in sidebar
   set sidebar_short_path
   set sidebar_delim_chars = "/"

   # Gmail mailboxes in sidebar
   mailboxes "+--- gmail --------"
   mailboxes +gmail/INBOX # Always have inbox at top of list
   mailboxes `find ~/.mail/* -maxdepth 1 -mindepth 1 | sort | cut
-d/ -f5- | sed 's/^/+/' | sed '/notmuch/d' | sed '/INBOX/d' | sed
'/riseup/d' | tr '\n' ' '`

   # Riseup mailboxes in sidebar
   mailboxes "+--- riseup --------"
   mailboxes +riseup/INBOX
   mailboxes `find ~/.mail/* -maxdepth 1 -mindepth 1 | sort | cut
-d/ -f5- | sed 's/^/+/' | sed '/INBOX/d' | sed '/gmail/d' | tr '\n'
' '`

   # Multiple account setup
   # Default to gmail setup
   source ~/.mutt/accounts/gmail

   # Folder hooks
   folder-hook gmail/*      source ~/.mutt/accounts/gmail
   folder-hook riseup/*     source ~/.mutt/accounts/riseup

   # Macros for switching accounts - sources a separate config
when a folder is opened
   macro index } '<sync-mailbox><enter-command>source
~/.mutt/accounts/gmail<enter><change-folder>!<enter>'
   macro index { '<sync-mailbox><enter-command>source
~/.mutt/accounts/riseup<enter><change-folder>!<enter>'

   # Keybindings

   bind index,pager R group-reply
   bind index,pager r reply
   bind index <space>  collapse-thread
   macro index C "<copy-message>?<toggle-mailboxes>" "copy a
message to a mailbox"
   macro index M "<save-message>?<toggle-mailboxes>" "move a
message to a mailbox"

   ## Disable arrow keys
   bind generic,pager,editor,index <Up>       noop
   bind generic,pager,editor,index <Down>     noop
   bind generic,pager,editor,index <Left>     noop
   bind generic,pager,editor,index <Right>    noop

   ## Tagging and manipulating basics
   bind  index,pager c  mail                      # compose
   bind  generic     x  tag-entry                 # Select
Conversation
   bind  index       x  tag-thread                # Select
Conversation
   bind  pager       x  tag-message               # Select
Conversation
   bind  index,pager *  flag-message              # Star a message
   bind  index,pager a  group-reply               # Reply all
   bind  index,pager \# delete-thread             # Delete
   bind  index,pager l  copy-message              # Label
   bind  index       v  save-message              # Move to

   ## Sidebar interaction
   bind  index,pager B  sidebar-toggle-visible
   bind  index,pager >  sidebar-next
   bind  index,pager <  sidebar-prev
   bind  index,pager ?  sidebar-open

   ## Movement
   bind  pager  i  noop
   bind  index  G  last-entry
   bind  index  j  next-entry
   bind  index  k  previous-entry
   bind  pager  k  previous-line
   bind  pager  j  next-line
   bind  pager  J  next-entry
   bind  pager  K  previous-entry
   bind  pager  q  exit
   bind  pager  v  view-attachments

   ## Fast movement
   bind editor <space> noop
   bind  index,pager g noop
   macro index,pager gi "<change-folder>=gmail/INBOX<enter>" "Go
to inbox"

   bind  index,pager h  help

   # Mail handling
   set move = no
   # Always include original message in reply and always reply to
sender
   set include = yes
   set fast_reply

   # Ask if unsent message should be kept as postponed
   set postpone = ask-yes

It's mostly a mash up of things I found online, but these are the
bits I think are interesting.

I use a set of sed manipulations to get the names of mailboxes in
both my gmail and riseup directories to fill the sidebar, using:

   mailboxes `find ~/.mail/* -maxdepth 1 -mindepth 1 | sort | cut
-d/ -f5- | sed 's/^/+/' | sed '/notmuch/d' | sed '/INBOX/d' | sed
'/riseup/d' | tr '\n' ' '`

I use folder hooks and macros to load separate configs which change
settings for replying to emails depending on which account I want
to use. The extra configs look like this:

   set from = "<EMAIL ADDRESS>"
   set spoolfile = gmail/INBOX
   set postponed = +gmail/drafts
   set record = +gmail/sent

   set sendmail = "/usr/local/bin/msmtp -a gmail"

   macro index D \
       "<delete-message><enter>" \
       "Delete message permanently"

See that set sendmail uses msmtp to send the email, and uses the
account called gmail. This is what my .msmtprc looks like, which
defines those accounts:

   account riseup
   host mail.riseup.net
   port 587
   protocol smtp
   auth on
   tls on
   tls_trust_file /usr/local/etc/openssl/cert.pem
   from <EMAIL ADDRESS>
   user <EMAIL ADDRESS>
   passwordeval "gpg --quiet --for-your-eyes-only --no-tty
--decrypt ~/.password-store/email/riseup.gpg | sed -n 2p"

   account gmail
   host smtp.gmail.com
   port 587
   protocol smtp
   auth on
   tls on
   tls_trust_file /usr/local/etc/openssl/cert.pem
   from <EMAIL ADDRESS>
   user <EMAIL ADDRESS>
   passwordeval "gpg --quiet --for-your-eyes-only --no-tty
--decrypt ~/.password-store/email/gmail.gpg"

   account default : gmail

The last thing is the mailcap, which uses w3m to parse HTML email
as plain text, and is called into mutt using set mailcap_path:

   text/html;  w3m -dump -o document_charset=%{charset} '%s';
nametemplate=%s.html; copiousoutput