#!/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 () { # 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); } }