/*	Copyright (c) 1990 UNIX System Laboratories, Inc.	*/
/*	Copyright (c) 1984, 1986, 1987, 1988, 1989, 1990 AT&T	*/
/*	  All Rights Reserved  	*/

/*	THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF     	*/
/*	UNIX System Laboratories, Inc.                     	*/
/*	The copyright notice above does not evidence any   	*/
/*	actual or intended publication of such source code.	*/

#ident	"@(#)ucbsendmail:aux/vacation.c	1.1.2.1"

/*******************************************************************

		PROPRIETARY NOTICE (Combined)

This source code is unpublished proprietary information
constituting, or derived under license from AT&T's UNIX(r) System V.
In addition, portions of such source code were derived from Berkeley
4.3 BSD under license from the Regents of the University of
California.



		Copyright Notice 

Notice of copyright on this source code product does not indicate 
publication.

	(c) 1986,1987,1988,1989  Sun Microsystems, Inc
	(c) 1983,1984,1985,1986,1987,1988,1989  AT&T.
	          All rights reserved.
********************************************************************/ 

/*
**  Vacation
**  Copyright (c) 1983  Eric P. Allman
**  Berkeley, California
**
**  Copyright (c) 1983 Regents of the University of California.
**  All rights reserved.  The Berkeley software License Agreement
**  specifies the terms and conditions for redistribution.
*/

#ifndef lint
static char	SccsId[] = "@(#)vacation.c 1.7 87/10/14 SMI"; /* from UCB 5.3 7/1/85 */
#endif not lint

# include <pwd.h>
# include <stdio.h>
# include <sysexits.h>
# include <ctype.h>
# include "useful.h"
# include "userdbm.h"

/*
**  VACATION -- return a message to the sender when on vacation.
**
**	This program could be invoked as a message receiver
**	when someone is on vacation.  It returns a message
**	specified by the user to whoever sent the mail, taking
**	care not to return a message too often to prevent
**	"I am on vacation" loops.
**
**	For best operation, this program should run setuid to
**	root or uucp or someone else that sendmail will believe
**	a -f flag from.  Otherwise, the user must be careful
**	to include a header on his .vacation.msg file.
**
**	Positional Parameters:
**		the user to collect the vacation message from.
**
**	Flag Parameters:
**		-I	initialize the database.
**		-d	turn on debugging.
**		-tT	set the timeout to T.  messages arriving more
**			often than T will be ignored to avoid loops.
**
**	Side Effects:
**		A message is sent back to the sender.
**
**	Author:
**		Eric Allman
**		UCB/INGRES
*/

# define MAXLINE	256	/* max size of a line */

# define ONEWEEK	(60L*60L*24L*7L)
# define MsgFile "/.vacation.msg"

time_t	Timeout = ONEWEEK;	/* timeout between notices per user */

struct dbrec
{
	long	sentdate;
};

bool	Debug = FALSE;
bool	AnswerAll = FALSE;	/* default to answer if in To:/Cc: only */
char	*myname;		/* name of person "on vacation" */
char	*homedir;		/* home directory of said person */
char	*Subject = NULL;	/* subject in message header */
char	*AliasList[MAXLINE];	/* list of aliases to allow */
int	AliasCount = 0;

