[comp.mail.mh] MH head-splitting and other neat Perl goodies

ziegast@ENG.UMD.EDU (Eric Ziegast) (02/27/91)

After reading a few sections of the Perl Nutshell book, I came across
a neat little s/// and split routine that split all of the headers
of a mail message into an associative array.  Unfortunately, it did't
do a good job on the last line of the header.  I've made my own header
splitter to strip apart a message piped from slocal (man mhook).

What it does is read a message from standard input and split it into
two strings: $header and $body.  It then cleans it up and splits the
header enties into associative keys (header names) and values (header
info).

#!/usr/bin/perl
#
# I claim no copyright to anything below.  Read Perl's copyleft though.
# If it doesn't work, don't sue me, 'cause don't guarantee anything.
#

######## Extract header and body info
#
#  While $body is undefined (by default), concatenate lines into $header.
#  When first blank line ("\n") is read, define $body so that all lines
#  will be concatenated to $body
#

while (<STDIN>) {
	if ($_ eq "\n") {$body = ""; }
	elsif (! defined $body) {$header .= $_; }
	else { $body .= $_; }
}

######## Make all headers "one-liners" and strip unwanted Received:
# 
# This part first sets a special variable ($*) so that pattern matching
# will be done accross multiple lines.  The first search turns "\n" into
# " " so that all headers are one line.  The second search (optional),
# removes all "Received:" headers. (I don't want them.)  The last line
# resets pattern matching to be single-line only (the default).
#

$* = 1;
$header =~ s/\n\s+/ /g;
$header =~ s/Received:.*\n//g;
undef $*;

######## Load headers into an associative array
#
# For every header (note, they're all one-liners),
#
#	set $head{KEY} = VALUE
#
# where KEY is the name of the header and VALUE is the text after the ":".
#

foreach $ln (split("\n", $header)) {
	if ($ln =~ /^([-\w]+):\s+(.*)$/) { $head{$1} = $2; } }

################# Rest of program #################


Now, all you need to do when you need to process any header info is
use the associative array entry.  For example, if I wanted to capitalize
the Subject: info, I'd use:

	$head{'Subject'} =~ tr/a-z/A-Z/;

or maybe:

	$head{'From'} =~ tr/A-Z/a-z/;
	if ($head{'From'} =~ /jromine@(\w*.?ics.uci.edu)/) {
		print "You got mail from John Romine at $1\n";
		if ($head{'Subject'} =~ /[Mm][Hh].*6.7.2/) {
			print "NEW MH PATCH!!\n";
		}
	}


A good place to use the $header and $body info is to reconstruct the
message for storage or inclusion in another message.  As an example,
I provide the subroutine &adios.  If I come upon some error while pro-
cessing a message, I just "&adios(MESSAGE)" and it spits back an error
message to the sender (from Return-Path: if provided) and an error
logging e-Mail address.

Note:
	$ADDRESS_OWNER = "e-mail address for error logging";
	$ADDRES_REQUEST = "e-mail address of human administrator";
	$DRAFTS = "draft folder to create draft messages";

##########################################################################
# adios($STRING);
#	Adios - Fatal error handler
#	Returns message to sender (if possible) and notifies GM.
##########################################################################

sub adios {
	local($ERRSTR) = @_;

	# Get new message number in a drafts folder to create message
	local($emsg) = `mhpath +$DRAFTS new`;
	open(EMSG,"> $emsg")  || exit 1;

	# Use Return-Path if available.
	if ($rpath eq "") { print EMSG "To: $ADDRESS_OWNER\n"; }
	else {	print EMSG "To: $rpath\ncc: $ADDRESS_OWNER\n"; }
	print EMSG <<EMEOF;
Subject: Error processing mail message
--------
The address you sent your message to is processed automatically by a
mail filter.  Obviously, something went wrong.  Please mail

	$ADDRESS_REQUEST

with questions and/or problems or try again.

Cause of error:
$ERRSTR

------- Included message

$header
$body

------- End of included message
EMEOF
	close EMSG;
	exec "send $emsg";
	exit 0;
}

######## END OF SUBROUTINE #########



But wait! There's more!
Did you ever want to split apart your .mh_profile entries?  With the
Jinsu(tm) .mh_profile knife attachment you can slice and dice away!

Why not use the same techniques as headers?  The only exception here,
is that there are no "Received:" lines to worry about, and comments (#)
should be stripped with:

	$header =~ s/#.*\n//;

Once you have a %profile associative array, you can attept something
like:

   if (! defined $profile('Draft-Folder')) {
      $profile('Draft-Folder') = "+drafts";
      printf "No draft folder specified.  Using +drafts.\n";
   }
   $newmsg = `mhpath $profile('Draft-Folder') new`;
   open(MESSAGE,"> $newmsg") || die "Draft message \"$newmsg\": $!\n";
   #
   #process rest of draft
   #
   close(MESSAGE);
   exec "send $newmsg" || die "EEEK! \"send $newmsg\": $!\n";



If using a combination of shell and awk scripts were a pain in the past,
learning Perl is the best thing you can do for yourself.  I used to despise
awk because I couldn't understand it.  I learned awk and learned to like
its pattern matching abilities.  I used to doubt how useful Perl is for
me until I started writing 40 lines of C code to do what Perl does in one.
I still hate Lisp.  But you never know... ;-)
________________________________________________________________________
Eric W. Ziegast, University of Merryland, Engineering Computing Services
ziegast@eng.umd.edu - Eric@[301.405.3689]