#! /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:  Makefile vacation.1 vacation.c
# Wrapped by brant@manta on Sun Apr 16 17:35:04 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'Makefile' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'Makefile'\"
else
echo shar: Extracting \"'Makefile'\" \(251 characters\)
sed "s/^X//" >'Makefile' <<'END_OF_FILE'
X# Makefile for vacation(1)
X# for a UNIXpc
X
XCC=gcc
XDBM=-lgdbm
XCFLAGS=-O -c
XLDFLAGS=-s -shlib
X
Xvacation: vacation.o
X	$(CC) $(LDFLAGS) -o vacation vacation.o $(DBM)
X
Xvacation.o: vacation.c
X	$(CC) $(CFLAGS) vacation.c
X
Xclean:
X	rm -f vacation.o a.out core
END_OF_FILE
if test 251 -ne `wc -c <'Makefile'`; then
    echo shar: \"'Makefile'\" unpacked with wrong size!
fi
# end of 'Makefile'
fi
if test -f 'vacation.1' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'vacation.1'\"
else
echo shar: Extracting \"'vacation.1'\" \(2943 characters\)
sed "s/^X//" >'vacation.1' <<'END_OF_FILE'
X.\" Copyright (c) 1985, 1987 Regents of the University of California.
X.\" All rights reserved.
X.\"
X.\" Redistribution and use in source and binary forms are permitted
X.\" provided that this notice is preserved and that due credit is given
X.\" to the University of California at Berkeley. The name of the University
X.\" may not be used to endorse or promote products derived from this
X.\" software without specific prior written permission. This software
X.\" is provided ``as is'' without express or implied warranty.
X.\"
X.\"	@(#)vacation.1	6.5 (Berkeley) 12/26/87
X.\"
X.TH VACATION 1 "December 26, 1987"
X.UC 6
X.SH NAME
Xvacation \- return ``I am not here'' indication
X.SH SYNOPSIS
X.B vacation
X.B -i
X.br
X.B vacation
X[
X.B -a
Xalias ] login
X.SH DESCRIPTION
X\fIVacation\fP returns a message to the sender of a message telling
Xthem that you are currently not reading your mail.  The intended use
Xis in a \fI.forward\fP file.  For example, your \fI.forward\fP file
Xmight have:
X.PP
X.ti +5
X\eeric, "|/usr/ucb/vacation -a allman eric"
X.PP
Xwhich would send messages to you (assuming your login name was eric) and
Xreply to any messages for ``eric'' or ``allman''.
X.PP
XNo message will be sent unless \fIlogin\fP or an \fIalias\fP supplied
Xusing the \fB-a\fP option is a substring of either the ``To:'' or ``Cc:''
Xheaders of the mail.  No messages from ``???-REQUEST'', ``Postmaster'',
X``UUCP'', ``MAILER'', or ``MAILER-DAEMON'' will be replied to, nor is a
Xnotification sent if a ``Precedence: bulk'' or ``Precedence: junk'' line
Xis included in the mail headers.  Only one message per week will be sent
Xto each unique sender.  The people who have sent you messages are
Xmaintained as an \fIndbm\fP(3) database in the files \fI.vacation.pag\fP
Xand \fI.vacation.dir\fP in your home directory.
X.PP
XThe \fB-i\fP flag initializes the vacation database files.  It should be
Xused before you modify your \fI.forward\fP file.
X.PP
X\fIVacation\fP expects a file \fI.vacation.msg\fP, in your home directory,
Xcontaining a message to be sent back to each sender.  It should be an entire
Xmessage (including headers).  For example, it might say:
X.PP
X.in +5
X.nf
XFrom: eric@ucbmonet.Berkeley.EDU (Eric Allman)
XSubject: I am on vacation
XDelivered-By-The-Graces-Of: The Vacation program
XPrecedence: bulk
X
XI am on vacation until July 22.  If you have something urgent,
Xplease contact Joe Kalash <kalash@ucbingres.Berkeley.EDU>.
X	--eric
X.fi
X.in -5
X.PP
X\fIVacation\fP reads the first line from the standard input for
Xa \s-1UNIX\s0-style ``From'' line to determine the sender.
X\fISendmail\fP(8) includes this ``From'' line automatically.
X.PP
XFatal errors, such as calling \fIvacation\fP with incorrect arguments,
Xor with non-existent \fIlogin\fPs, are logged in the system log file,
Xusing \fIsyslog\fP(8).
X.SH FILES
X.nf
X.ta \w'~/.vacation.msg    'u
X~/.vacation.dir	database file
X~/.vacation.msg	message to send
X~/.vacation.pag	database file
X.fi
X.SH "SEE ALSO"
Xsendmail(8), syslog(8)
END_OF_FILE
if test 2943 -ne `wc -c <'vacation.1'`; then
    echo shar: \"'vacation.1'\" unpacked with wrong size!
