Recently a user revived an old thread by @20-100 concerning mail clients.
Today we discuss how to setup neomutt as a comfortable, integrated and versatile CLI MUA, while trying to keep resource requirements as low as possible, so as to ideally provide a viable solution for legacy hardware, embedded systems limited in specs and headless / console-only computers.
requirements
- mail/neomutt (you may also use mutt, but some features will be unsupported unless adding local patches)
- mail/msmtp, as SMTP client
- security/gnupg2, to encrypt/decrypt and sign messages, as well as encrypt/decrypt passwords on the fly. This tutorial assumes you already configured GnuPG properly to use your secret key(s).
- databases/abook as an addressbook to store and manage aliases
- a third-party editor, if you find nvi (or whatever $EDITOR is set to) to be too conservative
preliminary tasks
- Create directories to store mutt files
$ for d in accounts bodies cache certificates headers private themes tmp
> do mkdir -p ~/.mutt/${d}
> done
- store your imap/smtp password as an encrypted hidden file
$ echo -e "your password here\n" | gpg2 --encrypt -o ~/.msmtp.gpg # enter key ID
$ chmod 600 ~/.msmtp.gpg
- generate self-signed client certificate and for mutt:
$ openssl req -x509 -newkey rsa:4096 -days 365 -keyout ~/.mutt/private/mutt.key -out ~/.mutt/certificates/mutt.crt
- create a ~/.signature file with your signature, try not to make it longer than 4 lines:
Real Name ~ xmpp:buddy@jabber.sdf.org
SDF Public Access UNIX System - https://sdf.org
PGP fingerprint = XXXX XXXX XXXX XXXX
- configure msmtp by editing a ~/.msmtprc file like below:
# Set default values for all following accounts.
defaults
auth on
tls on
tls_starttls on
tls_trust_file /etc/openssl/certs/ca-certificates.crt
tls_cert_file ~/.mutt/certificates/mutt.crt
tls_key_file ~/.mutt/private/mutt.key
syslog on
tls_certcheck on
logfile ~/.msmtp.log
# RiseUp
account riseup
host mail.riseup.net
port 587
user account@riseup.net
from your real name <account@riseup.net>
passwordeval "gpg2 --quiet --for-your-eyes-only --no-tty --decrypt ~/.msmtp.gpg"
# Outlook
account outlook
host smtp.office365.com
port 587
user account@outlook.it
from your real name <account@outlook.it>
passwordeval "gpg2 --quiet --for-your-eyes-only --no-tty --decrypt ~/.outlook.gpg"
account default : riseup
configuration files
What follows is a sample configuration, modeled after my personal one. It is purposedly commented for readers to understand what the various options stand for. Feel free to experiment with it a bit and adjust it to your needs.
DISCLAIMER: I've put this setup together by copying, adapting, drawing
ideas from and mixing, various other configurations found online and
therefore do not retain any right over the files that follow. If you
stumble across this post and want to be credited as author or wish for
it to be removed do not hesitate to email me.
Change working directory to ~/.mutt and touch/edit the required files, as shown below:
# =================== MUTT CONFIGURATION ==================== #
## S S L
# force encryption
set ssl_force_tls = "yes"
# default to TLSSv1.3
set ssl_use_tlsv1_3 = "yes"
# and STARTTLS
set ssl_starttls = "yes"
## e n c o d i n g
# default to unicode (using iso-8859-1 as fallback)
set send_charset = "us-ascii:utf-8:iso-8859-1"
# decode RFC-2047-encoded MIME parameters
set rfc2047_parameters = "yes"
# decode headers/bodies before searching
set thorough_search = "yes"
## a p p e a r a n c e
set date_format = "%y/%m/%d %I:%M%p"
set index_format = "%2C %zs %?X?A& ? %D %-15.15F %s (%-4.4c)"
set pager_format = "-%Z- %C/%m: %-20.20n %s%* -- (%P)"
set pager_context = "3"
set forward_format = "Fwd: %s"
# Disables the `+` displayed at line wraps
set markers = "no"
## h e a d e r s
ignore "Authentication-Results:"
ignore "DomainKey-Sighnature:"
ignore "DKIM-Signature:"
hdr_order From: Resent-From: Reply-To: X-Mailer: User-Agent: Date: To: Cc: Subject:
my_hdr Organization: Riseup Networks Collective
my_hdr PGP: s
my_hdr User-Agent: `/usr/pkg/bin/neomutt -v | head -n 1`
my_hdr X-Operating-System: `uname -s`, kernel `uname -r`
## d i r e c t o r i e s
set header_cache = "~/.mutt/cache/headers"
set message_cachedir = "~/.mutt/cache/bodies"
set certificate_file = "~/.mutt/certificates"
set tmpdir = "~/.mutt/tmp"
## a l i a s e s
set alias_file = "~/.mutt/aliases"
# sort aliases alphabetically
set sort_alias = alias
# display real names first
set reverse_alias = yes
# external address book command
set query_command = "abook --mutt-query '%s'"
## m i s c
# supress waiting for informational messages
set sleep_time = "0"
# lock session after 15 min
set timeout = "900"
# display plain text bodies whenever possible
alternative_order text/plain text/enriched text/html
# most recent messages displayed first
set sort = "reverse-date"
# external editor
set editor = "/usr/pkg/bin/vim"
# do not automatically jump to next message
set pager_stop = "yes"
# Unread mail stay unread until read
set mark_old = "no"
# attachments are forwarded with mail
set mime_forward = "yes"
# mutt won't ask "press key to continue"
set wait_key = "no"
# skip to compose when replying
set fast_reply = "yes"
# save attachments with the body
set fcc_attach = "yes"
# include message in forwards
set forward_quote = "yes"
# reply as whomever it was to
set reverse_name = "yes"
# include message in replies
set include = "yes"
# avoid lags using IMAP with some email providers
set mail_check = "60"
# automatically show html (mailcap uses w3m)
auto_view text/html
# and encrypted messages
auto_view application/pgp-encrypted
# notifications
set new_mail_command = "notify-send -i /usr/pkg/share/icons/Adwaita/16x16/status/mail-unread.png -a "mutt" 'New Email in %f' '%n new messages, %u unread.' &"
# external configuration files to read
source ~/.mutt/accounts/imap.riseup
source ~/.mutt/sidebar
source ~/.mutt/binds
source ~/.mutt/gpg.rc
source ~/.mutt/themes/custom
source ~/.mutt/aliases
# =================== ACCOUNT SETTINGS==================== #
# general settings
set realname = "your real name here"
set imap_user = "account@riseup.net"
set imap_pass = "`gpg2 --quiet --for-your-eyes-only --no-tty --decrypt ~/.msmtp.gpg`"
set folder = "imaps://mail.riseup.net:993/"
set spoolfile = +INBOX
set postponed = +Drafts
set record = +Sent
set trash = +Trash
set sendmail = "/usr/pkg/bin/msmtp -a riseup -t"
set from = "your real name <account@riseup.net>"
set signature = "~/.signature"
set mail_check = 90
# generate 'From:' headers
set use_from = yes
# fetch the set of subscribed folders from server
set imap_check_subscribed = "yes"
# only display subscribed folders
set imap_list_subscribed = "yes"
# make use of the IMAP IDLE extension
set imap_idle = "yes"
# poll open IMAP conections every 3 mins
set imap_keepalive = 180
# =================== PGP SETTINGS ==================== #
# g e n e r a l
set crypt_autosign = "yes"
set pgp_sign_as = "account@riseup.net"
set pgp_sign_as = "your PGP key ID"
# no mail encryption by default
unset crypt_autoencrypt
# but encrypt replies to encrypted mails
set crypt_replyencrypt = "yes"
# always attempt to verify signatures
set pgp_verify_sig = "yes"
# encode signed messages as quoted-printable
set pgp_strict_enc = "yes"
# let gpg-agent handle passphrase prompts
set pgp_use_gpg_agent=yes
# cached passphrase shall expire after 1h time
set pgp_timeout = 3600
# m a c r o s
set pgp_decode_command = "gpg2 %?p?--passphrase-fd 0 --pinentry-mode=loopback? --no-verbose --batch --output - %f"
set pgp_verify_command = "gpg2 --no-verbose --batch --output - --verify %s %f"
set pgp_decrypt_command = "gpg2 %?p?--passphrase-fd 0 --pinentry-mode=loopback? --no-verbose --batch --output - %f"rsa
set pgp_sign_command = "gpg2 --no-verbose --batch --output - %?p?--passphrase-fd 0 --pinentry-mode=loopback? --armor --detach-sign --textmode %?a?-u %a? %f"
set pgp_clearsign_command = "gpg2 --no-verbose --batch --output - %?p?--passphrase-fd 0 --pinentry-mode=loopback? --armor --textmode --clearsign %?a?-u %a? %f"
set pgp_encrypt_only_command = "/usr/pkg/libexec/neomutt/pgpewrap gpg2 --batch --quiet --no-verbose --output - --encrypt --textmode --armor --always-trust -- -r %r -- %f"
set pgp_encrypt_sign_command = "/usr/pkg/libexec/neomutt/pgpewrap gpg2 %?p?--passphrase-fd 0 --pinentry-mode=loopback? --batch --quiet --no-verbose --textmode --output - --encrypt --sign %?a?-u %a? --armor --always-trust -- -r %r -- %f"
set pgp_import_command = "gpg2 --no-verbose --import -v %f"
set pgp_export_command = "gpg2 --no-verbose --export --armor %r"
set pgp_verify_key_command = "gpg2 --no-verbose --batch --fingerprint --check-sigs %r"
set pgp_list_pubring_command = "gpg2 --no-verbose --batch --quiet --with-colons --with-fingerprint --list-keys %r"
set pgp_list_secring_command = "gpg2 --no-verbose --batch --with-colons --with-fingerprint --list-secret-keys %r"
# =================== SHORTCUTS ==================== #
# Non-standard, more vim-like key mappings
#
# Free keys
bind index h noop
bind index,pager d noop #used for dX
bind index,pager i noop #used for goto iXY and i[1-9] in account muttrc (XY = 2 mailbox letters)
bind index,pager M noop #used for CXY, "move" to XY in account muttrc
bind index,pager C noop #used for CXY, "copy" to XY in account muttrc
bind pager,attach,browser,index g noop
# i n d e x
bind index gg first-entry
bind index G last-entry
bind index p recall-message
bind index R group-reply
bind index <tab> sync-mailbox
bind index <space> collapse-thread
macro index,pager H view-mailcap
bind index l display-message
bind browser l select-entry
bind index gl limit
macro index gL "<limit>all\n" "show all messages (undo limit)"
bind index \031 previous-undeleted # Mouse wheel
bind index \005 next-undeleted # Mouse wheel
bind browser,pager,index N search-opposite
bind pager,index dT delete-thread
bind pager,index dt delete-subthread
bind pager,index gt next-thread
bind pager,index gT previous-thread
bind index za collapse-thread
bind index zA collapse-all
bind index - collapse-thread
bind index _ collapse-all
macro index \Cr "<tag-pattern>~U<enter>\
<tag-prefix><clear-flag>N<untag-pattern>.<enter>" \
"mark all messages as read"
# m i s c
bind browser,pager,index N search-opposite
bind pager,index dT delete-thread
bind pager,index dt delete-subthread
bind pager,index gt next-thread
bind pager,index gT previous-thread
bind index,pager V view-raw-message
macro index dd "<save-message>+Trash<enter><sync-mailbox>" "move message to Trash"
bind pager,index D purge-message
bind index,pager gr group-reply #R is recall postponed by mutt
# p a g e r
bind pager k previous-line
bind pager j next-line
bind pager l view-attachments
bind pager R group-reply
bind pager gg top
bind pager G bottom
bind pager,attach h exit
bind pager \031 previous-line # Mouse wheel
bind pager \005 next-line # Mouse wheel
bind pager t display-toggle-weed
# b r o w s e r
bind browser gg top-page
bind browser G bottom-page
bind browser l select-entry
### a t t a c h m e n t s
bind attach l view-text
bind attach <return> view-mailcap
bind attach gg first-entry
bind attach G last-entry
bind attach l view-text
bind attach <return> view-mailcap
# e d i t o r
bind editor <space> noop
bind editor <Tab> complete-query
# a d d r e s s b o o k
macro index,pager a "<pipe-message>abook --add-email-quiet<return>" "Add this sender to Abook"
# p g p
bind compose p pgp-menu
macro compose Y pfy "send mail without GPG"
# m a i l b o x e s
macro index <f4> '<sync-mailbox><enter-command>source ~/.mutt/accounts/mdir.riseup<enter><change-folder>!<enter>'
macro index,pager c "<change-folder>?<toggle-mailboxes>" "open a different folder"
macro index,pager iqu "<change-folder>=Queue<enter>" "go to Queue"
macro index,pager Mqu "<save-message>=Queue<enter>" "move mail to Queue"
macro index,pager Cqu "<copy-message>=Queue<enter>" "copy mail to Queue"
macro index,pager itr "<change-folder>=Trash<enter>" "go to Trash"
macro index,pager Mtr "<save-message>=Trash<enter>" "move mail to Trash"
macro index,pager Ctr "<copy-message>=Trash<enter>" "copy mail to Trash"
macro index,pager isp "<change-folder>=Spam<enter>" "go to Spam"
macro index,pager Msp "<save-message>=Spam<enter>" "move mail to Spam"
macro index,pager Csp "<copy-message>=Spam<enter>" "copy mail to Spam"
macro index,pager iin "<change-folder>=INBOX<enter>" "go to INBOX"
macro index,pager Min "<save-message>=INBOX<enter>" "move mail to INBOX"
macro index,pager Cin "<copy-message>=INBOX<enter>" "copy mail to INBOX"
macro index,pager ise "<change-folder>=Sent<enter>" "go to Sent"
macro index,pager Mse "<save-message>=Sent<enter>" "move mail to Sent"
macro index,pager Cse "<copy-message>=Sent<enter>" "copy mail to Sent"
macro index,pager idr "<change-folder>=Drafts<enter>" "go to Drafts"
macro index,pager Mdr "<save-message>=Drafts<enter>" "move mail to Drafts"
macro index,pager Cdr "<copy-message>=Drafts<enter>" "copy mail to Drafts"
# Fetch mail shortcut
bind index G imap-fetch-mail
# =================== SIDEBAR PATCH ==================== #
# l a y o u t
set sidebar_visible = yes
set sidebar_width = 12
set sidebar_short_path = yes
set sidebar_next_new_wrap = yes
set mail_check_stats
set sidebar_format = '%B%?F? [%F]?%* %?N?%N/? %?S?%S?'
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 )?───"
color sidebar_new color221 color233
# b i n d i n g s
bind index,pager \Ck sidebar-prev
bind index,pager \Cj sidebar-next
bind index,pager \Co sidebar-open
bind index,pager \Cl sidebar-open
bind index,pager \Cp sidebar-prev-new
bind index,pager \Cn sidebar-next-new
macro index b "<enter-command>toggle sidebar_visible<enter>" # b toggles sidebar visibility
macro pager b "<enter-command>toggle sidebar_visible<enter>" # b toggles sidebar visibility
Take whatever theme you like and put it inside ~/.mutt/themes, then
source it in ~/.mutt/muttrc, as shown above.
Some good ones are listed on the official colorschemes page.
As a final note, I'd change the permissions of ~/.msmtprc.
~/.mutt/muttrc, ~/.mutt/accounts/* and ~/.mutt/certificates to be 600.
That's all folks, enjoy!