[comp.mail.sendmail] Mail filters

kenc@suntan.viewlogic.com (Kenstir) (03/16/91)

I'd like to set up a filter on my mail, and I'd like it
to happen between the time sendmail gets its paws on the
text and the time it puts the file in /usr/spool/mail.

I *have* been successful in doing this via two methods,
neither of which is completely satisfactory:

1.  Set up the YP alias:
	kenc:"|/u/kenc/mail-filter kenc@host"

    Set up /u/kenc/mail-filter:
	awk -f foo.awk | /usr/lib/sendmail -n $1

PROBLEMS:  The mail-filter gets run on the machine the mail
from which it is sent and also the machine *to* which it is sent.
This also creates extra sysadmin work setting up the aliases.

2.  Set up a .forward:
	"|/u/kenc/mail-filter2 kenc"

    And then in /u/kenc/mail-filter2:
	awk -f foo.awk >> /usr/spool/mail/kenc

PROBLEMS:  I'm not doing any file locking and I'm sure eventually
I would run into race conditions.



I would greatly appreciate any advice!
-- 
Kenneth H. Cox
Viewlogic Systems, Inc.
kenc@viewlogic.com
..!harvard!cg-atla!viewlog!kenc

kaul@icarus.eng.ohio-state.edu (Rich Kaul) (03/17/91)

In article <1991Mar15.195109.12234@viewlogic.com> kenc@suntan.viewlogic.com (Kenstir) writes:
   I'd like to set up a filter on my mail, and I'd like it
   to happen between the time sendmail gets its paws on the
   text and the time it puts the file in /usr/spool/mail.
   [...]
   2.  Set up a .forward:
	   "|/u/kenc/mail-filter2 kenc"

       And then in /u/kenc/mail-filter2:
	   awk -f foo.awk >> /usr/spool/mail/kenc

   PROBLEMS:  I'm not doing any file locking and I'm sure eventually
   I would run into race conditions.

Use a better tool.  I do a similar thing to filter my mail, but I use
perl to do the filtering and file locking.  I go further and deliver
the mail to different directories for use by GNUS to mix reading of
the tons of system mail, etc. that I get and news.  Only critical
stuff from a group of people gets through to my mailbox.  I use a
.forward that looks like:

"|/usr/1/kaul/bin/ppmd /usr/1/kaul kaul >>/usr/1/kaul/.maillog 2>&1"

and the following perl script.

-rich

#!/usr/bin/perl

# An attempt at a perl mail delivery agent.  The basic premise is
# that mail from certain people demands attention immediately, so it
# gets put in the normal system mailbox.  Mail from mailing lists is
# to be placed in various subdirectories in a format that GNUS can
# handle for reading.  Mail from the system, random users, etc, gets
# placed in a third directory ($root).  This is, of course, easily
# customizable.  Delivering into a directory requires a .last file to
# keep track of article numbers.
#
# Pardon the perl style; I'm just learning but this works!  Comments
# welcome.
#
# Author:	Rich Kaul (kaul@icarus.eng.ohio-state.edu)
# Date:		11/13/90
# The credit where credit is due department:  the core of the message
# parsing is based on Larry Wall's mailagent script, although the rest
# of this ugly mess (which nobody else would want to claim) is mine.

($HOME, $USER) = @ARGV;

$root = "$HOME/Memos/personal";		# root of personal news tree.
$dest = $root;
$box = "/usr/spool/mail/$USER";		# default mail box.
$GIVE_UP_BOX = "$HOME/mail_dump";	# emergency dumping box.

$LOCK_SH = 1;				# Values for flock() calls.
$LOCK_EX = 2;
$LOCK_NB = 4;
$LOCK_UN = 8;

umask(077);				# Get a little privacy.

# Now run and get the headers.  We have to parse the headers before we
# can figure out delivery.
while (<stdin>) {
    # Do this on the From_ line only.
    if (1 .. 1) {
	if (/^From\s+(\S+)\s+[^\n]*(\w{3}\s+\d+\s+\d+:\d+)/) { $from = $1; }
	$from =~ s/@.*//;		# remove trailing @machine
	$from =~ s/%.*//;		# remove trailing %machine
	$from = $1 if $from =~ /.*!([^\n]+)/; # remove leading ! paths
    }

    # This section operates on the header of the message.  We create
    # an array of header keys to work on later.
    if (1 .. /^\s*$/) {
	s/^From: ([^<]*)\s+<(.*)>$/From: $2 ($1)/;	# rewrite ugly header
	$header .= $_;
	chop;
	if (/^\s*$/) {
	    foreach $key (keys(header)) {
		eval "\$H$key = \$header{'$key'}";
	    }
	}
	else {
	    if (s/^([-\w]+):\s*//) {
		($headline = $1) =~ y/A-Z-/a-z_/;
		$header{$headline} .= "\n" if $header{$headline} ne '';
	    }
	    else {
		s/^\s+/ /;
	    }
	    $header{$headline} .= $_;
	}
    }
    # And here we make the body of the message.
    else {
	$body .= $_;
	}
}


# Now we list the people who can crash into the mailbox.
if($from =~ /karl_kl/ || $from =~ /alden/ || $from =~ /monty/) {$dest = $box;}
elsif($from=~/pnelson/ || $from=~/wilson/i || $from =~ /gratz/i) {$dest=$box;}
elsif($from =~ /bibyk/ || $from =~ /adkins/ || $from =~ /zaka/) {$dest = $box;}
elsif($from =~ /kaul/ || $from =~ /aspark/ || $from =~ /george/) {$dest = $box;}