fi
# end of 'vacation.1'
fi
if test -f 'vacation.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'vacation.c'\"
else
echo shar: Extracting \"'vacation.c'\" \(12153 characters\)
sed "s/^X//" >'vacation.c' <<'END_OF_FILE'
X/*
X * Vacation
X *  - acknowledge receipt of mail while I'm away
X *
X * Hacked by Brant Cheikes <brant@manta.pha.pa.us>, 11 Mar 1989.
X *
X * Based heavily (and including chunks of original code) on the
X * vacation(1) program written by Eric P. Allman of UC-Berkeley.
X *
X * I'm not sure of the copyright status of this program.  The original
X * program bore these copyright legends:
X *
X * Copyright (c) 1983  Eric P. Allman
X * Berkeley, California
X *
X * Copyright (c) 1983, 1987 Regents of the University of California.
X * All rights reserved.
X *
X * Redistribution and use in source and binary forms are permitted
X * provided that this notice is preserved and that due credit is given
X * to the University of California at Berkeley. The name of the University
X * may not be used to endorse or promote products derived from this
X * software without specific prior written permission. This software
X * is provided ``as is'' without express or implied warranty.
X */
X
X#include <stdio.h>
X#include <ctype.h>
X#include <fcntl.h>
X#include <pwd.h>
X#include <string.h>
X#include <memory.h>
X
X#define	MAXLINE	500			/* max line from mail header */
X#define	ONEDAY	(60L*60L*24L)		/* a day's worth of seconds */
X#define	VACAT	".vacation"		/* dbm's database prefix */
X#define	VMSG	".vacation.msg"		/* vacation message */
X#define	VDIR	".vacation.dir"		/* dbm's DB prefix, part 1 */
X#define	VPAG	".vacation.pag"		/* dbm's DB prefix, part 2 */
X
Xtypedef struct alias {
X  struct alias *next;
X  char	*name;
X} ALIAS;
XALIAS *names = (ALIAS *)0;	/* head of linked list */
X
Xtypedef struct {
X  char *dptr;
X  int dsize;
X} DATUM;
X
Xlong period = ONEDAY*7L;	/* default is one week */
X
Xvoid errlog();			/* log errors */
Xchar msgbuf[1024];		/* for sprintf'd messages */
X
Xextern char *malloc();
Xextern long time();
Xextern int dbminit(), dbmclose(), store();
Xextern DATUM fetch();
X
Xmain(argc, argv)
X     int argc;
X     char **argv;
X{
X  ALIAS *cur;
X  int ch, iflag = 0;
X  struct passwd *pw;
X  char from[MAXLINE];
X  void parse(), initialize(), setreply(), sendmessage();
X  extern int optind, getuid();
X  extern long atol();
X  extern char *optarg;
X  extern struct passwd *getpwuid(), *getpwnam();
X
X  /*
X   * Parse command-line arguments.
X   */
X  while ((ch = getopt(argc, argv, "a:p:Ii")) != EOF)
X    switch((char)ch) {
X    case 'a':			/* alias */
X      if (!(cur = (ALIAS *)malloc((unsigned)sizeof(ALIAS)))) {
X	(void) sprintf(msgbuf,"%s: memory allocation failed\n",*argv);
X	errlog(msgbuf);
X      }
X      cur->name = optarg;
X      cur->next = names;
X      names = cur;
X      break;
X    case 'p':			/* period between notifications */
X      period = ONEDAY*atol(optarg);
X      break;
X    case 'i':			/* init the database */
X    case 'I':			/* backward compatible */
X      ++iflag;
X      break;
X    case '?':
X    default:
X      goto usage;
X    }
X  argc -= optind;
X
X  /*
X   * Verify that we've consumed all our arguments.
X   */
X  if (argc)
X    if (!iflag) {
Xusage:
X      (void) fprintf(stderr,"Usage: %s [-i] [-a alias] [-p n]\n",*argv);
X      exit(0);
X    }
X
X  /*
X   * By the good graces of sendmail, vacation should be running
X   * set-uid of the local user.  We now figure out who that is.
X   */
X  if (!(pw = getpwuid(getuid()))) {
X    /* this error should *not* occur! */
X    (void) sprintf(msgbuf,"%s: no user for uid %u?\n",*argv,getuid());
X    errlog(msgbuf);
X  }
X
X  /*
X   * Set current directory to local user's home directory.
X   */
X  if (chdir(pw->pw_dir)) {
X    (void) sprintf(msgbuf,"%s: cannot chdir to %s\n",*argv,pw->pw_dir);
X    errlog(msgbuf);
X  }
X
X  /*
X   * Initialize the vacation dbm database if requested.
X   */
X  if (iflag) {
X    initialize();
X    exit(0);
X  }
X
X  /*
X   * Make current userid one of the looked-for names.
X   */
X  if (!(cur = (ALIAS *)malloc((unsigned)sizeof(ALIAS)))) {
X    (void) sprintf(msgbuf,"%s: memory allocation failed\n",*argv);
X    errlog(msgbuf);
X  }
X  cur->name = pw->pw_name;
X  cur->next = names;
X  names = cur;
X
X  /*
X   * Parse message.  Exit if junkmail.
X   */
X  parse(from);
X  if (junkmail(from))
X    exit(0);
X
X  if (access(VDIR, 0))		/* does database exist? */
X    initialize();
X  else
X    (void) dbminit(VACAT);
X
X  /*
X   * Only acknowledge mail from persons who have not recently
X   * been notified of the vacation.
X   */
X  if (!recent(from)) {
X    setreply(from);
X    (void) dbmclose(VACAT);
X    sendmessage(pw->pw_name,from);
X  }
X  return(0);
X}
X
X/* void
X * parse(from)
X *	char *from;
X *
X * Parse mail message (on stdin) looking for sender.  "From:" headers
X * are considered more reliable than "From" or ">From " lines.  Sender
X * address is copied into the buffer pointed to by from.
X *
X * parse() verifies that the incoming message is To: or Cc: the
X * current user.
X */
Xvoid
Xparse(from)
X     char *from;
X{
X  register ALIAS *cur;
X  register char *p, *f;
X  int tome = 0;			/* this message is to: me */
X  int cont = 0;			/* scan continuation lines */
X  int ofs = 0;			/* offset to account for '>' in >From */
X  char buf[MAXLINE];
X
X  /*
X   * Scan input until the first blank line is encountered (marking
X   * the end of the mail headers).
X   */
X  *from = (char)NULL;
X  while (fgets(buf, sizeof(buf), stdin) && *buf != '\n')
X    switch(*buf) {
X    case '>':			/* ">From " */
X      ofs = 1;
X    case 'F':			/* "From " */
X      if (!strncmp(buf+ofs, "From ", 5)) {
X	/*
X	 * Extract sender address from Unix "From " line.
X	 * "From " line has the form
X	 * From sender [date, time, etc.]
X	 *  *or*
X	 * >From sender [date, time, etc.]
X	 * Copy sender address into from.
X	 *
X	 * NB: Multiple ">From " or "From " lines are not parsed
X	 *  correctly (the last one wins).  But that crap, the evil
X	 *  work of obsolescent uucp implementations, is (thankfully)
X	 *  extremely rare in these days of sendmail and smail.
X	 */
X	for (f = from, p = buf+ofs+5; *p && *p != ' '; ++p, ++f)
X	  *f = *p;
X	*f = '\0';
X      } else if (!strncmp(buf, "From: ", 6)) {
X	/*
X	 * Determine sender's address from contents of "From:" header.
X	 * Two formats for this header are allowed:
X	 *  1. From: Joe User <joe@foo.bar.edu>
X	 *  2. From: joe@foo.bar.edu (Joe User)
X	 * NB: contents of From: line will overwrite any From_ info
X	 * already extracted.  This is a Good Thing; dumb Unix mailers,
X	 * which do not generate From: headers, are tolerated as well
X	 * as more sophisticated smail-/sendmail-based mail systems.
X	 */
X	if ((p = strchr(buf+6,'<')) != (char *)NULL) {
X	  /*
X	   * Extract address from between <>.
X	   */
X	  for (f = from, ++p; *p && *p != '>'; ++p, ++f)
X	    *f = *p;
X	  *f = '\0';
X	} else {
X	  /*
X	   * Assume return address immediately follows the From:
X	   */
X	  for (f = from, p = buf+6; *p && !isspace(*p); ++p, ++f)
X	    *f = *p;
X	  *f = '\0';
X	}
X      }
X      ofs = 0;
X      break;
X    case 'P':			/* "Precedence:" */
X      if (!strncmp(buf, "Precedence: ", 12)) {
X	/*
X	 * Messages with precedence "Junk" or "Bulk" are ignored.
X	 */
X	if (!strncasecmp("junk",buf+12,4) || !strncasecmp("bulk",buf+12,4))
X	  exit(0);
X      }
X      break;
X    case 'C':			/* "Cc:" */
X      if (strncmp(buf, "Cc:", 3))
X	break;			/* skip; not a Cc: header */
X      cont = 1;
X      goto findme;
X    case 'T':			/* "To:" */
X      if (strncmp(buf, "To:", 3))
X	break;			/* skip; not a To: header */
X      cont = 1;
X      goto findme;
X    default:
X      /*
X       * If this line does not begin with white space or we've already
X       * found our name, skip the search.
X       */
X      if (!isspace(*buf) || !cont || tome) {
X	cont = 0;
X	break;
X      }
Xfindme:
X      for (cur = names; !tome && cur; cur = cur->next)
X	tome += nsearch(cur->name, buf);
X    }
X
X  /*
X   * If this message was not To: or Cc: me, then exit quietly.
X   */
X  if (!tome)
X    exit(0);
X
X  /*
X   * Log error if sender address was not found.
X   */
X  if (!*from)
X    errlog("vacation: could not find from\n");
X}
X
X/* int
X * nsearch(name,str)
X *	char *name, *str;
X *
X * Search str for instance of name.
X */
Xnsearch(name, str)
X     register char *name, *str;
X{
X  register int len;
X
X  for (len = strlen(name); *str; ++str)
X    if (*str == *name && !strncasecmp(name, str, len))
X      return(1);
X  return(0);
X}
X
X/* int
X * junkmail(from)
X *    char *from;
X *
X * Scan reply address for senders we should ignore.
X * Return non-zero if "from" field indicates junk mail.
X */
Xjunkmail(from)
X     char *from;
X{
X  static struct ignore {
X    char *name;
X    int len;
X  } ignore[] = {
X    "MAILER-DAEMON", 13,
X    "MAILER", 6,
X    "uucp", 4,
X    "nuucp", 5,
X    "postmaster", 10,
X    "-REQUEST", 8,		/* this is not handled right */
X    NULL, NULL,
X  };
X  register struct ignore *cur;
X  register int len;
X  register char *p, *fromcopy;
X
X  /*
X   * Simple-minded scheme for identifying true sender.
X   * Basic algorithm:
X   *  0. Set p to point to end of string.
X   *  1. Scan backward from right end looking for a '@'.  If found,
X   *     p = (addr of '@')-1, else p unchanged.
X   *  2. Now scan backward from p, looking for a '!'.  If found, sender
X   *     is between '!' and '@', else sender is entire string to left of '@'.
X   *  3. Now if a '%' is found, user is to left of that.
X   *
X   * NB: This algorithm expects addresses to have one of these forms:
X   *  user
X   *  user@foo.dom.ain
X   *  user%foo.dom.ain
X   *  user%host@foo.dom.ain
X   *  bar!baz!user@foo.dom.ain
X   *  bar!baz!user%host@foo.dom.ain
X   *  bar!baz!user%host
X   */
X  if ((fromcopy = (char *)malloc((unsigned)strlen(from)+1)) == (char *)NULL)
X    errlog("vacation: malloc failed in junkmail\n");
X  (void) strcpy(fromcopy,from);
X  if ((p = strrchr(fromcopy,'@')) != (char *)NULL)
X    *p = '\0';
X  if ((p = strrchr(fromcopy,'!')) != (char *)NULL)
X    fromcopy = p+1;
X  if ((p = strchr(fromcopy,'%')) != (char *)NULL)
X    *p = '\0';
X
X  /*
X   * fromcopy now points to what junkmail thinks the sender's name is.
X   */
X  len = strlen(fromcopy);
X  for (cur = ignore; cur->name; ++cur)
X    if (len >= cur->len && !strncasecmp(cur->name, fromcopy, cur->len))
X      return(1);
X  return(0);
X}
X
X/* int
X * recent(from)
X *	char *from;
X *
X *	find out if user has gotten a vacation message recently.
X */
Xrecent(from)
X     char *from;
X{
X  DATUM k, d;
X  long now, then;
X
X  k.dptr = from;
X  k.dsize = strlen(from)+1;
X  d = fetch(k);
X  if (d.dptr) {
X    now = time(0L);
X    (void) memcpy((char *)&then,d.dptr,sizeof(then));
X    if (!then || then+period > now)
X      return(1);
X  }
X  return(0);
X}
X
X/* void
X * setreply(from)
X *	char *from;
X *
X * Store that this user knows about the vacation.
X */
Xvoid
Xsetreply(from)
X     char *from;
X{
X  DATUM k, d;
X  long sentdate;
X
X  k.dptr = from;
X  k.dsize = strlen(from)+1;
X  sentdate = time(0L);
X  d.dptr = (char *)(&sentdate);
X  d.dsize = sizeof(long);
X  if (store(k,d)) {
X    (void) sprintf(msgbuf,"vacation: store() failed for %s\n",from);
X    errlog(msgbuf);
X  }
X}
X
X/* void
X * sendmessage(myname,to)
X *	char *myname, *to;
X *
X * Execute sendmail to send the vacation file to sender.
X */
Xvoid
Xsendmessage(myname,to)
X     char *myname, *to;
X{
X  if (!freopen(VMSG, "r", stdin)) {
X    (void) sprintf(msgbuf,"vacation: no ~%s/%s file\n", myname, VMSG);
X    errlog(msgbuf);
X  }
X  execl("/usr/lib/sendmail", "sendmail", "-f", myname, to, (char *)NULL);
X  (void) sprintf(msgbuf,"vacation: execl failed, to = %s\n",to);
X  errlog(msgbuf);
X}
X
X/* void
X * initialize()
X *
X * Initialize the dbm database.
X */
Xvoid
Xinitialize()
X{
X  extern int errno;
X  extern char *sys_errlist[];
X  int fd;
X
X  if ((fd = open(VDIR, O_WRONLY|O_CREAT|O_TRUNC, 0644)) < 0) {
X    (void) sprintf(msgbuf,"vacation: %s: %s\n", VDIR, sys_errlist[errno]);
X    errlog(msgbuf);
X  }
X  (void) close(fd);
X  if ((fd = open(VPAG, O_WRONLY|O_CREAT|O_TRUNC, 0644)) < 0) {
X    (void) sprintf(msgbuf,"vacation: %s: %s\n", VPAG, sys_errlist[errno]);
X    errlog(msgbuf);
X  }
X  (void) close(fd);
X  (void) dbminit(VACAT);
X}
X
X/* int
X * strncasecmp(char *s1, char *s2, int n)
X *
X * Compare the first n characters of s2 with s1 (case insensitive).
X * Return zero if they are equal.
X */
Xstrncasecmp(s1,s2,n)
X     register char *s1, *s2;
X     register int n;
X{
X  while (n--)
X    if (tolower(*s1++) != tolower(*s2++))
X      return(1);
X  return(0);
X}
X
X#include <status.h>
X
Xvoid
Xerrlog(msg)
X     char *msg;
X{
X  extern void eprintf();
X
X  eprintf(ST_OTHER,ST_DISPLAY,(char *)NULL,msg);
X  exit(0);
X}
END_OF_FILE
if test 12153 -ne `wc -c <'vacation.c'`; then
    echo shar: \"'vacation.c'\" unpacked with wrong size!
fi
# end of 'vacation.c'
fi
echo shar: End of shell archive.
exit 0

