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