news.utdallas.edu!wupost!darwin.sura.net!sgiblab!munnari.oz.au!metro!usage!news Mon Feb 22 07:54:35 CST 1993 Article: 1071 of comp.lang.perl Xref: feenix.metronet.com comp.lang.perl:1071 Newsgroups: comp.lang.perl Path: feenix.metronet.com!news.utdallas.edu!wupost!darwin.sura.net!sgiblab!munnari.oz.au!metro!usage!news From: cameron@cs.unsw.oz.au #Subject: Re: Mail Filter Message-ID: To: mbl@msen.com (Matthew B Landry) Followup-To: comp.lang.perl Sender: news@usage.csd.unsw.OZ.AU Nntp-Posting-Host: fuligin.spectrum.cs.unsw.oz.au Reply-To: cameron@cs.unsw.oz.au Organization: CS&E Computing Facility, Uni Of NSW, Oz References: <1lrvcgINN26n@nigel.msen.com> Errors-To: cameron@cs.unsw.oz.au Date: Thu, 18 Feb 1993 02:51:09 GMT Return-Receipt-To: cameron@cs.unsw.oz.au Lines: 1118 mbl@msen.com (Matthew B Landry) writes: | I'm looking for a program to sort email. Specifically, I want | something that can take all the mail from two specific addresses (digests, | actually), and save it into two corresponding files, leaving the rest of | my mail in the system box. | I could have the program run regularly from cron to scan the | mailbox, so a .forward shouldn't be necessary. | I'm posting this here because I've heard that perl would be best | for this sort of thing. If there's anyone who has a program that can be | adapted for this (or who can tell me how to build my own), I'd greatly | appreciate it. I use the filemail command appended below, which allows recurse rule based filing. Filemail expects a single item on stdin, so I run the mail file through splitmail (appended) to do the job. Thus: mv $MAIL /tmp/split$$ && \ splitmail -m maildir /tmp/split$$ && \ rm /tmp/split$$ I haven't used splitmail for a long time, so test it before use. I use filemail all the time; I append the .file ruleset in my inbox so you can see how it's used. [Types...] There are some library routines too, appended, take what you need. - Cameron Simpson cameron@cse.unsw.edu.au, DoD#743 -- I don't waste my money; I invest it in ventures with high negative returns. #/bin/sh sed 's/^X//' > filemail <<'EOF-/home/cs/spectrum/fuligin/1/cameron/bin/filemail' X#!/usr/local/bin/perl X# X# Usage: filemail [-a announce] [mailbox] = $[ && $ARGV[$[] =~ /^-/) X { $_=shift @ARGV; X if ($_ eq '-a') X { $ENV{'CONSOLE'}=$CONSOLE=shift @ARGV; X } X else X { print STDERR "$cmd: bad option \"$_\"\n"; X $badopts=1; X } X } X Xif (defined($inbox=shift)) X { $ENV{'MAILBOX'}=$MAILBOX=$inbox; X } X Xif ($#ARGV >= $[) X { print STDERR "$cmd: extra arguments: @ARGV\n"; X $badopts=1; X } X Xdie $usage if $badopts; X X# snarf stdin first up X@INPUT=; Xclose(STDIN); X Xdie "$cmd: no input!\n" if $#INPUT < 0; X Xopen(STDOUT,">>$CONSOLE") || die "can't open $CONSOLE: $!\n"; Xopen(STDERR,">&STDOUT") || die "can't dup STDOUT to STDERR\n"; X Xundef $from_, $from, $From, $to, $subject, %hdrs; X$INPUT[0] =~ s/^From\s/From_: /; X X@hdrs=(); undef %hdrs; X@INPUT=&add822lines(@INPUT); Xwhile ($#INPUT >= $[ && $INPUT[$#INPUT] eq "\n") X { pop(@INPUT); X } X X$subject=$hdrs{'subject'}; X$F_HAS_SUBJECT=length($subject); X$from=''; X$from_=''; X X$From=$hdrs{'from'}; X@From=&getaddrs($From); Xwhile ($#From >= $[) X { local($pre,$addr,$post); X X $pre=shift @From; X $addr=shift @From; X $post=shift @From; X X if ($addr =~ /^<([^>\s]*)>/) X { $addr=$1; X } X X next if $addr eq $USER || $addr eq $EMAIL; X X $from=$addr; X last; X } X X$to=$hdrs{'to'}; X$cc=$hdrs{'cc'}; X$newsgroups=$hdrs{'newsgroups'}; X Xif (length($hdrs{'from-'})) X { $from_=$hdrs{'from-'}; X } X Xif (!length($subject)) X { $subject='NO SUBJECT SUPPLIED BY SENDER'; X unshift(@hdrs,"Subject: $subject"); X $hdrs{'subject'}=$subject; X } X X$legend=(length($from) X ? "From: $From" X : (length($from_) && ($from_ ne $USER && $from_ ne $EMAIL X || !length($to)) X ? "From_ $from_" X : (length($to) X ? "To: $to" X : (length($newsgroups) X ? "Newsgroups: $newsgroups" X : "No source or destination." X ) X ) X ) X ); X Xlength($subject) && ($legend.="; $subject"); Xlength($from) || ($from=$from_); X X($reply_to=$hdrs{'reply-to'}) =~ s/^\s+//; $reply_to =~ s/\s+$//; Xlength($reply_to) || ($reply_to=$from); X X($errors_to=$hdrs{'errors-to'}) =~ s/^\s+//; $errors_to =~ s/\s+$//; Xlength($errors_to) || ($errors_to=$reply_to); X X($sec,$min,$hr,$mday,$mon,$yr,$wday,@etc)=gmtime(time); X Xundef %FILED; # places we have already filed this item X@MAILBOXES=($MAILBOX); # places to try filing this item X@INNAMES=(''); # prefix for name X@FILES=(); # originals, used for links X$xit=0; X XMAILBOX: X while (defined($MAILBOX=shift @MAILBOXES)) X { { local($filed,$oMAILBOX); X X $INNAME=shift @INNAMES; X X # loop until the item is filed or we lack a .file file X X # Code to file incoming mail. X # When called we have X # @INPUT All the lines in the mail item. X # We are guarrenteed a From_ line as $INPUT[0]. X # %hdrs Bodies of header lines keyed by downcased names. X # @hdrs Complete headers. X # $legend "From: who; subject" X # $MAILBOX The normal inbox directory. X # $HOME Home directory. X # $USER User name. X # $subject, $s Subject field. X # $from, $f From: or From_. X # $reply_to Reply-To: or $from. X # $errors_to Errors-To: or $reply_to. X # $to To: X # $cc CC: X # X # If this message was successfully filed then $filed will X # be true at the end. X # X # It is possible to arrange that filemail files the mail X # in another place by changing $MAILBOX. If you wish a specific X # name for the filed item, set $INNAME; otherwise the X # pick-a-number method used for ordinary mail will be used. X # X X DOTFILE: X while (-f "$MAILBOX/.file") # implies -d "$MAILBOX/." X { $oMAILBOX=$MAILBOX; X $filed=0; X { local($s,$f,$to,$cc) X =($hdrs{'subject'},$hdrs{'from'},$hdrs{'to'},$hdrs{'cc'}); X local($_)=$s; X X do "$MAILBOX/.file"; X warn $@ if $@; X } X X next MAILBOX if $filed; X last DOTFILE if $oMAILBOX eq $MAILBOX; X } X } X X # it should be a file or a directory X if (! -f $MAILBOX && !&mkdir($MAILBOX)) X { &legend("can't make directory $MAILBOX"); X $xit=1; X next MAILBOX; X } X X if (-d $MAILBOX) X { local($filed); X X $filed=&fileitem($MAILBOX,$INNAME); X X if (!defined($filed)) X { &legend("can't save in $MAILBOX/$INNAME\n"); X $xit=1; X } X } X else X { if (!open(MAILBOX,">>$MAILBOX")) X { &legend("can't append to $MAILBOX: $!"); X if (!open(MAILBOX,">>$MAILBOX.$$")) X { &legend("can't append to $MAILBOX.$$: $!"); X } X } X X &writeitem(MAILBOX); X close(MAILBOX); X &legend(&shorten($MAILBOX)." $legend"); X } X } X Xexit $xit; X###################### X X# On failure $! is useful. Xsub mklink # (fname) -> ok X { local($fname)=@_; X X for (@FILES) X { if (link($_,$fname)) X { &legend(&shorten($_)." <-> ".&shorten($fname)); X return 1; X } X X last if ($! != &EXDEV); X } X X local($tmp); X X # stash @INPUT X $tmp=&dirname($fname)."/.$cmd-$$"; X X if (!open(TMP,">> $tmp\0")) X { &legend("can't append to $tmp: $!"); X return 0; X } X X &writeitem(TMP); X close(TMP); X X if (link($tmp,$fname)) X { unlink($tmp) || &legend("unlink($tmp): $!"); X push(@FILES,$fname); X &legend(&shorten($fname).": $legend"); X return 1; X } X X return 0; X } X Xsub writeitem # ($FILE) X { local($FILE)=@_; X X for (@hdrs) X { print $FILE $_, "\n"; X } X X print $FILE "\n"; X for (@INPUT) X { print $FILE $_; X } X } X X# file an item in directory $MAILBOX, with prefix $INNAME. X# Xsub fileitem # ($MAILBOX,$INNAME) -> basename-of-filed-item X { local($MAILBOX,$INNAME)=@_; X local($filed); X X # attempt link to unadorned INNAME X if (length($INNAME) && &mklink("$MAILBOX/$INNAME")) X { return "$MAILBOX/$INNAME"; X } X X # not linked to simple name, try INNAME_n X local($n)=1; X X # walk directory, picking $n > any already present X if (!opendir(MAILBOX,$MAILBOX)) X { &legend("warning: can't opendir($MAILBOX): $!\n"); X return undef; X } X X local(@dir)=readdir(MAILBOX); X closedir(MAILBOX); X X local($ptn)=$INNAME; X X $ptn =~ s,\W,\\$&,g; X eval X ' for (grep(/^'.$ptn.'/,@dir)) X { s/^'.$ptn.'//; X /^\d+$/ || next; X X if ($& >= $n) X { $n=$&+1; X } X } X '; X X local($ok)=1; X X while (!&mklink("$MAILBOX/$INNAME$n")) X { $ok=0; X last if $! != &EEXIST; X $ok=1; X $n++; X } X X if ($ok) X { return "$MAILBOX/$INNAME$n"; X } X X return undef; X } X X# forward a mail item and say so Xsub forw # (subj,who,@WHAT) X { local($subj)=shift; X local($who)=shift; X X $filed=&forward($who,@_); X $filed && &legend("==> $who: $subj"); X } X Xsub forward X { local($to,@INPUT)=@_; X local($shifted,@fields,@bodies,%ndx); X local($[)=1; X local($_); X local($i); X X die "$cmd: &forward($to): no input!\n" if $#INPUT < 1; X X $shifted=shift(@INPUT) if $INPUT[1] =~ '^From '; X X die "$cmd: &forward($to): short input!\n" if $#INPUT < 1; X die "$cmd: &forward($to): malformed input\n" if $INPUT[1] =~ /^\s/; X X while (defined($_=shift(@INPUT))) X { if (/^[ \t]/) X { @bodies[$#bodies].=$_; X } X elsif (/^(\S*):[ \t]*/) X { local($hdr)=$1; X X push(@fields,$hdr); X push(@bodies,$'); X $hdr =~ tr/A-Z/a-z/; X $ndx{$hdr}=$#fields; X } X else X # not a header line X { last; X } X } X X if (defined($_)) X { unshift(@INPUT,$_); X } X X # tidy up Sender: line X if (($i=$ndx{'sender'}) >= 1) X { $fields[$i]="Original-".$fields[$i]; X delete $ndx{'sender'}; X $ndx{'original-sender'}=$i; X } X X push(@fields,'Sender'); X push(@bodies,"$USER\n"); X $ndx{'sender'}=$#fields; X X # create Reply-To: if missing X if (($i=$ndx{'reply-to'}) < 1 X && ($j=$ndx{'from'}) >= 1) X { push(@fields,'Reply-To'); X push(@bodies,$bodies[$j]); X $ndx{'reply-to'}=$#fields; X } X X while (defined($_=pop(@fields))) X { unshift(@INPUT,$_.': '.pop(@bodies)); X } X X unshift(@INPUT,$shifted) if defined($shifted); X X &sendmail($to,@INPUT); X } X Xsub sendmail # ($to,@INPUT) -> success X { local($to)=shift; X X if (open(SENDMAIL,"|sendmail -oi $to")) X { if ($_[0] =~ /^From /) X { shift; X } X X for (@_) X { print SENDMAIL $_; X } X X close(SENDMAIL); X return 1; X } X else X { &legend("can't pipe to sendmail: $!"); X } X X 0; X } X Xsub fileas # (inbox,inname) -> void X { local($inbox,$inname)=@_; X X push(@MAILBOXES,"$inbox"); X push(@INNAMES,"$inname"); X } X Xsub shorten # (pathname) -> indicator X { local($_)=@_; X X if (length($_) > length($HOME) X && substr($_,$[,length($HOME)) eq $HOME) X { $_=substr($_,$[+length($HOME)); X s,^/+,,; X } X X s,^private/+,,; X s,^etc/mail/+,+,; X X $_; X } X Xsub legend # (message) -> void X { local($_)=@_; X X if ($didlegend) X { print " " x $didlegend; X } X else X { local($str)=&datestr(time,1).": "; X print $str; X $didlegend=length($str); X } X X s/\n+$//; X printf("%.160s\n",$_); X } EOF-/home/cs/spectrum/fuligin/1/cameron/bin/filemail sed 's/^X//' > splitmail <<'EOF-/home/cs/spectrum/fuligin/1/cameron/bin/splitmail' X#!/usr/local/bin/perl X# X# Split up an ordinary mailbox (From_ separated). X# Uses filemail to deposit the mail, so the .file refiler works. X# - Cameron Simpson, February 1992 X# X X($cmd=$0) =~ s,.*/,,; X$usage="Usage: $cmd [-m mailbox] [mailfiles...] X -m mailbox Specify directory into which to place mail. X"; X X# option defaults Xif (!defined($ENV{'MAILBOX'})) X { $mailbox='.'; X } Xelse X{ $mailbox=$ENV{'MAILBOX'}; X} X X# option parsing Xif ($#ARGV > 0 && $ARGV[0] eq '-m') X { shift; X $mailbox=shift; X } X X# export to filemail X$ENV{'MAILBOX'}=$mailbox; X X$xit=0; Xif ($#ARGV < 0) X { &splitmail('STDIN','stdin'); X } Xelse X{ for (@ARGV) X { if (!open(IN,"< $_\0")) X { print STDERR "$cmd: can't open $_: $!\n"; X $xit=1; X next; X } X X &splitmail('IN',$_); X close(IN); X } X} X Xexit $xit; X Xsub splitmail # (STREAM,fname) X { local($F,$f)=@_; X local($hot); X X $hot=0; # is our pipe hot? X while (<$F>) X { if (/^From /o) X { if ($hot) X { close(PIPE); # Phew! X } X X if (open(PIPE,"|filemail")) X { $hot=1; X } X else X { print STDERR "$cmd: can't pipe to filemail ($!)\n"; X $hot=0; X $xit=1; X } X } X X if ($hot) X { print PIPE $_; X } X else X { print STDERR "$cmd: discarding: $_"; X $xit=1; X } X } X X if ($hot) X { close(PIPE); X } X } EOF-/home/cs/spectrum/fuligin/1/cameron/bin/splitmail sed 's/^X//' > dotfile.inbox <<'EOF-/home/cs/spectrum/fuligin/1/cameron/etc/mail/inbox/.file' X#!/usr/local/bin/perl X Xif (!$F_HAS_SUBJECT) X { &legend("no subject, rejecting message from $f"); X if (!open(M,"| m -S -s 'your message has been rejected' '$errors_to'")) X { &legend("can't pipe to m: $!"); X } X else X { print M <<'X' XYour message has been rejected because you did not supply a subject line. XThe rejected message is appended below in case you want to resend it. XX X; X X if (defined($SIGNATURE) X && length($SIGNATURE) X && open(S,"< $SIGNATURE\0")) X { while () { print M $_; } X close(S); X } X X if (open(S,"sig |")) X { while () { print M $_; } X close(S); X } X X print M "\n"; X for (@hdrs) X { print M $_, "\n"; X } X X print M "\n", @INPUT; X close(M); X } X } X Xif (/^ACSnet badhandler/) X { &forw($_,'neilb',@INPUT); # Forward to NeilB. X } X# Stuff sent over ACSnet. Xelsif (/^(Files|".*") from (\w+) at \S+$/) X { &legend("$s for $to"); X if ($1 eq 'Files') X { for (@INPUT) X { next unless /^\s+Mode\s+Size\s+Modify time\s+Name/../^Please use/; X next if /^Please use/ || /^\s*$/; X print "\t\t$_"; X } X } X else X { local($fname,$who)=($1,$2); X X $fname =~ s/"(.*)"/$1/; X X if ($fname eq "$who.acc") X { system("cd \$HOME/admin/mkacc && getfile -Y '$fname' 2>&1"); X } X } X X $filed=1; X } Xelsif ($from eq 'neilb@cs.unsw.oz.au' X && $s eq 'I am on vacation at the moment.') X { &legend("$from is on vacation"); X $files=1; X } Xelsif ($to =~ /faces@Aus.Sun.COM/ X || $cc =~ /faces@Aus.Sun.COM/) X { $MAILBOX.='/faces'; X } Xelsif ($f eq 'funny-request-daemon@clarinet.com (rec.humor.funny autoreply)') X { &legend("joke received by rec.humour.funny: $s"); X $filed=1; X } Xelsif ($f eq 'postmaster@cs.unsw.oz.au' && $s eq 'Receipt for mail') X { $filed=1; # swallow these X } Xelsif ($from =~ /^(\w+!)*(postmaster|(mailer-)?daemon|mailer-agent|uucp)@/i X && ($s eq 'Returned mail: Return receipt' X || $s eq 'Return receipt' || $s eq 'Return Receipt' X || $s eq 'Delivery report: Return Receipt' X ) X ) X { &legend("mail acknowledgement from $from"); X $filed=1; X } Xelsif (defined($hdrs{'x-msmail-mailclass'}) X && $hdrs{'x-msmail-mailclass'} eq 'IPM.Microsoft Mail.Read Receipt') X { &legend("$from has read \"$hdrs{'x-msmail-entitled'}\""); X $filed=1; X } Xelsif ($f eq 'archie@plaza.aarnet.edu.au' X && $s =~ /^archie reply: / X ) X { $MAILBOX="$ENV{'HOME'}/doc/archives/archie.au"; X } EOF-/home/cs/spectrum/fuligin/1/cameron/etc/mail/inbox/.file sed 's/^X//' > dotfile.prog <<'EOF-/usr/local/doc/misc/archives/archie.au/prog/.file' X#!/usr/local/bin/perl X# X# Filing routine for my mail autofiler. X# We expect output from archie's email `prog' query facility. X# X Xif ($s =~ /^archie reply: prog\s+(.*\S)/) X { $F="$MAILBOX/$1.Z"; X if (open(UNPROG,"| unprog | compress > '$F'\0")) X { for (@INPUT) X { print UNPROG $_; X } X X close(UNPROG); X $legend="saved as $F"; X $filed=1; X } X else X { print STDERR "$cmd: can't pipe to unprog: $!\n"; X } X } EOF-/usr/local/doc/misc/archives/archie.au/prog/.file sed 's/^X//' > libcs.pl <<'EOF-/home/cs/spectrum/fuligin/1/cameron/etc/pl/libcs.pl' X#!/usr/local/bin/perl X# X# This will be an autoload library at some stage. X# X X# numerical comparitor for sorts Xsub ncmp X { $a <=> $b; X } X Xsub min { local($min)=shift; X for (@_) { ($_ < $min) && ($min=$_); } X $min; X } X Xsub max { local($max)=shift; X for (@_) { ($_ > $max) && ($max=$_); } X $max; X } X Xsub basename # (@pathnames) -> @basenames X { local(@paths)=@_; X X for (@paths) X { s,/+$,,; X s,.*/,,; X length || ($_='.'); X } X X return @paths; X } X Xsub dirname # (@pathnames) -> @dirnames X { local(@paths)=@_; X local($pfx); X X for (@paths) X { m,^(/?/?)/*,; $pfx=$1; $_=$'; # collect leading slashes X s,/+$,,; # strip trailing slashes X s,[^/]+$,,; # strip basename X s,/+$,,; # strip trailing slashes again X length($pfx) || ($pfx='./'); # no null paths X $_=$pfx.$_; # prefix + tail X } X X return @paths; X } X Xsub eval # string -> result X { print STDERR "eval($_[0])\n"; X eval $_[0]; X } X Xsub prt { print STDERR $_[0]; X 1; X } Xsub err { &prt($_[0]); X 0; X } X X# ensure a directory exists Xsub mkdir # (dir) -> ok X { local($dir)=@_; X X -d $dir X || (&mkdir(&dirname($dir)) X && (-d $dir X || mkdir($dir,0777) X ) X ) X ; X } X Xsub open # (handle,filename,mode) -> ok X { local($handle,$file,$mode)=@_; X X &mkdir(&dirname($file)) && open($handle,"$mode$file"); X } X Xsub isatty X { local($_)=$_[0]; X local($dev,$ino,$mode,@etc); X X if (/^\d+$/) X { if (!open(_FD_ISATTY,"<&$_")) X { print STDERR "isatty: can't open &$_ ($!)\n"; X return undef; X } X ($dev,$ino,$mode,@etc)=stat _FD_ISATTY; X # no close since it may eat the fd X } X elsif (/^[A-Z_]+$/) X { ($dev,$ino,$mode,@etc)=eval "stat $_"; X } X else X { ($dev,$ino,$mode,@etc)=stat($_); X } X X return (defined($mode) X ? (($mode&(&S_IFMT)) == &S_IFCHR) X : undef); X } X Xsub catpath # (dir,path) -> fullpath X { local($_,$path)=@_; X X if (length == 0) X { return $path; X } X elsif (length($path) == 0) X { return $_; X } X else X { return m,/$, ? "$_$path" : "$_/$path"; X } X } X Xsub detab # (tabbed,tabsize) -> untabbed X { local($line,$tabsize)=@_; X local($_,$chunk); X X defined($tabsize) || ($tabsize=8); X X # Bug in regexps? X # s/\t/' ' x ($tabsize-(length($`)%$tabsize))/eg; X X $_=''; X for $chunk (split(/\t/,$line)) X { $_.=$chunk; X $_.=(' ' x ($tabsize-(length($_) % $tabsize))); X } X X s/[ \t]+$//; X X return $_; X } X X# safe rename - doesn't tromp target file if present Xsub rename # (from,to) -> success X { local($from,$to)=@_; X local($ok); X X $ok=0; X if (link($from,$to)) X { $ok=1; X if (!unlink($from)) X { print STDERR "$cmd: unlink($from): $!, $from still linked to $to\n"; X } X } X elsif ($! == &EXDEV) X # cross device link X { if (lstat($to)) X { print STDERR "$cmd: $to exists\n"; X } X else X { if (!open(RENAME_FROM,"<$from")) X { print STDERR "$cmd: can't open $from for read: $!\n"; X } X else X { if (!open(RENAME_TO,">$to")) X { print STDERR "$cmd: can't open $to for write: $!\n"; X } X else X { while () X { print RENAME_TO; X } X X close(RENAME_TO); X X if (unlink($from)) X { $ok=1; X } X else X { print STDERR "$cmd: can't unlink $from ($!), unlinking $to\n"; X if (!unlink($to)) X { print STDERR "$cmd: can't unlink $to: $!\n\tboth $from and $to now exist\n"; X } X } X } X X close(RENAME_FROM); X } X } X } X else X { print STDERR "$cmd: link($from,$to): $!\n"; X } X X return $ok; X } X X# weekday names X@wday_names=('sun','mon','tue','wed','thu','fri','sat'); X@Wday_names=('Sun','Mon','Tue','Wed','Thu','Fri','Sat'); X@Weekday_names=('Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'); X X# month names X@mon_names=('jan','feb','mar','apr','may','jun','jul','aug','sep','oct','nov','dec'); X@Mon_names=('Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'); X@Month_names=('January','February','March','April','May','June','July','August','September','October','November','December'); X Xsub datestr # (time,uselocaltime) -> "MMmonYY, hh:mm:ss" X { local($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) X =($_[1] ? localtime($_[0]) : gmtime($_[0])); X X sprintf("%02d%s%02d, %02d:%02d:%02d", X $mday,$mon_names[$[+$mon],$year,$hour,$min,$sec); X } X Xsub timestr # (time) -> "[[[days, ]hours, ]minutes, ]seconds" X { local($time)=$_[0]; X local($str,$slop); X X $str=""; X if ($time >= 86400) X { $slop=$time%86400; X $time-=$slop; X $str.=($time/86400)." days, "; X $time=$slop; X } X X if ($time >= 3600) X { $slop=$time%3600; X $time-=$slop; X $str.=($time/3600)." hours, "; X $time=$slop; X } X X if ($time >= 60) X { $slop=$time%60; X $time-=$slop; X $str.=($time/60)." minutes, "; X $time=$slop; X } X X $str.$time." seconds"; X } X X$_subopen_handler_number=0; Xsub subopen # (open-name) -> handle X { local($file)=@_; X local($handle)='_SUBOPEN_HANDLE_'.$_subopen_handler_number++; X X if (open($handle,$file)) X { return $handle; X } X X undef; X } X Xrequire 'cs/env/misc.pl'; X X1; # make require happy EOF-/home/cs/spectrum/fuligin/1/cameron/etc/pl/libcs.pl sed 's/^X//' > cs.env.mail.pl <<'EOF-/home/cs/spectrum/fuligin/1/cameron/etc/pl/cs/env/mail.pl' Xrequire 'cs/env/misc.pl'; X Xdefined($SITENAME) || ($ENV{'SITENAME'}=$SITENAME='cs.unsw.oz.au'); X$ENV{'ORGANIZATION'}=$ORGANIZATION='CS&E Computing Facility, Uni Of NSW, Oz'; X Xdefined($MAILDIR) || ($ENV{'MAILDIR'}=$MAILDIR="$HOME/etc/mail"); Xdefined($MAILRC) || ($ENV{'MAILRC'}=$MAILRC="$MAILDIR/mailrc"); Xdefined($SIGNATURE) || ($ENV{'SIGNATURE'}=$SIGNATURE="$MAILDIR/signature"); Xdefined($OUTMAIL) || ($ENV{'OUTMAIL'}=$OUTMAIL="$MAILDIR/outmail"); Xdefined($DEADMAIL) || ($ENV{'DEADMAIL'}=$DEADMAIL="$MAILDIR/dead.letter"); Xdefined($PFX) || ($ENV{'PFX'}=$PFX='| '); Xdefined($EMAIL) || ($ENV{'EMAIL'}=$EMAIL="$LUSER\@$SITENAME"); Xdefined($REPLY_TO) || ($ENV{'REPLY_TO'}=$REPLY_TO=$EMAIL); X X# for filemail Xdefined($ANNOUNCE) || ($ENV{'ANNOUNCE'}=$ANNOUNCE=$CONSOLE); X X1; # for require EOF-/home/cs/spectrum/fuligin/1/cameron/etc/pl/cs/env/mail.pl sed 's/^X//' > cs.rfc822.pl <<'EOF-/home/cs/spectrum/fuligin/1/cameron/etc/pl/cs/rfc822.pl' X@rfc822'mailhdrs=('to','cc','bcc','from','sender','reply-to','return-recipt-to', X 'errors-to'); X@rfc822'newshdrs=('newsgroups','followup-to'); X$rfc822'mailptn=join('|',@mailhdrs); X$rfc822'newsptn=join('|',@newshdrs); X$rfc822'listfieldptn="$rfc822'mailptn|$rfc822'newsptn"; X X# add a line to @hdrs and %hdrs X# just adds to @hdrs until it gets "" or "\n" and then sets up %hdrs X# field names matching $commaptn are concatenated with commas, X# otherwise with "\n\t". Xsub add822lines # @lines -> @remaining_lines X { local($commaptn)=$rfc822'listfieldptn; X local($_,$hdr); X X $hdr=''; X while (defined($_=shift)) X { s/\r?\n$//; X last if !length; X X if (/^\s/) X { $hdr.="\n$_"; X } X else X { length($hdr) && push(@hdrs,$hdr); X $hdr=$_; X } X } X X length($hdr) && push(@hdrs,$hdr); X X if (defined) X # parse headers X { local($key,$field); X X undef %hdrs; X for (@hdrs) X { if (/^([^\s:]+):\s*/) X { $key=$1; X $field=$'; $field =~ s/^\s+//; X $key =~ tr/_A-Z/-a-z/; X if (defined($hdrs{$key})) X { if ($key =~ /^$commaptn$/o) X { $hdrs{$key}.=', '; X } X X $hdrs{$key}.="\n\t"; X } X X $hdrs{$key}.=$field; X } X } X } X X @_; X } X X# parse an RFC822 address list returning a list of tuples X# (leading command, address, trailing comment, ...) Xsub getaddrs # (addrlist) -> @(precomment, addr, postcomment) X { local($_)=@_; X local(@parsed,$pre,$addr,$post); X X s/^\s+//; X while (length) X { if (/^,/) X { $_=$'; X if (length($pre) && !length($addr)) X { $addr=$pre; $pre=''; X } X X if (length($pre) || length($addr) || length($post)) X { push(@parsed,$pre,$addr,$post); X } X X $pre=''; X $addr=''; X $post=''; X } X elsif (!length($addr) && /^[-\w_.]+@[-\w_.]+/) X { $_=$'; X $addr=$&; X } X elsif (/^"([^"]|\\")*"/ || /^'([^']|\\')*'/) X { $_=$'; X if (length($addr)) X { $post .= " $&"; X } X else X { $pre .= " $&"; X } X } X elsif (/^<[^>\s]*>/) X { $_=$'; X if (length($addr)) X { $pre.=" $addr"; X } X X $addr=$&; X } X elsif (/^[^,\s]+/) X { $_=$'; X if (length($addr)) X { $post.=" $&"; X } X else X { $pre.=" $&"; X } X } X else X { print STDERR "trouble parsing, remaining address is \"$_\"\n"; X } X X s/^\s+//; X } X X if (length($pre) && !length($addr)) X { $addr=$pre; $pre=''; X } X X if (length($pre) || length($addr) || length($post)) X { push(@parsed,$pre,$addr,$post); X } X X for (@parsed) X { s/^\s+//; X } X X @parsed; X } X Xsub msgid X { local($sec,$min,$hour,$mday,$mon,$year,@etc)=localtime(time); X X $_msgid_count++; X sprintf("<%s-%02d%02d%02d%02d%02d%02d-%d-%05d@%s>", X $USER, X $year,$mon+1,$mday,$hour,$min,$sec, X $_msgid_count, X $$, X $HOSTNAME); X } X X1; # for require EOF-/home/cs/spectrum/fuligin/1/cameron/etc/pl/cs/rfc822.pl exit 0 .