vacation.c revision 77349
1/*
2 * Copyright (c) 1999-2001 Sendmail, Inc. and its suppliers.
3 *	All rights reserved.
4 * Copyright (c) 1983, 1987, 1993
5 *	The Regents of the University of California.  All rights reserved.
6 * Copyright (c) 1983 Eric P. Allman.  All rights reserved.
7 *
8 * By using this file, you agree to the terms and conditions set
9 * forth in the LICENSE file which can be found at the top level of
10 * the sendmail distribution.
11 *
12 */
13
14#ifndef lint
15static char copyright[] =
16"@(#) Copyright (c) 1999-2001 Sendmail, Inc. and its suppliers.\n\
17	All rights reserved.\n\
18     Copyright (c) 1983, 1987, 1993\n\
19	The Regents of the University of California.  All rights reserved.\n\
20     Copyright (c) 1983 Eric P. Allman.  All rights reserved.\n";
21#endif /* ! lint */
22
23#ifndef lint
24static char id[] = "@(#)$Id: vacation.c,v 8.68.4.21 2001/05/07 22:06:41 gshapiro Exp $";
25#endif /* ! lint */
26
27
28#include <ctype.h>
29#include <stdlib.h>
30#include <syslog.h>
31#include <time.h>
32#include <unistd.h>
33#ifdef EX_OK
34# undef EX_OK		/* unistd.h may have another use for this */
35#endif /* EX_OK */
36#include <sysexits.h>
37
38#include "sendmail/sendmail.h"
39#include "libsmdb/smdb.h"
40
41#if defined(__hpux) && !defined(HPUX11)
42# undef syslog		/* Undo hard_syslog conf.h change */
43#endif /* defined(__hpux) && !defined(HPUX11) */
44
45#ifndef _PATH_SENDMAIL
46# define _PATH_SENDMAIL "/usr/lib/sendmail"
47#endif /* ! _PATH_SENDMAIL */
48
49#define ONLY_ONCE	((time_t) 0)	/* send at most one reply */
50#define INTERVAL_UNDEF	((time_t) (-1))	/* no value given */
51
52#ifndef TRUE
53# define TRUE	1
54# define FALSE	0
55#endif /* ! TRUE */
56
57uid_t	RealUid;
58gid_t	RealGid;
59char	*RealUserName;
60uid_t	RunAsUid;
61uid_t	RunAsGid;
62char	*RunAsUserName;
63int	Verbose = 2;
64bool	DontInitGroups = FALSE;
65uid_t	TrustedUid = 0;
66BITMAP256 DontBlameSendmail;
67
68/*
69**  VACATION -- return a message to the sender when on vacation.
70**
71**	This program is invoked as a message receiver.  It returns a
72**	message specified by the user to whomever sent the mail, taking
73**	care not to return a message too often to prevent "I am on
74**	vacation" loops.
75*/
76
77#define	VDB	".vacation"		/* vacation database */
78#define	VMSG	".vacation.msg"		/* vacation message */
79#define SECSPERDAY	(60 * 60 * 24)
80#define DAYSPERWEEK	7
81
82#ifndef __P
83# ifdef __STDC__
84#  define __P(protos)	protos
85# else /* __STDC__ */
86#  define __P(protos)	()
87#  define const
88# endif /* __STDC__ */
89#endif /* ! __P */
90
91typedef struct alias
92{
93	char *name;
94	struct alias *next;
95} ALIAS;
96
97ALIAS *Names = NULL;
98
99SMDB_DATABASE *Db;
100
101char From[MAXLINE];
102
103#if _FFR_DEBUG
104void (*msglog)(int, const char *, ...) = &syslog;
105static void debuglog __P((int, const char *, ...));
106#else /* _FFR_DEBUG */
107# define msglog		syslog
108#endif /* _FFR_DEBUG */
109
110static void eatmsg __P((void));
111
112/* exit after reading input */
113#define EXITIT(excode)	{ \
114				eatmsg(); \
115				return excode; \
116			}
117
118int
119main(argc, argv)
120	int argc;
121	char **argv;
122{
123	bool iflag, emptysender, exclude;
124#if _FFR_BLACKBOX
125	bool runasuser = FALSE;
126#endif /* _FFR_BLACKBOX */
127#if _FFR_LISTDB
128	bool lflag = FALSE;
129#endif /* _FFR_LISTDB */
130	int mfail = 0, ufail = 0;
131	int ch;
132	int result;
133	long sff;
134	time_t interval;
135	struct passwd *pw;
136	ALIAS *cur;
137	char *dbfilename = NULL;
138	char *msgfilename = NULL;
139	char *name;
140	SMDB_USER_INFO user_info;
141	static char rnamebuf[MAXNAME];
142	extern int optind, opterr;
143	extern char *optarg;
144	extern void usage __P((void));
145	extern void setinterval __P((time_t));
146	extern int readheaders __P((void));
147	extern bool recent __P((void));
148	extern void setreply __P((char *, time_t));
149	extern void sendmessage __P((char *, char *, bool));
150	extern void xclude __P((FILE *));
151#if _FFR_LISTDB
152#define EXITM(excode)	{ \
153				if (!iflag && !lflag) \
154					eatmsg(); \
155				exit(excode); \
156			}
157#else /* _FFR_LISTDB */
158#define EXITM(excode)	{ \
159				if (!iflag) \
160					eatmsg(); \
161				exit(excode); \
162			}
163#endif /* _FFR_LISTDB */
164
165	/* Vars needed to link with smutil */
166	clrbitmap(DontBlameSendmail);
167	RunAsUid = RealUid = getuid();
168	RunAsGid = RealGid = getgid();
169	pw = getpwuid(RealUid);
170	if (pw != NULL)
171	{
172		if (strlen(pw->pw_name) > MAXNAME - 1)
173			pw->pw_name[MAXNAME] = '\0';
174		snprintf(rnamebuf, sizeof rnamebuf, "%s", pw->pw_name);
175	}
176	else
177		snprintf(rnamebuf, sizeof rnamebuf,
178			 "Unknown UID %d", (int) RealUid);
179	RunAsUserName = RealUserName = rnamebuf;
180
181# ifdef LOG_MAIL
182	openlog("vacation", LOG_PID, LOG_MAIL);
183# else /* LOG_MAIL */
184	openlog("vacation", LOG_PID);
185# endif /* LOG_MAIL */
186
187	opterr = 0;
188	iflag = FALSE;
189	emptysender = FALSE;
190	exclude = FALSE;
191	interval = INTERVAL_UNDEF;
192	*From = '\0';
193
194#define OPTIONS		"a:df:Iilm:r:s:t:Uxz"
195
196	while (mfail == 0 && ufail == 0 &&
197	       (ch = getopt(argc, argv, OPTIONS)) != -1)
198	{
199		switch((char)ch)
200		{
201		  case 'a':			/* alias */
202			cur = (ALIAS *)malloc((u_int)sizeof(ALIAS));
203			if (cur == NULL)
204			{
205				mfail++;
206				break;
207			}
208			cur->name = optarg;
209			cur->next = Names;
210			Names = cur;
211			break;
212
213#if _FFR_DEBUG
214		  case 'd':			/* debug mode */
215			msglog = &debuglog;
216			break;
217#endif /* _FFR_DEBUG */
218
219		  case 'f':		/* alternate database */
220			dbfilename = optarg;
221			break;
222
223		  case 'I':			/* backward compatible */
224		  case 'i':			/* init the database */
225			iflag = TRUE;
226			break;
227
228#if _FFR_LISTDB
229		  case 'l':
230			lflag = TRUE;		/* list the database */
231			break;
232#endif /* _FFR_LISTDB */
233
234		  case 'm':		/* alternate message file */
235			msgfilename = optarg;
236			break;
237
238		  case 'r':
239			if (isascii(*optarg) && isdigit(*optarg))
240			{
241				interval = atol(optarg) * SECSPERDAY;
242				if (interval < 0)
243					ufail++;
244			}
245			else
246				interval = ONLY_ONCE;
247			break;
248
249		  case 's':		/* alternate sender name */
250			(void) strlcpy(From, optarg, sizeof From);
251			break;
252
253		  case 't':		/* SunOS: -t1d (default expire) */
254			break;
255
256#if _FFR_BLACKBOX
257		  case 'U':		/* run as single user mode */
258			runasuser = TRUE;
259			break;
260#endif /* _FFR_BLACKBOX */
261
262		  case 'x':
263			exclude = TRUE;
264			break;
265
266		  case 'z':
267			emptysender = TRUE;
268			break;
269
270		  case '?':
271		  default:
272			ufail++;
273			break;
274		}
275	}
276	argc -= optind;
277	argv += optind;
278
279	if (mfail != 0)
280	{
281		msglog(LOG_NOTICE,
282		       "vacation: can't allocate memory for alias.\n");
283		EXITM(EX_TEMPFAIL);
284	}
285	if (ufail != 0)
286		usage();
287
288	if (argc != 1)
289	{
290		if (!iflag &&
291#if _FFR_LISTDB
292		    !lflag &&
293#endif /* _FFR_LISTDB */
294		    !exclude)
295			usage();
296		if ((pw = getpwuid(getuid())) == NULL)
297		{
298			msglog(LOG_ERR,
299			       "vacation: no such user uid %u.\n", getuid());
300			EXITM(EX_NOUSER);
301		}
302		name = pw->pw_name;
303		user_info.smdbu_id = pw->pw_uid;
304		user_info.smdbu_group_id = pw->pw_gid;
305		(void) strlcpy(user_info.smdbu_name, pw->pw_name,
306			       SMDB_MAX_USER_NAME_LEN);
307		if (chdir(pw->pw_dir) != 0)
308		{
309			msglog(LOG_NOTICE, "vacation: no such directory %s.\n",
310			       pw->pw_dir);
311			EXITM(EX_NOINPUT);
312		}
313	}
314#if _FFR_BLACKBOX
315	else if (runasuser)
316	{
317		name = *argv;
318		if (dbfilename == NULL || msgfilename == NULL)
319		{
320			msglog(LOG_NOTICE,
321			       "vacation: -U requires setting both -f and -m\n");
322			EXITM(EX_NOINPUT);
323		}
324		user_info.smdbu_id = pw->pw_uid;
325		user_info.smdbu_group_id = pw->pw_gid;
326		(void) strlcpy(user_info.smdbu_name, pw->pw_name,
327			       SMDB_MAX_USER_NAME_LEN);
328	}
329#endif /* _FFR_BLACKBOX */
330	else if ((pw = getpwnam(*argv)) == NULL)
331	{
332		msglog(LOG_ERR, "vacation: no such user %s.\n", *argv);
333		EXITM(EX_NOUSER);
334	}
335	else
336	{
337		name = pw->pw_name;
338		if (chdir(pw->pw_dir) != 0)
339		{
340			msglog(LOG_NOTICE, "vacation: no such directory %s.\n",
341			       pw->pw_dir);
342			EXITM(EX_NOINPUT);
343		}
344		user_info.smdbu_id = pw->pw_uid;
345		user_info.smdbu_group_id = pw->pw_gid;
346		(void) strlcpy(user_info.smdbu_name, pw->pw_name,
347			       SMDB_MAX_USER_NAME_LEN);
348	}
349
350	if (dbfilename == NULL)
351		dbfilename = VDB;
352	if (msgfilename == NULL)
353		msgfilename = VMSG;
354
355	sff = SFF_CREAT;
356#if _FFR_BLACKBOX
357	if (getegid() != getgid())
358	{
359		/* Allow a set-group-id vacation binary */
360		RunAsGid = user_info.smdbu_group_id = getegid();
361		sff |= SFF_NOPATHCHECK|SFF_OPENASROOT;
362	}
363#endif /* _FFR_BLACKBOX */
364
365	result = smdb_open_database(&Db, dbfilename,
366				    O_CREAT|O_RDWR | (iflag ? O_TRUNC : 0),
367				    S_IRUSR|S_IWUSR, sff,
368				    SMDB_TYPE_DEFAULT, &user_info, NULL);
369	if (result != SMDBE_OK)
370	{
371		msglog(LOG_NOTICE, "vacation: %s: %s\n", dbfilename,
372		       errstring(result));
373		EXITM(EX_DATAERR);
374	}
375
376#if _FFR_LISTDB
377	if (lflag)
378	{
379		static void listdb __P((void));
380
381		listdb();
382		(void) Db->smdb_close(Db);
383		exit(EX_OK);
384	}
385#endif /* _FFR_LISTDB */
386
387	if (interval != INTERVAL_UNDEF)
388		setinterval(interval);
389
390	if (iflag && !exclude)
391	{
392		(void) Db->smdb_close(Db);
393		exit(EX_OK);
394	}
395
396	if (exclude)
397	{
398		xclude(stdin);
399		(void) Db->smdb_close(Db);
400		EXITM(EX_OK);
401	}
402
403	if ((cur = (ALIAS *)malloc((u_int)sizeof(ALIAS))) == NULL)
404	{
405		msglog(LOG_NOTICE,
406		       "vacation: can't allocate memory for username.\n");
407		(void) Db->smdb_close(Db);
408		EXITM(EX_OSERR);
409	}
410	cur->name = name;
411	cur->next = Names;
412	Names = cur;
413
414	result = readheaders();
415	if (result == EX_OK && !recent())
416	{
417		time_t now;
418
419		(void) time(&now);
420		setreply(From, now);
421		(void) Db->smdb_close(Db);
422		sendmessage(name, msgfilename, emptysender);
423	}
424	else
425		(void) Db->smdb_close(Db);
426	if (result == EX_NOUSER)
427		result = EX_OK;
428	exit(result);
429}
430
431/*
432** EATMSG -- read stdin till EOF
433**
434**	Parameters:
435**		none.
436**
437**	Returns:
438**		nothing.
439**
440*/
441
442static void
443eatmsg()
444{
445	/*
446	**  read the rest of the e-mail and ignore it to avoid problems
447	**  with EPIPE in sendmail
448	*/
449	while (getc(stdin) != EOF)
450		continue;
451}
452
453/*
454** READHEADERS -- read mail headers
455**
456**	Parameters:
457**		none.
458**
459**	Returns:
460**		a exit code: NOUSER if no reply, OK if reply, * if error
461**
462**	Side Effects:
463**		may exit().
464**
465*/
466
467int
468readheaders()
469{
470	bool tome, cont;
471	register char *p;
472	register ALIAS *cur;
473	char buf[MAXLINE];
474	extern bool junkmail __P((char *));
475	extern bool nsearch __P((char *, char *));
476
477	cont = tome = FALSE;
478	while (fgets(buf, sizeof(buf), stdin) && *buf != '\n')
479	{
480		switch(*buf)
481		{
482		  case 'F':		/* "From " */
483			cont = FALSE;
484			if (strncmp(buf, "From ", 5) == 0)
485			{
486				bool quoted = FALSE;
487
488				p = buf + 5;
489				while (*p != '\0')
490				{
491					/* escaped character */
492					if (*p == '\\')
493					{
494						p++;
495						if (*p == '\0')
496						{
497							msglog(LOG_NOTICE,
498							       "vacation: badly formatted \"From \" line.\n");
499							EXITIT(EX_DATAERR);
500						}
501					}
502					else if (*p == '"')
503						quoted = !quoted;
504					else if (*p == '\r' || *p == '\n')
505						break;
506					else if (*p == ' ' && !quoted)
507						break;
508					p++;
509				}
510				if (quoted)
511				{
512					msglog(LOG_NOTICE,
513					       "vacation: badly formatted \"From \" line.\n");
514					EXITIT(EX_DATAERR);
515				}
516				*p = '\0';
517
518				/* ok since both strings have MAXLINE length */
519				if (*From == '\0')
520					(void) strlcpy(From, buf + 5,
521						       sizeof From);
522				if ((p = strchr(buf + 5, '\n')) != NULL)
523					*p = '\0';
524				if (junkmail(buf + 5))
525					EXITIT(EX_NOUSER);
526			}
527			break;
528
529		  case 'P':		/* "Precedence:" */
530		  case 'p':
531			cont = FALSE;
532			if (strlen(buf) <= 10 ||
533			    strncasecmp(buf, "Precedence", 10) != 0 ||
534			    (buf[10] != ':' && buf[10] != ' ' &&
535			     buf[10] != '\t'))
536				break;
537			if ((p = strchr(buf, ':')) == NULL)
538				break;
539			while (*++p != '\0' && isascii(*p) && isspace(*p));
540			if (*p == '\0')
541				break;
542			if (strncasecmp(p, "junk", 4) == 0 ||
543			    strncasecmp(p, "bulk", 4) == 0 ||
544			    strncasecmp(p, "list", 4) == 0)
545				EXITIT(EX_NOUSER);
546			break;
547
548		  case 'C':		/* "Cc:" */
549		  case 'c':
550			if (strncasecmp(buf, "Cc:", 3) != 0)
551				break;
552			cont = TRUE;
553			goto findme;
554
555		  case 'T':		/* "To:" */
556		  case 't':
557			if (strncasecmp(buf, "To:", 3) != 0)
558				break;
559			cont = TRUE;
560			goto findme;
561
562		  default:
563			if (!isascii(*buf) || !isspace(*buf) || !cont || tome)
564			{
565				cont = FALSE;
566				break;
567			}
568findme:
569			for (cur = Names;
570			     !tome && cur != NULL;
571			     cur = cur->next)
572				tome = nsearch(cur->name, buf);
573		}
574	}
575	if (!tome)
576		EXITIT(EX_NOUSER);
577	if (*From == '\0')
578	{
579		msglog(LOG_NOTICE, "vacation: no initial \"From \" line.\n");
580		EXITIT(EX_DATAERR);
581	}
582	EXITIT(EX_OK);
583}
584
585/*
586** NSEARCH --
587**	do a nice, slow, search of a string for a substring.
588**
589**	Parameters:
590**		name -- name to search.
591**		str -- string in which to search.
592**
593**	Returns:
594**		is name a substring of str?
595**
596*/
597
598bool
599nsearch(name, str)
600	register char *name, *str;
601{
602	register size_t len;
603	register char *s;
604
605	len = strlen(name);
606
607	for (s = str; *s != '\0'; ++s)
608	{
609		/*
610		**  Check to make sure that the string matches and
611		**  the previous character is not an alphanumeric and
612		**  the next character after the match is not an alphanumeric.
613		**
614		**  This prevents matching "eric" to "derick" while still
615		**  matching "eric" to "<eric+detail>".
616		*/
617
618		if (tolower(*s) == tolower(*name) &&
619		    strncasecmp(name, s, len) == 0 &&
620		    (s == str || !isascii(*(s - 1)) || !isalnum(*(s - 1))) &&
621		    (!isascii(*(s + len)) || !isalnum(*(s + len))))
622			return TRUE;
623	}
624	return FALSE;
625}
626
627/*
628** JUNKMAIL --
629**	read the header and return if automagic/junk/bulk/list mail
630**
631**	Parameters:
632**		from -- sender address.
633**
634**	Returns:
635**		is this some automated/junk/bulk/list mail?
636**
637*/
638
639struct ignore
640{
641	char	*name;
642	size_t	len;
643};
644
645typedef struct ignore IGNORE_T;
646
647#define MAX_USER_LEN 256	/* maximum length of local part (sender) */
648
649/* delimiters for the local part of an address */
650#define isdelim(c)	((c) == '%' || (c) == '@' || (c) == '+')
651
652bool
653junkmail(from)
654	char *from;
655{
656	bool quot;
657	char *e;
658	size_t len;
659	IGNORE_T *cur;
660	char sender[MAX_USER_LEN];
661	static IGNORE_T ignore[] =
662	{
663		{ "postmaster",		10	},
664		{ "uucp",		4	},
665		{ "mailer-daemon",	13	},
666		{ "mailer",		6	},
667		{ NULL,			0	}
668	};
669
670	static IGNORE_T ignorepost[] =
671	{
672		{ "-request",		8	},
673		{ "-relay",		6	},
674		{ "-owner",		6	},
675		{ NULL,			0	}
676	};
677
678	static IGNORE_T ignorepre[] =
679	{
680		{ "owner-",		6	},
681		{ NULL,			0	}
682	};
683
684	/*
685	**  This is mildly amusing, and I'm not positive it's right; trying
686	**  to find the "real" name of the sender, assuming that addresses
687	**  will be some variant of:
688	**
689	**  From site!site!SENDER%site.domain%site.domain@site.domain
690	*/
691
692	quot = FALSE;
693	e = from;
694	len = 0;
695	while (*e != '\0' && (quot || !isdelim(*e)))
696	{
697		if (*e == '"')
698		{
699			quot = !quot;
700			++e;
701			continue;
702		}
703		if (*e == '\\')
704		{
705			if (*(++e) == '\0')
706			{
707				/* '\\' at end of string? */
708				break;
709			}
710			if (len < MAX_USER_LEN)
711				sender[len++] = *e;
712			++e;
713			continue;
714		}
715		if (*e == '!' && !quot)
716		{
717			len = 0;
718			sender[len] = '\0';
719		}
720		else
721			if (len < MAX_USER_LEN)
722				sender[len++] = *e;
723		++e;
724	}
725	if (len < MAX_USER_LEN)
726		sender[len] = '\0';
727	else
728		sender[MAX_USER_LEN - 1] = '\0';
729
730	if (len <= 0)
731		return FALSE;
732#if 0
733	if (quot)
734		return FALSE;	/* syntax error... */
735#endif /* 0 */
736
737	/* test prefixes */
738	for (cur = ignorepre; cur->name != NULL; ++cur)
739	{
740		if (len >= cur->len &&
741		    strncasecmp(cur->name, sender, cur->len) == 0)
742			return TRUE;
743	}
744
745	/*
746	**  If the name is truncated, don't test the rest.
747	**	We could extract the "tail" of the sender address and
748	**	compare it it ignorepost, however, it seems not worth
749	**	the effort.
750	**	The address surely can't match any entry in ignore[]
751	**	(as long as all of them are shorter than MAX_USER_LEN).
752	*/
753
754	if (len > MAX_USER_LEN)
755		return FALSE;
756
757	/* test full local parts */
758	for (cur = ignore; cur->name != NULL; ++cur)
759	{
760		if (len == cur->len &&
761		    strncasecmp(cur->name, sender, cur->len) == 0)
762			return TRUE;
763	}
764
765	/* test postfixes */
766	for (cur = ignorepost; cur->name != NULL; ++cur)
767	{
768		if (len >= cur->len &&
769		    strncasecmp(cur->name, e - cur->len - 1,
770				cur->len) == 0)
771			return TRUE;
772	}
773	return FALSE;
774}
775
776#define	VIT	"__VACATION__INTERVAL__TIMER__"
777
778/*
779** RECENT --
780**	find out if user has gotten a vacation message recently.
781**
782**	Parameters:
783**		none.
784**
785**	Returns:
786**		TRUE iff user has gotten a vacation message recently.
787**
788*/
789
790bool
791recent()
792{
793	SMDB_DBENT key, data;
794	time_t then, next;
795	bool trydomain = FALSE;
796	int st;
797	char *domain;
798
799	memset(&key, '\0', sizeof key);
800	memset(&data, '\0', sizeof data);
801
802	/* get interval time */
803	key.data = VIT;
804	key.size = sizeof(VIT);
805
806	st = Db->smdb_get(Db, &key, &data, 0);
807	if (st != SMDBE_OK)
808		next = SECSPERDAY * DAYSPERWEEK;
809	else
810		memmove(&next, data.data, sizeof(next));
811
812	memset(&data, '\0', sizeof data);
813
814	/* get record for this address */
815	key.data = From;
816	key.size = strlen(From);
817
818	do
819	{
820		st = Db->smdb_get(Db, &key, &data, 0);
821		if (st == SMDBE_OK)
822		{
823			memmove(&then, data.data, sizeof(then));
824			if (next == ONLY_ONCE || then == ONLY_ONCE ||
825			    then + next > time(NULL))
826				return TRUE;
827		}
828		if ((trydomain = !trydomain) &&
829		    (domain = strchr(From, '@')) != NULL)
830		{
831			key.data = domain;
832			key.size = strlen(domain);
833		}
834	} while (trydomain);
835	return FALSE;
836}
837
838/*
839** SETINTERVAL --
840**	store the reply interval
841**
842**	Parameters:
843**		interval -- time interval for replies.
844**
845**	Returns:
846**		nothing.
847**
848**	Side Effects:
849**		stores the reply interval in database.
850*/
851
852void
853setinterval(interval)
854	time_t interval;
855{
856	SMDB_DBENT key, data;
857
858	memset(&key, '\0', sizeof key);
859	memset(&data, '\0', sizeof data);
860
861	key.data = VIT;
862	key.size = sizeof(VIT);
863	data.data = (char*) &interval;
864	data.size = sizeof(interval);
865	(void) (Db->smdb_put)(Db, &key, &data, 0);
866}
867
868/*
869** SETREPLY --
870**	store that this user knows about the vacation.
871**
872**	Parameters:
873**		from -- sender address.
874**		when -- last reply time.
875**
876**	Returns:
877**		nothing.
878**
879**	Side Effects:
880**		stores user/time in database.
881*/
882
883void
884setreply(from, when)
885	char *from;
886	time_t when;
887{
888	SMDB_DBENT key, data;
889
890	memset(&key, '\0', sizeof key);
891	memset(&data, '\0', sizeof data);
892
893	key.data = from;
894	key.size = strlen(from);
895	data.data = (char*) &when;
896	data.size = sizeof(when);
897	(void) (Db->smdb_put)(Db, &key, &data, 0);
898}
899
900/*
901** XCLUDE --
902**	add users to vacation db so they don't get a reply.
903**
904**	Parameters:
905**		f -- file pointer with list of address to exclude
906**
907**	Returns:
908**		nothing.
909**
910**	Side Effects:
911**		stores users in database.
912*/
913
914void
915xclude(f)
916	FILE *f;
917{
918	char buf[MAXLINE], *p;
919
920	if (f == NULL)
921		return;
922	while (fgets(buf, sizeof buf, f))
923	{
924		if ((p = strchr(buf, '\n')) != NULL)
925			*p = '\0';
926		setreply(buf, ONLY_ONCE);
927	}
928}
929
930/*
931** SENDMESSAGE --
932**	exec sendmail to send the vacation file to sender
933**
934**	Parameters:
935**		myname -- user name.
936**		msgfn -- name of file with vacation message.
937**		emptysender -- use <> as sender address?
938**
939**	Returns:
940**		nothing.
941**
942**	Side Effects:
943**		sends vacation reply.
944*/
945
946void
947sendmessage(myname, msgfn, emptysender)
948	char *myname;
949	char *msgfn;
950	bool emptysender;
951{
952	FILE *mfp, *sfp;
953	int i;
954	int pvect[2];
955	char *pv[8];
956	char buf[MAXLINE];
957
958	mfp = fopen(msgfn, "r");
959	if (mfp == NULL)
960	{
961		if (msgfn[0] == '/')
962			msglog(LOG_NOTICE, "vacation: no %s file.\n", msgfn);
963		else
964			msglog(LOG_NOTICE, "vacation: no ~%s/%s file.\n",
965			       myname, msgfn);
966		exit(EX_NOINPUT);
967	}
968	if (pipe(pvect) < 0)
969	{
970		msglog(LOG_ERR, "vacation: pipe: %s", errstring(errno));
971		exit(EX_OSERR);
972	}
973	pv[0] = "sendmail";
974	pv[1] = "-oi";
975	pv[2] = "-f";
976	if (emptysender)
977		pv[3] = "<>";
978	else
979		pv[3] = myname;
980	pv[4] = "--";
981	pv[5] = From;
982	pv[6] = NULL;
983	i = fork();
984	if (i < 0)
985	{
986		msglog(LOG_ERR, "vacation: fork: %s", errstring(errno));
987		exit(EX_OSERR);
988	}
989	if (i == 0)
990	{
991		(void) dup2(pvect[0], 0);
992		(void) close(pvect[0]);
993		(void) close(pvect[1]);
994		(void) fclose(mfp);
995		(void) execv(_PATH_SENDMAIL, pv);
996		msglog(LOG_ERR, "vacation: can't exec %s: %s",
997			_PATH_SENDMAIL, errstring(errno));
998		exit(EX_UNAVAILABLE);
999	}
1000	/* check return status of the following calls? XXX */
1001	(void) close(pvect[0]);
1002	if ((sfp = fdopen(pvect[1], "w")) != NULL)
1003	{
1004		(void) fprintf(sfp, "To: %s\n", From);
1005		(void) fprintf(sfp, "Auto-Submitted: auto-generated\n");
1006		while (fgets(buf, sizeof buf, mfp))
1007			(void) fputs(buf, sfp);
1008		(void) fclose(mfp);
1009		(void) fclose(sfp);
1010	}
1011	else
1012	{
1013		(void) fclose(mfp);
1014		msglog(LOG_ERR, "vacation: can't open pipe to sendmail");
1015		exit(EX_UNAVAILABLE);
1016	}
1017}
1018
1019void
1020usage()
1021{
1022	msglog(LOG_NOTICE,
1023	       "uid %u: usage: vacation [-a alias]%s [-f db] [-i]%s [-m msg] [-r interval] [-s sender] [-t time]%s [-x] [-z] login\n",
1024	       getuid(),
1025#if _FFR_DEBUG
1026	       " [-d]",
1027#else /* _FFR_DEBUG */
1028	       "",
1029#endif /* _FFR_DEBUG */
1030#if _FFR_LISTDB
1031	       " [-l]",
1032#else /* _FFR_LISTDB */
1033	       "",
1034#endif /* _FFR_LISTDB */
1035#if _FFR_BLACKBOX
1036	       " [-U]"
1037#else /* _FFR_BLACKBOX */
1038	       ""
1039#endif /* _FFR_BLACKBOX */
1040	       );
1041	exit(EX_USAGE);
1042}
1043
1044#if _FFR_LISTDB
1045/*
1046** LISTDB -- list the contents of the vacation database
1047**
1048**	Parameters:
1049**		none.
1050**
1051**	Returns:
1052**		nothing.
1053*/
1054
1055static void
1056listdb()
1057{
1058	int result;
1059	time_t t;
1060	SMDB_CURSOR *cursor = NULL;
1061	SMDB_DBENT db_key, db_value;
1062
1063	memset(&db_key, '\0', sizeof db_key);
1064	memset(&db_value, '\0', sizeof db_value);
1065
1066	result = Db->smdb_cursor(Db, &cursor, 0);
1067	if (result != SMDBE_OK)
1068	{
1069		fprintf(stderr, "vacation: set cursor: %s\n",
1070			errstring(result));
1071		return;
1072	}
1073
1074	while ((result = cursor->smdbc_get(cursor, &db_key, &db_value,
1075					   SMDB_CURSOR_GET_NEXT)) == SMDBE_OK)
1076	{
1077		/* skip magic VIT entry */
1078		if ((int)db_key.size -1 == strlen(VIT) &&
1079		    strncmp((char *)db_key.data, VIT,
1080			    (int)db_key.size - 1) == 0)
1081			continue;
1082
1083		/* skip bogus values */
1084		if (db_value.size != sizeof t)
1085		{
1086			fprintf(stderr, "vacation: %.*s invalid time stamp\n",
1087				(int) db_key.size, (char *) db_key.data);
1088			continue;
1089		}
1090
1091		memcpy(&t, db_value.data, sizeof t);
1092
1093		if (db_key.size > 40)
1094			db_key.size = 40;
1095
1096		printf("%-40.*s %-10s",
1097		       (int) db_key.size, (char *) db_key.data, ctime(&t));
1098
1099		memset(&db_key, '\0', sizeof db_key);
1100		memset(&db_value, '\0', sizeof db_value);
1101	}
1102
1103	if (result != SMDBE_OK && result != SMDBE_LAST_ENTRY)
1104	{
1105		fprintf(stderr,	"vacation: get value at cursor: %s\n",
1106			errstring(result));
1107		if (cursor != NULL)
1108		{
1109			(void) cursor->smdbc_close(cursor);
1110			cursor = NULL;
1111		}
1112		return;
1113	}
1114	(void) cursor->smdbc_close(cursor);
1115	cursor = NULL;
1116}
1117#endif /* _FFR_LISTDB */
1118
1119#if _FFR_DEBUG
1120/*
1121** DEBUGLOG -- write message to standard error
1122**
1123**	Append a message to the standard error for the convenience of
1124**	end-users debugging without access to the syslog messages.
1125**
1126**	Parameters:
1127**		i -- syslog log level
1128**		fmt -- string format
1129**
1130**	Returns:
1131**		nothing.
1132*/
1133
1134/*VARARGS2*/
1135static void
1136#ifdef __STDC__
1137debuglog(int i, const char *fmt, ...)
1138#else /* __STDC__ */
1139debuglog(i, fmt, va_alist)
1140	int i;
1141	const char *fmt;
1142	va_dcl
1143#endif /* __STDC__ */
1144
1145{
1146	VA_LOCAL_DECL
1147
1148	VA_START(fmt);
1149	vfprintf(stderr, fmt, ap);
1150	VA_END;
1151}
1152#endif /* _FFR_DEBUG */
1153
1154/*VARARGS1*/
1155void
1156#ifdef __STDC__
1157message(const char *msg, ...)
1158#else /* __STDC__ */
1159message(msg, va_alist)
1160	const char *msg;
1161	va_dcl
1162#endif /* __STDC__ */
1163{
1164	const char *m;
1165	VA_LOCAL_DECL
1166
1167	m = msg;
1168	if (isascii(m[0]) && isdigit(m[0]) &&
1169	    isascii(m[1]) && isdigit(m[1]) &&
1170	    isascii(m[2]) && isdigit(m[2]) && m[3] == ' ')
1171		m += 4;
1172	VA_START(msg);
1173	(void) vfprintf(stderr, m, ap);
1174	VA_END;
1175	(void) fprintf(stderr, "\n");
1176}
1177
1178/*VARARGS1*/
1179void
1180#ifdef __STDC__
1181syserr(const char *msg, ...)
1182#else /* __STDC__ */
1183syserr(msg, va_alist)
1184	const char *msg;
1185	va_dcl
1186#endif /* __STDC__ */
1187{
1188	const char *m;
1189	VA_LOCAL_DECL
1190
1191	m = msg;
1192	if (isascii(m[0]) && isdigit(m[0]) &&
1193	    isascii(m[1]) && isdigit(m[1]) &&
1194	    isascii(m[2]) && isdigit(m[2]) && m[3] == ' ')
1195		m += 4;
1196	VA_START(msg);
1197	(void) vfprintf(stderr, m, ap);
1198	VA_END;
1199	(void) fprintf(stderr, "\n");
1200}
1201
1202void
1203dumpfd(fd, printclosed, logit)
1204	int fd;
1205	bool printclosed;
1206	bool logit;
1207{
1208	return;
1209}
1210