But before we jump right to the configuration of Mutt a short explanation of general concepts in email delivery are required to clarify what Mutt is actually doing and what not.
When sending an email quite a number of different programs are involved. Firstly, you write the mail in a Mail User Agent (MUA), basically a small program that allows you to compose and en- and decrypt MIME messages (emails). It hands the mail to the Mail Transport Agent (MTA), a SMTP server handling, as the name already suggests, the transport through your local system or the internet. Depending on the location of the server the mailbox of the receiver is located at also many MTAs can be involved in the transport. But eventually all emails will end up at a Mail Delivery Agent (MDA), which accepts the mail and stores it into its local database. In order to obtain the new email, the receiver has to use a Mail Retrieval Agent (MRA) to download it to her local system and than inspect it using a MUA.
What a mess! Why it’s not as simple as a HTTP request done when e.g. retrieving this blog post and doesn’t it work using a single program like Thunderbird? The point of having such an involved infrastructure is because you want to receive your email regardless of if your computer is running or not. In addition, you probably also want to access it from multiple locations and devices. The reason you were probably unaware of the multitude of programs involved is because big programs, like Thunderbird, tend to not stick to the Unix principle and try to do everything themselves causing them to be a combination of a MUA, MRA, and MTA. This won’t be the case for Mutt. Luckily it features some IMAP capabilities to directly obtain emails from your provider but it is intended to be just a MUA and we have to set up a MTA ourselves to deliver the mails to our email providers. But don’t worry. Our software of choice, postfix, is quite easily to configure, which will be covered in the end of this post. In case your interested about how mail protocols and the individual parts in the corresponding infrastructure do work, check out the documentation of Mutt.
Mutt
Like many elderly Linux/Unix software Mutt is best to be compiled freshly from source with all options you want to en- or disable provided as additional arguments. But before we do, we need to install some dependencies.
sudo apt update
sudo apt install libncursesw5-dev libgpgme-dev gpgsm
Use the ncurses with a w at the end. This fixes charset and
display errors. At least at my end. GPG made easy (GPGME) is an API
wrapper around the command line gpg
program. It also is able to
support S/MIME, which will be covered internally by the gpgsm
package. Both tools make it super easy to use encryption with GPG in
Mutt. Now, it’s time to compile the source.
## Obtain a copy of the Mutt source code
git clone https://gitlab.com/muttmua/mutt
cd mutt
## Compile it with a number of options.
./configure --enable-pgp --enable-gpgme --enable-compressed --enable-hcache --enable-imap --enable-sidebar --with-gnutls --with-curses=/usr/lib/x86_64-linux-gnu/
In this custom compilations we enabled encryption using GPG with
--enable-pgp --enable-gpgme
, asked Mutt to optimize its resource
management by compressing the emails with --enable-compressed
, and
to speed up mail inspection by storing some metadata of the recently
explored emails with --enable-hcache
. The
--enable-imap --enable-sidebar --with-gnutls
arguments allow us to
directly retrieve emails from a provider using the IMAP protocol
encrypted with GnuTLS and to display various content in a sidebar. The
--with-curses
option was important for me to render non-ASCII
symbols properly.
If you for some reason want to use the version of Mutt provided by the package system of your operation system, you can install it via
sudo apt update
sudo apt install mutt
But some of the features described in the following may not be enabled in this default version.
IMAP setup
First things first. Let’s start with getting access to our emails using Mutt. Classically, you would install a MDA, like fetchmail, getmail, or retchmail, download all emails into a local mailbox of the e.g. Maildir format, and tell Mutt to use this folder. It sure has the benefit of being able to automatically process incoming mail with programs like SpamAssassin but such setup is too involved for us right now. Instead, we will just retrieve the individual mails using IMAP.
We start with some general configuration
### IMAP
# Activate TLS
set ssl_starttls=yes
# Always use SSL when connecting to server
set ssl_force_tls=yes
# Don't wait till a manual entering of the mailbox occurred
unset imap_passive
# Poll every 15 seconds
set mail_check=15
# Keep a cache of the headers for faster loading
set header_cache=~/.muttHeaderCache
# Display the download progress every 5KB
set net_inc=5
# View the mail in threaded mode
set sort = threads
set sort_aux = reverse-last-date-received
# Only notify me for new mails since the last poll and not about all
# new mails in the mailbox.
set mail_check_recent = yes
# Check for new mail in the current mailbox.
set imap_idle = yes
You can just copy-paste these lines into your ~/.mutt/muttrc as they enable encrypted communication and just set some general stuff. A full list of all options can be found in the official documentation.
Next, we have to add all settings for a specific email account of ours, e.g. Gmail or Posteo.
# General settings
set from = "USER@googlemail.com"
set folder = "imaps://imap.gmail.com"
set record = "=Sent"
set spoolfile = "=Inbox"
set postponed = "=%5BGoogle%20Mail%5D/Entw%26APw-rfe"
set trash = "=Trash"
set use_envelope_from
set envelope_from_address = "USER@googlemail.com"
# Retrieve the password from the 'pass' password manager.
set my_pw_gmail=`pass show Email/gmail`
# IMAP
set imap_user = "USER@googlemail.com"
set imap_pass = $my_pw_gmail
### Custom macros
# Move to the 'Alle Nachrichten' folder
set my_gmail_location_all_messages = "=%5BGoogle%20Mail%5D/Alle%20Nachrichten"
macro index,pager \Ca "<change-folder>$my_gmail_location_all_messages<enter>"
# Monitor incoming mail from the following folders
set my_gmail_location_xxx = "=%5BGoogle%20Mail%5D/XXX"
set my_gmail_location_yyy = "=%5BGoogle%20Mail%5D/YYY"
mailboxes $spoolfile $postponed $record $trash \
$my_gmail_location_all_messages \
$my_gmail_location_xxx $my_gmail_location_yyy
# Custom status bar
set status_format = "-%r-Gmail: %f [Msgs:%?M?%M/?%m%?n? New:%n?%?o? Old:%o?%?d? Del:%d?%?F? Flag:%F?%?t? Tag:%t?%?p? Post:%p?%?b? Inc:%b?%?l? %l?]---(%s/%S)-%>-(%P)---"
That’s quite a lot! Let’s go through it line by line. The variable
folder
is maybe the most important one and specifies the
particular mailbox we want to use. Other locations, like record
,
spoolfile
, postponed
, and trash
, are default locations, which
are almost always in use regardless of your provider. Please note that
you have to enter the proper name for your mailbox. The English
version of the postponed
folder will most properly be different from
my German one.
The use_envelope_from
and envelope_from_address
override the
from
field in an email and ensure the receiver does know it was you
who sent it. Without these options I had problem with some of my
accounts. That’s why I stick to it by default.
To be able to identify yourself to the IMAP server, you have to
provide both a username and a password. You can decide to type both of
them either every time you open the mail folder in Mutt, which is a
quite tedious thing to do, or to store them in the imap_user
and
imap_pass
variables. But then again, you maybe do not want your
password to be written in plain text in an unencrypted configuration
file. If you by accident upload it to some server or post it on a
mailing list, you are in deep trouble. To solve this issue, I store
the password using the Linux program pass
, which is explained in a
previous
post. Regardless
of the mail account you use, you will be prompted for your GPG
passphrase and only have to reenter it if more than 5 minutes after
the last call to pass
did passed.
The remainder is some convenience configuration, which establishes the
shortcut CTRL+a to jump to the folder containing all mails, sets the
mailboxes
variable to show a couple of subfolders including the
number of unread mails in the sidebar, and defines a custom status
bar. The latter is important if you handle multiple IMAP accounts and
doesn’t want to loose sight of which one you are handling.
Using multiple IMAP accounts
Usually, people do have quite a number of email addresses. But how to use all of them within Mutt? I would recommend to write for each account a separate configuration file like above and store them in files like ~/.mutt/account.0, ~/.mutt/account.1 etc. Then we define a shortcut in the ~/.mutt/muttrc to load the individual files and thus to switch between the folders using e.g. the F5, F6, and F7 keys, and pick one of them to act as our default account.
## Keyboard shortcuts for switching between different accounts.
macro index <f5> '<sync-mailbox><enter-command>source ~/.mutt/account.0<enter><change-folder>!<enter>'
macro index <f6> '<sync-mailbox><enter-command>source ~/.mutt/account.1<enter><change-folder>!<enter>'
macro index <f7> '<sync-mailbox><enter-command>source ~/.mutt/account.2<enter><change-folder>!<enter>'
# Set the default account initialized during startup
source ~/.mutt/account.1
To ensure everything will work properly and the configuration of one account does not interfere with the settings of another, you should add the following lines to the beginning of each account configuration.
# Reset variables specified in other accounts
unmailboxes *
unset signature
unset realname
unset use_envelope_from
GPG setup
Of course we still want to encrypt our mails using GPG and I promised it to be very easy. But first we have to enter a couple of lines to our ~/.mutt/muttrc.
### Use GPG en|decryption
# Enable the automatic key retrieval of GPG
source ~/.mutt/gpg.rc
# Default key used during the encryption
set pgp_default_key = "KEY_ID"
# Uses 'gpg-agent'. Since gpg > version 2.1 this option has to be set.
set pgp_use_gpg_agent = yes
# Use automated encryption whenever the public keys of all recipients
# are in the keyring.
set crypt_opportunistic_encrypt = yes
# Reply encrypted to encrypted mails
set crypt_replyencrypt = yes
# Automatically verify signatures upon opening an email.
set crypt_verify_sig = yes
# Postponed messages that are marked for encruption will be
# self-encrypted.
set postpone_encrypt = yes
Be sure to replace KEY_ID with the ID of your personal key. You can
find it using gpg --list-secret-keys
. In addition, we also need the
corresponding ~/.mutt/gpg.rc file.
### beginning of ~/.mutt/gpg.rc
#
# Command formats for gpg.
#
# $Id$
#
# %p The empty string when no passphrase is needed,
# the string "PGPPASSFD=0" if one is needed.
#
# This is mostly used in conditional % sequences.
#
# %f Most PGP commands operate on a single file or a file
# containing a message. %f expands to this file's name.
#
# %s When verifying signatures, there is another temporary file
# containing the detached signature. %s expands to this
# file's name.
#
# %a In "signing" contexts, this expands to the value of the
# configuration variable $pgp_sign_as. You probably need to
# use this within a conditional % sequence.
#
# %r In many contexts, mutt passes key IDs to pgp. %r expands to
# a list of key IDs.
# Note that we explicitly set the comment armor header since GnuPG, when used
# in some localiaztion environments, generates 8bit data in that header, thereby
# breaking PGP/MIME.
# Note from the Debian mutt maintainers: starting from 1.7.0-2 GPGME is enabled
# by default. More info in NEWS.Debian.
# THe pgp_* commands are left here for people who disable gpgme in their
# ~/.muttrc
set crypt_use_gpgme=yes
# Note from the Debian mutt maintainers: the addition of
# "--pinentry-mode loopback" breaks gpgv1 compatiblity, if you need to use gpgv1
# remove that statement.
# decode application/pgp
set pgp_decode_command="gpg --status-fd=2 %?p?--passphrase-fd 0 --pinentry-mode=loopback? --no-verbose --quiet --batch --output - %f"
# verify a pgp/mime signature
set pgp_verify_command="gpg --status-fd=2 --no-verbose --quiet --batch --output - --verify %s %f"
# decrypt a pgp/mime attachment
set pgp_decrypt_command="gpg --status-fd=2 %?p?--passphrase-fd 0 --pinentry-mode=loopback? --no-verbose --quiet --batch --output - %f"
# create a pgp/mime signed attachment
set pgp_sign_command="gpg --no-verbose --batch --quiet --output - %?p?--passphrase-fd 0 --pinentry-mode=loopback? --armor --detach-sign --textmode %?a?-u %a? %f"
# create a application/pgp signed (old-style) message
set pgp_clearsign_command="gpg --no-verbose --batch --quiet --output - %?p?--passphrase-fd 0 --pinentry-mode=loopback? --armor --textmode --clearsign %?a?-u %a? %f"
# create a pgp/mime encrypted attachment
set pgp_encrypt_only_command="/usr/lib/mutt/pgpewrap gpg --batch --quiet --no-verbose --output - --encrypt --textmode --armor --always-trust -- -r %r -- %f"
# create a pgp/mime encrypted and signed attachment
set pgp_encrypt_sign_command="/usr/lib/mutt/pgpewrap gpg %?p?--passphrase-fd 0 --pinentry-mode=loopback? --batch --quiet --no-verbose --textmode --output - --encrypt --sign %?a?-u %a? --armor --always-trust -- -r %r -- %f"
# import a key into the public key ring
set pgp_import_command="gpg --no-verbose --import %f"
# export a key from the public key ring
set pgp_export_command="gpg --no-verbose --export --armor %r"
# verify a key
set pgp_verify_key_command="gpg --verbose --batch --fingerprint --check-sigs %r"
# read in the public key ring
set pgp_list_pubring_command="gpg --no-verbose --batch --quiet --with-colons --with-fingerprint --with-fingerprint --list-keys %r"
# read in the secret key ring
set pgp_list_secring_command="gpg --no-verbose --batch --quiet --with-colons --with-fingerprint --with-fingerprint --list-secret-keys %r"
# fetch keys
# set pgp_getkeys_command="pkspxycwrap %r"
# pattern for good signature - may need to be adapted to locale!
# set pgp_good_sign="^gpgv?: Good signature from "
# OK, here's a version which uses gnupg's message catalog:
# set pgp_good_sign="`gettext -d gnupg -s 'Good signature from "' | tr -d '"'`"
# This version uses --status-fd messages
set pgp_good_sign="^\\[GNUPG:\\] GOODSIG"
# pattern to verify a decryption occurred
set pgp_decryption_okay="^\\[GNUPG:\\] DECRYPTION_OKAY"
### end off ~/.mutt/gpg.rc
Various stuff
Here are a some additional configurations to ease the handling of Mutt. First, we will set up the sidebar and an easy navigation through it.
### Configuring the sidebar
# Show the sidebar (all folders defined in the 'mailbox' variable)
set sidebar_visible = yes
# Don't print the full path of the individual folders
set sidebar_short_path = yes
# Width in number of characters
set sidebar_width = 15
# Keybindings to handle the sidebar
# macro index,pager \C<Up> '<sidebar-prev>' "move up in the sidebar"
macro index,pager i '<sidebar-prev>' "move down in the sidebar"
macro index,pager o '<sidebar-next>' "move down in the sidebar"
macro index,pager p '<sidebar-open>' "open a folder from the sidebar"
macro index,pager <ESC>i '<sidebar-page-up>' "move one page up in the sidebar"
macro index,pager <ESC>o '<sidebar-page-down>' "move one page down in
the sidebar"
In addition, we will use Emacs as our editor
of choice for
composing emails. You probably have a quite complex ~/.emacs
file, which makes the program on the one hand quite flexible and
powerful but on the other hand also slow to start up. Therefore, you
should either boil down your configuration to the most essential stuff
or just use the my minimal
one.
### Various
# Use Emacs as default editor
set editor = "emacs %s --no-init-file --no-x-resources --no-site-file --no-window-system --load ~/.mutt/.emacs-mutt -f mutt-mode"
# Use sendmail to transfer mails via SMTP to an MTA
set sendmail="/usr/sbin/sendmail -oi -oem"
# Use a custom color theming
source ~/.mutt/colors
# Include messages in replies
set include
# Don't ask me about postponing a message when writing a new email.
set recall = ask-no
Also we use some custom coloring
### Beginning of ~/.mutt/colors
### Custom colors used within mutt
# Default colour definitions
color quoted red black
color indicator white brightblack
color error brightyellow black
color message brightyellow black
color search brightwhite magenta
color status black brightyellow
color tree red black
color normal white black
set header_color_partial = yes
# # Colours for items in the reader
color header brightwhite black "^(From|Subject):"
# Colours for items in the index
color index brightwhite black ~O # old messages
color index brightwhite black ~N # new messages
color index white brightred ~D # deleted messages
color index brightyellow black ~T # tagged messages
# Highbrights inside the body of a message.
# URLs
color body brightblue black "(http|https|ftp|news|telnet|finger)://[^ \">\t\r\n]*"
color body brightblue black "mailto:[-a-z_0-9.]+@[-a-z_0-9.]+"
color body brightblue black "news:[^ \">\t\r\n]*"
# email addresses
color body brightblue black "[-a-z_0-9.%$]+@[-a-z_0-9.]+\\.[-a-z][-a-z]+"
### end of ~/.mutt/colors
The full configuration I use with Mutt can be found in here. If you also plan to upload yours, be sure to encrypt all files containing either passwords or other confidential stuff as well as all your files listing email addresses, be it of friends, mailing lists, or even yourself. Unobscured addresses should be never uploaded into public locations.
Postfix
Last but not least, we still have to install a MTA to be able to send our emails. In here we will use postfix.
sudo apt update
sudo apt install mutt postfix
It will replace the default MTA/MDA sendmail
by linking the binary
/usr/sbin/sendmail to its own variant capable of handling the same
input arguments as the original.
The main configuration of postfix can be found in /etc/postfix/main.cf. We will just add several lines and keep the rest of the file intact.
### SMTP spass
# Enables client-side authentication
smtp_sasl_auth_enable = yes
# Ensures encryption
smtp_tls_security_level = encrypt
# Removes prohibition on plaintext passwords
smtp_sasl_tls_security_options = noanonymous
# Send username and password information to the mail gateway server.
smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd
# Support multiple accounts
smtp_sender_dependent_authentication = yes
sender_dependent_relayhost_maps = hash:/etc/postfix/sender_relay
# Use the Gmail one as the default
relayhost = [smtp.gmail.com]
In order to use postfix with state-of-the-art email providers we have to activate encryption and supply it with our credentials. The file /etc/postfix/sender_relay specifies which outgoing SMTP server to use for which email account. This is key when using multiple ones.
### beginning of /etc/postfix/sender_relay
# Use different providers for different senders
USER@ladi.da [ladi.da]:587
USER@gmx.net [mail.gmx.net]
### end of /etc/postfix/sender_relay
The corresponding passwords are stored in the /etc/postfix/sasl_password file.
### beginning of /etc/postfix/sasl_passwd
# Use different authentication for different senders
USER@ladi.da USER:PASSWORD
USER@gmx.net USER@gmx.net:PASSWORD
# Login information for the defaul relay
# Destination credentials
[smtp.gmail.com] USER@googlemail.com:PASSWORD
### end of /etc/postfix/sasl_passwd
In both the passwords and addresses have to be inserted in plaintext. Be sure to configure the permissions properly or other users can read them too!
After modifying those files, run the following commands to write the
changes into the postfix
database. The actual files won’t be
read/touched.
sudo postmap /etc/postfix/sender_relay
sudo postmap /etc/postfix/sasl_passwd
Finally, make postfix
aware of the changes by invoking
sudo postfix reload
Now, everything is set and done and you should be able to do all your daily email stuff with Mutt.