extern char *newstr();
main(argc, argv)
	char **argv;
{
	char *from;
	register char *p;
	struct passwd *pw;
	char *shortfrom;
	char buf[MAXLINE];
	extern struct passwd *getpwnam();
	extern char *getfrom();
	extern bool knows();
	extern bool junkmail();
	extern time_t convtime();

	/* process arguments */
	while (--argc > 0 && (p = *++argv) != NULL && *p == '-')
	{
		switch (*++p)
		{
		  case 'a':	/* answer all mail, even if not in To/Cc */
			AliasList[AliasCount++] = argv[1];
			if (argc > 0) {
				argc--; argv++;
			}
			break;

		  case 'd':	/* debug */
			Debug = TRUE;
			break;

		  case 'I':	/* initialize */
			initialize();
			exit(EX_OK);

		  case 'j':	/* answer all mail, even if not in To/Cc */
			AnswerAll = TRUE;
			break;

		  case 't':	/* set timeout */
			Timeout = convtime(++p);
			break;

		  default:
			usrerr("Unknown flag -%s", p);
			exit(EX_USAGE);
		}
	}

	/* verify recipient argument */
	if (argc != 1)
	{
		if (argc == 0)
			AutoInstall();
		else
			usrerr("Usage: vacation username (or) vacation -I");
		exit(EX_USAGE);
	}

	myname = p;

	/* find user's home directory */
	pw = getpwnam(myname);
	if (pw == NULL)
	{
		usrerr("Unknown user %s", myname);
		exit(EX_NOUSER);
	}
	homedir = newstr(pw->pw_dir);

	(void) strcpy(buf, homedir);
	(void) strcat(buf, "/.vacation");
	dbminit(buf);

	/* read message from standard input (just from line) */
	from = getfrom(&shortfrom);

	/* check if junk mail or this person is already informed */
	if (!junkmail(shortfrom) && !knows(shortfrom))
	{
		/* mark this person as knowing */
		setknows(shortfrom);

		/* send the message back */
		(void) strcpy(buf, homedir);
		(void) strcat(buf, MsgFile);
		if (Debug)
			printf("Sending %s to %s\n", buf, from);
		else
		{
			sendmessage(buf, from, myname);
			/*NOTREACHED*/
		}
	}
	exit (EX_OK);
}
/*
**  GETFROM -- read message from standard input and return sender
**
**	Parameters:
**		none.
**
**	Returns:
**		pointer to the sender address.
**
**	Side Effects:
**		Reads first line from standard input.
*/

char *
getfrom(shortp)
char **shortp;
{
	static char line[MAXLINE];
	register char *p, *start, *at, *bang;
	char saveat;

	/* read the from line */
	if (fgets(line, sizeof line, stdin) == NULL ||
	    strncmp(line, "From ", 5) != NULL)
	{
		usrerr("No initial From line");
		exit(EX_USAGE);
	}

	/* find the end of the sender address and terminate it */
	start = &line[5];
	p = index(start, ' ');
	if (p == NULL)
	{
		usrerr("Funny From line '%s'", line);
		exit(EX_USAGE);
	}
	*p = '\0';

	/*
	 * Strip all but the rightmost UUCP host
	 * to prevent loops due to forwarding.
	 * Start searching leftward from the leftmost '@'.
	 *	a!b!c!d yields a short name of c!d
	 *	a!b!c!d@e yields a short name of c!d@e
	 *	e@a!b!c yields the same short name
	 */
#ifdef VDEBUG
printf("start='%s'\n", start);
#endif VDEBUG
	*shortp = start;			/* assume whole addr */
	if ((at = index(start, '@')) == NULL)	/* leftmost '@' */
		at = p;				/* if none, use end of addr */
	saveat = *at;
	*at = '\0';
	if ((bang = rindex(start, '!')) != NULL) {	/* rightmost '!' */
		char *bang2;
		*bang = '\0';
		if ((bang2 = rindex(start, '!')) != NULL) /* 2nd rightmost '!' */
			*shortp = bang2 + 1;		/* move past ! */
		*bang = '!';
	}
	*at = saveat;
#ifdef VDEBUG
printf("place='%s'\n", *shortp);
#endif VDEBUG

	/* return the sender address */
	return start;
}
/*
**  JUNKMAIL -- read the header and tell us if this is junk/bulk mail.
**
**	Parameters:
**		from -- the Return-Path of the sender.  We assume that
**			anything from "*-REQUEST@*" is bulk mail.
**
**	Returns:
**		TRUE -- if this is junk or bulk mail (that is, if the
**			sender shouldn't receive a response).
**		FALSE -- if the sender deserves a response.
**
**	Side Effects:
**		May read the header from standard input.  When this
**		returns the position on stdin is undefined.
*/

