Reading emails in Mutt


This is a post about configuring your command line environment to let you read e-mails in Mutt. Sorry for small assumption. I write for people familiar with SSH, with some knowledge of key agents, able to edit in Vim and so on. No need to know how to read emails in Emacs or write your own OS kernel.

The benefits of this e-mail client configuration are:
  1. access from everywhere, only terminal and SSH client required
  2. fast browsing, no graphics, loading, rendering and similar stuff
  3. full text search
  4. deep customization - flexibility of Mutt and associated utilities
This is going to be updated from time to time, or I'll add new posts about different components of the configuration, so stay tuned.


First, I need the source of e-mails. If you read directly on the server where exim or postfix work as SMTP servers, you can skip the IMAP stuff.

To make the search of e-mails really fast in full-text search mode, we need to download all e-mails locally and index them. We use IMAP protocol to fetch them. Before we fetch, we make some sorting on server side. This is what I usually do locally with procmail.
There is a helper, that makes it all possible to publish without disclosing my password: GnuPG.


I start GnuPG agent without any magic - from bashrc:
export GPG_TTY=$(tty)
if [[ ! -f $HOME/.gnupg/S.gpg-agent ]]; then
    gpg-agent \
    --use-standard-socket \
    --daemon \
    --verbose \
    --write-env-file $HOME/.gpg-agent-info \
    --log-file $HOME/local/var/log/gpg-agent.log \
    --pinentry-program /usr/bin/pinentry-curses \
    --default-cache-ttl 28800 \
    --max-cache-ttl 288000 > /dev/null
eval $(cat $HOME/.gpg-agent-info)
Remeber, the TTL periods above are pretty long and this makes all the system more convenient, but less secure. Adjust to meet your needs.
Then you can generate new key, if you do not have any. zacharyvoase describes it pretty well. Get your key number, if you do not have it written down:
gpg2 --list-keys

Password storage

Install also tree and other dependencies from README.
make test # it failed in some cases, but app works perfectly
pass init YOUR_GPG_KEY
pass insert gmail_or_whatever
pass gmail_or_whatever
Should ask for a password to GPG keyring, and keep it for 8 hours (if do not ask again in that time).


To sort e-mails locally, Mutt could not help me. I wanted to do it before fetching and synchronize already sorted folders. I took IMAPfilter to do the job. The configuration for IMAPfilter is located in ~/.imapfilter/config.lua. I needed to add the following code to convince IMAPfilter to ask gpg-agent:
myuser = 'your_imap_login'
local cmd = 'FULL_PATH_TO/pass pass_description'
local fd = io.popen(cmd, 'r')
pass_string = fd:read("*a")
mypass = pass_string:sub(0, -2)

IMAP synchronization

This is job for offlineIMAP. Its written in Python and configuration is in the same language. So some knowledge in this matter can help, however there are many tutorials and examples how to make it work some common scenarios.


Full text search

OfflineIMAP offers a hook that can trigger mail indexer. I am using not-much, which relies on Xapian. Its invoked this way:
postsynchook = FULL_PATH_TO/notmuch new
in [Account] section.


 To give Mutt an interface to notmuch, a Python script was created. My version is available on GitHub.


I installed so called "patched" version of Mutt, that one with the side panel.
The main things that are here to adjust:
  • basic stuff like folders, mailboxes, names, addresses
  • some helpful macros (search)
  • scoring
  • highlighting
  • keys binding to make Mutt more similar to Vim
  • HTML rendering


Mutt scoring

Eventually you found that making some e-mails distinct on index can be helpful, or you want to sort by some-kind-of-weight. Scoring to the rescue.

