vacation.c revision 90792
164562Sgshapiro/*
277349Sgshapiro * Copyright (c) 1999-2001 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
2390792SgshapiroSM_IDSTR(id, "@(#)$Id: vacation.c,v 8.131 2001/12/12 00:02:42 gshapiro 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;
4964562Sgshapirouid_t	RunAsGid;
5064562Sgshapirochar	*RunAsUserName;
5164562Sgshapiroint	Verbose = 2;
5290792Sgshapirobool	DontInitGroups = false;
5364562Sgshapirouid_t	TrustedUid = 0;
5464562SgshapiroBITMAP256 DontBlameSendmail;
5564562Sgshapiro
5664562Sgshapiro/*
5764562Sgshapiro**  VACATION -- return a message to the sender when on vacation.
5864562Sgshapiro**
5964562Sgshapiro**	This program is invoked as a message receiver.  It returns a
6064562Sgshapiro**	message specified by the user to whomever sent the mail, taking
6164562Sgshapiro**	care not to return a message too often to prevent "I am on
6264562Sgshapiro**	vacation" loops.
6364562Sgshapiro*/
6464562Sgshapiro
6564562Sgshapiro#define	VDB	".vacation"		/* vacation database */
6664562Sgshapiro#define	VMSG	".vacation.msg"		/* vacation message */
6764562Sgshapiro#define SECSPERDAY	(60 * 60 * 24)
6864562Sgshapiro#define DAYSPERWEEK	7
6964562Sgshapiro
7064562Sgshapirotypedef struct alias
7164562Sgshapiro{
7264562Sgshapiro	char *name;
7364562Sgshapiro	struct alias *next;
7464562Sgshapiro} ALIAS;
7564562Sgshapiro
7664562SgshapiroALIAS *Names = NULL;
7764562Sgshapiro
7864562SgshapiroSMDB_DATABASE *Db;
7964562Sgshapiro
8064562Sgshapirochar From[MAXLINE];
8164562Sgshapiro
8290792Sgshapiro#if defined(__hpux) || defined(__osf__)
8390792Sgshapiro# ifndef SM_CONF_SYSLOG_INT
8490792Sgshapiro#  define SM_CONF_SYSLOG_INT	1
8590792Sgshapiro# endif /* SM_CONF_SYSLOG_INT */
8690792Sgshapiro#endif /* defined(__hpux) || defined(__osf__) */
8764562Sgshapiro
8890792Sgshapiro#if SM_CONF_SYSLOG_INT
8990792Sgshapiro# define SYSLOG_RET_T	int
9090792Sgshapiro# define SYSLOG_RET	return 0
9190792Sgshapiro#else /* SM_CONF_SYSLOG_INT */
9290792Sgshapiro# define SYSLOG_RET_T	void
9390792Sgshapiro# define SYSLOG_RET
9490792Sgshapiro#endif /* SM_CONF_SYSLOG_INT */
9590792Sgshapiro
9690792Sgshapirotypedef SYSLOG_RET_T SYSLOG_T __P((int, const char *, ...));
9790792SgshapiroSYSLOG_T *msglog = syslog;
9890792Sgshapirostatic SYSLOG_RET_T debuglog __P((int, const char *, ...));
9966494Sgshapirostatic void eatmsg __P((void));
10090792Sgshapirostatic void listdb __P((void));
10166494Sgshapiro
10266494Sgshapiro/* exit after reading input */
10390792Sgshapiro#define EXITIT(excode) \
10490792Sgshapiro{ \
10590792Sgshapiro	eatmsg(); \
10690792Sgshapiro	return excode; \
10790792Sgshapiro}
10877349Sgshapiro
10990792Sgshapiro#define EXITM(excode) \
11090792Sgshapiro{ \
11190792Sgshapiro	if (!iflag && !lflag) \
11290792Sgshapiro		eatmsg(); \
11390792Sgshapiro	exit(excode); \
11490792Sgshapiro}
11590792Sgshapiro
11664562Sgshapiroint
11764562Sgshapiromain(argc, argv)
11864562Sgshapiro	int argc;
11964562Sgshapiro	char **argv;
12064562Sgshapiro{
12190792Sgshapiro	bool iflag, exclude;
12290792Sgshapiro	bool runasuser = false;
12390792Sgshapiro	bool lflag = false;
12464562Sgshapiro	int mfail = 0, ufail = 0;
12564562Sgshapiro	int ch;
12664562Sgshapiro	int result;
12766494Sgshapiro	long sff;
12864562Sgshapiro	time_t interval;
12964562Sgshapiro	struct passwd *pw;
13064562Sgshapiro	ALIAS *cur;
13177349Sgshapiro	char *dbfilename = NULL;
13277349Sgshapiro	char *msgfilename = NULL;
13390792Sgshapiro	char *cfpath = NULL;
13464562Sgshapiro	char *name;
13590792Sgshapiro	char *returnaddr = NULL;
13664562Sgshapiro	SMDB_USER_INFO user_info;
13764562Sgshapiro	static char rnamebuf[MAXNAME];
13864562Sgshapiro	extern int optind, opterr;
13964562Sgshapiro	extern char *optarg;
14064562Sgshapiro	extern void usage __P((void));
14164562Sgshapiro	extern void setinterval __P((time_t));
14271345Sgshapiro	extern int readheaders __P((void));
14364562Sgshapiro	extern bool recent __P((void));
14464562Sgshapiro	extern void setreply __P((char *, time_t));
14590792Sgshapiro	extern void sendmessage __P((char *, char *, char *));
14690792Sgshapiro	extern void xclude __P((SM_FILE_T *));
14764562Sgshapiro
14864562Sgshapiro	/* Vars needed to link with smutil */
14964562Sgshapiro	clrbitmap(DontBlameSendmail);
15064562Sgshapiro	RunAsUid = RealUid = getuid();
15164562Sgshapiro	RunAsGid = RealGid = getgid();
15264562Sgshapiro	pw = getpwuid(RealUid);
15364562Sgshapiro	if (pw != NULL)
15464562Sgshapiro	{
15564562Sgshapiro		if (strlen(pw->pw_name) > MAXNAME - 1)
15664562Sgshapiro			pw->pw_name[MAXNAME] = '\0';
15790792Sgshapiro		sm_snprintf(rnamebuf, sizeof rnamebuf, "%s", pw->pw_name);
15864562Sgshapiro	}
15964562Sgshapiro	else
16090792Sgshapiro		sm_snprintf(rnamebuf, sizeof rnamebuf,
16190792Sgshapiro			    "Unknown UID %d", (int) RealUid);
16264562Sgshapiro	RunAsUserName = RealUserName = rnamebuf;
16364562Sgshapiro
16477349Sgshapiro# ifdef LOG_MAIL
16564562Sgshapiro	openlog("vacation", LOG_PID, LOG_MAIL);
16677349Sgshapiro# else /* LOG_MAIL */
16764562Sgshapiro	openlog("vacation", LOG_PID);
16877349Sgshapiro# endif /* LOG_MAIL */
16964562Sgshapiro
17064562Sgshapiro	opterr = 0;
17190792Sgshapiro	iflag = false;
17290792Sgshapiro	exclude = false;
17364562Sgshapiro	interval = INTERVAL_UNDEF;
17464562Sgshapiro	*From = '\0';
17564562Sgshapiro
17664562Sgshapiro
17790792Sgshapiro#if _FFR_RETURN_ADDR
17890792Sgshapiro# define OPTIONS	"a:C:df:Iilm:R:r:s:t:Uxz"
17990792Sgshapiro#else /* _FFR_RETURN_ADDR */
18090792Sgshapiro# define OPTIONS	"a:C:df:Iilm:r:s:t:Uxz"
18190792Sgshapiro#endif /* _FFR_RETURN_ADDR */
18290792Sgshapiro
18364562Sgshapiro	while (mfail == 0 && ufail == 0 &&
18464562Sgshapiro	       (ch = getopt(argc, argv, OPTIONS)) != -1)
18564562Sgshapiro	{
18664562Sgshapiro		switch((char)ch)
18764562Sgshapiro		{
18864562Sgshapiro		  case 'a':			/* alias */
18990792Sgshapiro			cur = (ALIAS *) malloc((unsigned int) sizeof(ALIAS));
19064562Sgshapiro			if (cur == NULL)
19164562Sgshapiro			{
19264562Sgshapiro				mfail++;
19364562Sgshapiro				break;
19464562Sgshapiro			}
19564562Sgshapiro			cur->name = optarg;
19664562Sgshapiro			cur->next = Names;
19764562Sgshapiro			Names = cur;
19864562Sgshapiro			break;
19964562Sgshapiro
20090792Sgshapiro		  case 'C':
20190792Sgshapiro			cfpath = optarg;
20290792Sgshapiro			break;
20390792Sgshapiro
20477349Sgshapiro		  case 'd':			/* debug mode */
20590792Sgshapiro			msglog = debuglog;
20664562Sgshapiro			break;
20764562Sgshapiro
20864562Sgshapiro		  case 'f':		/* alternate database */
20964562Sgshapiro			dbfilename = optarg;
21064562Sgshapiro			break;
21164562Sgshapiro
21264562Sgshapiro		  case 'I':			/* backward compatible */
21364562Sgshapiro		  case 'i':			/* init the database */
21490792Sgshapiro			iflag = true;
21564562Sgshapiro			break;
21664562Sgshapiro
21764562Sgshapiro		  case 'l':
21890792Sgshapiro			lflag = true;		/* list the database */
21964562Sgshapiro			break;
22064562Sgshapiro
22164562Sgshapiro		  case 'm':		/* alternate message file */
22264562Sgshapiro			msgfilename = optarg;
22364562Sgshapiro			break;
22464562Sgshapiro
22590792Sgshapiro#if _FFR_RETURN_ADDR
22690792Sgshapiro		  case 'R':
22790792Sgshapiro			returnaddr = optarg;
22890792Sgshapiro			break;
22990792Sgshapiro#endif /* _FFR_RETURN_ADDR */
23090792Sgshapiro
23164562Sgshapiro		  case 'r':
23264562Sgshapiro			if (isascii(*optarg) && isdigit(*optarg))
23364562Sgshapiro			{
23464562Sgshapiro				interval = atol(optarg) * SECSPERDAY;
23564562Sgshapiro				if (interval < 0)
23664562Sgshapiro					ufail++;
23764562Sgshapiro			}
23864562Sgshapiro			else
23964562Sgshapiro				interval = ONLY_ONCE;
24064562Sgshapiro			break;
24164562Sgshapiro
24264562Sgshapiro		  case 's':		/* alternate sender name */
24390792Sgshapiro			(void) sm_strlcpy(From, optarg, sizeof From);
24464562Sgshapiro			break;
24564562Sgshapiro
24664562Sgshapiro		  case 't':		/* SunOS: -t1d (default expire) */
24764562Sgshapiro			break;
24864562Sgshapiro
24977349Sgshapiro		  case 'U':		/* run as single user mode */
25090792Sgshapiro			runasuser = true;
25177349Sgshapiro			break;
25277349Sgshapiro
25364562Sgshapiro		  case 'x':
25490792Sgshapiro			exclude = true;
25564562Sgshapiro			break;
25664562Sgshapiro
25764562Sgshapiro		  case 'z':
25890792Sgshapiro			returnaddr = "<>";
25964562Sgshapiro			break;
26064562Sgshapiro
26164562Sgshapiro		  case '?':
26264562Sgshapiro		  default:
26364562Sgshapiro			ufail++;
26464562Sgshapiro			break;
26564562Sgshapiro		}
26664562Sgshapiro	}
26764562Sgshapiro	argc -= optind;
26864562Sgshapiro	argv += optind;
26964562Sgshapiro
27064562Sgshapiro	if (mfail != 0)
27164562Sgshapiro	{
27264562Sgshapiro		msglog(LOG_NOTICE,
27364562Sgshapiro		       "vacation: can't allocate memory for alias.\n");
27466494Sgshapiro		EXITM(EX_TEMPFAIL);
27564562Sgshapiro	}
27664562Sgshapiro	if (ufail != 0)
27764562Sgshapiro		usage();
27864562Sgshapiro
27964562Sgshapiro	if (argc != 1)
28064562Sgshapiro	{
28190792Sgshapiro		if (!iflag && !lflag && !exclude)
28264562Sgshapiro			usage();
28364562Sgshapiro		if ((pw = getpwuid(getuid())) == NULL)
28464562Sgshapiro		{
28564562Sgshapiro			msglog(LOG_ERR,
28664562Sgshapiro			       "vacation: no such user uid %u.\n", getuid());
28766494Sgshapiro			EXITM(EX_NOUSER);
28864562Sgshapiro		}
28977349Sgshapiro		name = pw->pw_name;
29077349Sgshapiro		user_info.smdbu_id = pw->pw_uid;
29177349Sgshapiro		user_info.smdbu_group_id = pw->pw_gid;
29290792Sgshapiro		(void) sm_strlcpy(user_info.smdbu_name, pw->pw_name,
29390792Sgshapiro				  SMDB_MAX_USER_NAME_LEN);
29477349Sgshapiro		if (chdir(pw->pw_dir) != 0)
29577349Sgshapiro		{
29690792Sgshapiro			msglog(LOG_NOTICE,
29790792Sgshapiro			       "vacation: no such directory %s.\n",
29877349Sgshapiro			       pw->pw_dir);
29977349Sgshapiro			EXITM(EX_NOINPUT);
30077349Sgshapiro		}
30164562Sgshapiro	}
30277349Sgshapiro	else if (runasuser)
30377349Sgshapiro	{
30477349Sgshapiro		name = *argv;
30577349Sgshapiro		if (dbfilename == NULL || msgfilename == NULL)
30677349Sgshapiro		{
30777349Sgshapiro			msglog(LOG_NOTICE,
30877349Sgshapiro			       "vacation: -U requires setting both -f and -m\n");
30977349Sgshapiro			EXITM(EX_NOINPUT);
31077349Sgshapiro		}
31177349Sgshapiro		user_info.smdbu_id = pw->pw_uid;
31277349Sgshapiro		user_info.smdbu_group_id = pw->pw_gid;
31390792Sgshapiro		(void) sm_strlcpy(user_info.smdbu_name, pw->pw_name,
31477349Sgshapiro			       SMDB_MAX_USER_NAME_LEN);
31577349Sgshapiro	}
31677349Sgshapiro	else
31764562Sgshapiro	{
31890792Sgshapiro		int err;
31990792Sgshapiro		SM_CF_OPT_T mbdbname;
32090792Sgshapiro		SM_MBDB_T user;
32190792Sgshapiro
32290792Sgshapiro		cfpath = getcfname(0, 0, SM_GET_SENDMAIL_CF, NULL);
32390792Sgshapiro		mbdbname.opt_name = "MailboxDatabase";
32490792Sgshapiro		mbdbname.opt_val = "pw";
32590792Sgshapiro		(void) sm_cf_getopt(cfpath, 1, &mbdbname);
32690792Sgshapiro		err = sm_mbdb_initialize(mbdbname.opt_val);
32790792Sgshapiro		if (err != EX_OK)
32877349Sgshapiro		{
32990792Sgshapiro			msglog(LOG_ERR,
33090792Sgshapiro			       "vacation: can't open mailbox database: %s.\n",
33190792Sgshapiro			       sm_strexit(err));
33290792Sgshapiro			EXITM(err);
33390792Sgshapiro		}
33490792Sgshapiro		err = sm_mbdb_lookup(*argv, &user);
33590792Sgshapiro		if (err == EX_NOUSER)
33690792Sgshapiro		{
33790792Sgshapiro			msglog(LOG_ERR, "vacation: no such user %s.\n", *argv);
33890792Sgshapiro			EXITM(EX_NOUSER);
33990792Sgshapiro		}
34090792Sgshapiro		if (err != EX_OK)
34190792Sgshapiro		{
34290792Sgshapiro			msglog(LOG_ERR,
34390792Sgshapiro			       "vacation: can't read mailbox database: %s.\n",
34490792Sgshapiro			       sm_strexit(err));
34590792Sgshapiro			EXITM(err);
34690792Sgshapiro		}
34790792Sgshapiro		name = user.mbdb_name;
34890792Sgshapiro		if (chdir(user.mbdb_homedir) != 0)
34990792Sgshapiro		{
35090792Sgshapiro			msglog(LOG_NOTICE,
35190792Sgshapiro			       "vacation: no such directory %s.\n",
35290792Sgshapiro			       user.mbdb_homedir);
35377349Sgshapiro			EXITM(EX_NOINPUT);
35477349Sgshapiro		}
35590792Sgshapiro		user_info.smdbu_id = user.mbdb_uid;
35690792Sgshapiro		user_info.smdbu_group_id = user.mbdb_gid;
35790792Sgshapiro		(void) sm_strlcpy(user_info.smdbu_name, user.mbdb_name,
35877349Sgshapiro			       SMDB_MAX_USER_NAME_LEN);
35964562Sgshapiro	}
36064562Sgshapiro
36177349Sgshapiro	if (dbfilename == NULL)
36277349Sgshapiro		dbfilename = VDB;
36377349Sgshapiro	if (msgfilename == NULL)
36477349Sgshapiro		msgfilename = VMSG;
36577349Sgshapiro
36666494Sgshapiro	sff = SFF_CREAT;
36766494Sgshapiro	if (getegid() != getgid())
36877349Sgshapiro	{
36990792Sgshapiro		/* Allow a set-group-ID vacation binary */
37066494Sgshapiro		RunAsGid = user_info.smdbu_group_id = getegid();
37190792Sgshapiro		sff |= SFF_OPENASROOT;
37277349Sgshapiro	}
37366494Sgshapiro
37464562Sgshapiro	result = smdb_open_database(&Db, dbfilename,
37564562Sgshapiro				    O_CREAT|O_RDWR | (iflag ? O_TRUNC : 0),
37666494Sgshapiro				    S_IRUSR|S_IWUSR, sff,
37764562Sgshapiro				    SMDB_TYPE_DEFAULT, &user_info, NULL);
37864562Sgshapiro	if (result != SMDBE_OK)
37964562Sgshapiro	{
38064562Sgshapiro		msglog(LOG_NOTICE, "vacation: %s: %s\n", dbfilename,
38190792Sgshapiro		       sm_errstring(result));
38266494Sgshapiro		EXITM(EX_DATAERR);
38364562Sgshapiro	}
38464562Sgshapiro
38564562Sgshapiro	if (lflag)
38664562Sgshapiro	{
38764562Sgshapiro		listdb();
38871345Sgshapiro		(void) Db->smdb_close(Db);
38964562Sgshapiro		exit(EX_OK);
39064562Sgshapiro	}
39164562Sgshapiro
39264562Sgshapiro	if (interval != INTERVAL_UNDEF)
39364562Sgshapiro		setinterval(interval);
39464562Sgshapiro
39571345Sgshapiro	if (iflag && !exclude)
39664562Sgshapiro	{
39771345Sgshapiro		(void) Db->smdb_close(Db);
39871345Sgshapiro		exit(EX_OK);
39964562Sgshapiro	}
40064562Sgshapiro
40164562Sgshapiro	if (exclude)
40264562Sgshapiro	{
40390792Sgshapiro		xclude(smioin);
40471345Sgshapiro		(void) Db->smdb_close(Db);
40566494Sgshapiro		EXITM(EX_OK);
40664562Sgshapiro	}
40764562Sgshapiro
40890792Sgshapiro	if ((cur = (ALIAS *) malloc((unsigned int) sizeof(ALIAS))) == NULL)
40964562Sgshapiro	{
41064562Sgshapiro		msglog(LOG_NOTICE,
41164562Sgshapiro		       "vacation: can't allocate memory for username.\n");
41271345Sgshapiro		(void) Db->smdb_close(Db);
41366494Sgshapiro		EXITM(EX_OSERR);
41464562Sgshapiro	}
41564562Sgshapiro	cur->name = name;
41664562Sgshapiro	cur->next = Names;
41764562Sgshapiro	Names = cur;
41864562Sgshapiro
41971345Sgshapiro	result = readheaders();
42071345Sgshapiro	if (result == EX_OK && !recent())
42164562Sgshapiro	{
42264562Sgshapiro		time_t now;
42364562Sgshapiro
42464562Sgshapiro		(void) time(&now);
42564562Sgshapiro		setreply(From, now);
42671345Sgshapiro		(void) Db->smdb_close(Db);
42790792Sgshapiro		sendmessage(name, msgfilename, returnaddr);
42864562Sgshapiro	}
42964562Sgshapiro	else
43071345Sgshapiro		(void) Db->smdb_close(Db);
43171345Sgshapiro	if (result == EX_NOUSER)
43271345Sgshapiro		result = EX_OK;
43371345Sgshapiro	exit(result);
43464562Sgshapiro}
43564562Sgshapiro
43664562Sgshapiro/*
43766494Sgshapiro** EATMSG -- read stdin till EOF
43866494Sgshapiro**
43966494Sgshapiro**	Parameters:
44066494Sgshapiro**		none.
44166494Sgshapiro**
44266494Sgshapiro**	Returns:
44366494Sgshapiro**		nothing.
44466494Sgshapiro**
44566494Sgshapiro*/
44677349Sgshapiro
44766494Sgshapirostatic void
44866494Sgshapiroeatmsg()
44966494Sgshapiro{
45066494Sgshapiro	/*
45166494Sgshapiro	**  read the rest of the e-mail and ignore it to avoid problems
45266494Sgshapiro	**  with EPIPE in sendmail
45366494Sgshapiro	*/
45466494Sgshapiro	while (getc(stdin) != EOF)
45566494Sgshapiro		continue;
45666494Sgshapiro}
45766494Sgshapiro
45866494Sgshapiro/*
45964562Sgshapiro** READHEADERS -- read mail headers
46064562Sgshapiro**
46164562Sgshapiro**	Parameters:
46264562Sgshapiro**		none.
46364562Sgshapiro**
46464562Sgshapiro**	Returns:
46571345Sgshapiro**		a exit code: NOUSER if no reply, OK if reply, * if error
46664562Sgshapiro**
46766494Sgshapiro**	Side Effects:
46866494Sgshapiro**		may exit().
46966494Sgshapiro**
47064562Sgshapiro*/
47171345Sgshapiro
47271345Sgshapiroint
47364562Sgshapiroreadheaders()
47464562Sgshapiro{
47564562Sgshapiro	bool tome, cont;
47664562Sgshapiro	register char *p;
47764562Sgshapiro	register ALIAS *cur;
47864562Sgshapiro	char buf[MAXLINE];
47964562Sgshapiro	extern bool junkmail __P((char *));
48064562Sgshapiro	extern bool nsearch __P((char *, char *));
48164562Sgshapiro
48290792Sgshapiro	cont = tome = false;
48390792Sgshapiro	while (sm_io_fgets(smioin, SM_TIME_DEFAULT, buf, sizeof(buf)) &&
48490792Sgshapiro	       *buf != '\n')
48564562Sgshapiro	{
48664562Sgshapiro		switch(*buf)
48764562Sgshapiro		{
48864562Sgshapiro		  case 'F':		/* "From " */
48990792Sgshapiro			cont = false;
49064562Sgshapiro			if (strncmp(buf, "From ", 5) == 0)
49164562Sgshapiro			{
49290792Sgshapiro				bool quoted = false;
49364562Sgshapiro
49464562Sgshapiro				p = buf + 5;
49564562Sgshapiro				while (*p != '\0')
49664562Sgshapiro				{
49764562Sgshapiro					/* escaped character */
49864562Sgshapiro					if (*p == '\\')
49964562Sgshapiro					{
50064562Sgshapiro						p++;
50164562Sgshapiro						if (*p == '\0')
50264562Sgshapiro						{
50364562Sgshapiro							msglog(LOG_NOTICE,
50464562Sgshapiro							       "vacation: badly formatted \"From \" line.\n");
50566494Sgshapiro							EXITIT(EX_DATAERR);
50664562Sgshapiro						}
50764562Sgshapiro					}
50864562Sgshapiro					else if (*p == '"')
50964562Sgshapiro						quoted = !quoted;
51064562Sgshapiro					else if (*p == '\r' || *p == '\n')
51164562Sgshapiro						break;
51264562Sgshapiro					else if (*p == ' ' && !quoted)
51364562Sgshapiro						break;
51464562Sgshapiro					p++;
51564562Sgshapiro				}
51664562Sgshapiro				if (quoted)
51764562Sgshapiro				{
51864562Sgshapiro					msglog(LOG_NOTICE,
51964562Sgshapiro					       "vacation: badly formatted \"From \" line.\n");
52066494Sgshapiro					EXITIT(EX_DATAERR);
52164562Sgshapiro				}
52264562Sgshapiro				*p = '\0';
52364562Sgshapiro
52464562Sgshapiro				/* ok since both strings have MAXLINE length */
52564562Sgshapiro				if (*From == '\0')
52690792Sgshapiro					(void) sm_strlcpy(From, buf + 5,
52790792Sgshapiro							  sizeof From);
52864562Sgshapiro				if ((p = strchr(buf + 5, '\n')) != NULL)
52964562Sgshapiro					*p = '\0';
53064562Sgshapiro				if (junkmail(buf + 5))
53171345Sgshapiro					EXITIT(EX_NOUSER);
53264562Sgshapiro			}
53364562Sgshapiro			break;
53464562Sgshapiro
53564562Sgshapiro		  case 'P':		/* "Precedence:" */
53664562Sgshapiro		  case 'p':
53790792Sgshapiro			cont = false;
53864562Sgshapiro			if (strlen(buf) <= 10 ||
53964562Sgshapiro			    strncasecmp(buf, "Precedence", 10) != 0 ||
54064562Sgshapiro			    (buf[10] != ':' && buf[10] != ' ' &&
54164562Sgshapiro			     buf[10] != '\t'))
54264562Sgshapiro				break;
54364562Sgshapiro			if ((p = strchr(buf, ':')) == NULL)
54464562Sgshapiro				break;
54564562Sgshapiro			while (*++p != '\0' && isascii(*p) && isspace(*p));
54664562Sgshapiro			if (*p == '\0')
54764562Sgshapiro				break;
54864562Sgshapiro			if (strncasecmp(p, "junk", 4) == 0 ||
54964562Sgshapiro			    strncasecmp(p, "bulk", 4) == 0 ||
55064562Sgshapiro			    strncasecmp(p, "list", 4) == 0)
55171345Sgshapiro				EXITIT(EX_NOUSER);
55264562Sgshapiro			break;
55364562Sgshapiro
55464562Sgshapiro		  case 'C':		/* "Cc:" */
55564562Sgshapiro		  case 'c':
55664562Sgshapiro			if (strncasecmp(buf, "Cc:", 3) != 0)
55764562Sgshapiro				break;
55890792Sgshapiro			cont = true;
55964562Sgshapiro			goto findme;
56064562Sgshapiro
56164562Sgshapiro		  case 'T':		/* "To:" */
56264562Sgshapiro		  case 't':
56364562Sgshapiro			if (strncasecmp(buf, "To:", 3) != 0)
56464562Sgshapiro				break;
56590792Sgshapiro			cont = true;
56664562Sgshapiro			goto findme;
56764562Sgshapiro
56864562Sgshapiro		  default:
56964562Sgshapiro			if (!isascii(*buf) || !isspace(*buf) || !cont || tome)
57064562Sgshapiro			{
57190792Sgshapiro				cont = false;
57264562Sgshapiro				break;
57364562Sgshapiro			}
57464562Sgshapirofindme:
57564562Sgshapiro			for (cur = Names;
57664562Sgshapiro			     !tome && cur != NULL;
57764562Sgshapiro			     cur = cur->next)
57864562Sgshapiro				tome = nsearch(cur->name, buf);
57964562Sgshapiro		}
58064562Sgshapiro	}
58164562Sgshapiro	if (!tome)
58271345Sgshapiro		EXITIT(EX_NOUSER);
58364562Sgshapiro	if (*From == '\0')
58464562Sgshapiro	{
58564562Sgshapiro		msglog(LOG_NOTICE, "vacation: no initial \"From \" line.\n");
58666494Sgshapiro		EXITIT(EX_DATAERR);
58764562Sgshapiro	}
58871345Sgshapiro	EXITIT(EX_OK);
58964562Sgshapiro}
59064562Sgshapiro
59164562Sgshapiro/*
59264562Sgshapiro** NSEARCH --
59364562Sgshapiro**	do a nice, slow, search of a string for a substring.
59464562Sgshapiro**
59564562Sgshapiro**	Parameters:
59664562Sgshapiro**		name -- name to search.
59764562Sgshapiro**		str -- string in which to search.
59864562Sgshapiro**
59964562Sgshapiro**	Returns:
60064562Sgshapiro**		is name a substring of str?
60164562Sgshapiro**
60264562Sgshapiro*/
60377349Sgshapiro
60464562Sgshapirobool
60564562Sgshapironsearch(name, str)
60664562Sgshapiro	register char *name, *str;
60764562Sgshapiro{
60864562Sgshapiro	register size_t len;
60964562Sgshapiro	register char *s;
61064562Sgshapiro
61164562Sgshapiro	len = strlen(name);
61264562Sgshapiro
61364562Sgshapiro	for (s = str; *s != '\0'; ++s)
61464562Sgshapiro	{
61564562Sgshapiro		/*
61664562Sgshapiro		**  Check to make sure that the string matches and
61764562Sgshapiro		**  the previous character is not an alphanumeric and
61864562Sgshapiro		**  the next character after the match is not an alphanumeric.
61964562Sgshapiro		**
62064562Sgshapiro		**  This prevents matching "eric" to "derick" while still
62164562Sgshapiro		**  matching "eric" to "<eric+detail>".
62264562Sgshapiro		*/
62364562Sgshapiro
62464562Sgshapiro		if (tolower(*s) == tolower(*name) &&
62564562Sgshapiro		    strncasecmp(name, s, len) == 0 &&
62664562Sgshapiro		    (s == str || !isascii(*(s - 1)) || !isalnum(*(s - 1))) &&
62764562Sgshapiro		    (!isascii(*(s + len)) || !isalnum(*(s + len))))
62890792Sgshapiro			return true;
62964562Sgshapiro	}
63090792Sgshapiro	return false;
63164562Sgshapiro}
63264562Sgshapiro
63364562Sgshapiro/*
63464562Sgshapiro** JUNKMAIL --
63564562Sgshapiro**	read the header and return if automagic/junk/bulk/list mail
63664562Sgshapiro**
63764562Sgshapiro**	Parameters:
63864562Sgshapiro**		from -- sender address.
63964562Sgshapiro**
64064562Sgshapiro**	Returns:
64164562Sgshapiro**		is this some automated/junk/bulk/list mail?
64264562Sgshapiro**
64364562Sgshapiro*/
64471345Sgshapiro
64571345Sgshapirostruct ignore
64671345Sgshapiro{
64771345Sgshapiro	char	*name;
64871345Sgshapiro	size_t	len;
64971345Sgshapiro};
65071345Sgshapiro
65171345Sgshapirotypedef struct ignore IGNORE_T;
65271345Sgshapiro
65371345Sgshapiro#define MAX_USER_LEN 256	/* maximum length of local part (sender) */
65471345Sgshapiro
65571345Sgshapiro/* delimiters for the local part of an address */
65671345Sgshapiro#define isdelim(c)	((c) == '%' || (c) == '@' || (c) == '+')
65771345Sgshapiro
65864562Sgshapirobool
65964562Sgshapirojunkmail(from)
66064562Sgshapiro	char *from;
66164562Sgshapiro{
66271345Sgshapiro	bool quot;
66371345Sgshapiro	char *e;
66471345Sgshapiro	size_t len;
66571345Sgshapiro	IGNORE_T *cur;
66671345Sgshapiro	char sender[MAX_USER_LEN];
66771345Sgshapiro	static IGNORE_T ignore[] =
66864562Sgshapiro	{
66964562Sgshapiro		{ "postmaster",		10	},
67064562Sgshapiro		{ "uucp",		4	},
67164562Sgshapiro		{ "mailer-daemon",	13	},
67264562Sgshapiro		{ "mailer",		6	},
67371345Sgshapiro		{ NULL,			0	}
67471345Sgshapiro	};
67571345Sgshapiro
67671345Sgshapiro	static IGNORE_T ignorepost[] =
67771345Sgshapiro	{
67871345Sgshapiro		{ "-request",		8	},
67964562Sgshapiro		{ "-relay",		6	},
68071345Sgshapiro		{ "-owner",		6	},
68164562Sgshapiro		{ NULL,			0	}
68264562Sgshapiro	};
68364562Sgshapiro
68471345Sgshapiro	static IGNORE_T ignorepre[] =
68571345Sgshapiro	{
68671345Sgshapiro		{ "owner-",		6	},
68771345Sgshapiro		{ NULL,			0	}
68871345Sgshapiro	};
68971345Sgshapiro
69064562Sgshapiro	/*
69171345Sgshapiro	**  This is mildly amusing, and I'm not positive it's right; trying
69271345Sgshapiro	**  to find the "real" name of the sender, assuming that addresses
69371345Sgshapiro	**  will be some variant of:
69471345Sgshapiro	**
69571345Sgshapiro	**  From site!site!SENDER%site.domain%site.domain@site.domain
69671345Sgshapiro	*/
69771345Sgshapiro
69890792Sgshapiro	quot = false;
69971345Sgshapiro	e = from;
70071345Sgshapiro	len = 0;
70171345Sgshapiro	while (*e != '\0' && (quot || !isdelim(*e)))
70264562Sgshapiro	{
70371345Sgshapiro		if (*e == '"')
70471345Sgshapiro		{
70571345Sgshapiro			quot = !quot;
70671345Sgshapiro			++e;
70771345Sgshapiro			continue;
70871345Sgshapiro		}
70971345Sgshapiro		if (*e == '\\')
71071345Sgshapiro		{
71171345Sgshapiro			if (*(++e) == '\0')
71271345Sgshapiro			{
71371345Sgshapiro				/* '\\' at end of string? */
71471345Sgshapiro				break;
71571345Sgshapiro			}
71671345Sgshapiro			if (len < MAX_USER_LEN)
71771345Sgshapiro				sender[len++] = *e;
71871345Sgshapiro			++e;
71971345Sgshapiro			continue;
72071345Sgshapiro		}
72171345Sgshapiro		if (*e == '!' && !quot)
72271345Sgshapiro		{
72371345Sgshapiro			len = 0;
72471345Sgshapiro			sender[len] = '\0';
72571345Sgshapiro		}
72664562Sgshapiro		else
72771345Sgshapiro			if (len < MAX_USER_LEN)
72871345Sgshapiro				sender[len++] = *e;
72971345Sgshapiro		++e;
73064562Sgshapiro	}
73171345Sgshapiro	if (len < MAX_USER_LEN)
73271345Sgshapiro		sender[len] = '\0';
73371345Sgshapiro	else
73471345Sgshapiro		sender[MAX_USER_LEN - 1] = '\0';
73571345Sgshapiro
73671345Sgshapiro	if (len <= 0)
73790792Sgshapiro		return false;
73871345Sgshapiro#if 0
73971345Sgshapiro	if (quot)
74090792Sgshapiro		return false;	/* syntax error... */
74171345Sgshapiro#endif /* 0 */
74271345Sgshapiro
74371345Sgshapiro	/* test prefixes */
74471345Sgshapiro	for (cur = ignorepre; cur->name != NULL; ++cur)
74571345Sgshapiro	{
74671345Sgshapiro		if (len >= cur->len &&
74771345Sgshapiro		    strncasecmp(cur->name, sender, cur->len) == 0)
74890792Sgshapiro			return true;
74971345Sgshapiro	}
75071345Sgshapiro
75171345Sgshapiro	/*
75271345Sgshapiro	**  If the name is truncated, don't test the rest.
75371345Sgshapiro	**	We could extract the "tail" of the sender address and
75471345Sgshapiro	**	compare it it ignorepost, however, it seems not worth
75571345Sgshapiro	**	the effort.
75671345Sgshapiro	**	The address surely can't match any entry in ignore[]
75771345Sgshapiro	**	(as long as all of them are shorter than MAX_USER_LEN).
75871345Sgshapiro	*/
75971345Sgshapiro
76071345Sgshapiro	if (len > MAX_USER_LEN)
76190792Sgshapiro		return false;
76271345Sgshapiro
76371345Sgshapiro	/* test full local parts */
76464562Sgshapiro	for (cur = ignore; cur->name != NULL; ++cur)
76564562Sgshapiro	{
76671345Sgshapiro		if (len == cur->len &&
76771345Sgshapiro		    strncasecmp(cur->name, sender, cur->len) == 0)
76890792Sgshapiro			return true;
76971345Sgshapiro	}
77071345Sgshapiro
77171345Sgshapiro	/* test postfixes */
77271345Sgshapiro	for (cur = ignorepost; cur->name != NULL; ++cur)
77371345Sgshapiro	{
77464562Sgshapiro		if (len >= cur->len &&
77571345Sgshapiro		    strncasecmp(cur->name, e - cur->len - 1,
77671345Sgshapiro				cur->len) == 0)
77790792Sgshapiro			return true;
77864562Sgshapiro	}
77990792Sgshapiro	return false;
78064562Sgshapiro}
78164562Sgshapiro
78264562Sgshapiro#define	VIT	"__VACATION__INTERVAL__TIMER__"
78364562Sgshapiro
78464562Sgshapiro/*
78564562Sgshapiro** RECENT --
78664562Sgshapiro**	find out if user has gotten a vacation message recently.
78764562Sgshapiro**
78864562Sgshapiro**	Parameters:
78964562Sgshapiro**		none.
79064562Sgshapiro**
79164562Sgshapiro**	Returns:
79290792Sgshapiro**		true iff user has gotten a vacation message recently.
79364562Sgshapiro**
79464562Sgshapiro*/
79577349Sgshapiro
79664562Sgshapirobool
79764562Sgshapirorecent()
79864562Sgshapiro{
79964562Sgshapiro	SMDB_DBENT key, data;
80064562Sgshapiro	time_t then, next;
80190792Sgshapiro	bool trydomain = false;
80264562Sgshapiro	int st;
80364562Sgshapiro	char *domain;
80464562Sgshapiro
80564562Sgshapiro	memset(&key, '\0', sizeof key);
80664562Sgshapiro	memset(&data, '\0', sizeof data);
80764562Sgshapiro
80864562Sgshapiro	/* get interval time */
80971345Sgshapiro	key.data = VIT;
81071345Sgshapiro	key.size = sizeof(VIT);
81164562Sgshapiro
81264562Sgshapiro	st = Db->smdb_get(Db, &key, &data, 0);
81364562Sgshapiro	if (st != SMDBE_OK)
81464562Sgshapiro		next = SECSPERDAY * DAYSPERWEEK;
81564562Sgshapiro	else
81671345Sgshapiro		memmove(&next, data.data, sizeof(next));
81764562Sgshapiro
81864562Sgshapiro	memset(&data, '\0', sizeof data);
81964562Sgshapiro
82064562Sgshapiro	/* get record for this address */
82171345Sgshapiro	key.data = From;
82271345Sgshapiro	key.size = strlen(From);
82364562Sgshapiro
82464562Sgshapiro	do
82564562Sgshapiro	{
82664562Sgshapiro		st = Db->smdb_get(Db, &key, &data, 0);
82764562Sgshapiro		if (st == SMDBE_OK)
82864562Sgshapiro		{
82971345Sgshapiro			memmove(&then, data.data, sizeof(then));
83064562Sgshapiro			if (next == ONLY_ONCE || then == ONLY_ONCE ||
83164562Sgshapiro			    then + next > time(NULL))
83290792Sgshapiro				return true;
83364562Sgshapiro		}
83464562Sgshapiro		if ((trydomain = !trydomain) &&
83564562Sgshapiro		    (domain = strchr(From, '@')) != NULL)
83664562Sgshapiro		{
83771345Sgshapiro			key.data = domain;
83871345Sgshapiro			key.size = strlen(domain);
83964562Sgshapiro		}
84064562Sgshapiro	} while (trydomain);
84190792Sgshapiro	return false;
84264562Sgshapiro}
84364562Sgshapiro
84464562Sgshapiro/*
84564562Sgshapiro** SETINTERVAL --
84664562Sgshapiro**	store the reply interval
84764562Sgshapiro**
84864562Sgshapiro**	Parameters:
84964562Sgshapiro**		interval -- time interval for replies.
85064562Sgshapiro**
85164562Sgshapiro**	Returns:
85264562Sgshapiro**		nothing.
85364562Sgshapiro**
85464562Sgshapiro**	Side Effects:
85564562Sgshapiro**		stores the reply interval in database.
85664562Sgshapiro*/
85777349Sgshapiro
85864562Sgshapirovoid
85964562Sgshapirosetinterval(interval)
86064562Sgshapiro	time_t interval;
86164562Sgshapiro{
86264562Sgshapiro	SMDB_DBENT key, data;
86364562Sgshapiro
86464562Sgshapiro	memset(&key, '\0', sizeof key);
86564562Sgshapiro	memset(&data, '\0', sizeof data);
86664562Sgshapiro
86771345Sgshapiro	key.data = VIT;
86871345Sgshapiro	key.size = sizeof(VIT);
86971345Sgshapiro	data.data = (char*) &interval;
87071345Sgshapiro	data.size = sizeof(interval);
87171345Sgshapiro	(void) (Db->smdb_put)(Db, &key, &data, 0);
87264562Sgshapiro}
87364562Sgshapiro
87464562Sgshapiro/*
87564562Sgshapiro** SETREPLY --
87664562Sgshapiro**	store that this user knows about the vacation.
87764562Sgshapiro**
87864562Sgshapiro**	Parameters:
87964562Sgshapiro**		from -- sender address.
88064562Sgshapiro**		when -- last reply time.
88164562Sgshapiro**
88264562Sgshapiro**	Returns:
88364562Sgshapiro**		nothing.
88464562Sgshapiro**
88564562Sgshapiro**	Side Effects:
88664562Sgshapiro**		stores user/time in database.
88764562Sgshapiro*/
88877349Sgshapiro
88964562Sgshapirovoid
89064562Sgshapirosetreply(from, when)
89164562Sgshapiro	char *from;
89264562Sgshapiro	time_t when;
89364562Sgshapiro{
89464562Sgshapiro	SMDB_DBENT key, data;
89564562Sgshapiro
89664562Sgshapiro	memset(&key, '\0', sizeof key);
89764562Sgshapiro	memset(&data, '\0', sizeof data);
89864562Sgshapiro
89971345Sgshapiro	key.data = from;
90071345Sgshapiro	key.size = strlen(from);
90171345Sgshapiro	data.data = (char*) &when;
90271345Sgshapiro	data.size = sizeof(when);
90371345Sgshapiro	(void) (Db->smdb_put)(Db, &key, &data, 0);
90464562Sgshapiro}
90564562Sgshapiro
90664562Sgshapiro/*
90764562Sgshapiro** XCLUDE --
90864562Sgshapiro**	add users to vacation db so they don't get a reply.
90964562Sgshapiro**
91064562Sgshapiro**	Parameters:
91164562Sgshapiro**		f -- file pointer with list of address to exclude
91264562Sgshapiro**
91364562Sgshapiro**	Returns:
91464562Sgshapiro**		nothing.
91564562Sgshapiro**
91664562Sgshapiro**	Side Effects:
91764562Sgshapiro**		stores users in database.
91864562Sgshapiro*/
91977349Sgshapiro
92064562Sgshapirovoid
92164562Sgshapiroxclude(f)
92290792Sgshapiro	SM_FILE_T *f;
92364562Sgshapiro{
92464562Sgshapiro	char buf[MAXLINE], *p;
92564562Sgshapiro
92664562Sgshapiro	if (f == NULL)
92764562Sgshapiro		return;
92890792Sgshapiro	while (sm_io_fgets(f, SM_TIME_DEFAULT, buf, sizeof buf))
92964562Sgshapiro	{
93064562Sgshapiro		if ((p = strchr(buf, '\n')) != NULL)
93164562Sgshapiro			*p = '\0';
93264562Sgshapiro		setreply(buf, ONLY_ONCE);
93364562Sgshapiro	}
93464562Sgshapiro}
93564562Sgshapiro
93664562Sgshapiro/*
93764562Sgshapiro** SENDMESSAGE --
93864562Sgshapiro**	exec sendmail to send the vacation file to sender
93964562Sgshapiro**
94064562Sgshapiro**	Parameters:
94164562Sgshapiro**		myname -- user name.
94264562Sgshapiro**		msgfn -- name of file with vacation message.
94390792Sgshapiro**		sender -- use as sender address
94464562Sgshapiro**
94564562Sgshapiro**	Returns:
94664562Sgshapiro**		nothing.
94764562Sgshapiro**
94864562Sgshapiro**	Side Effects:
94964562Sgshapiro**		sends vacation reply.
95064562Sgshapiro*/
95177349Sgshapiro
95264562Sgshapirovoid
95390792Sgshapirosendmessage(myname, msgfn, sender)
95464562Sgshapiro	char *myname;
95564562Sgshapiro	char *msgfn;
95690792Sgshapiro	char *sender;
95764562Sgshapiro{
95890792Sgshapiro	SM_FILE_T *mfp, *sfp;
95964562Sgshapiro	int i;
96064562Sgshapiro	int pvect[2];
96177349Sgshapiro	char *pv[8];
96264562Sgshapiro	char buf[MAXLINE];
96364562Sgshapiro
96490792Sgshapiro	mfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, msgfn, SM_IO_RDONLY, NULL);
96564562Sgshapiro	if (mfp == NULL)
96664562Sgshapiro	{
96764562Sgshapiro		if (msgfn[0] == '/')
96864562Sgshapiro			msglog(LOG_NOTICE, "vacation: no %s file.\n", msgfn);
96964562Sgshapiro		else
97064562Sgshapiro			msglog(LOG_NOTICE, "vacation: no ~%s/%s file.\n",
97164562Sgshapiro			       myname, msgfn);
97264562Sgshapiro		exit(EX_NOINPUT);
97364562Sgshapiro	}
97464562Sgshapiro	if (pipe(pvect) < 0)
97564562Sgshapiro	{
97690792Sgshapiro		msglog(LOG_ERR, "vacation: pipe: %s", sm_errstring(errno));
97764562Sgshapiro		exit(EX_OSERR);
97864562Sgshapiro	}
97977349Sgshapiro	pv[0] = "sendmail";
98077349Sgshapiro	pv[1] = "-oi";
98177349Sgshapiro	pv[2] = "-f";
98290792Sgshapiro	if (sender != NULL)
98390792Sgshapiro		pv[3] = sender;
98477349Sgshapiro	else
98577349Sgshapiro		pv[3] = myname;
98677349Sgshapiro	pv[4] = "--";
98777349Sgshapiro	pv[5] = From;
98877349Sgshapiro	pv[6] = NULL;
98964562Sgshapiro	i = fork();
99064562Sgshapiro	if (i < 0)
99164562Sgshapiro	{
99290792Sgshapiro		msglog(LOG_ERR, "vacation: fork: %s", sm_errstring(errno));
99364562Sgshapiro		exit(EX_OSERR);
99464562Sgshapiro	}
99564562Sgshapiro	if (i == 0)
99664562Sgshapiro	{
99764562Sgshapiro		(void) dup2(pvect[0], 0);
99864562Sgshapiro		(void) close(pvect[0]);
99964562Sgshapiro		(void) close(pvect[1]);
100090792Sgshapiro		(void) sm_io_close(mfp, SM_TIME_DEFAULT);
100177349Sgshapiro		(void) execv(_PATH_SENDMAIL, pv);
100264562Sgshapiro		msglog(LOG_ERR, "vacation: can't exec %s: %s",
100390792Sgshapiro			_PATH_SENDMAIL, sm_errstring(errno));
100464562Sgshapiro		exit(EX_UNAVAILABLE);
100564562Sgshapiro	}
100664562Sgshapiro	/* check return status of the following calls? XXX */
100764562Sgshapiro	(void) close(pvect[0]);
100890792Sgshapiro	if ((sfp = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT,
100990792Sgshapiro			      (void *) &(pvect[1]),
101090792Sgshapiro			      SM_IO_WRONLY, NULL)) != NULL)
101164562Sgshapiro	{
101290792Sgshapiro		(void) sm_io_fprintf(sfp, SM_TIME_DEFAULT, "To: %s\n", From);
101390792Sgshapiro		(void) sm_io_fprintf(sfp, SM_TIME_DEFAULT,
101490792Sgshapiro				     "Auto-Submitted: auto-replied\n");
101590792Sgshapiro		while (sm_io_fgets(mfp, SM_TIME_DEFAULT, buf, sizeof buf))
101690792Sgshapiro			(void) sm_io_fputs(sfp, SM_TIME_DEFAULT, buf);
101790792Sgshapiro		(void) sm_io_close(mfp, SM_TIME_DEFAULT);
101890792Sgshapiro		(void) sm_io_close(sfp, SM_TIME_DEFAULT);
101964562Sgshapiro	}
102064562Sgshapiro	else
102164562Sgshapiro	{
102290792Sgshapiro		(void) sm_io_close(mfp, SM_TIME_DEFAULT);
102364562Sgshapiro		msglog(LOG_ERR, "vacation: can't open pipe to sendmail");
102464562Sgshapiro		exit(EX_UNAVAILABLE);
102564562Sgshapiro	}
102664562Sgshapiro}
102764562Sgshapiro
102864562Sgshapirovoid
102964562Sgshapirousage()
103064562Sgshapiro{
103190792Sgshapiro	char *retusage;
103290792Sgshapiro
103390792Sgshapiro#if _FFR_RETURN_ADDR
103490792Sgshapiro	retusage = "[-R returnaddr] ";
103590792Sgshapiro#else /* _FFR_RETURN_ADDR */
103690792Sgshapiro	retusage = "";
103790792Sgshapiro#endif /* _FFR_RETURN_ADDR */
103890792Sgshapiro
103977349Sgshapiro	msglog(LOG_NOTICE,
104090792Sgshapiro	       "uid %u: usage: vacation [-a alias] [-C cfpath] [-d] [-f db] [-i] [-l] [-m msg] %s[-r interval] [-s sender] [-t time] [-U] [-x] [-z] login\n",
104190792Sgshapiro	       getuid(), retusage);
104264562Sgshapiro	exit(EX_USAGE);
104364562Sgshapiro}
104464562Sgshapiro
104564562Sgshapiro/*
104664562Sgshapiro** LISTDB -- list the contents of the vacation database
104764562Sgshapiro**
104864562Sgshapiro**	Parameters:
104964562Sgshapiro**		none.
105064562Sgshapiro**
105164562Sgshapiro**	Returns:
105264562Sgshapiro**		nothing.
105364562Sgshapiro*/
105464562Sgshapiro
105564562Sgshapirostatic void
105664562Sgshapirolistdb()
105764562Sgshapiro{
105864562Sgshapiro	int result;
105964562Sgshapiro	time_t t;
106064562Sgshapiro	SMDB_CURSOR *cursor = NULL;
106164562Sgshapiro	SMDB_DBENT db_key, db_value;
106264562Sgshapiro
106364562Sgshapiro	memset(&db_key, '\0', sizeof db_key);
106464562Sgshapiro	memset(&db_value, '\0', sizeof db_value);
106564562Sgshapiro
106664562Sgshapiro	result = Db->smdb_cursor(Db, &cursor, 0);
106764562Sgshapiro	if (result != SMDBE_OK)
106864562Sgshapiro	{
106990792Sgshapiro		sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
107090792Sgshapiro			      "vacation: set cursor: %s\n",
107190792Sgshapiro			      sm_errstring(result));
107264562Sgshapiro		return;
107364562Sgshapiro	}
107464562Sgshapiro
107564562Sgshapiro	while ((result = cursor->smdbc_get(cursor, &db_key, &db_value,
107664562Sgshapiro					   SMDB_CURSOR_GET_NEXT)) == SMDBE_OK)
107764562Sgshapiro	{
107864562Sgshapiro		/* skip magic VIT entry */
107990792Sgshapiro		if ((int)db_key.size - 1 == strlen(VIT) &&
108071345Sgshapiro		    strncmp((char *)db_key.data, VIT,
108171345Sgshapiro			    (int)db_key.size - 1) == 0)
108264562Sgshapiro			continue;
108364562Sgshapiro
108464562Sgshapiro		/* skip bogus values */
108571345Sgshapiro		if (db_value.size != sizeof t)
108664562Sgshapiro		{
108790792Sgshapiro			sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
108890792Sgshapiro				      "vacation: %.*s invalid time stamp\n",
108990792Sgshapiro				      (int) db_key.size, (char *) db_key.data);
109064562Sgshapiro			continue;
109164562Sgshapiro		}
109264562Sgshapiro
109371345Sgshapiro		memcpy(&t, db_value.data, sizeof t);
109464562Sgshapiro
109571345Sgshapiro		if (db_key.size > 40)
109671345Sgshapiro			db_key.size = 40;
109764562Sgshapiro
109890792Sgshapiro		sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%-40.*s %-10s",
109990792Sgshapiro			      (int) db_key.size, (char *) db_key.data,
110090792Sgshapiro			      ctime(&t));
110164562Sgshapiro
110264562Sgshapiro		memset(&db_key, '\0', sizeof db_key);
110364562Sgshapiro		memset(&db_value, '\0', sizeof db_value);
110464562Sgshapiro	}
110564562Sgshapiro
110664562Sgshapiro	if (result != SMDBE_OK && result != SMDBE_LAST_ENTRY)
110764562Sgshapiro	{
110890792Sgshapiro		sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
110990792Sgshapiro			      "vacation: get value at cursor: %s\n",
111090792Sgshapiro			      sm_errstring(result));
111164562Sgshapiro		if (cursor != NULL)
111264562Sgshapiro		{
111364562Sgshapiro			(void) cursor->smdbc_close(cursor);
111464562Sgshapiro			cursor = NULL;
111564562Sgshapiro		}
111664562Sgshapiro		return;
111764562Sgshapiro	}
111864562Sgshapiro	(void) cursor->smdbc_close(cursor);
111964562Sgshapiro	cursor = NULL;
112064562Sgshapiro}
112164562Sgshapiro
112264562Sgshapiro/*
112364562Sgshapiro** DEBUGLOG -- write message to standard error
112464562Sgshapiro**
112564562Sgshapiro**	Append a message to the standard error for the convenience of
112664562Sgshapiro**	end-users debugging without access to the syslog messages.
112764562Sgshapiro**
112864562Sgshapiro**	Parameters:
112964562Sgshapiro**		i -- syslog log level
113064562Sgshapiro**		fmt -- string format
113164562Sgshapiro**
113264562Sgshapiro**	Returns:
113364562Sgshapiro**		nothing.
113464562Sgshapiro*/
113564562Sgshapiro
113664562Sgshapiro/*VARARGS2*/
113790792Sgshapirostatic SYSLOG_RET_T
113864562Sgshapiro#ifdef __STDC__
113964562Sgshapirodebuglog(int i, const char *fmt, ...)
114064562Sgshapiro#else /* __STDC__ */
114164562Sgshapirodebuglog(i, fmt, va_alist)
114264562Sgshapiro	int i;
114364562Sgshapiro	const char *fmt;
114464562Sgshapiro	va_dcl
114564562Sgshapiro#endif /* __STDC__ */
114664562Sgshapiro
114764562Sgshapiro{
114890792Sgshapiro	SM_VA_LOCAL_DECL
114964562Sgshapiro
115090792Sgshapiro	SM_VA_START(ap, fmt);
115190792Sgshapiro	sm_io_vfprintf(smioerr, SM_TIME_DEFAULT, fmt, ap);
115290792Sgshapiro	SM_VA_END(ap);
115390792Sgshapiro	SYSLOG_RET;
115464562Sgshapiro}
1155