bool
junkmail(from)
	char *from;
{
	register char *p;
	char buf[MAXLINE+1];
#ifndef SYSV
	extern char *index();
	extern char *rindex();
#endif
	extern char *strtok();
	extern bool sameword();
	bool inside, foundto, onlist;

	/* test for inhuman sender */
	p = rindex(from, '@');
	if (p != NULL)
	{
		*p = '\0';
		if (sameword(&p[-8],  "-REQUEST") ||
		    sameword(&p[-10], "Postmaster") ||
		    sameword(&p[-13], "MAILER-DAEMON"))
		{
			*p = '@';
			return (TRUE);
		}
		*p = '@';
	}

# define Delims " \t:,:;()<>@.!"

	/* read the header looking for "interesting" lines */
	inside = FALSE;
	onlist = FALSE;
	foundto = FALSE;
	while (fgets(buf, MAXLINE, stdin) != NULL && buf[0] != '\n')
	{
		if (buf[0]!=' ' && buf[0] != '\t' && index(buf,':') == NULL)
			return(FALSE);			/* no header found */

		p = strtok(buf, Delims);
		if (p==NULL)
			continue;

		if (sameword(p, "To") || sameword(p, "Cc"))
		{
			inside = TRUE;
			p = strtok(NULL, Delims);
			if (p==NULL)
				continue;

		}
		else				/* continuation line? */
		    if (inside)			
		    	inside =  (buf[0]==' ' || buf[0]=='\t');

		if (inside) {
		    int i;

		    do {
			if (sameword(p,myname))
				onlist = TRUE;		/* I am on the list */

			for (i=0;i<AliasCount;i++)
			    if (sameword(p,AliasList[i]))
				onlist = TRUE;		/* alias on list */

		    } while (p = strtok(NULL, Delims));
		    foundto = TRUE;
		    continue;
		}
		
		if (sameword(p, "Precedence"))
		{
			/* find the value of this field */
			p = strtok(NULL, Delims);
			if (p == NULL)
				continue;

			/* see if it is "junk" or "bulk" */
			p[4] = '\0';
			if (sameword(p, "junk") || sameword(p, "bulk"))
				return (TRUE);
		}

		if (sameword(p, "Subject"))
		{
			Subject = newstr(buf+9);
			if (p = rindex(Subject,'\n'))
				*p = '\0';
			if (Debug) 
				printf("Subject=%s\n", Subject);
		}
	}
	if (AnswerAll)
		return (FALSE);
	else
		return (foundto && !onlist);
}
/*
**  KNOWS -- predicate telling if user has already been informed.
**
**	Parameters:
**		user -- the user who sent this message.
**
**	Returns:
**		TRUE if 'user' has already been informed that the
**			recipient is on vacation.
**		FALSE otherwise.
**
**	Side Effects:
**		none.
*/

bool
knows(user)
	char *user;
{
	DATUM k, d;
	long now;
	auto long then;

	time(&now);
	k.dptr = user;
	k.dsize = strlen(user) + 1;
	d = fetch(k);
	if (d.dptr == NULL)
		return (FALSE);
	
	/* be careful on 68k's and others with alignment restrictions */
	bcopy((char *) &((struct dbrec *) d.dptr)->sentdate, (char *) &then, sizeof then);
	if (then + Timeout < now)
		return (FALSE);
	if (Debug)
		printf("User %s already knows\n");
	return (TRUE);
}
/*
**  SETKNOWS -- set that this user knows about the vacation.
**
**	Parameters:
**		user -- the user who should be marked.
**
**	Returns:
**		none.
**
**	Side Effects:
**		The dbm file is updated as appropriate.
*/