Some rules of thumb:
  • add some credit to all e-mail at the start. It lets you decrease scoring later - based on further rules - down to zero, what is a sign of spam; Some people add 2, some 100 at the beginning
  • with the rule above in mind, add set score_threshold_delete=0
  • choose one of the two scenarios: you add points after parsing all rules and based on achieved scoring you want Mutt to "F"lag a message, or you "F"lag a message manually what adds some points to a message (what brings it higher when sorting by score). In the first case add set score_threshold_flag=15, in the latter score "~F" +5
  • if you have a lot of e-mails just as a reference/documentation, and do not read it manually, you can find it useful: set score_threshold_read=5
  • use George Boole algebra: score "~s Apple | ~s apple" +5
  • increase scoring for e-mails you want (score "~f wife@domain.com" +666) and punish mails you hate (score "~s training | ~s Certificate" -100). After that (and score_threshold_delete set) all you need to remove spam is to press '$'.
  • make obsolete obsolete: score "~d >1m" =2
Paint e-mails based on scoring: 
color index  brightcyan      default  "~n 5-15"
color index  brightmagenta   default  "~n 16-100"

You can always score manually executing the commands above in Mutt after a colon:
:score "~f myboss@mycompany.com" +10
and then sort by pressing 'o'.

Useful links:


Want to connect from Linux to SQL Server?
1. add Linux to domain with CentrifyDC
2. install unixodbc
3. install Microsoft ODBC driver manager
4. optionally pyodbc
5. install krb5-user
6. addspn in domain
7. set static port on SQL Server
8. $ kinit
9. $ sqlcmd -Sdatabase-server,port

More details soon, or e-mail me.


Wrapper on check_nt!UPTIME


MINWARN=$2 # in hours
MINCRIT=$3 # in hours

SECONDS=`/usr/local/nagios/libexec/check_nt -H $HOSTADDRESS -p 12489 -s $ECRET$ -v COUNTER -l "\\System\\System Up Time"`
#### IS:           6817
#### SHOULD BE:    WARNING: uptime: 1:53 < warning|'uptime'=6817000;172800000;3600000;

HOURS=$(( $SECONDS / 60 / 60 ))
SECONDSINHOURS=$(( $HOURS * 60 * 60 ))

if [[ $HOURS -lt $MINCRIT ]]; then
    echo "CRITICAL: System started ${FORMEDUPTIME}h ago."
    exit 2

if [[ $HOURS -lt $MINWARN ]]; then
    echo "WARNING: System started ${FORMEDUPTIME}h ago."
    exit 1

echo "OK. Uptime $FORMEDUPTIME.|'uptime'=${SECONDS}000;7200000;3600000;"


Multisite Drupal

postgres=# CREATE DATABASE example_eu OWNER drupal6 ENCODING 'UTF8';
cp ./drupal/sites/default ./drupal/sites/www.example.eu
vim ./drupal/sites/www.example.eu/dbconfig.php
vim /etc/apache2/sites-available/example.eu
a2ensite example.eu
firefox https://www.example.eu/


Cutting mailq

Removing selected mail messages from postfix's mail queue based on sender on recipients:

mailq | tail +2 | grep -v ’^ *(’ | awk ´BEGIN { RS = "" }
# $7=sender, $8=recipient1, $9=recipient2
{ if ($8 == "user@example.com" && $9 == "")
print $1 }
´ | tr -d ’*!’ | postsuper -d -


Print folded book in Evice

To print folded "book" in Evince its necessary to define what pages and in what order to print on both sides.

If the number of pages is not divided by 4, increase it (e.g. 62 increase to 64).

Then generate two series of page numbers:

#!/usr/bin/perl -w

use strict;

my $pages=64;
my $modifier=0;

for (my $i=1; $i<=$pages/4; $i++) {
  my $left_page  = $pages-$modifier;
  my $right_page = $modifier + 1;
  print "$left_page,";
  print "$right_page,";

print "\n";


for (my $i=1; $i<=$pages/4; $i++) {
  my $right_page = $modifier + 1;
  my $left_page  = $pages-$modifier;
  print "$right_page,";
  print "$left_page,";

print "\n";
exit 0;

Paste it in the print form (without the last comma).