Unix mail tips

Some hints for using mutt, spamassassin, razor, procmail, formail:

  • To set up razor correctly, run razor-admin -create, razor-admin -discover, and razor-admin -register in that order.  Not doing this will result in a 202 error.
  • When delivering to a maildir or filtering mail with formail or some other stdin/stdout filter that does not otherwise create a file, the procmail recipe does not require a lock colon as in (:0:), only (:0).
  • Automatic expiration of list mail can be done by adding an Expires: header with formail.


How to implement tagged spamassassin reporting in mutt

Reporting each mail as spam individually in mutt is too time consuming because spamassassin must usually contact several servers, some of which may time out, and there may be 20 or more spams in your inbox if you have not paid attention to it for a while.

It is tempting to use mutt’s message tagging feature to tag several messages and send them all to spamassassin at once.

set pipe_split = yes
macro index S "<enter-command>unset wait_key<enter><tag-prefix-cond>
<pipe-message>spamassassin --report<enter><tag-prefix-cond>
<delete-message><enter-command>set wait_key<enter>"

(tag-prefix-cond is a relatively new addition to mutt that aborts execution of the rest of the macro if no messages are tagged.)

The problem with this approach is that the only thing that both mutt produces and that spamassassin can accept is a single message.  Setting pipe_split = yes in muttrc has the intended effect, that spamassassin is invoked once for each message.  But spamassassin is quite slow in this mode because it must establish a new session with each of the spam reporting servers for each piece of mail.  It is possible to background spamassassin, but without some form of external serialization, many spamassassins will be started at once, overloading the VM and competing for the write lock on the user’s Bayesian database.

Setting pipe_split = no (the default) causes mutt to concatenate all of the messages and pipe them to the program once.  But spamassassin can’t accept this input since it is not in a format that spamassassin recognizes.  Since spamassassin can accept an mbox as input, it is tempting to use something like

pipe_sep = "From abuse@localhost\n"

to emulate a mbox output.  But the first message is ignored by spamassassin, because no separator is placed at the top of the output.
This last problem can be addressed with a script like the following (mutt-sa-report).  It adds a From_ line at the beginning of mutt’s output.  After processing with this script, mutt’s piped output is now a mbox that spamassassin can recognize:

#!/usr/bin/perl --
use warnings;
use strict;
use File::Temp qw/tempfile/;
use Fcntl qw/SEEK_SET/; 
my $fh = tempfile;
my $back = "From abuse@localhost\n";
while (<STDIN>) { print $fh $back; $back = $_; }
seek $fh,0,SEEK_SET;
open my $sa, "| spamassassin --mbox --report" or die "Couldn't exec spamassassin ";
while (<$fh>) { print $sa $_; }
close $sa;
close $fh;
exit 0;

Now, this script can be called from mutt:

pipe_split = no
macro index S "<enter-command>unset wait_key<enter><tag-prefix-cond>
<pipe-message>mutt-sa-report >>/home/nemesis/Mail/logfile 2>&1<enter>
<tag-prefix-cond><delete-message><enter-command>set wait_key<enter>"

Look in logfile for the output of spamassassin. You should see “15 messages examined” or something to that effect. If so, it is working and you can remove everything but mutt-sa-report in between the pipe-message and enter tags.

Leave a Reply