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