Vacation programs that reply to mailing list postings are evil things...

A vacation program should only reply to a message if the recipient
appears explicitly in the "To:" or "Cc:" line and if there's not a
"Precedence: bulk" or "Precedence: junk" header.

Sending a vacation reply back to somebody who sends something to a
mailing list is real annoying (particularly when _lots_ of people on
the mailing list turn on broken versions of vacation at the same time,
such as during a conference).

Here's a version of "vacation" written in PERL that "does the right
thing".  I believe that the latest version of the BSD vacation program
(available as "systems/unix/bsd-sources/usr.bin/vacation" by anonymous
FTP from ftp.uu.net) also "does the right thing", but I think this PERL
one is better.


-Brent
--
Brent Chapman                                   Great Circle Associates
Brent@GreatCircle.COM                           1057 West Dana Street
+1 415 962 0841                                 Mountain View, CA  94041


#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of shell archive."
# Contents:  README nvacation nvacation.man
# Wrapped by brent@mycroft on Wed Oct 28 10:06:45 1992
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'README' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'README'\"
else
echo shar: Extracting \"'README'\" \(2566 characters\)
sed "s/^X//" >'README' <<'END_OF_FILE'
XForwarded: Thu, 16 Jul 92 11:03:03 CDT
XForwarded: datri
XForwarded: Wed, 15 Jul 92 20:14:53 CDT
XForwarded: Martin Streicher <strike@micrografx.com>
XForwarded: Wed, 20 May 92 19:12:30 CDT
XForwarded: jmb@ideas.com
XReplied: Wed, 06 May 92 12:12:20 CDT
XReplied: Rex-Pruess@uiowa.edu
XReceived: from convex.convex.com by pixel.convex.com (5.64/1.28)
X	id AA01977; Tue, 5 May 92 21:17:31 -0500
XReceived: from ns-mx.uiowa.edu by convex.convex.com (5.64/1.35)
X	id AA04097; Tue, 5 May 92 16:49:02 -0500
XReceived: from yoyo.weeg.uiowa.edu by ns-mx.uiowa.edu (5.64.jnf/920224)
X	  on Tue, 5 May 92 16:49:06 -0500 id AA21022 with SMTP 
XReceived: by yoyo.weeg.uiowa.edu (NeXT-1.0 (From Sendmail 5.52)/NeXT-2.0)
X	id AA12012; Tue, 5 May 92 16:49:05 CDT
XDate: Tue, 5 May 92 16:49:05 CDT
XFrom: rpruess@yoyo.weeg.uiowa.edu (Rex Pruess)
XMessage-Id: <9205052149.AA12012@yoyo.weeg.uiowa.edu>
XReply-To: Rex-Pruess@uiowa.edu
XReceived: by NeXT Mailer (1.63)
XTo: tchrist@convex.com
XSubject: Changes to the vacation program
X
XTom,
XI tinkered with the vacation program.  It seems to behave for me, although
Xit sure could use more testing.  Here's what I've done:
X
X  o The code for the "-a alias" option works.  I made it case insensitive too.
X
X  o The code which creates the ".forward" file honors the appropriate
X    command line options (-a, -f, -j, -t).
X
X  o If you specify "-I" or "-i" to initialize files, vacation will now
X    ask you for permission to overwrite the ".forward" and ".vacation.msg"
X    files.  (The old behavior of overwriting those files really annoyed me.)
X
X  o A new option, "-l", lists the addresses of those who received the
X    vacation notice.  I sometimes like to verify vacation is behaving
X    without resetting the dbm files.
X
X  o "operator" has been added to the list of exempt IDs.
X
X  o I created a man page.  I borrowed a bit from an old vacation man page,
X    but my version is more verbose.  And, of course, it reflects the current
X    vacation code.
X
X  o Several comments have been added to the beginning of the program.  These
X    comments describe the vacation options.  The comments were borrowed from
X    the man page.
X
X  o My name/address has been added to the header of the program.
X
XFeel free to use, ignore, change, etc. as you see fit.  I hope I've been
Xof some help.
X
XI'll send you the program and the man page as separate e-mail notes.
X------------------------------------------------------------------------
X  Rex Pruess, Weeg Computing Center, University of Iowa, Iowa City, IA
X     NeXTmail: Rex-Pruess@uiowa.edu ......... Phone: (319) 335-5452
END_OF_FILE
if test 2566 -ne `wc -c <'README'`; then
    echo shar: \"'README'\" unpacked with wrong size!