setknows(user)
	char *user;
{
	DATUM k, d;
	struct dbrec xrec;

	k.dptr = user;
	k.dsize = strlen(user) + 1;
	time(&xrec.sentdate);
	d.dptr = (char *) &xrec;
	d.dsize = sizeof xrec;
	store(k, d);
}
/*
**  SENDMESSAGE -- send a message to a particular user.
**
**	Parameters:
**		msgf -- filename containing the message.
**		user -- user who should receive it.
**
**	Returns:
**		none.
**
**	Side Effects:
**		sends mail to 'user' using /usr/lib/sendmail.
*/

sendmessage(msgf, user, myname)
	char *msgf;
	char *user;
	char *myname;
{
	FILE *f, *pipe;
	char line[MAXLINE];
	char *p;

	/* find the message to send */
	f = fopen(msgf, "r");
	if (f == NULL)
	{
		f = fopen("/usr/ucblib/vacation.def", "r");
		if (f == NULL)
			syserr("No message to send");
	}

	(void) sprintf(line, "/usr/ucblib/sendmail -eq -f %s %s", myname, user);
	pipe = popen(line, "w");
	if (pipe == NULL)
		syserr("Cannot exec /usr/ucblib/sendmail");
	while (fgets(line, MAXLINE, f)) {
		p = index(line,'$');
		if (p && strncmp(p,"$SUBJECT",8)==0) {
			*p = '\0';
			fputs(line, pipe);
			fputs(Subject, pipe);
			fputs(p+8, pipe);
			continue;
		}
		fputs(line, pipe);
	}
	pclose(pipe);
	fclose(f);
}
/*
**  INITIALIZE -- initialize the database before leaving for vacation
**
**	Parameters:
**		none.
**
**	Returns:
**		none.
**
**	Side Effects:
**		Initializes the files .vacation.{pag,dir} in the
**		caller's home directory.
*/

initialize()
{
	char *homedir;
	char buf[MAXLINE];
	extern char *getenv();

	setgid(getgid());
	setuid(getuid());
	homedir = getenv("HOME");
	if (homedir == NULL)
		syserr("No home!");
	(void) strcpy(buf, homedir);
	(void) strcat(buf, "/.vacation.dir");
	if (close(creat(buf, 0644)) < 0)
		syserr("Cannot create %s", buf);
	(void) strcpy(buf, homedir);
	(void) strcat(buf, "/.vacation.pag");
	if (close(creat(buf, 0644)) < 0)
		syserr("Cannot create %s", buf);
}
/*
**  USRERR -- print user error
**
**	Parameters:
**		f -- format.
**		p -- first parameter.
**
**	Returns:
**		none.
**
**	Side Effects:
**		none.
*/

usrerr(f, a,b,c,d)
	char *f;
{
	fprintf(stderr, "vacation: ");
	fprintf(stderr,f,a,b,c,d);
	fprintf(stderr, "\n");
}
/*
**  SYSERR -- print system error
**
**	Parameters:
**		f -- format.
**		p -- first parameter.
**
**	Returns:
**		none.
**
**	Side Effects:
**		none.
*/

syserr(f, a, b, c, d)
	char *f;
{
	fprintf(stderr, "vacation: ");
	fprintf(stderr,f,a,b,c,d);
	fprintf(stderr, "\n");
	exit(EX_USAGE);
}
/*
**  NEWSTR -- copy a string
**
**	Parameters:
**		s -- the string to copy.
**
**	Returns:
**		A copy of the string.
**
**	Side Effects:
**		none.
*/

char *
newstr(s)
	char *s;
{
	char *p;
	extern char *malloc();

	p = malloc((unsigned)strlen(s) + 1);
	if (p == NULL)
	{
		syserr("newstr: cannot alloc memory");
		exit(EX_OSERR);
	}
	strcpy(p, s);
	return (p);
}
/*
**  SAMEWORD -- return TRUE if the words are the same
**
**	Ignores case.
**
**	Parameters:
**		a, b -- the words to compare.
**
**	Returns:
**		TRUE if a & b match exactly (modulo case)
**		FALSE otherwise.
**
**	Side Effects:
**		none.
*/

