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