vacation.c revision 173340
164562Sgshapiro/*
294334Sgshapiro * Copyright (c) 1999-2002 Sendmail, Inc. and its suppliers.
364562Sgshapiro *	All rights reserved.
464562Sgshapiro * Copyright (c) 1983, 1987, 1993
564562Sgshapiro *	The Regents of the University of California.  All rights reserved.
664562Sgshapiro * Copyright (c) 1983 Eric P. Allman.  All rights reserved.
764562Sgshapiro *
864562Sgshapiro * By using this file, you agree to the terms and conditions set
964562Sgshapiro * forth in the LICENSE file which can be found at the top level of
1064562Sgshapiro * the sendmail distribution.
1164562Sgshapiro *
1264562Sgshapiro */
1364562Sgshapiro
1490792Sgshapiro#include <sm/gen.h>
1590792Sgshapiro
1690792SgshapiroSM_IDSTR(copyright,
1777349Sgshapiro"@(#) Copyright (c) 1999-2001 Sendmail, Inc. and its suppliers.\n\
1864562Sgshapiro	All rights reserved.\n\
1964562Sgshapiro     Copyright (c) 1983, 1987, 1993\n\
2064562Sgshapiro	The Regents of the University of California.  All rights reserved.\n\
2190792Sgshapiro     Copyright (c) 1983 Eric P. Allman.  All rights reserved.\n")
2264562Sgshapiro
23173340SgshapiroSM_IDSTR(id, "@(#)$Id: vacation.c,v 8.144 2007/05/11 18:50:36 ca Exp $")
2464562Sgshapiro
2577349Sgshapiro
2664562Sgshapiro#include <ctype.h>
2764562Sgshapiro#include <stdlib.h>
2864562Sgshapiro#include <syslog.h>
2964562Sgshapiro#include <time.h>
3064562Sgshapiro#include <unistd.h>
3164562Sgshapiro#ifdef EX_OK
3264562Sgshapiro# undef EX_OK		/* unistd.h may have another use for this */
3364562Sgshapiro#endif /* EX_OK */
3490792Sgshapiro#include <sm/sysexits.h>
3564562Sgshapiro
3690792Sgshapiro#include <sm/cf.h>
3790792Sgshapiro#include <sm/mbdb.h>
3864562Sgshapiro#include "sendmail/sendmail.h"
3990792Sgshapiro#include <sendmail/pathnames.h>
4064562Sgshapiro#include "libsmdb/smdb.h"
4164562Sgshapiro
4264562Sgshapiro#define ONLY_ONCE	((time_t) 0)	/* send at most one reply */
4364562Sgshapiro#define INTERVAL_UNDEF	((time_t) (-1))	/* no value given */
4464562Sgshapiro
4564562Sgshapirouid_t	RealUid;
4664562Sgshapirogid_t	RealGid;
4764562Sgshapirochar	*RealUserName;
4864562Sgshapirouid_t	RunAsUid;
49173340Sgshapirogid_t	RunAsGid;
5064562Sgshapirochar	*RunAsUserName;
5164562Sgshapiroint	Verbose = 2;
5290792Sgshapirobool	DontInitGroups = false;
5364562Sgshapirouid_t	TrustedUid = 0;
5464562SgshapiroBITMAP256 DontBlameSendmail;
5564562Sgshapiro
56168515Sgshapirostatic int readheaders __P((bool));
57168515Sgshapirostatic bool junkmail __P((char *));
58168515Sgshapirostatic bool nsearch __P((char *, char *));
59168515Sgshapirostatic void usage __P((void));
60168515Sgshapirostatic void setinterval __P((time_t));
61168515Sgshapirostatic bool recent __P((void));
62168515Sgshapirostatic void setreply __P((char *, time_t));
63168515Sgshapirostatic void sendmessage __P((char *, char *, char *));
64168515Sgshapirostatic void xclude __P((SM_FILE_T *));
65168515Sgshapiro
6664562Sgshapiro/*
6764562Sgshapiro**  VACATION -- return a message to the sender when on vacation.
6864562Sgshapiro**
6964562Sgshapiro**	This program is invoked as a message receiver.  It returns a
7064562Sgshapiro**	message specified by the user to whomever sent the mail, taking
7164562Sgshapiro**	care not to return a message too often to prevent "I am on
7264562Sgshapiro**	vacation" loops.
7364562Sgshapiro*/
7464562Sgshapiro
7564562Sgshapiro#define	VDB	".vacation"		/* vacation database */
7664562Sgshapiro#define	VMSG	".vacation.msg"		/* vacation message */
7764562Sgshapiro#define SECSPERDAY	(60 * 60 * 24)
7864562Sgshapiro#define DAYSPERWEEK	7
7964562Sgshapiro
8064562Sgshapirotypedef struct alias
8164562Sgshapiro{
8264562Sgshapiro	char *name;
8364562Sgshapiro	struct alias *next;
8464562Sgshapiro} ALIAS;
8564562Sgshapiro
8664562SgshapiroALIAS *Names = NULL;
8764562Sgshapiro
8864562SgshapiroSMDB_DATABASE *Db;
8964562Sgshapiro
9064562Sgshapirochar From[MAXLINE];
91141858Sgshapirobool CloseMBDB = false;
9264562Sgshapiro
9390792Sgshapiro#if defined(__hpux) || defined(__osf__)
9490792Sgshapiro# ifndef SM_CONF_SYSLOG_INT
9590792Sgshapiro#  define SM_CONF_SYSLOG_INT	1
9690792Sgshapiro# endif /* SM_CONF_SYSLOG_INT */
9790792Sgshapiro#endif /* defined(__hpux) || defined(__osf__) */
9864562Sgshapiro
9990792Sgshapiro#if SM_CONF_SYSLOG_INT
10090792Sgshapiro# define SYSLOG_RET_T	int
10190792Sgshapiro# define SYSLOG_RET	return 0
10290792Sgshapiro#else /* SM_CONF_SYSLOG_INT */
10390792Sgshapiro# define SYSLOG_RET_T	void
10490792Sgshapiro# define SYSLOG_RET
10590792Sgshapiro#endif /* SM_CONF_SYSLOG_INT */
10690792Sgshapiro
10790792Sgshapirotypedef SYSLOG_RET_T SYSLOG_T __P((int, const char *, ...));
10890792SgshapiroSYSLOG_T *msglog = syslog;
10990792Sgshapirostatic SYSLOG_RET_T debuglog __P((int, const char *, ...));
11066494Sgshapirostatic void eatmsg __P((void));
11190792Sgshapirostatic void listdb __P((void));
11266494Sgshapiro
11366494Sgshapiro/* exit after reading input */
114141858Sgshapiro#define EXITIT(excode)			\
115141858Sgshapiro{					\
116141858Sgshapiro	eatmsg();			\
117141858Sgshapiro	if (CloseMBDB)			\
118141858Sgshapiro	{				\
119141858Sgshapiro		sm_mbdb_terminate();	\
120141858Sgshapiro		CloseMBDB = false;	\
121141858Sgshapiro	}				\
122141858Sgshapiro	return excode;			\
12390792Sgshapiro}
12477349Sgshapiro
125141858Sgshapiro#define EXITM(excode)			\
126141858Sgshapiro{					\
127141858Sgshapiro	if (!initdb && !list)		\
128141858Sgshapiro		eatmsg();		\
129141858Sgshapiro	if (CloseMBDB)			\
130141858Sgshapiro	{				\
131141858Sgshapiro		sm_mbdb_terminate();	\
132141858Sgshapiro		CloseMBDB = false;	\
133141858Sgshapiro	}				\
134141858Sgshapiro	exit(excode);			\
13590792Sgshapiro}
13690792Sgshapiro
13764562Sgshapiroint
13864562Sgshapiromain(argc, argv)
13964562Sgshapiro	int argc;
14064562Sgshapiro	char **argv;
14164562Sgshapiro{
14298121Sgshapiro	bool alwaysrespond = false;
14398121Sgshapiro	bool initdb, exclude;
14490792Sgshapiro	bool runasuser = false;
14598121Sgshapiro	bool list = false;
14664562Sgshapiro	int mfail = 0, ufail = 0;
14764562Sgshapiro	int ch;
14864562Sgshapiro	int result;
14966494Sgshapiro	long sff;
15064562Sgshapiro	time_t interval;
15164562Sgshapiro	struct passwd *pw;
15264562Sgshapiro	ALIAS *cur;
15377349Sgshapiro	char *dbfilename = NULL;
15477349Sgshapiro	char *msgfilename = NULL;
15590792Sgshapiro	char *cfpath = NULL;
15664562Sgshapiro	char *name;
15790792Sgshapiro	char *returnaddr = NULL;
15864562Sgshapiro	SMDB_USER_INFO user_info;
15964562Sgshapiro	static char rnamebuf[MAXNAME];
16064562Sgshapiro	extern int optind, opterr;
16164562Sgshapiro	extern char *optarg;
16264562Sgshapiro
16364562Sgshapiro	/* Vars needed to link with smutil */
16464562Sgshapiro	clrbitmap(DontBlameSendmail);
16564562Sgshapiro	RunAsUid = RealUid = getuid();
16664562Sgshapiro	RunAsGid = RealGid = getgid();
16764562Sgshapiro	pw = getpwuid(RealUid);
16864562Sgshapiro	if (pw != NULL)
16964562Sgshapiro	{
17064562Sgshapiro		if (strlen(pw->pw_name) > MAXNAME - 1)
17164562Sgshapiro			pw->pw_name[MAXNAME] = '\0';
17290792Sgshapiro		sm_snprintf(rnamebuf, sizeof rnamebuf, "%s", pw->pw_name);
17364562Sgshapiro	}
17464562Sgshapiro	else
17590792Sgshapiro		sm_snprintf(rnamebuf, sizeof rnamebuf,
17690792Sgshapiro			    "Unknown UID %d", (int) RealUid);
17764562Sgshapiro	RunAsUserName = RealUserName = rnamebuf;
17864562Sgshapiro
17977349Sgshapiro# ifdef LOG_MAIL
18064562Sgshapiro	openlog("vacation", LOG_PID, LOG_MAIL);
18177349Sgshapiro# else /* LOG_MAIL */
18264562Sgshapiro	openlog("vacation", LOG_PID);
18377349Sgshapiro# endif /* LOG_MAIL */
18464562Sgshapiro
18564562Sgshapiro	opterr = 0;
18698121Sgshapiro	initdb = false;
18790792Sgshapiro	exclude = false;
18864562Sgshapiro	interval = INTERVAL_UNDEF;
18964562Sgshapiro	*From = '\0';
19064562Sgshapiro
19164562Sgshapiro
19298121Sgshapiro#define OPTIONS	"a:C:df:Iijlm:R:r:s:t:Uxz"
19390792Sgshapiro
19464562Sgshapiro	while (mfail == 0 && ufail == 0 &&
19564562Sgshapiro	       (ch = getopt(argc, argv, OPTIONS)) != -1)
19664562Sgshapiro	{
19764562Sgshapiro		switch((char)ch)
19864562Sgshapiro		{
19964562Sgshapiro		  case 'a':			/* alias */
20090792Sgshapiro			cur = (ALIAS *) malloc((unsigned int) sizeof(ALIAS));
20164562Sgshapiro			if (cur == NULL)
20264562Sgshapiro			{
20364562Sgshapiro				mfail++;
20464562Sgshapiro				break;
20564562Sgshapiro			}
20664562Sgshapiro			cur->name = optarg;
20764562Sgshapiro			cur->next = Names;
20864562Sgshapiro			Names = cur;
20964562Sgshapiro			break;
21064562Sgshapiro
21190792Sgshapiro		  case 'C':
21290792Sgshapiro			cfpath = optarg;
21390792Sgshapiro			break;
21490792Sgshapiro
21577349Sgshapiro		  case 'd':			/* debug mode */
21690792Sgshapiro			msglog = debuglog;
21764562Sgshapiro			break;
21864562Sgshapiro
21964562Sgshapiro		  case 'f':		/* alternate database */
22064562Sgshapiro			dbfilename = optarg;
22164562Sgshapiro			break;
22264562Sgshapiro
22364562Sgshapiro		  case 'I':			/* backward compatible */
22464562Sgshapiro		  case 'i':			/* init the database */
22598121Sgshapiro			initdb = true;
22664562Sgshapiro			break;
22764562Sgshapiro
22898121Sgshapiro		  case 'j':
22998121Sgshapiro			alwaysrespond = true;
23098121Sgshapiro			break;
23198121Sgshapiro
23264562Sgshapiro		  case 'l':
23398121Sgshapiro			list = true;		/* list the database */
23464562Sgshapiro			break;
23564562Sgshapiro
23664562Sgshapiro		  case 'm':		/* alternate message file */
23764562Sgshapiro			msgfilename = optarg;
23864562Sgshapiro			break;
23964562Sgshapiro
24090792Sgshapiro		  case 'R':
24190792Sgshapiro			returnaddr = optarg;
24290792Sgshapiro			break;
24390792Sgshapiro
24464562Sgshapiro		  case 'r':
24564562Sgshapiro			if (isascii(*optarg) && isdigit(*optarg))
24664562Sgshapiro			{
24764562Sgshapiro				interval = atol(optarg) * SECSPERDAY;
24864562Sgshapiro				if (interval < 0)
24964562Sgshapiro					ufail++;
25064562Sgshapiro			}
25164562Sgshapiro			else
25264562Sgshapiro				interval = ONLY_ONCE;
25364562Sgshapiro			break;
25464562Sgshapiro
25564562Sgshapiro		  case 's':		/* alternate sender name */
25690792Sgshapiro			(void) sm_strlcpy(From, optarg, sizeof From);
25764562Sgshapiro			break;
25864562Sgshapiro
25964562Sgshapiro		  case 't':		/* SunOS: -t1d (default expire) */
26064562Sgshapiro			break;
26164562Sgshapiro
26277349Sgshapiro		  case 'U':		/* run as single user mode */
26390792Sgshapiro			runasuser = true;
26477349Sgshapiro			break;
26577349Sgshapiro
26664562Sgshapiro		  case 'x':
26790792Sgshapiro			exclude = true;
26864562Sgshapiro			break;
26964562Sgshapiro
27064562Sgshapiro		  case 'z':
27190792Sgshapiro			returnaddr = "<>";
27264562Sgshapiro			break;
27364562Sgshapiro
27464562Sgshapiro		  case '?':
27564562Sgshapiro		  default:
27664562Sgshapiro			ufail++;
27764562Sgshapiro			break;
27864562Sgshapiro		}
27964562Sgshapiro	}
28064562Sgshapiro	argc -= optind;
28164562Sgshapiro	argv += optind;
28264562Sgshapiro
28364562Sgshapiro	if (mfail != 0)
28464562Sgshapiro	{
28564562Sgshapiro		msglog(LOG_NOTICE,
28664562Sgshapiro		       "vacation: can't allocate memory for alias.\n");
28766494Sgshapiro		EXITM(EX_TEMPFAIL);
28864562Sgshapiro	}
28964562Sgshapiro	if (ufail != 0)
29064562Sgshapiro		usage();
29164562Sgshapiro
29264562Sgshapiro	if (argc != 1)
29364562Sgshapiro	{
29498121Sgshapiro		if (!initdb && !list && !exclude)
29564562Sgshapiro			usage();
29664562Sgshapiro		if ((pw = getpwuid(getuid())) == NULL)
29764562Sgshapiro		{
29864562Sgshapiro			msglog(LOG_ERR,
29964562Sgshapiro			       "vacation: no such user uid %u.\n", getuid());
30066494Sgshapiro			EXITM(EX_NOUSER);
30164562Sgshapiro		}
30277349Sgshapiro		name = pw->pw_name;
30377349Sgshapiro		user_info.smdbu_id = pw->pw_uid;
30477349Sgshapiro		user_info.smdbu_group_id = pw->pw_gid;
30590792Sgshapiro		(void) sm_strlcpy(user_info.smdbu_name, pw->pw_name,
30690792Sgshapiro				  SMDB_MAX_USER_NAME_LEN);
30777349Sgshapiro		if (chdir(pw->pw_dir) != 0)
30877349Sgshapiro		{
30990792Sgshapiro			msglog(LOG_NOTICE,
31090792Sgshapiro			       "vacation: no such directory %s.\n",
31177349Sgshapiro			       pw->pw_dir);
31277349Sgshapiro			EXITM(EX_NOINPUT);
31377349Sgshapiro		}
31464562Sgshapiro	}
31577349Sgshapiro	else if (runasuser)
31677349Sgshapiro	{
31777349Sgshapiro		name = *argv;
31877349Sgshapiro		if (dbfilename == NULL || msgfilename == NULL)
31977349Sgshapiro		{
32077349Sgshapiro			msglog(LOG_NOTICE,
32177349Sgshapiro			       "vacation: -U requires setting both -f and -m\n");
32277349Sgshapiro			EXITM(EX_NOINPUT);
32377349Sgshapiro		}
32477349Sgshapiro		user_info.smdbu_id = pw->pw_uid;
32577349Sgshapiro		user_info.smdbu_group_id = pw->pw_gid;
32690792Sgshapiro		(void) sm_strlcpy(user_info.smdbu_name, pw->pw_name,
32777349Sgshapiro			       SMDB_MAX_USER_NAME_LEN);
32877349Sgshapiro	}
32977349Sgshapiro	else
33064562Sgshapiro	{
33190792Sgshapiro		int err;
33290792Sgshapiro		SM_CF_OPT_T mbdbname;
33390792Sgshapiro		SM_MBDB_T user;
33490792Sgshapiro
33594334Sgshapiro		cfpath = getcfname(0, 0, SM_GET_SENDMAIL_CF, cfpath);
33690792Sgshapiro		mbdbname.opt_name = "MailboxDatabase";
33790792Sgshapiro		mbdbname.opt_val = "pw";
33890792Sgshapiro		(void) sm_cf_getopt(cfpath, 1, &mbdbname);
33990792Sgshapiro		err = sm_mbdb_initialize(mbdbname.opt_val);
34090792Sgshapiro		if (err != EX_OK)
34177349Sgshapiro		{
34290792Sgshapiro			msglog(LOG_ERR,
34390792Sgshapiro			       "vacation: can't open mailbox database: %s.\n",
34490792Sgshapiro			       sm_strexit(err));
34590792Sgshapiro			EXITM(err);
34690792Sgshapiro		}
347141858Sgshapiro		CloseMBDB = true;
34890792Sgshapiro		err = sm_mbdb_lookup(*argv, &user);
34990792Sgshapiro		if (err == EX_NOUSER)
35090792Sgshapiro		{
35190792Sgshapiro			msglog(LOG_ERR, "vacation: no such user %s.\n", *argv);
35290792Sgshapiro			EXITM(EX_NOUSER);
35390792Sgshapiro		}
35490792Sgshapiro		if (err != EX_OK)
35590792Sgshapiro		{
35690792Sgshapiro			msglog(LOG_ERR,
35790792Sgshapiro			       "vacation: can't read mailbox database: %s.\n",
35890792Sgshapiro			       sm_strexit(err));
35990792Sgshapiro			EXITM(err);
36090792Sgshapiro		}
36190792Sgshapiro		name = user.mbdb_name;
36290792Sgshapiro		if (chdir(user.mbdb_homedir) != 0)
36390792Sgshapiro		{
36490792Sgshapiro			msglog(LOG_NOTICE,
36590792Sgshapiro			       "vacation: no such directory %s.\n",
36690792Sgshapiro			       user.mbdb_homedir);
36777349Sgshapiro			EXITM(EX_NOINPUT);
36877349Sgshapiro		}
36990792Sgshapiro		user_info.smdbu_id = user.mbdb_uid;
37090792Sgshapiro		user_info.smdbu_group_id = user.mbdb_gid;
37190792Sgshapiro		(void) sm_strlcpy(user_info.smdbu_name, user.mbdb_name,
37277349Sgshapiro			       SMDB_MAX_USER_NAME_LEN);
37364562Sgshapiro	}
37464562Sgshapiro
37577349Sgshapiro	if (dbfilename == NULL)
37677349Sgshapiro		dbfilename = VDB;
37777349Sgshapiro	if (msgfilename == NULL)
37877349Sgshapiro		msgfilename = VMSG;
37977349Sgshapiro
38066494Sgshapiro	sff = SFF_CREAT;
38166494Sgshapiro	if (getegid() != getgid())
38277349Sgshapiro	{
38390792Sgshapiro		/* Allow a set-group-ID vacation binary */
38466494Sgshapiro		RunAsGid = user_info.smdbu_group_id = getegid();
38590792Sgshapiro		sff |= SFF_OPENASROOT;
38677349Sgshapiro	}
38794334Sgshapiro	if (getuid() == 0)
38894334Sgshapiro	{
38994334Sgshapiro		/* Allow root to initialize user's vacation databases */
39094334Sgshapiro		sff |= SFF_OPENASROOT|SFF_ROOTOK;
39166494Sgshapiro
39294334Sgshapiro		/* ... safely */
39394334Sgshapiro		sff |= SFF_NOSLINK|SFF_NOHLINK|SFF_REGONLY;
39494334Sgshapiro	}
39594334Sgshapiro
39694334Sgshapiro
39764562Sgshapiro	result = smdb_open_database(&Db, dbfilename,
39898121Sgshapiro				    O_CREAT|O_RDWR | (initdb ? O_TRUNC : 0),
39966494Sgshapiro				    S_IRUSR|S_IWUSR, sff,
40064562Sgshapiro				    SMDB_TYPE_DEFAULT, &user_info, NULL);
40164562Sgshapiro	if (result != SMDBE_OK)
40264562Sgshapiro	{
40364562Sgshapiro		msglog(LOG_NOTICE, "vacation: %s: %s\n", dbfilename,
40490792Sgshapiro		       sm_errstring(result));
40566494Sgshapiro		EXITM(EX_DATAERR);
40664562Sgshapiro	}
40764562Sgshapiro
40898121Sgshapiro	if (list)
40964562Sgshapiro	{
41064562Sgshapiro		listdb();
41171345Sgshapiro		(void) Db->smdb_close(Db);
41264562Sgshapiro		exit(EX_OK);
41364562Sgshapiro	}
41464562Sgshapiro
41564562Sgshapiro	if (interval != INTERVAL_UNDEF)
41664562Sgshapiro		setinterval(interval);
41764562Sgshapiro
41898121Sgshapiro	if (initdb && !exclude)
41964562Sgshapiro	{
42071345Sgshapiro		(void) Db->smdb_close(Db);
42171345Sgshapiro		exit(EX_OK);
42264562Sgshapiro	}
42364562Sgshapiro
42464562Sgshapiro	if (exclude)
42564562Sgshapiro	{
42690792Sgshapiro		xclude(smioin);
42771345Sgshapiro		(void) Db->smdb_close(Db);
42866494Sgshapiro		EXITM(EX_OK);
42964562Sgshapiro	}
43064562Sgshapiro
43190792Sgshapiro	if ((cur = (ALIAS *) malloc((unsigned int) sizeof(ALIAS))) == NULL)
43264562Sgshapiro	{
43364562Sgshapiro		msglog(LOG_NOTICE,
43464562Sgshapiro		       "vacation: can't allocate memory for username.\n");
43571345Sgshapiro		(void) Db->smdb_close(Db);
43666494Sgshapiro		EXITM(EX_OSERR);
43764562Sgshapiro	}
43864562Sgshapiro	cur->name = name;
43964562Sgshapiro	cur->next = Names;
44064562Sgshapiro	Names = cur;
44164562Sgshapiro
44298121Sgshapiro	result = readheaders(alwaysrespond);
44371345Sgshapiro	if (result == EX_OK && !recent())
44464562Sgshapiro	{
44564562Sgshapiro		time_t now;
44664562Sgshapiro
44764562Sgshapiro		(void) time(&now);
44864562Sgshapiro		setreply(From, now);
44971345Sgshapiro		(void) Db->smdb_close(Db);
45090792Sgshapiro		sendmessage(name, msgfilename, returnaddr);
45164562Sgshapiro	}
45264562Sgshapiro	else
45371345Sgshapiro		(void) Db->smdb_close(Db);
45471345Sgshapiro	if (result == EX_NOUSER)
45571345Sgshapiro		result = EX_OK;
45671345Sgshapiro	exit(result);
45764562Sgshapiro}
45864562Sgshapiro
45964562Sgshapiro/*
46066494Sgshapiro** EATMSG -- read stdin till EOF
46166494Sgshapiro**
46266494Sgshapiro**	Parameters:
46366494Sgshapiro**		none.
46466494Sgshapiro**
46566494Sgshapiro**	Returns:
46666494Sgshapiro**		nothing.
46766494Sgshapiro**
46866494Sgshapiro*/
46977349Sgshapiro
47066494Sgshapirostatic void
47166494Sgshapiroeatmsg()
47266494Sgshapiro{
47366494Sgshapiro	/*
47466494Sgshapiro	**  read the rest of the e-mail and ignore it to avoid problems
47566494Sgshapiro	**  with EPIPE in sendmail
47666494Sgshapiro	*/
47766494Sgshapiro	while (getc(stdin) != EOF)
47866494Sgshapiro		continue;
47966494Sgshapiro}
48066494Sgshapiro
48166494Sgshapiro/*
48264562Sgshapiro** READHEADERS -- read mail headers
48364562Sgshapiro**
48464562Sgshapiro**	Parameters:
48598121Sgshapiro**		alwaysrespond -- respond regardless of whether msg is to me
48664562Sgshapiro**
48764562Sgshapiro**	Returns:
48871345Sgshapiro**		a exit code: NOUSER if no reply, OK if reply, * if error
48964562Sgshapiro**
49066494Sgshapiro**	Side Effects:
49166494Sgshapiro**		may exit().
49266494Sgshapiro**
49364562Sgshapiro*/
49471345Sgshapiro
495168515Sgshapirostatic int
49698121Sgshapiroreadheaders(alwaysrespond)
49798121Sgshapiro	bool alwaysrespond;
49864562Sgshapiro{
49964562Sgshapiro	bool tome, cont;
50064562Sgshapiro	register char *p;
50164562Sgshapiro	register ALIAS *cur;
50264562Sgshapiro	char buf[MAXLINE];
50364562Sgshapiro
50498121Sgshapiro	cont = false;
50598121Sgshapiro	tome = alwaysrespond;
50690792Sgshapiro	while (sm_io_fgets(smioin, SM_TIME_DEFAULT, buf, sizeof(buf)) &&
50790792Sgshapiro	       *buf != '\n')
50864562Sgshapiro	{
50964562Sgshapiro		switch(*buf)
51064562Sgshapiro		{
51164562Sgshapiro		  case 'F':		/* "From " */
51290792Sgshapiro			cont = false;
51364562Sgshapiro			if (strncmp(buf, "From ", 5) == 0)
51464562Sgshapiro			{
51590792Sgshapiro				bool quoted = false;
51664562Sgshapiro
51764562Sgshapiro				p = buf + 5;
51864562Sgshapiro				while (*p != '\0')
51964562Sgshapiro				{
52064562Sgshapiro					/* escaped character */
52164562Sgshapiro					if (*p == '\\')
52264562Sgshapiro					{
52364562Sgshapiro						p++;
52464562Sgshapiro						if (*p == '\0')
52564562Sgshapiro						{
52664562Sgshapiro							msglog(LOG_NOTICE,
52764562Sgshapiro							       "vacation: badly formatted \"From \" line.\n");
52866494Sgshapiro							EXITIT(EX_DATAERR);
52964562Sgshapiro						}
53064562Sgshapiro					}
53164562Sgshapiro					else if (*p == '"')
53264562Sgshapiro						quoted = !quoted;
53364562Sgshapiro					else if (*p == '\r' || *p == '\n')
53464562Sgshapiro						break;
53564562Sgshapiro					else if (*p == ' ' && !quoted)
53664562Sgshapiro						break;
53764562Sgshapiro					p++;
53864562Sgshapiro				}
53964562Sgshapiro				if (quoted)
54064562Sgshapiro				{
54164562Sgshapiro					msglog(LOG_NOTICE,
54264562Sgshapiro					       "vacation: badly formatted \"From \" line.\n");
54366494Sgshapiro					EXITIT(EX_DATAERR);
54464562Sgshapiro				}
54564562Sgshapiro				*p = '\0';
54664562Sgshapiro
54764562Sgshapiro				/* ok since both strings have MAXLINE length */
54864562Sgshapiro				if (*From == '\0')
54990792Sgshapiro					(void) sm_strlcpy(From, buf + 5,
55090792Sgshapiro							  sizeof From);
55164562Sgshapiro				if ((p = strchr(buf + 5, '\n')) != NULL)
55264562Sgshapiro					*p = '\0';
55364562Sgshapiro				if (junkmail(buf + 5))
55471345Sgshapiro					EXITIT(EX_NOUSER);
55564562Sgshapiro			}
55664562Sgshapiro			break;
55764562Sgshapiro
55864562Sgshapiro		  case 'P':		/* "Precedence:" */
55964562Sgshapiro		  case 'p':
56090792Sgshapiro			cont = false;
56164562Sgshapiro			if (strlen(buf) <= 10 ||
56264562Sgshapiro			    strncasecmp(buf, "Precedence", 10) != 0 ||
56364562Sgshapiro			    (buf[10] != ':' && buf[10] != ' ' &&
56464562Sgshapiro			     buf[10] != '\t'))
56564562Sgshapiro				break;
56664562Sgshapiro			if ((p = strchr(buf, ':')) == NULL)
56764562Sgshapiro				break;
56864562Sgshapiro			while (*++p != '\0' && isascii(*p) && isspace(*p));
56964562Sgshapiro			if (*p == '\0')
57064562Sgshapiro				break;
57164562Sgshapiro			if (strncasecmp(p, "junk", 4) == 0 ||
57264562Sgshapiro			    strncasecmp(p, "bulk", 4) == 0 ||
57364562Sgshapiro			    strncasecmp(p, "list", 4) == 0)
57471345Sgshapiro				EXITIT(EX_NOUSER);
57564562Sgshapiro			break;
57664562Sgshapiro
57764562Sgshapiro		  case 'C':		/* "Cc:" */
57864562Sgshapiro		  case 'c':
57964562Sgshapiro			if (strncasecmp(buf, "Cc:", 3) != 0)
58064562Sgshapiro				break;
58190792Sgshapiro			cont = true;
58264562Sgshapiro			goto findme;
58364562Sgshapiro
58464562Sgshapiro		  case 'T':		/* "To:" */
58564562Sgshapiro		  case 't':
58664562Sgshapiro			if (strncasecmp(buf, "To:", 3) != 0)
58764562Sgshapiro				break;
58890792Sgshapiro			cont = true;
58964562Sgshapiro			goto findme;
59064562Sgshapiro
59164562Sgshapiro		  default:
59264562Sgshapiro			if (!isascii(*buf) || !isspace(*buf) || !cont || tome)
59364562Sgshapiro			{
59490792Sgshapiro				cont = false;
59564562Sgshapiro				break;
59664562Sgshapiro			}
59764562Sgshapirofindme:
59864562Sgshapiro			for (cur = Names;
59964562Sgshapiro			     !tome && cur != NULL;
60064562Sgshapiro			     cur = cur->next)
60164562Sgshapiro				tome = nsearch(cur->name, buf);
60264562Sgshapiro		}
60364562Sgshapiro	}
60464562Sgshapiro	if (!tome)
60571345Sgshapiro		EXITIT(EX_NOUSER);
60664562Sgshapiro	if (*From == '\0')
60764562Sgshapiro	{
60864562Sgshapiro		msglog(LOG_NOTICE, "vacation: no initial \"From \" line.\n");
60966494Sgshapiro		EXITIT(EX_DATAERR);
61064562Sgshapiro	}
61171345Sgshapiro	EXITIT(EX_OK);
61264562Sgshapiro}
61364562Sgshapiro
61464562Sgshapiro/*
61564562Sgshapiro** NSEARCH --
61664562Sgshapiro**	do a nice, slow, search of a string for a substring.
61764562Sgshapiro**
61864562Sgshapiro**	Parameters:
61964562Sgshapiro**		name -- name to search.
62064562Sgshapiro**		str -- string in which to search.
62164562Sgshapiro**
62264562Sgshapiro**	Returns:
62364562Sgshapiro**		is name a substring of str?
62464562Sgshapiro**
62564562Sgshapiro*/
62677349Sgshapiro
627168515Sgshapirostatic bool
62864562Sgshapironsearch(name, str)
62964562Sgshapiro	register char *name, *str;
63064562Sgshapiro{
63164562Sgshapiro	register size_t len;
63264562Sgshapiro	register char *s;
63364562Sgshapiro
63464562Sgshapiro	len = strlen(name);
63564562Sgshapiro
63664562Sgshapiro	for (s = str; *s != '\0'; ++s)
63764562Sgshapiro	{
63864562Sgshapiro		/*
63964562Sgshapiro		**  Check to make sure that the string matches and
64064562Sgshapiro		**  the previous character is not an alphanumeric and
64164562Sgshapiro		**  the next character after the match is not an alphanumeric.
64264562Sgshapiro		**
64364562Sgshapiro		**  This prevents matching "eric" to "derick" while still
64464562Sgshapiro		**  matching "eric" to "<eric+detail>".
64564562Sgshapiro		*/
64664562Sgshapiro
64764562Sgshapiro		if (tolower(*s) == tolower(*name) &&
64864562Sgshapiro		    strncasecmp(name, s, len) == 0 &&
64964562Sgshapiro		    (s == str || !isascii(*(s - 1)) || !isalnum(*(s - 1))) &&
65064562Sgshapiro		    (!isascii(*(s + len)) || !isalnum(*(s + len))))
65190792Sgshapiro			return true;
65264562Sgshapiro	}
65390792Sgshapiro	return false;
65464562Sgshapiro}
65564562Sgshapiro
65664562Sgshapiro/*
65764562Sgshapiro** JUNKMAIL --
65864562Sgshapiro**	read the header and return if automagic/junk/bulk/list mail
65964562Sgshapiro**
66064562Sgshapiro**	Parameters:
66164562Sgshapiro**		from -- sender address.
66264562Sgshapiro**
66364562Sgshapiro**	Returns:
66464562Sgshapiro**		is this some automated/junk/bulk/list mail?
66564562Sgshapiro**
66664562Sgshapiro*/
66771345Sgshapiro
66871345Sgshapirostruct ignore
66971345Sgshapiro{
67071345Sgshapiro	char	*name;
67171345Sgshapiro	size_t	len;
67271345Sgshapiro};
67371345Sgshapiro
67471345Sgshapirotypedef struct ignore IGNORE_T;
67571345Sgshapiro
67671345Sgshapiro#define MAX_USER_LEN 256	/* maximum length of local part (sender) */
67771345Sgshapiro
67871345Sgshapiro/* delimiters for the local part of an address */
67971345Sgshapiro#define isdelim(c)	((c) == '%' || (c) == '@' || (c) == '+')
68071345Sgshapiro
681168515Sgshapirostatic bool
68264562Sgshapirojunkmail(from)
68364562Sgshapiro	char *from;
68464562Sgshapiro{
68571345Sgshapiro	bool quot;
68671345Sgshapiro	char *e;
68771345Sgshapiro	size_t len;
68871345Sgshapiro	IGNORE_T *cur;
68971345Sgshapiro	char sender[MAX_USER_LEN];
69071345Sgshapiro	static IGNORE_T ignore[] =
69164562Sgshapiro	{
69264562Sgshapiro		{ "postmaster",		10	},
69364562Sgshapiro		{ "uucp",		4	},
69464562Sgshapiro		{ "mailer-daemon",	13	},
69564562Sgshapiro		{ "mailer",		6	},
69671345Sgshapiro		{ NULL,			0	}
69771345Sgshapiro	};
69871345Sgshapiro
69971345Sgshapiro	static IGNORE_T ignorepost[] =
70071345Sgshapiro	{
70171345Sgshapiro		{ "-request",		8	},
70264562Sgshapiro		{ "-relay",		6	},
70371345Sgshapiro		{ "-owner",		6	},
70464562Sgshapiro		{ NULL,			0	}
70564562Sgshapiro	};
70664562Sgshapiro
70771345Sgshapiro	static IGNORE_T ignorepre[] =
70871345Sgshapiro	{
70971345Sgshapiro		{ "owner-",		6	},
71071345Sgshapiro		{ NULL,			0	}
71171345Sgshapiro	};
71271345Sgshapiro
71364562Sgshapiro	/*
71471345Sgshapiro	**  This is mildly amusing, and I'm not positive it's right; trying
71571345Sgshapiro	**  to find the "real" name of the sender, assuming that addresses
71671345Sgshapiro	**  will be some variant of:
71771345Sgshapiro	**
71871345Sgshapiro	**  From site!site!SENDER%site.domain%site.domain@site.domain
71971345Sgshapiro	*/
72071345Sgshapiro
72190792Sgshapiro	quot = false;
72271345Sgshapiro	e = from;
72371345Sgshapiro	len = 0;
72471345Sgshapiro	while (*e != '\0' && (quot || !isdelim(*e)))
72564562Sgshapiro	{
72671345Sgshapiro		if (*e == '"')
72771345Sgshapiro		{
72871345Sgshapiro			quot = !quot;
72971345Sgshapiro			++e;
73071345Sgshapiro			continue;
73171345Sgshapiro		}
73271345Sgshapiro		if (*e == '\\')
73371345Sgshapiro		{
73471345Sgshapiro			if (*(++e) == '\0')
73571345Sgshapiro			{
73671345Sgshapiro				/* '\\' at end of string? */
73771345Sgshapiro				break;
73871345Sgshapiro			}
73971345Sgshapiro			if (len < MAX_USER_LEN)
74071345Sgshapiro				sender[len++] = *e;
74171345Sgshapiro			++e;
74271345Sgshapiro			continue;
74371345Sgshapiro		}
74471345Sgshapiro		if (*e == '!' && !quot)
74571345Sgshapiro		{
74671345Sgshapiro			len = 0;
74771345Sgshapiro			sender[len] = '\0';
74871345Sgshapiro		}
74964562Sgshapiro		else
75071345Sgshapiro			if (len < MAX_USER_LEN)
75171345Sgshapiro				sender[len++] = *e;
75271345Sgshapiro		++e;
75364562Sgshapiro	}
75471345Sgshapiro	if (len < MAX_USER_LEN)
75571345Sgshapiro		sender[len] = '\0';
75671345Sgshapiro	else
75771345Sgshapiro		sender[MAX_USER_LEN - 1] = '\0';
75871345Sgshapiro
75971345Sgshapiro	if (len <= 0)
76090792Sgshapiro		return false;
76171345Sgshapiro#if 0
76271345Sgshapiro	if (quot)
76390792Sgshapiro		return false;	/* syntax error... */
76471345Sgshapiro#endif /* 0 */
76571345Sgshapiro
76671345Sgshapiro	/* test prefixes */
76771345Sgshapiro	for (cur = ignorepre; cur->name != NULL; ++cur)
76871345Sgshapiro	{
76971345Sgshapiro		if (len >= cur->len &&
77071345Sgshapiro		    strncasecmp(cur->name, sender, cur->len) == 0)
77190792Sgshapiro			return true;
77271345Sgshapiro	}
77371345Sgshapiro
77471345Sgshapiro	/*
77571345Sgshapiro	**  If the name is truncated, don't test the rest.
77671345Sgshapiro	**	We could extract the "tail" of the sender address and
77771345Sgshapiro	**	compare it it ignorepost, however, it seems not worth
77871345Sgshapiro	**	the effort.
77971345Sgshapiro	**	The address surely can't match any entry in ignore[]
78071345Sgshapiro	**	(as long as all of them are shorter than MAX_USER_LEN).
78171345Sgshapiro	*/
78271345Sgshapiro
78371345Sgshapiro	if (len > MAX_USER_LEN)
78490792Sgshapiro		return false;
78571345Sgshapiro
78671345Sgshapiro	/* test full local parts */
78764562Sgshapiro	for (cur = ignore; cur->name != NULL; ++cur)
78864562Sgshapiro	{
78971345Sgshapiro		if (len == cur->len &&
79071345Sgshapiro		    strncasecmp(cur->name, sender, cur->len) == 0)
79190792Sgshapiro			return true;
79271345Sgshapiro	}
79371345Sgshapiro
79471345Sgshapiro	/* test postfixes */
79571345Sgshapiro	for (cur = ignorepost; cur->name != NULL; ++cur)
79671345Sgshapiro	{
79764562Sgshapiro		if (len >= cur->len &&
79871345Sgshapiro		    strncasecmp(cur->name, e - cur->len - 1,
79971345Sgshapiro				cur->len) == 0)
80090792Sgshapiro			return true;
80164562Sgshapiro	}
80290792Sgshapiro	return false;
80364562Sgshapiro}
80464562Sgshapiro
80564562Sgshapiro#define	VIT	"__VACATION__INTERVAL__TIMER__"
80664562Sgshapiro
80764562Sgshapiro/*
80864562Sgshapiro** RECENT --
80964562Sgshapiro**	find out if user has gotten a vacation message recently.
81064562Sgshapiro**
81164562Sgshapiro**	Parameters:
81264562Sgshapiro**		none.
81364562Sgshapiro**
81464562Sgshapiro**	Returns:
81590792Sgshapiro**		true iff user has gotten a vacation message recently.
81664562Sgshapiro**
81764562Sgshapiro*/
81877349Sgshapiro
819168515Sgshapirostatic bool
82064562Sgshapirorecent()
82164562Sgshapiro{
82264562Sgshapiro	SMDB_DBENT key, data;
82364562Sgshapiro	time_t then, next;
82490792Sgshapiro	bool trydomain = false;
82564562Sgshapiro	int st;
82664562Sgshapiro	char *domain;
82764562Sgshapiro
82864562Sgshapiro	memset(&key, '\0', sizeof key);
82964562Sgshapiro	memset(&data, '\0', sizeof data);
83064562Sgshapiro
83164562Sgshapiro	/* get interval time */
83271345Sgshapiro	key.data = VIT;
83371345Sgshapiro	key.size = sizeof(VIT);
83464562Sgshapiro
83564562Sgshapiro	st = Db->smdb_get(Db, &key, &data, 0);
83664562Sgshapiro	if (st != SMDBE_OK)
83764562Sgshapiro		next = SECSPERDAY * DAYSPERWEEK;
83864562Sgshapiro	else
83971345Sgshapiro		memmove(&next, data.data, sizeof(next));
84064562Sgshapiro
84164562Sgshapiro	memset(&data, '\0', sizeof data);
84264562Sgshapiro
84364562Sgshapiro	/* get record for this address */
84471345Sgshapiro	key.data = From;
84571345Sgshapiro	key.size = strlen(From);
84664562Sgshapiro
84764562Sgshapiro	do
84864562Sgshapiro	{
84964562Sgshapiro		st = Db->smdb_get(Db, &key, &data, 0);
85064562Sgshapiro		if (st == SMDBE_OK)
85164562Sgshapiro		{
85271345Sgshapiro			memmove(&then, data.data, sizeof(then));
85364562Sgshapiro			if (next == ONLY_ONCE || then == ONLY_ONCE ||
85464562Sgshapiro			    then + next > time(NULL))
85590792Sgshapiro				return true;
85664562Sgshapiro		}
85764562Sgshapiro		if ((trydomain = !trydomain) &&
85864562Sgshapiro		    (domain = strchr(From, '@')) != NULL)
85964562Sgshapiro		{
86071345Sgshapiro			key.data = domain;
86171345Sgshapiro			key.size = strlen(domain);
86264562Sgshapiro		}
86364562Sgshapiro	} while (trydomain);
86490792Sgshapiro	return false;
86564562Sgshapiro}
86664562Sgshapiro
86764562Sgshapiro/*
86864562Sgshapiro** SETINTERVAL --
86964562Sgshapiro**	store the reply interval
87064562Sgshapiro**
87164562Sgshapiro**	Parameters:
87264562Sgshapiro**		interval -- time interval for replies.
87364562Sgshapiro**
87464562Sgshapiro**	Returns:
87564562Sgshapiro**		nothing.
87664562Sgshapiro**
87764562Sgshapiro**	Side Effects:
87864562Sgshapiro**		stores the reply interval in database.
87964562Sgshapiro*/
88077349Sgshapiro
881168515Sgshapirostatic void
88264562Sgshapirosetinterval(interval)
88364562Sgshapiro	time_t interval;
88464562Sgshapiro{
88564562Sgshapiro	SMDB_DBENT key, data;
88664562Sgshapiro
88764562Sgshapiro	memset(&key, '\0', sizeof key);
88864562Sgshapiro	memset(&data, '\0', sizeof data);
88964562Sgshapiro
89071345Sgshapiro	key.data = VIT;
89171345Sgshapiro	key.size = sizeof(VIT);
89271345Sgshapiro	data.data = (char*) &interval;
89371345Sgshapiro	data.size = sizeof(interval);
89471345Sgshapiro	(void) (Db->smdb_put)(Db, &key, &data, 0);
89564562Sgshapiro}
89664562Sgshapiro
89764562Sgshapiro/*
89864562Sgshapiro** SETREPLY --
89964562Sgshapiro**	store that this user knows about the vacation.
90064562Sgshapiro**
90164562Sgshapiro**	Parameters:
90264562Sgshapiro**		from -- sender address.
90364562Sgshapiro**		when -- last reply time.
90464562Sgshapiro**
90564562Sgshapiro**	Returns:
90664562Sgshapiro**		nothing.
90764562Sgshapiro**
90864562Sgshapiro**	Side Effects:
90964562Sgshapiro**		stores user/time in database.
91064562Sgshapiro*/
91177349Sgshapiro
912168515Sgshapirostatic void
91364562Sgshapirosetreply(from, when)
91464562Sgshapiro	char *from;
91564562Sgshapiro	time_t when;
91664562Sgshapiro{
91764562Sgshapiro	SMDB_DBENT key, data;
91864562Sgshapiro
91964562Sgshapiro	memset(&key, '\0', sizeof key);
92064562Sgshapiro	memset(&data, '\0', sizeof data);
92164562Sgshapiro
92271345Sgshapiro	key.data = from;
92371345Sgshapiro	key.size = strlen(from);
92471345Sgshapiro	data.data = (char*) &when;
92571345Sgshapiro	data.size = sizeof(when);
92671345Sgshapiro	(void) (Db->smdb_put)(Db, &key, &data, 0);
92764562Sgshapiro}
92864562Sgshapiro
92964562Sgshapiro/*
93064562Sgshapiro** XCLUDE --
93164562Sgshapiro**	add users to vacation db so they don't get a reply.
93264562Sgshapiro**
93364562Sgshapiro**	Parameters:
93464562Sgshapiro**		f -- file pointer with list of address to exclude
93564562Sgshapiro**
93664562Sgshapiro**	Returns:
93764562Sgshapiro**		nothing.
93864562Sgshapiro**
93964562Sgshapiro**	Side Effects:
94064562Sgshapiro**		stores users in database.
94164562Sgshapiro*/
94277349Sgshapiro
943168515Sgshapirostatic void
94464562Sgshapiroxclude(f)
94590792Sgshapiro	SM_FILE_T *f;
94664562Sgshapiro{
94764562Sgshapiro	char buf[MAXLINE], *p;
94864562Sgshapiro
94964562Sgshapiro	if (f == NULL)
95064562Sgshapiro		return;
95190792Sgshapiro	while (sm_io_fgets(f, SM_TIME_DEFAULT, buf, sizeof buf))
95264562Sgshapiro	{
95364562Sgshapiro		if ((p = strchr(buf, '\n')) != NULL)
95464562Sgshapiro			*p = '\0';
95564562Sgshapiro		setreply(buf, ONLY_ONCE);
95664562Sgshapiro	}
95764562Sgshapiro}
95864562Sgshapiro
95964562Sgshapiro/*
96064562Sgshapiro** SENDMESSAGE --
96164562Sgshapiro**	exec sendmail to send the vacation file to sender
96264562Sgshapiro**
96364562Sgshapiro**	Parameters:
96464562Sgshapiro**		myname -- user name.
96564562Sgshapiro**		msgfn -- name of file with vacation message.
96690792Sgshapiro**		sender -- use as sender address
96764562Sgshapiro**
96864562Sgshapiro**	Returns:
96964562Sgshapiro**		nothing.
97064562Sgshapiro**
97164562Sgshapiro**	Side Effects:
97264562Sgshapiro**		sends vacation reply.
97364562Sgshapiro*/
97477349Sgshapiro
975168515Sgshapirostatic void
97690792Sgshapirosendmessage(myname, msgfn, sender)
97764562Sgshapiro	char *myname;
97864562Sgshapiro	char *msgfn;
97990792Sgshapiro	char *sender;
98064562Sgshapiro{
98190792Sgshapiro	SM_FILE_T *mfp, *sfp;
98264562Sgshapiro	int i;
98364562Sgshapiro	int pvect[2];
98477349Sgshapiro	char *pv[8];
98564562Sgshapiro	char buf[MAXLINE];
98664562Sgshapiro
98790792Sgshapiro	mfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, msgfn, SM_IO_RDONLY, NULL);
98864562Sgshapiro	if (mfp == NULL)
98964562Sgshapiro	{
99064562Sgshapiro		if (msgfn[0] == '/')
99164562Sgshapiro			msglog(LOG_NOTICE, "vacation: no %s file.\n", msgfn);
99264562Sgshapiro		else
99364562Sgshapiro			msglog(LOG_NOTICE, "vacation: no ~%s/%s file.\n",
99464562Sgshapiro			       myname, msgfn);
99564562Sgshapiro		exit(EX_NOINPUT);
99664562Sgshapiro	}
99764562Sgshapiro	if (pipe(pvect) < 0)
99864562Sgshapiro	{
99990792Sgshapiro		msglog(LOG_ERR, "vacation: pipe: %s", sm_errstring(errno));
100064562Sgshapiro		exit(EX_OSERR);
100164562Sgshapiro	}
100277349Sgshapiro	pv[0] = "sendmail";
100377349Sgshapiro	pv[1] = "-oi";
100477349Sgshapiro	pv[2] = "-f";
100590792Sgshapiro	if (sender != NULL)
100690792Sgshapiro		pv[3] = sender;
100777349Sgshapiro	else
100877349Sgshapiro		pv[3] = myname;
100977349Sgshapiro	pv[4] = "--";
101077349Sgshapiro	pv[5] = From;
101177349Sgshapiro	pv[6] = NULL;
101264562Sgshapiro	i = fork();
101364562Sgshapiro	if (i < 0)
101464562Sgshapiro	{
101590792Sgshapiro		msglog(LOG_ERR, "vacation: fork: %s", sm_errstring(errno));
101664562Sgshapiro		exit(EX_OSERR);
101764562Sgshapiro	}
101864562Sgshapiro	if (i == 0)
101964562Sgshapiro	{
102064562Sgshapiro		(void) dup2(pvect[0], 0);
102164562Sgshapiro		(void) close(pvect[0]);
102264562Sgshapiro		(void) close(pvect[1]);
102390792Sgshapiro		(void) sm_io_close(mfp, SM_TIME_DEFAULT);
102477349Sgshapiro		(void) execv(_PATH_SENDMAIL, pv);
102564562Sgshapiro		msglog(LOG_ERR, "vacation: can't exec %s: %s",
102690792Sgshapiro			_PATH_SENDMAIL, sm_errstring(errno));
102764562Sgshapiro		exit(EX_UNAVAILABLE);
102864562Sgshapiro	}
102964562Sgshapiro	/* check return status of the following calls? XXX */
103064562Sgshapiro	(void) close(pvect[0]);
103190792Sgshapiro	if ((sfp = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT,
103290792Sgshapiro			      (void *) &(pvect[1]),
103390792Sgshapiro			      SM_IO_WRONLY, NULL)) != NULL)
103464562Sgshapiro	{
103590792Sgshapiro		(void) sm_io_fprintf(sfp, SM_TIME_DEFAULT, "To: %s\n", From);
103690792Sgshapiro		(void) sm_io_fprintf(sfp, SM_TIME_DEFAULT,
103790792Sgshapiro				     "Auto-Submitted: auto-replied\n");
103890792Sgshapiro		while (sm_io_fgets(mfp, SM_TIME_DEFAULT, buf, sizeof buf))
103990792Sgshapiro			(void) sm_io_fputs(sfp, SM_TIME_DEFAULT, buf);
104090792Sgshapiro		(void) sm_io_close(mfp, SM_TIME_DEFAULT);
104190792Sgshapiro		(void) sm_io_close(sfp, SM_TIME_DEFAULT);
104264562Sgshapiro	}
104364562Sgshapiro	else
104464562Sgshapiro	{
104590792Sgshapiro		(void) sm_io_close(mfp, SM_TIME_DEFAULT);
104664562Sgshapiro		msglog(LOG_ERR, "vacation: can't open pipe to sendmail");
104764562Sgshapiro		exit(EX_UNAVAILABLE);
104864562Sgshapiro	}
104964562Sgshapiro}
105064562Sgshapiro
1051168515Sgshapirostatic void
105264562Sgshapirousage()
105364562Sgshapiro{
105477349Sgshapiro	msglog(LOG_NOTICE,
1055132943Sgshapiro	       "uid %u: usage: vacation [-a alias] [-C cfpath] [-d] [-f db] [-i] [-j] [-l] [-m msg] [-R returnaddr] [-r interval] [-s sender] [-t time] [-U] [-x] [-z] login\n",
1056132943Sgshapiro	       getuid());
105764562Sgshapiro	exit(EX_USAGE);
105864562Sgshapiro}
105964562Sgshapiro
106064562Sgshapiro/*
106164562Sgshapiro** LISTDB -- list the contents of the vacation database
106264562Sgshapiro**
106364562Sgshapiro**	Parameters:
106464562Sgshapiro**		none.
106564562Sgshapiro**
106664562Sgshapiro**	Returns:
106764562Sgshapiro**		nothing.
106864562Sgshapiro*/
106964562Sgshapiro
107064562Sgshapirostatic void
107164562Sgshapirolistdb()
107264562Sgshapiro{
107364562Sgshapiro	int result;
107464562Sgshapiro	time_t t;
107564562Sgshapiro	SMDB_CURSOR *cursor = NULL;
107664562Sgshapiro	SMDB_DBENT db_key, db_value;
107764562Sgshapiro
107864562Sgshapiro	memset(&db_key, '\0', sizeof db_key);
107964562Sgshapiro	memset(&db_value, '\0', sizeof db_value);
108064562Sgshapiro
108164562Sgshapiro	result = Db->smdb_cursor(Db, &cursor, 0);
108264562Sgshapiro	if (result != SMDBE_OK)
108364562Sgshapiro	{
108490792Sgshapiro		sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
108590792Sgshapiro			      "vacation: set cursor: %s\n",
108690792Sgshapiro			      sm_errstring(result));
108764562Sgshapiro		return;
108864562Sgshapiro	}
108964562Sgshapiro
109064562Sgshapiro	while ((result = cursor->smdbc_get(cursor, &db_key, &db_value,
109164562Sgshapiro					   SMDB_CURSOR_GET_NEXT)) == SMDBE_OK)
109264562Sgshapiro	{
109398121Sgshapiro		char *timestamp;
109498121Sgshapiro
109564562Sgshapiro		/* skip magic VIT entry */
1096110560Sgshapiro		if (db_key.size == strlen(VIT) + 1 &&
109771345Sgshapiro		    strncmp((char *)db_key.data, VIT,
109871345Sgshapiro			    (int)db_key.size - 1) == 0)
109964562Sgshapiro			continue;
110064562Sgshapiro
110164562Sgshapiro		/* skip bogus values */
110271345Sgshapiro		if (db_value.size != sizeof t)
110364562Sgshapiro		{
110490792Sgshapiro			sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
110590792Sgshapiro				      "vacation: %.*s invalid time stamp\n",
110690792Sgshapiro				      (int) db_key.size, (char *) db_key.data);
110764562Sgshapiro			continue;
110864562Sgshapiro		}
110964562Sgshapiro
111071345Sgshapiro		memcpy(&t, db_value.data, sizeof t);
111164562Sgshapiro
111271345Sgshapiro		if (db_key.size > 40)
111371345Sgshapiro			db_key.size = 40;
111464562Sgshapiro
111598121Sgshapiro		if (t <= 0)
111698121Sgshapiro		{
111798121Sgshapiro			/* must be an exclude */
111898121Sgshapiro			timestamp = "(exclusion)\n";
111998121Sgshapiro		}
112098121Sgshapiro		else
112198121Sgshapiro		{
112298121Sgshapiro			timestamp = ctime(&t);
112398121Sgshapiro		}
112490792Sgshapiro		sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%-40.*s %-10s",
112590792Sgshapiro			      (int) db_key.size, (char *) db_key.data,
112698121Sgshapiro			      timestamp);
112764562Sgshapiro
112864562Sgshapiro		memset(&db_key, '\0', sizeof db_key);
112964562Sgshapiro		memset(&db_value, '\0', sizeof db_value);
113064562Sgshapiro	}
113164562Sgshapiro
113264562Sgshapiro	if (result != SMDBE_OK && result != SMDBE_LAST_ENTRY)
113364562Sgshapiro	{
113490792Sgshapiro		sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
113590792Sgshapiro			      "vacation: get value at cursor: %s\n",
113690792Sgshapiro			      sm_errstring(result));
113764562Sgshapiro		if (cursor != NULL)
113864562Sgshapiro		{
113964562Sgshapiro			(void) cursor->smdbc_close(cursor);
114064562Sgshapiro			cursor = NULL;
114164562Sgshapiro		}
114264562Sgshapiro		return;
114364562Sgshapiro	}
114464562Sgshapiro	(void) cursor->smdbc_close(cursor);
114564562Sgshapiro	cursor = NULL;
114664562Sgshapiro}
114764562Sgshapiro
114864562Sgshapiro/*
114964562Sgshapiro** DEBUGLOG -- write message to standard error
115064562Sgshapiro**
115164562Sgshapiro**	Append a message to the standard error for the convenience of
115264562Sgshapiro**	end-users debugging without access to the syslog messages.
115364562Sgshapiro**
115464562Sgshapiro**	Parameters:
115564562Sgshapiro**		i -- syslog log level
115664562Sgshapiro**		fmt -- string format
115764562Sgshapiro**
115864562Sgshapiro**	Returns:
115964562Sgshapiro**		nothing.
116064562Sgshapiro*/
116164562Sgshapiro
116264562Sgshapiro/*VARARGS2*/
116390792Sgshapirostatic SYSLOG_RET_T
116464562Sgshapiro#ifdef __STDC__
116564562Sgshapirodebuglog(int i, const char *fmt, ...)
116664562Sgshapiro#else /* __STDC__ */
116764562Sgshapirodebuglog(i, fmt, va_alist)
116864562Sgshapiro	int i;
116964562Sgshapiro	const char *fmt;
117064562Sgshapiro	va_dcl
117164562Sgshapiro#endif /* __STDC__ */
117264562Sgshapiro
117364562Sgshapiro{
117490792Sgshapiro	SM_VA_LOCAL_DECL
117564562Sgshapiro
117690792Sgshapiro	SM_VA_START(ap, fmt);
117790792Sgshapiro	sm_io_vfprintf(smioerr, SM_TIME_DEFAULT, fmt, ap);
117890792Sgshapiro	SM_VA_END(ap);
117990792Sgshapiro	SYSLOG_RET;
118064562Sgshapiro}
1181