bool
sameword(a, b)
	register char *a, *b;
{
	char ca, cb;

	do
	{
		ca = *a++;
		cb = *b++;
		if (isascii(ca) && isupper(ca))
			ca = ca - 'A' + 'a';
		if (isascii(cb) && isupper(cb))
			cb = cb - 'A' + 'a';
	} while (ca != '\0' && ca == cb);
	return (ca == cb);
}

/*
 * When invoked with no arguments, we fall into an automatic installation
 * mode, stepping the user through a default installation.
 */
AutoInstall()
{
	char file[MAXLINE];
	char forward[MAXLINE];
	char cmd[MAXLINE];
	char line[MAXLINE];
	char *editor;
	FILE *f;

myname = getenv("USER");
homedir = getenv("HOME");
if (homedir == NULL)
	syserr("Home directory unknown");

printf("This program can be used to answer your mail automatically\n");
printf("when you go away on vacation.\n");
(void) strcpy(file, homedir);
(void) strcat(file, MsgFile);
do {
	f = fopen(file,"r");
	if (f) {
printf("You have a message file in %s.\n", file);
if (ask("Would you like to see it")) {
	sprintf(cmd, "/usr/bin/more %s", file);
	system(cmd);
}
if (ask("Would you like to edit it"))
		f = NULL;
	}
	else {
printf("You need to create a message file in %s first.\n", file);
		f = fopen(file,"w");
		if (myname)
fprintf(f,"From: %s (via the vacation program)\n", myname);
fprintf(f,"Subject: away from my mail\n");
fprintf(f,"\nI will not be reading my mail for a while.\n");
fprintf(f,"Your mail regarding \"$SUBJECT\" will be read when I return.\n");
		fclose(f);
		f = NULL;
	}
	if (f == NULL) {
		editor = getenv("VISUAL");
		if (editor == NULL)
			editor = getenv("EDITOR");
		if (editor == NULL)
			editor = "/usr/bin/vi";
		sprintf(cmd, "%s %s", editor, file);
printf("Please use your editor (%s) to edit this file.\n", editor);
printf("Press return when ready to edit this file.\n");
		fflush(stdout);
		gets(line);
		system(cmd);
	}
} while (f == NULL);
fclose(f);
(void) strcpy(forward, homedir);
(void) strcat(forward, "/.forward");
f = fopen(forward,"r");
if (f) {
printf("You have a .forward file in your home directory containing:\n");
while (fgets(line, MAXLINE, f))
	printf("    %s", line);
fclose(f);
if (!ask("Would you like to remove it and disable the vacation feature")) 
	exit(0);
if (unlink(forward))
	perror("Error removing .forward file:");
else
	printf("Back to normal reception of mail.\n");
exit(0);
}

printf("To enable the vacation feature a \".forward\" file is created.\n");
if (!ask("Would you like to enable the vacation feature")) {
	printf("OK, vacation feature NOT enabled.\n");
	exit(0);
}
f = fopen(forward,"w");
if (f==NULL) {
	perror("Error opening .forward file");
	exit(EX_USAGE);
}
fprintf(f,"\\%s, \"|/usr/ucb/vacation %s\"\n", myname, myname);
fclose(f);
printf("Vacation feature ENABLED. Please remember to turn it off when\n");
printf("you get back from vacation. Bon voyage.\n");

initialize();
}


/*
 * Ask the user a question until we get a reasonable answer
 */
ask(prompt)
	char *prompt;
{
	char line[MAXLINE];

	while (1) {
		printf("%s? ", prompt);
		fflush(stdout);
		gets(line);
		if (line[0]=='y' || line[0]=='Y') return(TRUE);
		if (line[0]=='n' || line[0]=='N') return(FALSE);
		printf("Please reply \"yes\" or \"no\" (\'y\' or \'n\')\n");
	}
	
}