fi
# end of 'README'
fi
if test -f 'nvacation' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'nvacation'\"
else
echo shar: Extracting \"'nvacation'\" \(8279 characters\)
sed "s/^X//" >'nvacation' <<'END_OF_FILE'
X#!/usr/local/bin/perl
X#
X# vacation program
X# Larry Wall <lwall@jpl-devvax.jpl.nasa.gov>
X#
X# updates by Tom Christiansen <tchrist@convex.com>,
X#            Rex Pruess <Rex-Pruess@uiowa.edu> (May 1992).
X#
X# The vacation options are described below (as documented in the man page).
X#
X#  -aalias   Indicate that alias is one of the valid aliases for your login
X#            name.  Mail addressed to the alias generates a vacation reply.
X#            Use -a repetitively to specify multiple aliases.  An example
X#            is "-aSantaClaus -aSanta" for the login name of sclaus.  Mail
X#            addressed to SantaClaus, Santa, or sclaus results in a vaca-
X#            tion reply.  The case of letters is ignored in making com-
X#            parisons.
X#
X#  -ffile    Do not send vacation replies to the user names listed in file.
X#            User names are white-space separated.
X#
X#  -j        Do not check whether the recipient appears in the To: or the
X#            Cc: line.  In other words, send replies to users even if the
X#            mail is not directly addressed to your login name or an alias
X#            for it.
X#
X#  -I        Initialize the .forward, .vacation.msg, .vacation.dir, and
X#            .vacation.pag files.
X#
X#  -l        List the contents of the .vacation.dir and .vacation.pag
X#            files.  This lists the users who received your vacation
X#            notice.
X#
X#  -tN       Change the interval between repeat replies to the same sender.
X#            The default is "1w" (one week).  A trailing s, m, h, d, or w
X#            scales N to seconds, minutes, hours, days, or weeks, respec-
X#            tively.
X
X$vacation = $0;
X$vacation = '/usr/local/bin/nvacation' unless $vacation =~ m#^/#;
X
X$user = $ENV{'USER'} || $ENV{'LOGNAME'} || getlogin || (getpwuid($>))[0];
X$editor = $ENV{'VISUAL'} || $ENV{'EDITOR'} || 'vi';
X$pager = $ENV{'PAGER'} || 'more';
X
X$default_msg = <<'EOF';
XI will not be reading my mail for a while.
XYour mail regarding "$SUBJECT" will be read when I return.
XEOF
X
X
Xif (!@ARGV) {		# interactive use, a la Sun
X    &interactive();
X    die "not reached";
X}
X
X@ignores = ( 
X	'daemon',
X	'postmaster',
X	'mailer-daemon',
X	'mailer',
X	'operator',
X	'root',
X     );
X
X# set-up time scale suffix ratios
X%scale = (
X	's', 1,
X	'm', 60,
X	'h', 60 * 60,
X	'd', 24 * 60 * 60,
X	'w', 7 * 24 * 60 * 60,
X    );
X
X
Xwhile ($ARGV[0] =~ /^-/) {
X    $_ = shift;
X    if (/^-I/i) {  # eric allman's source has both cases
X	chdir;
X
X	&initialize;
X
X        if ( !(-f '.vacation.msg') || &yorn ("Do you wish to replace your \".vacation.msg\" file?") ) {
X           &make_default;
X        }
X
X	exit;
X    } elsif (/^-l/) {
X        &list_aliases ();
X        exit;
X    } elsif (/^-j/) {
X	push (@forOpts, '-j');
X	$opt_j++;
X    } elsif (/^-f(.*)/) {
X	&save_file($1 ? $1 : shift);
X    } elsif (/^-a(.*)/) {
X	&save_alias($1 ? $1 : shift);
X    } elsif (/^-t([\d.]*)([smhdw])/) {
X	push (@forOpts, $_);
X	$timeout = $1;
X	$timeout *= $scale{$2} if $2;
X    } else {
X	die "Unrecognized switch: $_\n";
X    }
X}
X
Xif (!@ARGV) {
X    &interactive();
X    die "not reached";
X}
X
X
X$user = shift;
X# push(@ignores, $user);
Xpush(@aliases, $user);
Xdie "Usage: vacation username\n" if $user eq '' || @ARGV;
X
X$home = (getpwnam($user))[7];
Xdie "No home directory for user $user\n" unless $home;
Xchdir $home || die "Can't chdir to $home: $!\n";
X
X$timeout = 7 * 24 * 60 * 60 unless $timeout;
X
Xdbmopen(VAC, ".vacation", 0666) || die "Can't open vacation dbm files: $!\n";
X
X
X$/ = '';			# paragraph mode
X$header = <>;
X$header =~ s/\n\s+/ /g;		# fix continuation lines
X$* = 1;
X
Xexit if $header =~ /^Precedence:\s*(bulk|junk)/i;
Xexit if $header =~ /^From.*-REQUEST@/i;
X
Xfor (@ignores) {
X    exit if $header =~ /^From.*\b$_\b/i;
X} 
X
X($from) = ($header =~ /^From\s+(\S+)/);	# that's the Unix-style From line
Xdie "No \"From\" line!!!!\n" if $from eq "";
X
X($subject) = ($header =~ /Subject:\s+(.*)/);
X$subject = "(No subject)" unless $subject;
X$subject =~ s/\s+$//;
X
X($to) = ($header =~ /To:\s+(.*)/);
X($cc) = ($header =~ /Cc:(\s+.*)/);
X$to .= ', '.$cc if $cc;
X
Xif (open(MSG,'.vacation.msg')) {
X    undef $/;
X    $msg = <MSG>;
X    close MSG;
X} else {
X    $msg = $default_msg;
X} 
X$msg =~ s/\$SUBJECT/$subject/g;		# Sun's vacation does this
X
Xunless ($opt_j) {
X    foreach $name (@aliases) {
X	$ok++ if $to =~ /\b$name\b/i;
X    }
X    exit unless $ok;
X}
X
X$lastdate = $VAC{$from};
X$now = time;
Xif ($lastdate ne '') {
X    ($lastdate) = unpack("L",$lastdate);
X    exit unless $lastdate;
X    exit if $now < $lastdate + $timeout;
X}
X
X$VAC{$from} = pack("L", $now);
Xdbmclose(VAC);
X
X
Xopen(MAIL, "|/usr/lib/sendmail -oi -t $from") || die "Can't run sendmail: $!\n";
X
Xprint MAIL <<EOF;
XTo: $from
XSubject: This is a recording... [Re: $subject]
XPrecedence: junk
X
XEOF
Xprint MAIL $msg;
Xclose MAIL;
Xexit;
X
Xsub yorn {
X    local($answer);
X    for (;;) {
X	print $_[0];
X	$answer = <STDIN>;
X	last if $answer =~ /^[yn]/i;
X	print "Please answer \"yes\" or \"no\" ('y' or 'n')\n";
X    }
X    $answer =~ /^y/i;
X}
X
Xsub interactive {
X    chdir;
X    chop($cwd = `pwd`);
X
X    &disable if -f '.forward';
X
X    print <<EOF;
XThis program can be used to answer your mail automatically
Xwhen you go away on vacation.
XEOF
X
X    for (;;) {
X	if (-f '.vacation.msg') {
X	    print "\nYou already have a message file in \"$cwd/.vacation.msg\".\n";
X	    $see = &yorn("Would you like to see it? ");
X	    system $pager, '.vacation.msg' if $see;
X	    $edit = &yorn("Would you like to edit it? ");
X	}
X	else {
X	    &make_default;
X	    print <<EOF;
X
XI've created a default vacation message in \"~/.vacation.msg\".  This
Xmessage will be automatically returned to anyone sending you mail while
Xyou're out.
X
XPress return when ready to continue, and you will enter your favorite
Xeditor ($editor) to edit the messasge to your own tastes.  
X
XEOF
X	    $| = 1;
X	    print "Press return to continue: "; 
X	    <STDIN>;
X	    $edit = 1;
X	}
X
X	last unless $edit;
X	system $editor, '.vacation.msg';
X	last;
X    }
X
X
X    print "\nTo enable the vacation feature a \".forward\" file is created.\n";
X    if (&yorn("Would you like to enable the vacation feature now? ")) {
X	&initialize;
X	print <<EOF;
X
XOk, vacation feature ENABLED.  Please remember to turn it off when
Xyou get back from vacation.  Bon voyage!
XEOF
X    }
X    else {
X	    print "Ok, vacation feature NOT enabled.\n";
X	}
X
X    exit;
X}
X
Xsub initialize {
X    local($opt);
X
X    &zero('.vacation.pag');
X    &zero('.vacation.dir');
X
X    if ( !(-f '.forward') || &yorn ("Do you wish to replace your \".forward\" file?") ) {
X       open(FOR, ">.forward") || die "Can't create .forward: $!\n";
X       print FOR "\\$user, \"|$vacation ";
X
X       if (defined (@forOpts)) {
X          foreach $opt (@forOpts) {
X             print FOR "$opt ";
X          }
X       }
X
X       printf FOR "$user\"\n";
X       close FOR;
X    }
X
X} 
X
Xsub zero {
X    local($FILE) = @_;
X    open (FILE, ">$FILE") || die "can't creat $FILE: $!";
X    close FILE;
X} 
X
Xsub save_alias {
X   push (@forOpts, '-a', @_);
X   push(@aliases, @_);
X}
X
Xsub save_file {
X    push (@forOpts, '-f', @_);
X    local($FILE) = @_;
X    local($_);
X
X    open FILE || die "can't open $FILE: $!";
X
X    while (<FILE>) {
X	push(@ignores, split);
X    } 
X    close FILE;
X} 
X
Xsub disable {
X    print "\nYou have a \".forward\" file in your home directory containing: ",
X	  "\n",
X	  `cat .forward`,
X	  "\n";
X    if (&yorn(
X"Would you like to remove it and disable the vacation feature? ")) {
X	unlink('.forward') || die "Can't unlink .forward: $!\n";
X        &list_aliases ();
X	print "Back to normal reception of mail.\n";
X	exit;
X    } else {
X	print "Ok, vacation feature NOT disabled.\n";
X    }
X} 
X
Xsub make_default {
X    open(MSG, ">.vacation.msg") || die "Can't create .vacation.msg: $!\n";
X    print MSG $default_msg;
X    close MSG;
X} 
X
Xsub list_aliases {
X    local($key, @keys, $when);    
X
X    dbmopen(VAC, '.vacation', 0444) || die "no .vacation dbmfile\n";
X
X    sub byvalue { $VAC{$a} <=> $VAC{$b}; }
X
X    if (@keys = sort byvalue keys %VAC) {
X       require 'ctime.pl';
X       open (PAGER, "|$pager") || die "can't open $pager: $!";
X       print PAGER <<EOF;
XWhile you were away, mail was sent to the following addresses:
X
XEOF
X       for $key (@keys) {
X   	   ($when) = unpack("L", $VAC{$key});
X           printf PAGER "%-20s %s", $key, &ctime($when);
X       } 
X
X       close PAGER;
X    }
X}
END_OF_FILE
if test 8279 -ne `wc -c <'nvacation'`; then
    echo shar: \"'nvacation'\" unpacked with wrong size!
