xref: /onnv-gate/usr/src/cmd/sendmail/aux/vacation.c (revision 2825:432df2a8510e)
10Sstevel@tonic-gate /*
21674Sjbeck  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
30Sstevel@tonic-gate  * Use is subject to license terms.
40Sstevel@tonic-gate  *
50Sstevel@tonic-gate  *	Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T
60Sstevel@tonic-gate  *	  All Rights Reserved
70Sstevel@tonic-gate  */
80Sstevel@tonic-gate 
90Sstevel@tonic-gate /*
100Sstevel@tonic-gate  *  Vacation
110Sstevel@tonic-gate  *  Copyright (c) 1983  Eric P. Allman
120Sstevel@tonic-gate  *  Berkeley, California
130Sstevel@tonic-gate  *
140Sstevel@tonic-gate  *  Copyright (c) 1983 Regents of the University of California.
150Sstevel@tonic-gate  *  All rights reserved.  The Berkeley software License Agreement
160Sstevel@tonic-gate  *  specifies the terms and conditions for redistribution.
170Sstevel@tonic-gate  */
180Sstevel@tonic-gate 
190Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
200Sstevel@tonic-gate 
210Sstevel@tonic-gate #ifndef lint
220Sstevel@tonic-gate static char	SccsId[] = "%W%	%E% SMI";
230Sstevel@tonic-gate #endif /* not lint */
240Sstevel@tonic-gate 
250Sstevel@tonic-gate #include <stdio.h>
260Sstevel@tonic-gate #include <stdarg.h>
270Sstevel@tonic-gate #include <stdlib.h>
280Sstevel@tonic-gate #include <unistd.h>
290Sstevel@tonic-gate #include <sysexits.h>
300Sstevel@tonic-gate #include <pwd.h>
310Sstevel@tonic-gate #include <ndbm.h>
320Sstevel@tonic-gate #include <string.h>
330Sstevel@tonic-gate #include <ctype.h>
340Sstevel@tonic-gate #include <fcntl.h>
350Sstevel@tonic-gate #include <strings.h>
360Sstevel@tonic-gate #include <errno.h>
370Sstevel@tonic-gate 
380Sstevel@tonic-gate /*
390Sstevel@tonic-gate  *  VACATION -- return a message to the sender when on vacation.
400Sstevel@tonic-gate  *
410Sstevel@tonic-gate  *	This program could be invoked as a message receiver
420Sstevel@tonic-gate  *	when someone is on vacation.  It returns a message
430Sstevel@tonic-gate  *	specified by the user to whoever sent the mail, taking
440Sstevel@tonic-gate  *	care not to return a message too often to prevent
450Sstevel@tonic-gate  *	"I am on vacation" loops.
460Sstevel@tonic-gate  *
470Sstevel@tonic-gate  *	For best operation, this program should run setuid to
480Sstevel@tonic-gate  *	root or uucp or someone else that sendmail will believe
490Sstevel@tonic-gate  *	a -f flag from.  Otherwise, the user must be careful
500Sstevel@tonic-gate  *	to include a header on his .vacation.msg file.
510Sstevel@tonic-gate  *
520Sstevel@tonic-gate  *	Positional Parameters:
530Sstevel@tonic-gate  *		the user to collect the vacation message from.
540Sstevel@tonic-gate  *
550Sstevel@tonic-gate  *	Flag Parameters:
560Sstevel@tonic-gate  *		-I	initialize the database.
570Sstevel@tonic-gate  *		-d	turn on debugging.
580Sstevel@tonic-gate  *		-tT	set the timeout to T.  messages arriving more
590Sstevel@tonic-gate  *			often than T will be ignored to avoid loops.
600Sstevel@tonic-gate  *
610Sstevel@tonic-gate  *	Side Effects:
620Sstevel@tonic-gate  *		A message is sent back to the sender.
630Sstevel@tonic-gate  *
640Sstevel@tonic-gate  *	Author:
650Sstevel@tonic-gate  *		Eric Allman
660Sstevel@tonic-gate  *		UCB/INGRES
670Sstevel@tonic-gate  */
680Sstevel@tonic-gate 
690Sstevel@tonic-gate #define	MAXLINE	256	/* max size of a line */
700Sstevel@tonic-gate 
710Sstevel@tonic-gate #define	ONEWEEK	(60L*60L*24L*7L)
720Sstevel@tonic-gate #define	MsgFile "/.vacation.msg"
730Sstevel@tonic-gate #define	FilterFile "/.vacation.filter"
740Sstevel@tonic-gate #define	DbFileBase "/.vacation"
750Sstevel@tonic-gate #define	_PATH_TMP	"/tmp/vacation.XXXXXX"
760Sstevel@tonic-gate 
770Sstevel@tonic-gate typedef int bool;
780Sstevel@tonic-gate 
790Sstevel@tonic-gate #define	FALSE	0
800Sstevel@tonic-gate #define	TRUE	1
810Sstevel@tonic-gate 
820Sstevel@tonic-gate static time_t	Timeout = ONEWEEK;	/* timeout between notices per user */
830Sstevel@tonic-gate static DBM	*db;
840Sstevel@tonic-gate static bool	Debug = FALSE;
85*2825Sjbeck static bool	ListMode = FALSE;
860Sstevel@tonic-gate static bool	AnswerAll = FALSE;	/* default: answer if in To:/Cc: only */
870Sstevel@tonic-gate static char	*Subject = NULL;	/* subject in message header */
880Sstevel@tonic-gate static char	*EncodedSubject = NULL;	/* subject in message header */
890Sstevel@tonic-gate static char	Charset[MAXLINE];	/* for use in reply message */
900Sstevel@tonic-gate static char	*AliasList[MAXLINE];	/* list of aliases to allow */
910Sstevel@tonic-gate static int	AliasCount = 0;
920Sstevel@tonic-gate static char	*myname;		/* name of person "on vacation" */
930Sstevel@tonic-gate static char	*homedir;		/* home directory of said person */
940Sstevel@tonic-gate 
950Sstevel@tonic-gate extern time_t	convtime(char *, char);
960Sstevel@tonic-gate extern bool	decode_rfc2047(char *, char *, char *);
970Sstevel@tonic-gate 
980Sstevel@tonic-gate static bool	ask(char *);
990Sstevel@tonic-gate static bool	junkmail(char *);
1000Sstevel@tonic-gate static bool	filter_ok(char *, char *);
1010Sstevel@tonic-gate static bool	knows(char *);
1020Sstevel@tonic-gate static bool	sameword(char *, char *);
1030Sstevel@tonic-gate static char	*getfrom(char **);
1040Sstevel@tonic-gate static char	*newstr(char *);
1050Sstevel@tonic-gate static void	AutoInstall();
1060Sstevel@tonic-gate static void	initialize(char *);
1070Sstevel@tonic-gate static void	sendmessage(char *, char *, char *);
1080Sstevel@tonic-gate static void	setknows(char *);
109*2825Sjbeck static void	dumplist();
1100Sstevel@tonic-gate 
1110Sstevel@tonic-gate void	usrerr(const char *, ...);
1120Sstevel@tonic-gate 
1130Sstevel@tonic-gate int
main(argc,argv)1140Sstevel@tonic-gate main(argc, argv)
1150Sstevel@tonic-gate 	int argc;
1160Sstevel@tonic-gate 	char **argv;
1170Sstevel@tonic-gate {
1180Sstevel@tonic-gate 	char *from;
1190Sstevel@tonic-gate 	char *p, *at, *c;
1200Sstevel@tonic-gate 	struct passwd *pw;
1210Sstevel@tonic-gate 	char *shortfrom;
1220Sstevel@tonic-gate 	char buf[MAXLINE];
1230Sstevel@tonic-gate 	char *message_file = MsgFile;
1240Sstevel@tonic-gate 	char *db_file_base = DbFileBase;
1250Sstevel@tonic-gate 	char *filter_file = FilterFile;
1260Sstevel@tonic-gate 	char *sender;
1270Sstevel@tonic-gate 	bool sender_oob = FALSE;
1280Sstevel@tonic-gate 	bool initialize_only = FALSE;
1290Sstevel@tonic-gate 
1300Sstevel@tonic-gate 	/* process arguments */
1310Sstevel@tonic-gate 	while (--argc > 0 && (p = *++argv) != NULL && *p == '-')
1320Sstevel@tonic-gate 	{
1330Sstevel@tonic-gate 		switch (*++p)
1340Sstevel@tonic-gate 		{
1350Sstevel@tonic-gate 		    case 'a':	/* add this to list of acceptable aliases */
1360Sstevel@tonic-gate 			AliasList[AliasCount++] = argv[1];
1370Sstevel@tonic-gate 			if (argc > 0) {
1380Sstevel@tonic-gate 				argc--; argv++;
1390Sstevel@tonic-gate 			}
1400Sstevel@tonic-gate 			break;
1410Sstevel@tonic-gate 
1420Sstevel@tonic-gate 		    case 'd':	/* debug */
1430Sstevel@tonic-gate 			Debug = TRUE;
1440Sstevel@tonic-gate 			break;
1450Sstevel@tonic-gate 
1460Sstevel@tonic-gate 		    case 'e':	/* alternate filter file */
1470Sstevel@tonic-gate 			filter_file = argv[1];
1480Sstevel@tonic-gate 			if (argc > 0) {
1490Sstevel@tonic-gate 				argc--; argv++;
1500Sstevel@tonic-gate 			}
1510Sstevel@tonic-gate 			break;
1520Sstevel@tonic-gate 
1530Sstevel@tonic-gate 		    case 'f':	/* alternate database file name base */
1540Sstevel@tonic-gate 			db_file_base = argv[1];
1550Sstevel@tonic-gate 			if (argc > 0) {
1560Sstevel@tonic-gate 				argc--; argv++;
1570Sstevel@tonic-gate 			}
1580Sstevel@tonic-gate 			break;
1590Sstevel@tonic-gate 
1600Sstevel@tonic-gate 		    case 'I':	/* initialize */
1610Sstevel@tonic-gate 			initialize_only = TRUE;
1620Sstevel@tonic-gate 			break;
1630Sstevel@tonic-gate 
1640Sstevel@tonic-gate 		    case 'j':	/* answer all mail, even if not in To/Cc */
1650Sstevel@tonic-gate 			AnswerAll = TRUE;
1660Sstevel@tonic-gate 			break;
1670Sstevel@tonic-gate 
168*2825Sjbeck 		    case 'l':	/* list all respondees */
169*2825Sjbeck 			ListMode = TRUE;
170*2825Sjbeck 			break;
171*2825Sjbeck 
1720Sstevel@tonic-gate 		    case 'm':	/* alternate message file */
1730Sstevel@tonic-gate 			message_file = argv[1];
1740Sstevel@tonic-gate 			if (argc > 0) {
1750Sstevel@tonic-gate 				argc--; argv++;
1760Sstevel@tonic-gate 			}
1770Sstevel@tonic-gate 			break;
1780Sstevel@tonic-gate 
1790Sstevel@tonic-gate 		    case 's':	/* sender: use this instead of getfrom() */
1800Sstevel@tonic-gate 			sender = argv[1];
1810Sstevel@tonic-gate 			sender_oob = TRUE;
1820Sstevel@tonic-gate 			if (argc > 0) {
1830Sstevel@tonic-gate 				argc--; argv++;
1840Sstevel@tonic-gate 			}
1850Sstevel@tonic-gate 			break;
1860Sstevel@tonic-gate 
1870Sstevel@tonic-gate 		    case 't':	/* set timeout */
1880Sstevel@tonic-gate 			Timeout = convtime(++p, 'w');
1890Sstevel@tonic-gate 			break;
1900Sstevel@tonic-gate 
1910Sstevel@tonic-gate 		    default:
1920Sstevel@tonic-gate 			usrerr("Unknown flag -%s", p);
1930Sstevel@tonic-gate 			exit(EX_USAGE);
1940Sstevel@tonic-gate 		}
1950Sstevel@tonic-gate 	}
1960Sstevel@tonic-gate 
1970Sstevel@tonic-gate 	if (initialize_only)
1980Sstevel@tonic-gate 	{
1990Sstevel@tonic-gate 		initialize(db_file_base);
2000Sstevel@tonic-gate 		exit(EX_OK);
2010Sstevel@tonic-gate 	}
2020Sstevel@tonic-gate 
2030Sstevel@tonic-gate 	/* verify recipient argument */
204*2825Sjbeck 	if (argc == 0 && !ListMode)
2050Sstevel@tonic-gate 		AutoInstall();
2060Sstevel@tonic-gate 
207*2825Sjbeck 	if (argc != 1 && !ListMode)
2080Sstevel@tonic-gate 	{
209*2825Sjbeck 		usrerr("Usage:\tvacation username\n\tvacation -I\n"
210*2825Sjbeck 		    "\tvacation -l");
2110Sstevel@tonic-gate 		exit(EX_USAGE);
2120Sstevel@tonic-gate 	}
2130Sstevel@tonic-gate 
2140Sstevel@tonic-gate 	myname = p;
2150Sstevel@tonic-gate 	Charset[0] = '\0';
2160Sstevel@tonic-gate 
2170Sstevel@tonic-gate 	/* find user's home directory */
218*2825Sjbeck 	if (ListMode)
219*2825Sjbeck 		pw = getpwuid(getuid());
220*2825Sjbeck 	else
221*2825Sjbeck 		pw = getpwnam(myname);
2220Sstevel@tonic-gate 	if (pw == NULL)
2230Sstevel@tonic-gate 	{
2240Sstevel@tonic-gate 		usrerr("user %s look up failed, name services outage ?",
2250Sstevel@tonic-gate 		    myname);
2260Sstevel@tonic-gate 		exit(EX_TEMPFAIL);
2270Sstevel@tonic-gate 	}
2280Sstevel@tonic-gate 	homedir = newstr(pw->pw_dir);
2290Sstevel@tonic-gate 
2300Sstevel@tonic-gate 	(void) snprintf(buf, sizeof (buf), "%s%s%s", homedir,
2310Sstevel@tonic-gate 			(db_file_base[0] == '/') ? "" : "/", db_file_base);
2320Sstevel@tonic-gate 	if (!(db = dbm_open(buf, O_RDWR, 0))) {
2330Sstevel@tonic-gate 		usrerr("%s: %s\n", buf, strerror(errno));
2340Sstevel@tonic-gate 		exit(EX_DATAERR);
2350Sstevel@tonic-gate 	}
2360Sstevel@tonic-gate 
237*2825Sjbeck 	if (ListMode) {
238*2825Sjbeck 		dumplist();
239*2825Sjbeck 		exit(EX_OK);
240*2825Sjbeck 	}
241*2825Sjbeck 
2420Sstevel@tonic-gate 	if (sender_oob)
2430Sstevel@tonic-gate 	{
2440Sstevel@tonic-gate 		at = strchr(sender, '@');
2450Sstevel@tonic-gate 		if (at != NULL)
2460Sstevel@tonic-gate 			for (c = at + 1; *c; c++)
2470Sstevel@tonic-gate 				*c = (char)tolower((char)*c);
2480Sstevel@tonic-gate 		from = sender;
2490Sstevel@tonic-gate 		shortfrom = sender;
2500Sstevel@tonic-gate 	}
2510Sstevel@tonic-gate 	else
2520Sstevel@tonic-gate 		/* read message from standard input (just from line) */
2530Sstevel@tonic-gate 		from = getfrom(&shortfrom);
2540Sstevel@tonic-gate 
2550Sstevel@tonic-gate 	/* check if junk mail or this person is already informed */
2560Sstevel@tonic-gate 	if (!junkmail(shortfrom) && filter_ok(shortfrom, filter_file) &&
2570Sstevel@tonic-gate 	    !knows(shortfrom))
2580Sstevel@tonic-gate 	{
2590Sstevel@tonic-gate 		/* mark this person as knowing */
2600Sstevel@tonic-gate 		setknows(shortfrom);
2610Sstevel@tonic-gate 
2620Sstevel@tonic-gate 		/* send the message back */
2630Sstevel@tonic-gate 		(void) strlcpy(buf, homedir, sizeof (buf));
2640Sstevel@tonic-gate 		if (message_file[0] != '/')
2650Sstevel@tonic-gate 		    (void) strlcat(buf, "/", sizeof (buf));
2660Sstevel@tonic-gate 		(void) strlcat(buf, message_file, sizeof (buf));
2670Sstevel@tonic-gate 		if (Debug)
2680Sstevel@tonic-gate 			printf("Sending %s to %s\n", buf, from);
2690Sstevel@tonic-gate 		else
2700Sstevel@tonic-gate 		{
2710Sstevel@tonic-gate 			sendmessage(buf, from, myname);
2720Sstevel@tonic-gate 			/*NOTREACHED*/
2730Sstevel@tonic-gate 		}
2740Sstevel@tonic-gate 	}
2751674Sjbeck 	while (fgets(buf, MAXLINE, stdin) != NULL)
2761674Sjbeck 		continue; /* drain input */
2770Sstevel@tonic-gate 	return (EX_OK);
2780Sstevel@tonic-gate }
2790Sstevel@tonic-gate 
280*2825Sjbeck struct entry {
281*2825Sjbeck 	time_t	when;
282*2825Sjbeck 	long	when_size;
283*2825Sjbeck 	char	*who;
284*2825Sjbeck 	long	who_size;
285*2825Sjbeck 	struct	entry *next;
286*2825Sjbeck 	struct	entry *prev;
287*2825Sjbeck };
288*2825Sjbeck 
289*2825Sjbeck static void
dump_content(key_size,key_ptr,content_size,content_ptr)290*2825Sjbeck dump_content(key_size, key_ptr, content_size, content_ptr)
291*2825Sjbeck 	long key_size, content_size;
292*2825Sjbeck 	char *key_ptr, *content_ptr;
293*2825Sjbeck {
294*2825Sjbeck 	time_t then;
295*2825Sjbeck 
296*2825Sjbeck 	if (content_size == sizeof (then)) {
297*2825Sjbeck 		bcopy(content_ptr, (char *)&then, sizeof (then));
298*2825Sjbeck 		(void) printf("%-53.40*s: %s", (int)key_size, key_ptr,
299*2825Sjbeck 		    ctime(&then));
300*2825Sjbeck 	} else {
301*2825Sjbeck 		(void) fprintf(stderr, "content size error: %d\n",
302*2825Sjbeck 		    (int)content_size);
303*2825Sjbeck 	}
304*2825Sjbeck }
305*2825Sjbeck 
306*2825Sjbeck static void
dump_all_content(first)307*2825Sjbeck dump_all_content(first)
308*2825Sjbeck 	struct entry *first;
309*2825Sjbeck {
310*2825Sjbeck 	struct entry *which;
311*2825Sjbeck 
312*2825Sjbeck 	for (which = first; which != NULL; which = which->next) {
313*2825Sjbeck 		dump_content(which->who_size, which->who, which->when_size,
314*2825Sjbeck 		    (char *)&(which->when));
315*2825Sjbeck 	}
316*2825Sjbeck }
317*2825Sjbeck 
318*2825Sjbeck static void
dumplist()319*2825Sjbeck dumplist()
320*2825Sjbeck {
321*2825Sjbeck 	datum content, key;
322*2825Sjbeck 	struct entry *first = NULL, *last = NULL, *new_entry, *curr;
323*2825Sjbeck 
324*2825Sjbeck 	for (key = dbm_firstkey(db); key.dptr != NULL; key = dbm_nextkey(db)) {
325*2825Sjbeck 		content = dbm_fetch(db, key);
326*2825Sjbeck 		new_entry = (struct entry *)malloc(sizeof (struct entry));
327*2825Sjbeck 		if (new_entry == NULL)
328*2825Sjbeck 			perror("out of memory");
329*2825Sjbeck 		new_entry->next = NULL;
330*2825Sjbeck 		new_entry->who = (char *)malloc(key.dsize);
331*2825Sjbeck 		if (new_entry->who == NULL)
332*2825Sjbeck 			perror("out of memory");
333*2825Sjbeck 		new_entry->who_size = key.dsize;
334*2825Sjbeck 		(void) strlcpy(new_entry->who, key.dptr, key.dsize);
335*2825Sjbeck 		bcopy(content.dptr, (char *)&(new_entry->when),
336*2825Sjbeck 		    sizeof (new_entry->when));
337*2825Sjbeck 		new_entry->when_size = content.dsize;
338*2825Sjbeck 		if (first == NULL) { /* => so is last */
339*2825Sjbeck 			new_entry->prev = NULL;
340*2825Sjbeck 			new_entry->next = NULL;
341*2825Sjbeck 			first = new_entry;
342*2825Sjbeck 			last = new_entry;
343*2825Sjbeck 		} else {
344*2825Sjbeck 			for (curr = first; curr != NULL &&
345*2825Sjbeck 			    new_entry->when > curr->when; curr = curr->next)
346*2825Sjbeck 				;
347*2825Sjbeck 			if (curr == NULL) {
348*2825Sjbeck 				last->next = new_entry;
349*2825Sjbeck 				new_entry->prev = last;
350*2825Sjbeck 				new_entry->next = NULL;
351*2825Sjbeck 				last = new_entry;
352*2825Sjbeck 			} else {
353*2825Sjbeck 				new_entry->next = curr;
354*2825Sjbeck 				new_entry->prev = curr->prev;
355*2825Sjbeck 				if (curr->prev == NULL)
356*2825Sjbeck 					first = new_entry;
357*2825Sjbeck 				else
358*2825Sjbeck 					curr->prev->next = new_entry;
359*2825Sjbeck 				curr->prev = new_entry;
360*2825Sjbeck 			}
361*2825Sjbeck 		}
362*2825Sjbeck 	}
363*2825Sjbeck 	dump_all_content(first);
364*2825Sjbeck 	dbm_close(db);
365*2825Sjbeck }
366*2825Sjbeck 
3670Sstevel@tonic-gate /*
3680Sstevel@tonic-gate  *  GETFROM -- read message from standard input and return sender
3690Sstevel@tonic-gate  *
3700Sstevel@tonic-gate  *	Parameters:
3710Sstevel@tonic-gate  *		none.
3720Sstevel@tonic-gate  *
3730Sstevel@tonic-gate  *	Returns:
3740Sstevel@tonic-gate  *		pointer to the sender address.
3750Sstevel@tonic-gate  *
3760Sstevel@tonic-gate  *	Side Effects:
3770Sstevel@tonic-gate  *		Reads first line from standard input.
3780Sstevel@tonic-gate  */
3790Sstevel@tonic-gate 
3800Sstevel@tonic-gate static char *
getfrom(shortp)3810Sstevel@tonic-gate getfrom(shortp)
3820Sstevel@tonic-gate char **shortp;
3830Sstevel@tonic-gate {
3840Sstevel@tonic-gate 	static char line[MAXLINE];
3850Sstevel@tonic-gate 	char *p, *start, *at, *bang, *c;
3860Sstevel@tonic-gate 	char saveat;
3870Sstevel@tonic-gate 
3880Sstevel@tonic-gate 	/* read the from line */
3890Sstevel@tonic-gate 	if (fgets(line, sizeof (line), stdin) == NULL ||
3900Sstevel@tonic-gate 	    strncmp(line, "From ", 5) != NULL)
3910Sstevel@tonic-gate 	{
3920Sstevel@tonic-gate 		usrerr("No initial From line");
3930Sstevel@tonic-gate 		exit(EX_PROTOCOL);
3940Sstevel@tonic-gate 	}
3950Sstevel@tonic-gate 
3960Sstevel@tonic-gate 	/* find the end of the sender address and terminate it */
3970Sstevel@tonic-gate 	start = &line[5];
3980Sstevel@tonic-gate 	p = strchr(start, ' ');
3990Sstevel@tonic-gate 	if (p == NULL)
4000Sstevel@tonic-gate 	{
4010Sstevel@tonic-gate 		usrerr("Funny From line '%s'", line);
4020Sstevel@tonic-gate 		exit(EX_PROTOCOL);
4030Sstevel@tonic-gate 	}
4040Sstevel@tonic-gate 	*p = '\0';
4050Sstevel@tonic-gate 
4060Sstevel@tonic-gate 	/*
4070Sstevel@tonic-gate 	 * Strip all but the rightmost UUCP host
4080Sstevel@tonic-gate 	 * to prevent loops due to forwarding.
4090Sstevel@tonic-gate 	 * Start searching leftward from the leftmost '@'.
4100Sstevel@tonic-gate 	 *	a!b!c!d yields a short name of c!d
4110Sstevel@tonic-gate 	 *	a!b!c!d@e yields a short name of c!d@e
4120Sstevel@tonic-gate 	 *	e@a!b!c yields the same short name
4130Sstevel@tonic-gate 	 */
4140Sstevel@tonic-gate #ifdef VDEBUG
4150Sstevel@tonic-gate printf("start='%s'\n", start);
4160Sstevel@tonic-gate #endif /* VDEBUG */
4170Sstevel@tonic-gate 	*shortp = start;			/* assume whole addr */
4180Sstevel@tonic-gate 	if ((at = strchr(start, '@')) == NULL)	/* leftmost '@' */
4190Sstevel@tonic-gate 		at = p;				/* if none, use end of addr */
4200Sstevel@tonic-gate 	saveat = *at;
4210Sstevel@tonic-gate 	*at = '\0';
4220Sstevel@tonic-gate 	if ((bang = strrchr(start, '!')) != NULL) {	/* rightmost '!' */
4230Sstevel@tonic-gate 		char *bang2;
4240Sstevel@tonic-gate 		*bang = '\0';
4250Sstevel@tonic-gate 		/* 2nd rightmost '!' */
4260Sstevel@tonic-gate 		if ((bang2 = strrchr(start, '!')) != NULL)
4270Sstevel@tonic-gate 			*shortp = bang2 + 1;		/* move past ! */
4280Sstevel@tonic-gate 		*bang = '!';
4290Sstevel@tonic-gate 	}
4300Sstevel@tonic-gate 	*at = saveat;
4310Sstevel@tonic-gate #ifdef VDEBUG
4320Sstevel@tonic-gate printf("place='%s'\n", *shortp);
4330Sstevel@tonic-gate #endif /* VDEBUG */
4340Sstevel@tonic-gate 	for (c = at + 1; *c; c++)
4350Sstevel@tonic-gate 		*c = (char)tolower((char)*c);
4360Sstevel@tonic-gate 
4370Sstevel@tonic-gate 	/* return the sender address */
4380Sstevel@tonic-gate 	return (start);
4390Sstevel@tonic-gate }
4400Sstevel@tonic-gate 
4410Sstevel@tonic-gate /*
4420Sstevel@tonic-gate  *  JUNKMAIL -- read the header and tell us if this is junk/bulk mail.
4430Sstevel@tonic-gate  *
4440Sstevel@tonic-gate  *	Parameters:
4450Sstevel@tonic-gate  *		from -- the Return-Path of the sender.  We assume that
4460Sstevel@tonic-gate  *			anything from "*-REQUEST@*" is bulk mail.
4470Sstevel@tonic-gate  *
4480Sstevel@tonic-gate  *	Returns:
4490Sstevel@tonic-gate  *		TRUE -- if this is junk or bulk mail (that is, if the
4500Sstevel@tonic-gate  *			sender shouldn't receive a response).
4510Sstevel@tonic-gate  *		FALSE -- if the sender deserves a response.
4520Sstevel@tonic-gate  *
4530Sstevel@tonic-gate  *	Side Effects:
4540Sstevel@tonic-gate  *		May read the header from standard input.  When this
4550Sstevel@tonic-gate  *		returns the position on stdin is undefined.
4560Sstevel@tonic-gate  */
4570Sstevel@tonic-gate 
4580Sstevel@tonic-gate static bool
junkmail(from)4590Sstevel@tonic-gate junkmail(from)
4600Sstevel@tonic-gate 	char *from;
4610Sstevel@tonic-gate {
4620Sstevel@tonic-gate 	register char *p;
4630Sstevel@tonic-gate 	char buf[MAXLINE+1];
4640Sstevel@tonic-gate 	bool inside, onlist;
4650Sstevel@tonic-gate 
4660Sstevel@tonic-gate 	/* test for inhuman sender */
4670Sstevel@tonic-gate 	p = strrchr(from, '@');
4680Sstevel@tonic-gate 	if (p != NULL)
4690Sstevel@tonic-gate 	{
4700Sstevel@tonic-gate 		*p = '\0';
4710Sstevel@tonic-gate 		if (sameword(&p[-8],  "-REQUEST") ||
4720Sstevel@tonic-gate 		    sameword(&p[-10], "Postmaster") ||
4730Sstevel@tonic-gate 		    sameword(&p[-13], "MAILER-DAEMON"))
4740Sstevel@tonic-gate 		{
4750Sstevel@tonic-gate 			*p = '@';
4760Sstevel@tonic-gate 			return (TRUE);
4770Sstevel@tonic-gate 		}
4780Sstevel@tonic-gate 		*p = '@';
4790Sstevel@tonic-gate 	}
4800Sstevel@tonic-gate 
4810Sstevel@tonic-gate #define	Delims " \n\t:,:;()<>@!"
4820Sstevel@tonic-gate 
4830Sstevel@tonic-gate 	/* read the header looking for "interesting" lines */
4840Sstevel@tonic-gate 	inside = FALSE;
4850Sstevel@tonic-gate 	onlist = FALSE;
4860Sstevel@tonic-gate 	while (fgets(buf, MAXLINE, stdin) != NULL && buf[0] != '\n')
4870Sstevel@tonic-gate 	{
4880Sstevel@tonic-gate 		if (buf[0] != ' ' && buf[0] != '\t' && strchr(buf, ':') == NULL)
4890Sstevel@tonic-gate 			return (FALSE);			/* no header found */
4900Sstevel@tonic-gate 
4910Sstevel@tonic-gate 		p = strtok(buf, Delims);
4920Sstevel@tonic-gate 		if (p == NULL)
4930Sstevel@tonic-gate 			continue;
4940Sstevel@tonic-gate 
4950Sstevel@tonic-gate 		if (sameword(p, "To") || sameword(p, "Cc"))
4960Sstevel@tonic-gate 		{
4970Sstevel@tonic-gate 			inside = TRUE;
4980Sstevel@tonic-gate 			p = strtok((char *)NULL, Delims);
4990Sstevel@tonic-gate 			if (p == NULL)
5000Sstevel@tonic-gate 				continue;
5010Sstevel@tonic-gate 
5020Sstevel@tonic-gate 		} else				/* continuation line? */
5030Sstevel@tonic-gate 		    if (inside)
5040Sstevel@tonic-gate 			inside =  (buf[0] == ' ' || buf[0] == '\t');
5050Sstevel@tonic-gate 
5060Sstevel@tonic-gate 		if (inside) {
5070Sstevel@tonic-gate 		    int i;
5080Sstevel@tonic-gate 
5090Sstevel@tonic-gate 		    do {
5100Sstevel@tonic-gate 			if (sameword(p, myname))
5110Sstevel@tonic-gate 				onlist = TRUE;		/* I am on the list */
5120Sstevel@tonic-gate 
5130Sstevel@tonic-gate 			for (i = 0; i < AliasCount; i++)
5140Sstevel@tonic-gate 			    if (sameword(p, AliasList[i]))
5150Sstevel@tonic-gate 				onlist = TRUE;		/* alias on list */
5160Sstevel@tonic-gate 
5170Sstevel@tonic-gate 		    } while (p = strtok((char *)NULL, Delims));
5180Sstevel@tonic-gate 		    continue;
5190Sstevel@tonic-gate 		}
5200Sstevel@tonic-gate 
5210Sstevel@tonic-gate 		if (sameword(p, "Precedence"))
5220Sstevel@tonic-gate 		{
5230Sstevel@tonic-gate 			/* find the value of this field */
5240Sstevel@tonic-gate 			p = strtok((char *)NULL, Delims);
5250Sstevel@tonic-gate 			if (p == NULL)
5260Sstevel@tonic-gate 				continue;
5270Sstevel@tonic-gate 
5280Sstevel@tonic-gate 			/* see if it is "junk" or "bulk" */
5290Sstevel@tonic-gate 			p[4] = '\0';
5300Sstevel@tonic-gate 			if (sameword(p, "junk") || sameword(p, "bulk"))
5310Sstevel@tonic-gate 				return (TRUE);
5320Sstevel@tonic-gate 		}
5330Sstevel@tonic-gate 
5340Sstevel@tonic-gate 		if (sameword(p, "Subject"))
5350Sstevel@tonic-gate 		{
5360Sstevel@tonic-gate 			char *decoded_subject;
5370Sstevel@tonic-gate 
5380Sstevel@tonic-gate 			Subject = newstr(buf+9);
5390Sstevel@tonic-gate 			if (p = strrchr(Subject, '\n'))
5400Sstevel@tonic-gate 				*p = '\0';
5410Sstevel@tonic-gate 			EncodedSubject = newstr(Subject);
5420Sstevel@tonic-gate 			decoded_subject = newstr(Subject);
5430Sstevel@tonic-gate 			if (decode_rfc2047(Subject, decoded_subject, Charset))
5440Sstevel@tonic-gate 				Subject = decoded_subject;
5450Sstevel@tonic-gate 			else
5460Sstevel@tonic-gate 				Charset[0] = '\0';
5470Sstevel@tonic-gate 			if (Debug)
5480Sstevel@tonic-gate 				printf("Subject=%s\n", Subject);
5490Sstevel@tonic-gate 		}
5500Sstevel@tonic-gate 	}
5510Sstevel@tonic-gate 	if (AnswerAll)
5520Sstevel@tonic-gate 		return (FALSE);
5530Sstevel@tonic-gate 	else
5540Sstevel@tonic-gate 		return (!onlist);
5550Sstevel@tonic-gate }
5560Sstevel@tonic-gate 
5570Sstevel@tonic-gate /*
5580Sstevel@tonic-gate  *  FILTER_OK -- see if the Return-Path is in the filter file.
5590Sstevel@tonic-gate  *		 Note that a non-existent filter file means everything
5600Sstevel@tonic-gate  *		 is OK, but an empty file means nothing is OK.
5610Sstevel@tonic-gate  *
5620Sstevel@tonic-gate  *	Parameters:
5630Sstevel@tonic-gate  *		from -- the Return-Path of the sender.
5640Sstevel@tonic-gate  *
5650Sstevel@tonic-gate  *	Returns:
5660Sstevel@tonic-gate  *		TRUE -- if this is in the filter file
5670Sstevel@tonic-gate  *			(sender should receive a response).
5680Sstevel@tonic-gate  *		FALSE -- if the sender does not deserve a response.
5690Sstevel@tonic-gate  */
5700Sstevel@tonic-gate 
5710Sstevel@tonic-gate static bool
filter_ok(from,filter_file)5720Sstevel@tonic-gate filter_ok(from, filter_file)
5730Sstevel@tonic-gate 	char *from;
5740Sstevel@tonic-gate 	char *filter_file;
5750Sstevel@tonic-gate {
5760Sstevel@tonic-gate 	char file[MAXLINE];
5770Sstevel@tonic-gate 	char line[MAXLINE];
578431Sgww 	char *match_start;
5790Sstevel@tonic-gate 	size_t line_len, from_len;
5800Sstevel@tonic-gate 	bool result = FALSE;
581431Sgww 	bool negated = FALSE;
5820Sstevel@tonic-gate 	FILE *f;
5830Sstevel@tonic-gate 
5840Sstevel@tonic-gate 	from_len = strlen(from);
5850Sstevel@tonic-gate 	(void) strlcpy(file, homedir, sizeof (file));
5860Sstevel@tonic-gate 	if (filter_file[0] != '/')
5870Sstevel@tonic-gate 	    (void) strlcat(file, "/", sizeof (file));
5880Sstevel@tonic-gate 	(void) strlcat(file, filter_file, sizeof (file));
5890Sstevel@tonic-gate 	f = fopen(file, "r");
5900Sstevel@tonic-gate 	if (f == NULL) {
5910Sstevel@tonic-gate 		/*
5920Sstevel@tonic-gate 		 * If the file does not exist, then there is no filter to
5930Sstevel@tonic-gate 		 * apply, so we simply return TRUE.
5940Sstevel@tonic-gate 		 */
5950Sstevel@tonic-gate 		if (Debug)
5960Sstevel@tonic-gate 			(void) printf("%s does not exist, filter ok.\n",
5970Sstevel@tonic-gate 			    file);
5980Sstevel@tonic-gate 		return (TRUE);
5990Sstevel@tonic-gate 	}
6000Sstevel@tonic-gate 	while (fgets(line, MAXLINE, f)) {
6010Sstevel@tonic-gate 		line_len = strlen(line);
6020Sstevel@tonic-gate 		/* zero out trailing newline */
6030Sstevel@tonic-gate 		if (line[line_len - 1] == '\n')
6040Sstevel@tonic-gate 			line[--line_len] = '\0';
6050Sstevel@tonic-gate 		/* skip blank lines */
6060Sstevel@tonic-gate 		if (line_len == 0)
6070Sstevel@tonic-gate 			continue;
6080Sstevel@tonic-gate 		/* skip comment lines */
6090Sstevel@tonic-gate 		if (line[0] == '#')
6100Sstevel@tonic-gate 			continue;
611431Sgww 		if (line[0] == '!') {
612431Sgww 			negated = TRUE;
613431Sgww 			match_start = &line[1];
614431Sgww 			line_len--;
615431Sgww 		} else {
616431Sgww 			negated = FALSE;
617431Sgww 			match_start = &line[0];
618431Sgww 		}
6190Sstevel@tonic-gate 		if (strchr(line, '@') != NULL) {
6200Sstevel@tonic-gate 			/* @ => full address */
621431Sgww 			if (strcasecmp(match_start, from) == 0) {
6220Sstevel@tonic-gate 				result = TRUE;
6230Sstevel@tonic-gate 				if (Debug)
6240Sstevel@tonic-gate 					(void) printf("filter match on %s\n",
6250Sstevel@tonic-gate 					    line);
6260Sstevel@tonic-gate 				break;
6270Sstevel@tonic-gate 			}
6280Sstevel@tonic-gate 		} else {
6290Sstevel@tonic-gate 			/* no @ => domain */
6300Sstevel@tonic-gate 			if (from_len <= line_len)
6310Sstevel@tonic-gate 				continue;
6320Sstevel@tonic-gate 			/*
6330Sstevel@tonic-gate 			 * Make sure the last part of from is the domain line
6340Sstevel@tonic-gate 			 * and that the character immediately preceding is an
6350Sstevel@tonic-gate 			 * '@' or a '.', otherwise we could get false positives
6360Sstevel@tonic-gate 			 * from e.g. twinsun.com for sun.com .
6370Sstevel@tonic-gate 			 */
638431Sgww 			if (strncasecmp(&from[from_len - line_len],
639431Sgww 			    match_start, line_len) == 0 &&
6400Sstevel@tonic-gate 			    (from[from_len - line_len -1] == '@' ||
6410Sstevel@tonic-gate 			    from[from_len - line_len -1] == '.')) {
6420Sstevel@tonic-gate 				result = TRUE;
6430Sstevel@tonic-gate 				if (Debug)
6440Sstevel@tonic-gate 					(void) printf("filter match on %s\n",
6450Sstevel@tonic-gate 					    line);
6460Sstevel@tonic-gate 				break;
6470Sstevel@tonic-gate 			}
6480Sstevel@tonic-gate 		}
6490Sstevel@tonic-gate 	}
6500Sstevel@tonic-gate 	(void) fclose(f);
6510Sstevel@tonic-gate 	if (Debug && !result)
6520Sstevel@tonic-gate 		(void) printf("no filter match\n");
653431Sgww 	return (!negated && result);
6540Sstevel@tonic-gate }
6550Sstevel@tonic-gate 
6560Sstevel@tonic-gate /*
6570Sstevel@tonic-gate  *  KNOWS -- predicate telling if user has already been informed.
6580Sstevel@tonic-gate  *
6590Sstevel@tonic-gate  *	Parameters:
6600Sstevel@tonic-gate  *		user -- the user who sent this message.
6610Sstevel@tonic-gate  *
6620Sstevel@tonic-gate  *	Returns:
6630Sstevel@tonic-gate  *		TRUE if 'user' has already been informed that the
6640Sstevel@tonic-gate  *			recipient is on vacation.
6650Sstevel@tonic-gate  *		FALSE otherwise.
6660Sstevel@tonic-gate  *
6670Sstevel@tonic-gate  *	Side Effects:
6680Sstevel@tonic-gate  *		none.
6690Sstevel@tonic-gate  */
6700Sstevel@tonic-gate 
6710Sstevel@tonic-gate static bool
knows(user)6720Sstevel@tonic-gate knows(user)
6730Sstevel@tonic-gate 	char *user;
6740Sstevel@tonic-gate {
6750Sstevel@tonic-gate 	datum key, data;
6760Sstevel@tonic-gate 	time_t now, then;
6770Sstevel@tonic-gate 
6780Sstevel@tonic-gate 	(void) time(&now);
6790Sstevel@tonic-gate 	key.dptr = user;
6800Sstevel@tonic-gate 	key.dsize = strlen(user) + 1;
6810Sstevel@tonic-gate 	data = dbm_fetch(db, key);
6820Sstevel@tonic-gate 	if (data.dptr == NULL)
6830Sstevel@tonic-gate 		return (FALSE);
6840Sstevel@tonic-gate 
6850Sstevel@tonic-gate 	bcopy(data.dptr, (char *)&then, sizeof (then));
6860Sstevel@tonic-gate 	if (then + Timeout < now)
6870Sstevel@tonic-gate 		return (FALSE);
6880Sstevel@tonic-gate 	if (Debug)
6890Sstevel@tonic-gate 		printf("User %s already knows\n", user);
6900Sstevel@tonic-gate 	return (TRUE);
6910Sstevel@tonic-gate }
6920Sstevel@tonic-gate 
6930Sstevel@tonic-gate /*
6940Sstevel@tonic-gate  *  SETKNOWS -- set that this user knows about the vacation.
6950Sstevel@tonic-gate  *
6960Sstevel@tonic-gate  *	Parameters:
6970Sstevel@tonic-gate  *		user -- the user who should be marked.
6980Sstevel@tonic-gate  *
6990Sstevel@tonic-gate  *	Returns:
7000Sstevel@tonic-gate  *		none.
7010Sstevel@tonic-gate  *
7020Sstevel@tonic-gate  *	Side Effects:
7030Sstevel@tonic-gate  *		The dbm file is updated as appropriate.
7040Sstevel@tonic-gate  */
7050Sstevel@tonic-gate 
7060Sstevel@tonic-gate static void
setknows(user)7070Sstevel@tonic-gate setknows(user)
7080Sstevel@tonic-gate 	char *user;
7090Sstevel@tonic-gate {
7100Sstevel@tonic-gate 	datum key, data;
7110Sstevel@tonic-gate 	time_t now;
7120Sstevel@tonic-gate 
7130Sstevel@tonic-gate 	key.dptr = user;
7140Sstevel@tonic-gate 	key.dsize = strlen(user) + 1;
7150Sstevel@tonic-gate 	(void) time(&now);
7160Sstevel@tonic-gate 	data.dptr = (char *)&now;
7170Sstevel@tonic-gate 	data.dsize = sizeof (now);
7180Sstevel@tonic-gate 	dbm_store(db, key, data, DBM_REPLACE);
7190Sstevel@tonic-gate }
7200Sstevel@tonic-gate 
7210Sstevel@tonic-gate static bool
any8bitchars(line)7220Sstevel@tonic-gate any8bitchars(line)
7230Sstevel@tonic-gate 	char *line;
7240Sstevel@tonic-gate {
7250Sstevel@tonic-gate 	char *c;
7260Sstevel@tonic-gate 
7270Sstevel@tonic-gate 	for (c = line; *c; c++)
7280Sstevel@tonic-gate 		if (*c & 0x80)
7290Sstevel@tonic-gate 			return (TRUE);
7300Sstevel@tonic-gate 	return (FALSE);
7310Sstevel@tonic-gate }
7320Sstevel@tonic-gate 
7330Sstevel@tonic-gate /*
7340Sstevel@tonic-gate  *  SENDMESSAGE -- send a message to a particular user.
7350Sstevel@tonic-gate  *
7360Sstevel@tonic-gate  *	Parameters:
7370Sstevel@tonic-gate  *		msgf -- filename containing the message.
7380Sstevel@tonic-gate  *		user -- user who should receive it.
7390Sstevel@tonic-gate  *
7400Sstevel@tonic-gate  *	Returns:
7410Sstevel@tonic-gate  *		none.
7420Sstevel@tonic-gate  *
7430Sstevel@tonic-gate  *	Side Effects:
7440Sstevel@tonic-gate  *		sends mail to 'user' using /usr/lib/sendmail.
7450Sstevel@tonic-gate  */
7460Sstevel@tonic-gate 
7470Sstevel@tonic-gate static void
sendmessage(msgf,user,myname)7480Sstevel@tonic-gate sendmessage(msgf, user, myname)
7490Sstevel@tonic-gate 	char *msgf;
7500Sstevel@tonic-gate 	char *user;
7510Sstevel@tonic-gate 	char *myname;
7520Sstevel@tonic-gate {
7530Sstevel@tonic-gate 	FILE *f, *fpipe, *tmpf;
7540Sstevel@tonic-gate 	char line[MAXLINE];
7550Sstevel@tonic-gate 	char *p, *tmpf_name;
7560Sstevel@tonic-gate 	int i, pipefd[2], tmpfd;
7570Sstevel@tonic-gate 	bool seen8bitchars = FALSE;
7580Sstevel@tonic-gate 	bool in_header = TRUE;
7590Sstevel@tonic-gate 
7600Sstevel@tonic-gate 	/* find the message to send */
7610Sstevel@tonic-gate 	f = fopen(msgf, "r");
7620Sstevel@tonic-gate 	if (f == NULL)
7630Sstevel@tonic-gate 	{
7640Sstevel@tonic-gate 		f = fopen("/etc/mail/vacation.def", "r");
7650Sstevel@tonic-gate 		if (f == NULL)
7660Sstevel@tonic-gate 			usrerr("No message to send");
7670Sstevel@tonic-gate 			exit(EX_OSFILE);
7680Sstevel@tonic-gate 	}
7690Sstevel@tonic-gate 
7700Sstevel@tonic-gate 	if (pipe(pipefd) < 0) {
7710Sstevel@tonic-gate 		usrerr("pipe() failed");
7720Sstevel@tonic-gate 		exit(EX_OSERR);
7730Sstevel@tonic-gate 	}
7740Sstevel@tonic-gate 	i = fork();
7750Sstevel@tonic-gate 	if (i < 0) {
7760Sstevel@tonic-gate 		usrerr("fork() failed");
7770Sstevel@tonic-gate 		exit(EX_OSERR);
7780Sstevel@tonic-gate 	}
7790Sstevel@tonic-gate 	if (i == 0) {
7800Sstevel@tonic-gate 		dup2(pipefd[0], 0);
7810Sstevel@tonic-gate 		close(pipefd[0]);
7820Sstevel@tonic-gate 		close(pipefd[1]);
7830Sstevel@tonic-gate 		fclose(f);
7840Sstevel@tonic-gate 		execl("/usr/lib/sendmail", "sendmail", "-eq", "-f", myname,
7850Sstevel@tonic-gate 			"--", user, NULL);
7860Sstevel@tonic-gate 		usrerr("can't exec /usr/lib/sendmail");
7870Sstevel@tonic-gate 		exit(EX_OSERR);
7880Sstevel@tonic-gate 	}
7890Sstevel@tonic-gate 	close(pipefd[0]);
7900Sstevel@tonic-gate 	fpipe = fdopen(pipefd[1], "w");
7910Sstevel@tonic-gate 	if (fpipe == NULL) {
7920Sstevel@tonic-gate 		usrerr("fdopen() failed");
7930Sstevel@tonic-gate 		exit(EX_OSERR);
7940Sstevel@tonic-gate 	}
7950Sstevel@tonic-gate 	fprintf(fpipe, "To: %s\n", user);
7960Sstevel@tonic-gate 	fputs("Auto-Submitted: auto-replied\n", fpipe);
7970Sstevel@tonic-gate 	fputs("X-Mailer: vacation %I%\n", fpipe);
7980Sstevel@tonic-gate 
7990Sstevel@tonic-gate 	/*
8000Sstevel@tonic-gate 	 * We used to write directly to the pipe.  But now we need to know
8010Sstevel@tonic-gate 	 * what character set to use, and we need to examine the entire
8020Sstevel@tonic-gate 	 * message to determine this.  So write to a temp file first.
8030Sstevel@tonic-gate 	 */
8040Sstevel@tonic-gate 	tmpf_name = strdup(_PATH_TMP);
8050Sstevel@tonic-gate 	if (tmpf_name == NULL) {
8060Sstevel@tonic-gate 		usrerr("newstr: cannot alloc memory");
8070Sstevel@tonic-gate 		exit(EX_OSERR);
8080Sstevel@tonic-gate 	}
8090Sstevel@tonic-gate 	tmpfd = -1;
8100Sstevel@tonic-gate 	tmpfd = mkstemp(tmpf_name);
8110Sstevel@tonic-gate 	if (tmpfd == -1) {
8120Sstevel@tonic-gate 		usrerr("can't open temp file %s", tmpf_name);
8130Sstevel@tonic-gate 		exit(EX_OSERR);
8140Sstevel@tonic-gate 	}
8150Sstevel@tonic-gate 	tmpf = fdopen(tmpfd, "w");
8160Sstevel@tonic-gate 	if (tmpf == NULL) {
8170Sstevel@tonic-gate 		usrerr("can't open temp file %s", tmpf_name);
8180Sstevel@tonic-gate 		exit(EX_OSERR);
8190Sstevel@tonic-gate 	}
8200Sstevel@tonic-gate 	while (fgets(line, MAXLINE, f)) {
8210Sstevel@tonic-gate 		/*
8220Sstevel@tonic-gate 		 * Check for a line with no ':' character.  If it's just \n,
8230Sstevel@tonic-gate 		 * we're at the end of the headers and all is fine.  Or if
8240Sstevel@tonic-gate 		 * it starts with white-space, then it's a continuation header.
8250Sstevel@tonic-gate 		 * Otherwise, it's the start of the body, which means the
8260Sstevel@tonic-gate 		 * header/body separator was skipped.  So output it.
8270Sstevel@tonic-gate 		 */
8280Sstevel@tonic-gate 		if (in_header && line[0] != '\0' && strchr(line, ':') == NULL) {
8290Sstevel@tonic-gate 			if (line[0] == '\n')
8300Sstevel@tonic-gate 				in_header = FALSE;
8310Sstevel@tonic-gate 			else if (!isspace(line[0])) {
8320Sstevel@tonic-gate 				in_header = FALSE;
8330Sstevel@tonic-gate 				fputs("\n", tmpf);
8340Sstevel@tonic-gate 			}
8350Sstevel@tonic-gate 		}
8360Sstevel@tonic-gate 		p = strchr(line, '$');
8370Sstevel@tonic-gate 		if (p && strncmp(p, "$SUBJECT", 8) == 0) {
8380Sstevel@tonic-gate 			*p = '\0';
8390Sstevel@tonic-gate 			seen8bitchars |= any8bitchars(line);
8400Sstevel@tonic-gate 			fputs(line, tmpf);
8410Sstevel@tonic-gate 			if (Subject) {
8420Sstevel@tonic-gate 				if (in_header)
8430Sstevel@tonic-gate 					fputs(EncodedSubject, tmpf);
8440Sstevel@tonic-gate 				else {
8450Sstevel@tonic-gate 					seen8bitchars |= any8bitchars(Subject);
8460Sstevel@tonic-gate 					fputs(Subject, tmpf);
8470Sstevel@tonic-gate 				}
8480Sstevel@tonic-gate 			}
8490Sstevel@tonic-gate 			seen8bitchars |= any8bitchars(p+8);
8500Sstevel@tonic-gate 			fputs(p+8, tmpf);
8510Sstevel@tonic-gate 			continue;
8520Sstevel@tonic-gate 		}
8530Sstevel@tonic-gate 		seen8bitchars |= any8bitchars(line);
8540Sstevel@tonic-gate 		fputs(line, tmpf);
8550Sstevel@tonic-gate 	}
8560Sstevel@tonic-gate 	fclose(f);
8570Sstevel@tonic-gate 	fclose(tmpf);
8580Sstevel@tonic-gate 
8590Sstevel@tonic-gate 	/*
8600Sstevel@tonic-gate 	 * If we haven't seen a funky Subject with Charset, use the default.
8610Sstevel@tonic-gate 	 * If we have and it's us-ascii, 8-bit chars in the message file will
8620Sstevel@tonic-gate 	 * still result in iso-8859-1.
8630Sstevel@tonic-gate 	 */
8640Sstevel@tonic-gate 	if (Charset[0] == '\0')
8650Sstevel@tonic-gate 		(void) strlcpy(Charset, (seen8bitchars) ? "iso-8859-1" :
8660Sstevel@tonic-gate 		    "us-ascii", sizeof (Charset));
8670Sstevel@tonic-gate 	else if ((strcasecmp(Charset, "us-ascii") == 0) && seen8bitchars)
8680Sstevel@tonic-gate 		(void) strlcpy(Charset, "iso-8859-1", sizeof (Charset));
8690Sstevel@tonic-gate 	if (Debug)
8700Sstevel@tonic-gate 		printf("Charset is %s\n", Charset);
8710Sstevel@tonic-gate 	fprintf(fpipe, "Content-Type: text/plain; charset=%s\n", Charset);
8720Sstevel@tonic-gate 	fputs("Mime-Version: 1.0\n", fpipe);
8730Sstevel@tonic-gate 
8740Sstevel@tonic-gate 	/*
8750Sstevel@tonic-gate 	 * Now read back in from the temp file and write to the pipe.
8760Sstevel@tonic-gate 	 */
8770Sstevel@tonic-gate 	tmpf = fopen(tmpf_name, "r");
8780Sstevel@tonic-gate 	if (tmpf == NULL) {
8790Sstevel@tonic-gate 		usrerr("can't open temp file %s", tmpf_name);
8800Sstevel@tonic-gate 		exit(EX_OSERR);
8810Sstevel@tonic-gate 	}
8820Sstevel@tonic-gate 	while (fgets(line, MAXLINE, tmpf))
8830Sstevel@tonic-gate 		fputs(line, fpipe);
8840Sstevel@tonic-gate 	fclose(fpipe);
8850Sstevel@tonic-gate 	fclose(tmpf);
8860Sstevel@tonic-gate 	(void) unlink(tmpf_name);
8870Sstevel@tonic-gate 	free(tmpf_name);
8880Sstevel@tonic-gate }
8890Sstevel@tonic-gate 
8900Sstevel@tonic-gate /*
8910Sstevel@tonic-gate  *  INITIALIZE -- initialize the database before leaving for vacation
8920Sstevel@tonic-gate  *
8930Sstevel@tonic-gate  *	Parameters:
8940Sstevel@tonic-gate  *		none.
8950Sstevel@tonic-gate  *
8960Sstevel@tonic-gate  *	Returns:
8970Sstevel@tonic-gate  *		none.
8980Sstevel@tonic-gate  *
8990Sstevel@tonic-gate  *	Side Effects:
9000Sstevel@tonic-gate  *		Initializes the files .vacation.{pag,dir} in the
9010Sstevel@tonic-gate  *		caller's home directory.
9020Sstevel@tonic-gate  */
9030Sstevel@tonic-gate 
9040Sstevel@tonic-gate static void
initialize(db_file_base)9050Sstevel@tonic-gate initialize(db_file_base)
9060Sstevel@tonic-gate 	char *db_file_base;
9070Sstevel@tonic-gate {
9080Sstevel@tonic-gate 	char *homedir;
9090Sstevel@tonic-gate 	char buf[MAXLINE];
9100Sstevel@tonic-gate 	DBM *db;
9110Sstevel@tonic-gate 
9120Sstevel@tonic-gate 	setgid(getgid());
9130Sstevel@tonic-gate 	setuid(getuid());
9140Sstevel@tonic-gate 	homedir = getenv("HOME");
9150Sstevel@tonic-gate 	if (homedir == NULL) {
9160Sstevel@tonic-gate 		usrerr("No home!");
9170Sstevel@tonic-gate 		exit(EX_NOUSER);
9180Sstevel@tonic-gate 	}
9190Sstevel@tonic-gate 	(void) snprintf(buf, sizeof (buf), "%s%s%s", homedir,
9200Sstevel@tonic-gate 		(db_file_base[0] == '/') ? "" : "/", db_file_base);
9210Sstevel@tonic-gate 
9220Sstevel@tonic-gate 	if (!(db = dbm_open(buf, O_WRONLY|O_CREAT|O_TRUNC, 0644))) {
9230Sstevel@tonic-gate 		usrerr("%s: %s\n", buf, strerror(errno));
9240Sstevel@tonic-gate 		exit(EX_DATAERR);
9250Sstevel@tonic-gate 	}
9260Sstevel@tonic-gate 	dbm_close(db);
9270Sstevel@tonic-gate }
9280Sstevel@tonic-gate 
9290Sstevel@tonic-gate /*
9300Sstevel@tonic-gate  *  USRERR -- print user error
9310Sstevel@tonic-gate  *
9320Sstevel@tonic-gate  *	Parameters:
9330Sstevel@tonic-gate  *		f -- format.
9340Sstevel@tonic-gate  *
9350Sstevel@tonic-gate  *	Returns:
9360Sstevel@tonic-gate  *		none.
9370Sstevel@tonic-gate  *
9380Sstevel@tonic-gate  *	Side Effects:
9390Sstevel@tonic-gate  *		none.
9400Sstevel@tonic-gate  */
9410Sstevel@tonic-gate 
9420Sstevel@tonic-gate /* PRINTFLIKE1 */
9430Sstevel@tonic-gate void
usrerr(const char * f,...)9440Sstevel@tonic-gate usrerr(const char *f, ...)
9450Sstevel@tonic-gate {
9460Sstevel@tonic-gate 	va_list alist;
9470Sstevel@tonic-gate 
9480Sstevel@tonic-gate 	va_start(alist, f);
9490Sstevel@tonic-gate 	(void) fprintf(stderr, "vacation: ");
9500Sstevel@tonic-gate 	(void) vfprintf(stderr, f, alist);
9510Sstevel@tonic-gate 	(void) fprintf(stderr, "\n");
9520Sstevel@tonic-gate 	va_end(alist);
9530Sstevel@tonic-gate }
9540Sstevel@tonic-gate 
9550Sstevel@tonic-gate /*
9560Sstevel@tonic-gate  *  NEWSTR -- copy a string
9570Sstevel@tonic-gate  *
9580Sstevel@tonic-gate  *	Parameters:
9590Sstevel@tonic-gate  *		s -- the string to copy.
9600Sstevel@tonic-gate  *
9610Sstevel@tonic-gate  *	Returns:
9620Sstevel@tonic-gate  *		A copy of the string.
9630Sstevel@tonic-gate  *
9640Sstevel@tonic-gate  *	Side Effects:
9650Sstevel@tonic-gate  *		none.
9660Sstevel@tonic-gate  */
9670Sstevel@tonic-gate 
9680Sstevel@tonic-gate static char *
newstr(s)9690Sstevel@tonic-gate newstr(s)
9700Sstevel@tonic-gate 	char *s;
9710Sstevel@tonic-gate {
9720Sstevel@tonic-gate 	char *p;
9730Sstevel@tonic-gate 	size_t s_sz = strlen(s);
9740Sstevel@tonic-gate 
9750Sstevel@tonic-gate 	p = malloc(s_sz + 1);
9760Sstevel@tonic-gate 	if (p == NULL)
9770Sstevel@tonic-gate 	{
9780Sstevel@tonic-gate 		usrerr("newstr: cannot alloc memory");
9790Sstevel@tonic-gate 		exit(EX_OSERR);
9800Sstevel@tonic-gate 	}
9810Sstevel@tonic-gate 	(void) strlcpy(p, s, s_sz + 1);
9820Sstevel@tonic-gate 	return (p);
9830Sstevel@tonic-gate }
9840Sstevel@tonic-gate 
9850Sstevel@tonic-gate /*
9860Sstevel@tonic-gate  *  SAMEWORD -- return TRUE if the words are the same
9870Sstevel@tonic-gate  *
9880Sstevel@tonic-gate  *	Ignores case.
9890Sstevel@tonic-gate  *
9900Sstevel@tonic-gate  *	Parameters:
9910Sstevel@tonic-gate  *		a, b -- the words to compare.
9920Sstevel@tonic-gate  *
9930Sstevel@tonic-gate  *	Returns:
9940Sstevel@tonic-gate  *		TRUE if a & b match exactly (modulo case)
9950Sstevel@tonic-gate  *		FALSE otherwise.
9960Sstevel@tonic-gate  *
9970Sstevel@tonic-gate  *	Side Effects:
9980Sstevel@tonic-gate  *		none.
9990Sstevel@tonic-gate  */
10000Sstevel@tonic-gate 
10010Sstevel@tonic-gate static bool
sameword(a,b)10020Sstevel@tonic-gate sameword(a, b)
10030Sstevel@tonic-gate 	register char *a, *b;
10040Sstevel@tonic-gate {
10050Sstevel@tonic-gate 	char ca, cb;
10060Sstevel@tonic-gate 
10070Sstevel@tonic-gate 	do
10080Sstevel@tonic-gate 	{
10090Sstevel@tonic-gate 		ca = *a++;
10100Sstevel@tonic-gate 		cb = *b++;
10110Sstevel@tonic-gate 		if (isascii(ca) && isupper(ca))
10120Sstevel@tonic-gate 			ca = ca - 'A' + 'a';
10130Sstevel@tonic-gate 		if (isascii(cb) && isupper(cb))
10140Sstevel@tonic-gate 			cb = cb - 'A' + 'a';
10150Sstevel@tonic-gate 	} while (ca != '\0' && ca == cb);
10160Sstevel@tonic-gate 	return (ca == cb);
10170Sstevel@tonic-gate }
10180Sstevel@tonic-gate 
10190Sstevel@tonic-gate /*
10200Sstevel@tonic-gate  * When invoked with no arguments, we fall into an automatic installation
10210Sstevel@tonic-gate  * mode, stepping the user through a default installation.
10220Sstevel@tonic-gate  */
10230Sstevel@tonic-gate 
10240Sstevel@tonic-gate static void
AutoInstall()10250Sstevel@tonic-gate AutoInstall()
10260Sstevel@tonic-gate {
10270Sstevel@tonic-gate 	char file[MAXLINE];
10280Sstevel@tonic-gate 	char forward[MAXLINE];
10290Sstevel@tonic-gate 	char cmd[MAXLINE];
10300Sstevel@tonic-gate 	char line[MAXLINE];
10310Sstevel@tonic-gate 	char *editor;
10320Sstevel@tonic-gate 	FILE *f;
10330Sstevel@tonic-gate 	struct passwd *pw;
10340Sstevel@tonic-gate 	extern mode_t umask(mode_t cmask);
10350Sstevel@tonic-gate 
10360Sstevel@tonic-gate 	umask(022);
10370Sstevel@tonic-gate 	pw = getpwuid(getuid());
10380Sstevel@tonic-gate 	if (pw == NULL) {
10390Sstevel@tonic-gate 		usrerr("User ID unknown");
10400Sstevel@tonic-gate 		exit(EX_NOUSER);
10410Sstevel@tonic-gate 	}
10420Sstevel@tonic-gate 	myname = strdup(pw->pw_name);
10430Sstevel@tonic-gate 	if (myname == NULL) {
10440Sstevel@tonic-gate 		usrerr("Out of memory");
10450Sstevel@tonic-gate 		exit(EX_OSERR);
10460Sstevel@tonic-gate 	}
10470Sstevel@tonic-gate 	homedir = getenv("HOME");
10480Sstevel@tonic-gate 	if (homedir == NULL) {
10490Sstevel@tonic-gate 		usrerr("Home directory unknown");
10500Sstevel@tonic-gate 		exit(EX_NOUSER);
10510Sstevel@tonic-gate 	}
10520Sstevel@tonic-gate 
10530Sstevel@tonic-gate 	printf("This program can be used to answer your mail automatically\n");
10540Sstevel@tonic-gate 	printf("when you go away on vacation.\n");
10550Sstevel@tonic-gate 	(void) strlcpy(file, homedir, sizeof (file));
10560Sstevel@tonic-gate 	(void) strlcat(file, MsgFile, sizeof (file));
10570Sstevel@tonic-gate 	do {
10580Sstevel@tonic-gate 		f = fopen(file, "r");
10590Sstevel@tonic-gate 		if (f) {
10600Sstevel@tonic-gate 			printf("You have a message file in %s.\n", file);
10610Sstevel@tonic-gate 			if (ask("Would you like to see it")) {
10620Sstevel@tonic-gate 				(void) snprintf(cmd, sizeof (cmd),
10630Sstevel@tonic-gate 				    "/usr/bin/more %s", file);
10640Sstevel@tonic-gate 				system(cmd);
10650Sstevel@tonic-gate 			}
10660Sstevel@tonic-gate 			if (ask("Would you like to edit it"))
10670Sstevel@tonic-gate 				f = NULL;
10680Sstevel@tonic-gate 		} else {
10690Sstevel@tonic-gate 			printf("You need to create a message file"
1070*2825Sjbeck 			    " in %s first.\n", file);
10710Sstevel@tonic-gate 			f = fopen(file, "w");
10720Sstevel@tonic-gate 			if (f == NULL) {
10730Sstevel@tonic-gate 				usrerr("Cannot open %s", file);
10740Sstevel@tonic-gate 				exit(EX_CANTCREAT);
10750Sstevel@tonic-gate 			}
10760Sstevel@tonic-gate 			fprintf(f, "Subject: away from my mail\n");
10770Sstevel@tonic-gate 			fprintf(f, "\nI will not be reading my mail"
1078*2825Sjbeck 			    " for a while.\n");
10790Sstevel@tonic-gate 			fprintf(f, "Your mail regarding \"$SUBJECT\" will"
1080*2825Sjbeck 			    " be read when I return.\n");
10810Sstevel@tonic-gate 			fclose(f);
10820Sstevel@tonic-gate 			f = NULL;
10830Sstevel@tonic-gate 		}
10840Sstevel@tonic-gate 		if (f == NULL) {
10850Sstevel@tonic-gate 			editor = getenv("VISUAL");
10860Sstevel@tonic-gate 			if (editor == NULL)
10870Sstevel@tonic-gate 				editor = getenv("EDITOR");
10880Sstevel@tonic-gate 			if (editor == NULL)
10890Sstevel@tonic-gate 				editor = "/usr/bin/vi";
10900Sstevel@tonic-gate 			(void) snprintf(cmd, sizeof (cmd), "%s %s", editor,
10910Sstevel@tonic-gate 			    file);
10920Sstevel@tonic-gate 			printf("Please use your editor (%s)"
1093*2825Sjbeck 			    " to edit this file.\n", editor);
10940Sstevel@tonic-gate 			system(cmd);
10950Sstevel@tonic-gate 		}
10960Sstevel@tonic-gate 	} while (f == NULL);
10970Sstevel@tonic-gate 	fclose(f);
10980Sstevel@tonic-gate 	(void) strlcpy(forward, homedir, sizeof (forward));
10990Sstevel@tonic-gate 	(void) strlcat(forward, "/.forward", sizeof (forward));
11000Sstevel@tonic-gate 	f = fopen(forward, "r");
11010Sstevel@tonic-gate 	if (f) {
11020Sstevel@tonic-gate 		printf("You have a .forward file"
1103*2825Sjbeck 		    " in your home directory containing:\n");
11040Sstevel@tonic-gate 		while (fgets(line, MAXLINE, f))
11050Sstevel@tonic-gate 			printf("    %s", line);
11060Sstevel@tonic-gate 		fclose(f);
11070Sstevel@tonic-gate 		if (!ask("Would you like to remove it and"
1108*2825Sjbeck 		    " disable the vacation feature"))
11090Sstevel@tonic-gate 			exit(EX_OK);
11100Sstevel@tonic-gate 		if (unlink(forward))
11110Sstevel@tonic-gate 			perror("Error removing .forward file:");
11120Sstevel@tonic-gate 		else
11130Sstevel@tonic-gate 			printf("Back to normal reception of mail.\n");
11140Sstevel@tonic-gate 		exit(EX_OK);
11150Sstevel@tonic-gate 	}
11160Sstevel@tonic-gate 
11170Sstevel@tonic-gate 	printf("To enable the vacation feature"
1118*2825Sjbeck 	    " a \".forward\" file is created.\n");
11190Sstevel@tonic-gate 	if (!ask("Would you like to enable the vacation feature")) {
11200Sstevel@tonic-gate 		printf("OK, vacation feature NOT enabled.\n");
11210Sstevel@tonic-gate 		exit(EX_OK);
11220Sstevel@tonic-gate 	}
11230Sstevel@tonic-gate 	f = fopen(forward, "w");
11240Sstevel@tonic-gate 	if (f == NULL) {
11250Sstevel@tonic-gate 		perror("Error opening .forward file");
11260Sstevel@tonic-gate 		exit(EX_CANTCREAT);
11270Sstevel@tonic-gate 	}
11280Sstevel@tonic-gate 	fprintf(f, "\\%s, \"|/usr/bin/vacation %s\"\n", myname, myname);
11290Sstevel@tonic-gate 	fclose(f);
11300Sstevel@tonic-gate 	printf("Vacation feature ENABLED."
1131*2825Sjbeck 	    " Please remember to turn it off when\n");
11320Sstevel@tonic-gate 	printf("you get back from vacation. Bon voyage.\n");
11330Sstevel@tonic-gate 
11340Sstevel@tonic-gate 	initialize(DbFileBase);
11350Sstevel@tonic-gate 	exit(EX_OK);
11360Sstevel@tonic-gate }
11370Sstevel@tonic-gate 
11380Sstevel@tonic-gate 
11390Sstevel@tonic-gate /*
11400Sstevel@tonic-gate  * Ask the user a question until we get a reasonable answer
11410Sstevel@tonic-gate  */
11420Sstevel@tonic-gate 
11430Sstevel@tonic-gate static bool
ask(prompt)11440Sstevel@tonic-gate ask(prompt)
11450Sstevel@tonic-gate 	char *prompt;
11460Sstevel@tonic-gate {
11470Sstevel@tonic-gate 	char line[MAXLINE];
11480Sstevel@tonic-gate 	char *res;
11490Sstevel@tonic-gate 
11500Sstevel@tonic-gate 	for (;;) {
11510Sstevel@tonic-gate 		printf("%s? ", prompt);
11520Sstevel@tonic-gate 		fflush(stdout);
11530Sstevel@tonic-gate 		res = fgets(line, sizeof (line), stdin);
11540Sstevel@tonic-gate 		if (res == NULL)
11550Sstevel@tonic-gate 			return (FALSE);
11560Sstevel@tonic-gate 		if (res[0] == 'y' || res[0] == 'Y')
11570Sstevel@tonic-gate 			return (TRUE);
11580Sstevel@tonic-gate 		if (res[0] == 'n' || res[0] == 'N')
11590Sstevel@tonic-gate 			return (FALSE);
11600Sstevel@tonic-gate 		printf("Please reply \"yes\" or \"no\" (\'y\' or \'n\')\n");
11610Sstevel@tonic-gate 	}
11620Sstevel@tonic-gate }
1163