#!/usr/local/bin/perl
# watchmail -- A polling-based "biff" for local or network mail files
#	Brian Katzung  27 March 1993
#
# usage:  watchmail [-a] [-d sound_directory] [-e sound_extension]
#	[-f mail_file] [-i interval] [-l label]
# Where -a prints entries for all messages, not just new ones.

###########################################################################
# Syntax:	&scanMail(printFlag)
# Function:	Scan mail for new messages.
sub scanMail
{
	local($printFlag) = $_[0];
	local($label) = $_[1];
	local($state) = 'body';
	local($newMail) = $false;
	local($unixFrom);
	local($fromColon);
	local($toColon);
	local($dateColon);
	local($subject);
	local(@senders);
	local(@replies);

	# Set the state of all messages to be deleted.
	# The state will be updated for each message that
	# we find is still present.
	foreach $key (keys(%mailMessage))
	{
		$mailMessage{$key} = 'delete';
	}

	open(MAIL) ||
	  return;
	flock(MAIL, 2);
	while (<MAIL>)
	{
		# If this is a UNIX From line, start a new mail message.
		if ($_ =~ /^From /o)
		{
			$state = 'header';
			$unixFrom = $_;
			$fromColon = '';
			$toColon = '';
			$dateColon = '';
			$subject = '';
		}

		elsif ($state eq 'header')
		{
			$fromColon = $_ if $_ =~ /^from:/io;
			$toColon = $_ if $_ =~ /^to:/io;
			$dateColon = $_ if $_ =~ /^date:/io;
			$subject = $_ if $_ =~ /^subject:/io;

			# A blank line after the header starts the body.
			if ($_ eq "\n")
			{
				$state = 'body';
				if ($mailMessage{$unixFrom} eq 'delete')
				{
					# It's one we've seen before
					$mailMessage{$unixFrom} = 'old';
				}
				else
				{
					# It's new
					$mailMessage{$unixFrom} = 'new';
					if ($printFlag == $true)
					{
						$newMail = $true;
						if ($fromColon ne '')
						{
							if ($subject =~
							  /^subject:[ \t]*re:/io)
							{
								@replies =
								  (@replies,
								  $fromColon);
							}
							else
							{
								@senders =
								  (@senders,
								  $fromColon);
							}
						}
						print "$label\n----\n";
						print $unixFrom
						  if $fromColon eq '' ||
						  $dateColon eq '';
						print $fromColon
						  if $fromColon ne '';
						print $toColon
						  if $toColon ne '';
						print $dateColon
						  if $dateColon ne '';
						print $subject
						  if $subject ne '';
						print "\n";
						$state = 'body1';
					}
				}
			}
		}

		# Look for the first two non-empty lines to
		# give a hint at the message content.
		elsif ($state eq 'body1' && $_ ne "\n")
		{
			print;
			$state = 'body2';
		}

		elsif ($state eq 'body2' && $_ ne "\n")
		{
			print;
			$state = 'body3';
		}

		# If there is more, say so.
		elsif ($state eq 'body3' && $_ ne "\n")
		{
			print "...more...\n";
			$state = 'body';
		}
	}
	flock(MAIL, 8);
	close(MAIL);

	syswrite(STDOUT, "\7", 1, 0) if $newMail == $true;
	if ($soundDir ne '' && $soundExt ne '')
	{
		if ($#senders >= 0)
		{
			open(PLAY,
			  "|playfrom -d $soundDir -e $soundExt -f NewMail");
			print(PLAY @senders);
			close(PLAY);
		}
		if ($#replies >= 0)
		{
			open(PLAY,
			  "|playfrom -d $soundDir -e $soundExt -f NewReplies");
			print(PLAY @replies);
			close(PLAY);
		}
	}

	# Forget about messages that got deleted.
	foreach $key (keys(%mailMessage))
	{
		delete $mailMessage{$key} if $mailMessage{$key} eq 'delete';
	}
}

###########################################################################
# Main program

$false = 0;
$true = 1;

# Default mail file
$MAIL = $ENV{'MAIL'} || "/usr/spool/mail/$ENV{'USER'}";

# Default scanning interval (seconds)
$interval = 60;

# Default label for new mail
$label = 'New mail has arrived:';

# Sound stuff
$soundDir = '';
$soundExt = '';

$all = $false;

# Process options
while ($#ARGV >= 0)
{
	$opt = shift;
	$all = $true if $opt eq '-a';
	$soundDir = shift if $opt eq '-d';
	$soundExt = shift if $opt eq '-e';
	$MAIL = shift if $opt eq '-f';
	$interval = shift if $opt eq '-i';
	$label = shift if $opt eq '-l';
}

# Scan mail (probably without printing headers) the first time.
&scanMail($all, $label);
$lastModTime = (stat($MAIL))[9];

while ($true)
{
	sleep($interval);
	$modTime = (stat($MAIL))[9] ||
	  next;
	if ($modTime != $lastModTime)
	{
		$lastModTime = $modTime;
		&scanMail($true, $label);
	}
}
