Command-line mail
Today, we will see how to get mails, read them, and answer them, inside a terminal. We will need four utilities for that :
- one to synchronize our local mails with the server :
mbsync
{.verbatim}, - a mail client for viewing them : emacs with
notmuch
{.verbatim} - an agent for sending mails :
msmtp
{.verbatim} a good choice (or sendmail, which should be installed by default on most Linux and BSD distribution )
Install each of this utilities according to the usual way of your distribution. The following tutorial assumes you have two accounts, one of them being Gmail.
Mbsync and Gmail
Here we will configure a gmail account using ~/.msmtprc
{.verbatim}.
Let's break down the configuration file. First, for increased security,
generate an app password using
https://myaccount.google.com/apppasswords and encrypt it with gpg:
#---------------------------------
# Gmail
#---------------------------------
IMAPAccount gmail
# Address to connect to
Host imap.gmail.com
User alexis.praga@gmail.com
PassCmd "gpg2 -q --for-your-eyes-only --no-tty -d ~/.gmailpass.gpg"
# Use SSL
SSLType IMAPS
CertificateFile /usr/local/share/certs/ca-root-nss.crt
IMAPStore gmail-remote
Account gmail
Then define the folders for your mail. ~mail/gmail
{.verbatim} contains
an Inbox and an Archive folder. It must be of the maildir
{.verbatim}
format. Using the fish shell syntax:
for i in cur new tmp
mkdir -p ~/mail/gmail/inbox/$i
mkdir -p ~/mail/gmail/archive/$i
end
Now, return to ~/.mbsyncrc
{.verbatim}:
MaildirStore gmail-local
# Important: we need to be able to move files. The "native" setting results in duplicates and errors+++
AltMap yes
Subfolders Verbatim
# The trailing "/" is important
Path ~/mail/gmail/
Inbox ~/mail/gmail/inbox
Then the hard part: how to synchronize folders with gmail ? I've chosen
to put incoming mail in inbox
{.verbatim} and everything else in
archive
{.verbatim}
# Exclude everything under the internal [Gmail] folder, except the interesting folders
# ALl (and I mean all) mail is in All mail.
# With this setup, we have duplicates in inbox and in all mail (that's ok, should not be much)
# There is no need for sent folder as it is also in all mail+++
# We also need deleted messages because the iPhone do not delete mail but create
# this label instead+++ So we have to get it here, delete et sync with the
# server
Channel gmail-default
Far :gmail-remote:
Near :gmail-local:
# Select some mailboxes to sync
Patterns "INBOX"
Create Near
# Save the synchronization state files in the relevant directory
SyncState *
# Name translation
Channel gmail-archive
Far :gmail-remote:"[Gmail]/All Mail"
Near :gmail-local:archive
Create Near
SyncState *
Channel gmail-trash
Far :gmail-remote:"Deleted Messages"
Near :gmail-local:trash
Create Near
SyncState *
# Get all the channels together into a group.
Group googlemail
Channel gmail-default
Channel gmail-archive
Channel gmail-trash
A second account can be set the same way :
#---------------------------------
# Free
#---------------------------------
IMAPAccount free
# Address to connect to
Host imap.free.fr
User alexis.praga@free.fr
# The file is encrypted with "gpg -e"
PassCmd "gpg2 -q --for-your-eyes-only --no-tty -d ~/.freepass.gpg"
# Use SSL
SSLType IMAPS
CertificateFile /usr/local/share/certs/ca-root-nss.crt
IMAPStore free-remote
Account free
MaildirStore free-local
# Important: we need to be able to move files. The "native" setting results in duplicates and errors+++
AltMap yes
Subfolders Verbatim
# The trailing "/" is important
Path ~/mail/free/
Inbox ~/mail/free/inbox
Channel free-default
Far :free-remote:
Near :free-local:
Patterns "INBOX"
Create Near
SyncState *
# Name translation
Channel free-archive
Far :free-remote:"Archive"
Near :free-local:archive
Create Near
SyncState *
# Name translation
Channel free-sent
Far :free-remote:"Sent"
Near :free-local:sent
Create Near
SyncState *
# Get all the channels together into a group.
Group freemail
Channel free-default
Channel free-archive
Msmtp
To send mail, I use the gmail account for that :
# Set default values for all following accounts.
defaults
auth on
tls on
tls_trust_file /usr/local/share/certs/ca-root-nss.crt
logfile ~/.msmtp.log
# Gmail
account gmail
host smtp.gmail.com
port 587
from horse1@gmail.com
user john.doe
password XXXXXXX
# Set a default account
account default : gmail
Change the permissions :
$ chmod 600 ~/.msmtprc
Then, you can try sending mail with the following command :
$ cat test.mail | msmtp -a default account1@gmail.com
where test.mail is an simple file like this one (there must be an empty line after the subject):
To: account1@gmail.com
From: fake@gmail.com
Subject: Test <br/>
Hello !
Notmuch and emacs
Notmuch is an awesome tool to manage your mail. Basically, it does not
touch your mail but rather operates on tags. So an incoming mail will be
tagged as inbox
{.verbatim} and if you delete it, it will be replaced
by the deleted
{.verbatim} tag. It allows for fast indexing and quick
search of your mail. The only drawback is that it does not move your
mail. So deleting for real must be done manually.
Anway, it's awesome and you should use it in 2021 !
Configuration is pretty straightforward. The first time, run
notmuch
notmuch new
and follow the instructions.
Then I have a script running as a cron job to synchronize my mail and
move mails in the proper folder (inbox
{.verbatim},
archive
{.verbatim}) or delete it :
#!/usr/local/bin/fish
# Combine mbsync and notmuch because mbsync may fail and we still want notmuch to run (as we keep getting quota errors)
# So we must have the two command here
mbsync -a
set args --output=files --format=text0
# Tagsent mails (by default, there are not tagged)
set filter "(folder:gmail/inbox or folder:free/inbox or tag:inbox) and from:\"Alexis Praga\""
notmuch tag +sent +archived -inbox -- $filter
# Move archived mail from inbox to archive folder
set filter tag:archived folder:gmail/inbox
notmuch search $args $filter | xargs -0 -J {} mv {} ~/mail/gmail/archive/cur
set filter tag:archived folder:free/inbox
notmuch search $args $filter | xargs -0 -J {} mv {} ~/mail/free/archive/cur
# Really delete "deleted messages" from gmail
set filter "folder:gmail/trash"
notmuch tag +deleted -- $filter
# delete mails as notmuch cannot do it
set filter "(folder:free/inbox or folder:gmail/inbox or folder:gmail/trash) and tag:deleted"
notmuch search $args $filter | xargs -0 -J {} mv {} ~/mail/trash/cur
# Get new mail
notmuch new
❯ crontab -l
MAILTO=""
*/5 * * * * $HOME/scripts/mbsync_notmuch.sh
Then I can read the email inside emacs with the notmuch
{.verbatim}
plugin.
What about gnus ?
I've tried it two times because the concept was appealing: manage your
mail as a newserver is cool. The major drawback is the lack of
integration for notmuch. You can make it work with mairix
{.verbatim}
but its super slow.