# Ok, now figure out where to deliver the message.  The first case is
# mail from mailing lists.
if($from =~ /firearms/) {$dest = "$root/firearms";}
elsif($Hto =~ /firearms/i || $Hcc =~ /firearms/i ) {$dest = "$root/firearms";}
elsif($from =~ /freemacs/) {$dest = "$root/freemacs";}
elsif ($Hto =~ /gif/ || $Hcc =~ /gif/) {$dest = "$root/gif";}
elsif($Hto =~ /gnuplot/ || $Hcc =~ /gnuplot/) {$dest = "$root/gnuplot";}
elsif ($Hto =~ /freemacs/ || $Hcc =~ /freemacs/) {$dest = "$root/freemacs";}
elsif ($Hto =~ /interviews/i || $Hcc =~ /interviews/i ) { 
    $dest = "$root/interviews";}
elsif ($Hto =~ /vem/i || $Hto =~ /oct/i || $Hcc =~ /vem/i || $Hcc =~ /oct/i) {
    $dest = "$root/vem";}
elsif($Hto =~ /xviewbug-trackers/ || $Hcc =~ /xviewbug-trackers/) {
    $dest = "$root/xview";}

# Here we dump system messages.  It grows too fast and should be split
# up soon...
if($from =~ /root/ || $from =~ /news/) { $dest = "$root/system";}
elsif($Hto =~ /site/ || $Hcc =~ /site/) { $dest = "$root/system";}

# Now we do the delivery.  There are two cases.  If we are delivering
# to a directory find and update the .last file there.
if ( -d $dest ) {
    # It's going to be a news article, so change the From_ line
    $header =~ s/^From /Unix-From: /;
    $all = $header . $body;

    $count_file = "$dest/\.last";
    open(COUNTER,"+<$count_file")|| do gag("Can't open $dest/.last ($< $>): $!");
    while (<COUNTER>) {
	chop;
	$count = $_;
	$count++;
    }
    do flocker(COUNTER,$LOCK_EX);	# Lock the file, just in case.
    seek(COUNTER,0,0);
    print COUNTER $count,"\n";
    do flocker(COUNTER,$LOCK_UN);

    # We now have the article number we need.
    open(ART,">>$dest/$count") || do gag("Can't open $dest/$count ($< $>): $!");
    print ART $all,"\n";
} else {
    # Here we're delivering to a file, which is easier.  Build and deliver.
    $all = $header . $body;
    open(BOX, ">>$dest") || do gag ("Can't open $dest ($< $>): $!");

    do flocker(BOX,$LOCK_EX);
    print BOX $all,"\n\n";
    do flocker(BOX,$LOCK_UN);
}

# File locking subroutine.
sub flocker {
    local ($file, $mode) = @_;
    eval 'flock($file,$mode);';
    seek($file, 0, 2);		# in case it was appended while we were waiting
}

# For some reason mail couldn't get delivered.  Dump the message in the
# emergency mailbox and exit.
sub gag {
    open(ERR_BOX, ">>$GIVE_UP_BOX") || die "`date`: I'm really hosed.  I can't even open the emergency box $GIVE_UP_BOX\n";

    print @_;
    do flocker(ERR_BOX,$LOCK_EX);
    print ERR_BOX $all,"\n\n";
    do flocker(ERR_BOX,$LOCK_UN);
    exit 1;
}
    
-- 
Rich Kaul                         | "They that can give up essential liberty
kaul@icarus.eng.ohio-state.edu    | to obtain a little temporary safety
or ...!osu-cis!kaul		  | deserve neither liberty nor safety."

jch@hollie.rdg.dec.com (John Haxby) (03/18/91)

In article <1991Mar15.195109.12234@viewlogic.com>, kenc@suntan.viewlogic.com (Kenstir) writes:
|> 
|> I'd like to set up a filter on my mail, and I'd like it
|> to happen between the time sendmail gets its paws on the
|> text and the time it puts the file in /usr/spool/mail.

The local mailer definition contains the name of
the program to do local delivery (often /bin/mail) --
just change it.  The program will have to do some
work if you want to only filter for some people
and not others.
-- 
John Haxby, Definitively Wrong.
Digital				<jch@wessex.rdg.dec.com>
Reading, England		<...!ukc!wessex!jch>

berg@marvin.e17.physik.tu-muenchen.de (Stephen R. van den Berg) (03/27/91)

Kenstir writes:

>I'd like to set up a filter on my mail, and I'd like it
>to happen between the time sendmail gets its paws on the
>text and the time it puts the file in /usr/spool/mail.

>PROBLEMS:  I'm not doing any file locking and I'm sure eventually
>I would run into race conditions.

>Kenneth H. Cox
>Viewlogic Systems, Inc.
>kenc@viewlogic.com
>..!harvard!cg-atla!viewlog!kenc

I suggest you take a look at the "procmail" package (see comp.sources.misc).
v1.30 is the most recent.  It will take care of all the locking for you.

If you need more information about "procmail", drop me a note (I wrote it :-)

P.S.  I tried to send the mail directly, but your mailer went berzerk.
      If you are interested in the error messages, I still have them.
      I can send them on request, but, be sure to remove your mail filter
      first :-)
--
Sincerely,                 berg@marvin.e17.physik.tu-muenchen.de
           Stephen R. van den Berg.
"I code it in 5 min, optimize it in 90 min, because it's so well optimized:
it runs in only 5 min.  Actually, most of the time I optimize programs."