fi
# end of 'nvacation'
fi
if test -f 'nvacation.man' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'nvacation.man'\"
else
echo shar: Extracting \"'nvacation.man'\" \(4207 characters\)
sed "s/^X//" >'nvacation.man' <<'END_OF_FILE'
X.TH VACATION 1
X.SH NAME
Xvacation \- reply to mail automatically
X.SH SYNOPSIS
X.B vacation
X[
X.B \-\^I
X]
X.br
X.B vacation
X[
X.B \-\^l
X]
X.br
X.B vacation
X[
X.B \-\^j
X]
X[
X.BI \-\^a alias
X]
X[
X[
X.BI \-\^f file
X]
X[
X.BI \-\^t N
X]
X.I username
X.SH DESCRIPTION
X.B vacation
Xreplies to incoming mail.  The intended use is in a
X.B \&.forward
Xfile.  The
X.B \&.forward
Xfile must exist in your home directory and contain a line similar to:
X.IP
X\e\f2username\fP, "|/usr/local/bin/vacation \f2username\fP"
X.PP
Xwhere
X.I username
Xis your login name.  Other
X.B vacation
Xoptions, such as -a, -f, -j, and -t, can be included
Xto tailor vacation to your liking.
X.PP
XIf
X.B vacation
Xis run with no arguments, it permits you to interactively turn
X.B vacation
Xon or off.  It creates a
X.B \&.vacation.msg
Xfile for you, or edits an existing one, using the editor specified by the
X.SM VISUAL
Xor
X.SM EDITOR
Xenvironment variable, or
X.BR vi
Xif neither of those environment variables are set.  If a
X\f3\%\&.forward\f1
Xfile is present in your home directory, it asks whether you want
Xto remove it and turn off
X.BR vacation .
XIf it is not present in your home directory, it creates
Xit for you, and automatically performs a
X.BR "vacation \-\^I" ,
Xturning on
X.BR vacation .
X.PP
X.B vacation
Xexpects a file
X.B \&.vacation.msg
Xin your home directory containing a message to be sent back
Xto each sender.  If the string
X.I $SUBJECT
Xappears in the
X.B \&.vacation.msg
Xfile, it is replaced with the subject of the original message when the
Xreply is sent.  An example of a 
X.B \&.vacation.msg
Xfile follows:
X.IP
XI will not be reading my mail for a while.
XYour mail regarding "$SUBJECT" will be read when I return.
X.IP
X  -Santa (sclaus@north.pole)
X.PP
X.I
X.BR sendmail
Xincludes the 
X.B From: 
Xand
X.B Subject: 
Xlines automatically.
X.PP
XThis message is only sent once a week to each unique
Xsender.
XThe people who have sent you messages are kept in the files
X.B \&.vacation.pag
Xand
X.B \&.vacation.dir
Xin your home directory.
XThe
X.B \-\^l
Xoption lists the contents of these files.
XThe
X.B \-\^I
Xoption initializes these files
Xand should be executed before you modify your
X.B \&.forward
Xfile.
X.PP
XTo avoid sending vacation notices to mailing lists and well\-known users,
X.B vacation
Xwill not send a reply if any of the following conditions are met:
X.IP
XThe initial 
X.B From: 
Xline includes the string 
X.RB \*Q \-REQUEST@ \*U.
X.IP
XA 
X.RB \*Q Precedence: \f2bulk\f1 \*U
Xor
X.RB \*Q Precedence: \f2junk\f1 \*U
Xline is included in the header.
X.IP
XThe
X.B To:
Xor the
X.B Cc:
Xline does not list your login name or one of a number of aliases for it.
X.IP
XMail is from one of these well-known users: daemon, postmaster,
Xmailer\-daemon, mailer, operator, or root.
X.IP
XMail is from one of the users listed in the optional exemption file, specified
Xwith the \-f option.
X.PP
X.B vacation
Xoptions are:
X.TP 10
X.BI \-\^a alias
XIndicate that
X.I alias
Xis one of the valid aliases for your login name.  Mail addressed to the
Xalias generates a vacation reply.  Use \-a repetitively to specify multiple
Xaliases.  An example is "\-aSantaClaus \-aSanta" for the login name of sclaus.
XMail addressed to SantaClaus, Santa, or sclaus results in a vacation reply.
XThe case of letters is ignored in making comparisons.
X.TP
X.BI \-\^f file
XDo not send vacation replies to the user names listed in
X.I file.
XUser names are white-space separated.
X.TP
X.B \-\^j
XDo not check whether the recipient appears in the
X.B To:
Xor the
X.B Cc:
Xline.  In other words, send replies to users even if the mail is not directly
Xaddressed to your login name or an alias for it.
X.TP
X.B \-\^I
XInitialize the .forward, .vacation.msg, .vacation.dir, and .vacation.pag
Xfiles.
X.TP
X.B \-\^l
XList the contents of the .vacation.dir and .vacation.pag files.  This lists
Xthe users who received your vacation notice.
X.TP
X.BI \-\^t N
XChange the interval between repeat replies to the same sender.
XThe default is "1w" (one week).
XA trailing
X.BR s ,
X.BR m ,
X.BR h ,
X.BR d ,
Xor
X.B w
Xscales
X.I N
Xto seconds, minutes, hours, days, or weeks, respectively.
X.SH FILES
X.nf
X.ta 5n 35n
X\&.forward\f1	Forward file
X\&.vacation.msg\f1	Reply automatically sent out
X\&.vacation.pag and \&.vacation.dir	List of senders
X.fi
X.SH SEE ALSO
Xvi(1)
X.br
Xsendmail(8)
END_OF_FILE
if test 4207 -ne `wc -c <'nvacation.man'`; then
    echo shar: \"'nvacation.man'\" unpacked with wrong size!
fi
# end of 'nvacation.man'
fi
echo shar: End of shell archive.
exit 0
