vacation.c revision 94334
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
2394334SgshapiroSM_IDSTR(id, "@(#)$Id: vacation.c,v 8.134 2002/03/01 20:45:00 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;
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
32294334Sgshapiro		cfpath = getcfname(0, 0, SM_GET_SENDMAIL_CF, cfpath);
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	}
37394334Sgshapiro	if (getuid() == 0)
37494334Sgshapiro	{
37594334Sgshapiro		/* Allow root to initialize user's vacation databases */
37694334Sgshapiro		sff |= SFF_OPENASROOT|SFF_ROOTOK;
37766494Sgshapiro
37894334Sgshapiro		/* ... safely */
37994334Sgshapiro		sff |= SFF_NOSLINK|SFF_NOHLINK|SFF_REGONLY;
38094334Sgshapiro	}
38194334Sgshapiro
38294334Sgshapiro
38364562Sgshapiro	result = smdb_open_database(&Db, dbfilename,
38464562Sgshapiro				    O_CREAT|O_RDWR | (iflag ? O_TRUNC : 0),
38566494Sgshapiro				    S_IRUSR|S_IWUSR, sff,
38664562Sgshapiro				    SMDB_TYPE_DEFAULT, &user_info, NULL);
38764562Sgshapiro	if (result != SMDBE_OK)
38864562Sgshapiro	{
38964562Sgshapiro		msglog(LOG_NOTICE, "vacation: %s: %s\n", dbfilename,
39090792Sgshapiro		       sm_errstring(result));
39166494Sgshapiro		EXITM(EX_DATAERR);
39264562Sgshapiro	}
39364562Sgshapiro
39464562Sgshapiro	if (lflag)
39564562Sgshapiro	{
39664562Sgshapiro		listdb();
39771345Sgshapiro		(void) Db->smdb_close(Db);
39864562Sgshapiro		exit(EX_OK);
39964562Sgshapiro	}
40064562Sgshapiro
40164562Sgshapiro	if (interval != INTERVAL_UNDEF)
40264562Sgshapiro		setinterval(interval);
40364562Sgshapiro
40471345Sgshapiro	if (iflag && !exclude)
40564562Sgshapiro	{
40671345Sgshapiro		(void) Db->smdb_close(Db);
40771345Sgshapiro		exit(EX_OK);
40864562Sgshapiro	}
40964562Sgshapiro
41064562Sgshapiro	if (exclude)
41164562Sgshapiro	{
41290792Sgshapiro		xclude(smioin);
41371345Sgshapiro		(void) Db->smdb_close(Db);
41466494Sgshapiro		EXITM(EX_OK);
41564562Sgshapiro	}
41664562Sgshapiro
41790792Sgshapiro	if ((cur = (ALIAS *) malloc((unsigned int) sizeof(ALIAS))) == NULL)
41864562Sgshapiro	{
41964562Sgshapiro		msglog(LOG_NOTICE,
42064562Sgshapiro		       "vacation: can't allocate memory for username.\n");
42171345Sgshapiro		(void) Db->smdb_close(Db);
42266494Sgshapiro		EXITM(EX_OSERR);
42364562Sgshapiro	}
42464562Sgshapiro	cur->name = name;
42564562Sgshapiro	cur->next = Names;
42664562Sgshapiro	Names = cur;
42764562Sgshapiro
42871345Sgshapiro	result = readheaders();
42971345Sgshapiro	if (result == EX_OK && !recent())
43064562Sgshapiro	{
43164562Sgshapiro		time_t now;
43264562Sgshapiro
43364562Sgshapiro		(void) time(&now);
43464562Sgshapiro		setreply(From, now);
43571345Sgshapiro		(void) Db->smdb_close(Db);
43690792Sgshapiro		sendmessage(name, msgfilename, returnaddr);
43764562Sgshapiro	}
43864562Sgshapiro	else
43971345Sgshapiro		(void) Db->smdb_close(Db);
44071345Sgshapiro	if (result == EX_NOUSER)
44171345Sgshapiro		result = EX_OK;
44271345Sgshapiro	exit(result);
44364562Sgshapiro}
44464562Sgshapiro
44564562Sgshapiro/*
44666494Sgshapiro** EATMSG -- read stdin till EOF
44766494Sgshapiro**
44866494Sgshapiro**	Parameters:
44966494Sgshapiro**		none.
45066494Sgshapiro**
45166494Sgshapiro**	Returns:
45266494Sgshapiro**		nothing.
45366494Sgshapiro**
45466494Sgshapiro*/
45577349Sgshapiro
45666494Sgshapirostatic void
45766494Sgshapiroeatmsg()
45866494Sgshapiro{
45966494Sgshapiro	/*
46066494Sgshapiro	**  read the rest of the e-mail and ignore it to avoid problems
46166494Sgshapiro	**  with EPIPE in sendmail
46266494Sgshapiro	*/
46366494Sgshapiro	while (getc(stdin) != EOF)
46466494Sgshapiro		continue;
46566494Sgshapiro}
46666494Sgshapiro
46766494Sgshapiro/*
46864562Sgshapiro** READHEADERS -- read mail headers
46964562Sgshapiro**
47064562Sgshapiro**	Parameters:
47164562Sgshapiro**		none.
47264562Sgshapiro**
47364562Sgshapiro**	Returns:
47471345Sgshapiro**		a exit code: NOUSER if no reply, OK if reply, * if error
47564562Sgshapiro**
47666494Sgshapiro**	Side Effects:
47766494Sgshapiro**		may exit().
47866494Sgshapiro**
47964562Sgshapiro*/
48071345Sgshapiro
48171345Sgshapiroint
48264562Sgshapiroreadheaders()
48364562Sgshapiro{
48464562Sgshapiro	bool tome, cont;
48564562Sgshapiro	register char *p;
48664562Sgshapiro	register ALIAS *cur;
48764562Sgshapiro	char buf[MAXLINE];
48864562Sgshapiro	extern bool junkmail __P((char *));
48964562Sgshapiro	extern bool nsearch __P((char *, char *));
49064562Sgshapiro
49190792Sgshapiro	cont = tome = false;
49290792Sgshapiro	while (sm_io_fgets(smioin, SM_TIME_DEFAULT, buf, sizeof(buf)) &&
49390792Sgshapiro	       *buf != '\n')
49464562Sgshapiro	{
49564562Sgshapiro		switch(*buf)
49664562Sgshapiro		{
49764562Sgshapiro		  case 'F':		/* "From " */
49890792Sgshapiro			cont = false;
49964562Sgshapiro			if (strncmp(buf, "From ", 5) == 0)
50064562Sgshapiro			{
50190792Sgshapiro				bool quoted = false;
50264562Sgshapiro
50364562Sgshapiro				p = buf + 5;
50464562Sgshapiro				while (*p != '\0')
50564562Sgshapiro				{
50664562Sgshapiro					/* escaped character */
50764562Sgshapiro					if (*p == '\\')
50864562Sgshapiro					{
50964562Sgshapiro						p++;
51064562Sgshapiro						if (*p == '\0')
51164562Sgshapiro						{
51264562Sgshapiro							msglog(LOG_NOTICE,
51364562Sgshapiro							       "vacation: badly formatted \"From \" line.\n");
51466494Sgshapiro							EXITIT(EX_DATAERR);
51564562Sgshapiro						}
51664562Sgshapiro					}
51764562Sgshapiro					else if (*p == '"')
51864562Sgshapiro						quoted = !quoted;
51964562Sgshapiro					else if (*p == '\r' || *p == '\n')
52064562Sgshapiro						break;
52164562Sgshapiro					else if (*p == ' ' && !quoted)
52264562Sgshapiro						break;
52364562Sgshapiro					p++;
52464562Sgshapiro				}
52564562Sgshapiro				if (quoted)
52664562Sgshapiro				{
52764562Sgshapiro					msglog(LOG_NOTICE,
52864562Sgshapiro					       "vacation: badly formatted \"From \" line.\n");
52966494Sgshapiro					EXITIT(EX_DATAERR);
53064562Sgshapiro				}
53164562Sgshapiro				*p = '\0';
53264562Sgshapiro
53364562Sgshapiro				/* ok since both strings have MAXLINE length */
53464562Sgshapiro				if (*From == '\0')
53590792Sgshapiro					(void) sm_strlcpy(From, buf + 5,
53690792Sgshapiro							  sizeof From);
53764562Sgshapiro				if ((p = strchr(buf + 5, '\n')) != NULL)
53864562Sgshapiro					*p = '\0';
53964562Sgshapiro				if (junkmail(buf + 5))
54071345Sgshapiro					EXITIT(EX_NOUSER);
54164562Sgshapiro			}
54264562Sgshapiro			break;
54364562Sgshapiro
54464562Sgshapiro		  case 'P':		/* "Precedence:" */
54564562Sgshapiro		  case 'p':
54690792Sgshapiro			cont = false;
54764562Sgshapiro			if (strlen(buf) <= 10 ||
54864562Sgshapiro			    strncasecmp(buf, "Precedence", 10) != 0 ||
54964562Sgshapiro			    (buf[10] != ':' && buf[10] != ' ' &&
55064562Sgshapiro			     buf[10] != '\t'))
55164562Sgshapiro				break;
55264562Sgshapiro			if ((p = strchr(buf, ':')) == NULL)
55364562Sgshapiro				break;
55464562Sgshapiro			while (*++p != '\0' && isascii(*p) && isspace(*p));
55564562Sgshapiro			if (*p == '\0')
55664562Sgshapiro				break;
55764562Sgshapiro			if (strncasecmp(p, "junk", 4) == 0 ||
55864562Sgshapiro			    strncasecmp(p, "bulk", 4) == 0 ||
55964562Sgshapiro			    strncasecmp(p, "list", 4) == 0)
56071345Sgshapiro				EXITIT(EX_NOUSER);
56164562Sgshapiro			break;
56264562Sgshapiro
56364562Sgshapiro		  case 'C':		/* "Cc:" */
56464562Sgshapiro		  case 'c':
56564562Sgshapiro			if (strncasecmp(buf, "Cc:", 3) != 0)
56664562Sgshapiro				break;
56790792Sgshapiro			cont = true;
56864562Sgshapiro			goto findme;
56964562Sgshapiro
57064562Sgshapiro		  case 'T':		/* "To:" */
57164562Sgshapiro		  case 't':
57264562Sgshapiro			if (strncasecmp(buf, "To:", 3) != 0)
57364562Sgshapiro				break;
57490792Sgshapiro			cont = true;
57564562Sgshapiro			goto findme;
57664562Sgshapiro
57764562Sgshapiro		  default:
57864562Sgshapiro			if (!isascii(*buf) || !isspace(*buf) || !cont || tome)
57964562Sgshapiro			{
58090792Sgshapiro				cont = false;
58164562Sgshapiro				break;
58264562Sgshapiro			}
58364562Sgshapirofindme:
58464562Sgshapiro			for (cur = Names;
58564562Sgshapiro			     !tome && cur != NULL;
58664562Sgshapiro			     cur = cur->next)
58764562Sgshapiro				tome = nsearch(cur->name, buf);
58864562Sgshapiro		}
58964562Sgshapiro	}
59064562Sgshapiro	if (!tome)
59171345Sgshapiro		EXITIT(EX_NOUSER);
59264562Sgshapiro	if (*From == '\0')
59364562Sgshapiro	{
59464562Sgshapiro		msglog(LOG_NOTICE, "vacation: no initial \"From \" line.\n");
59566494Sgshapiro		EXITIT(EX_DATAERR);
59664562Sgshapiro	}
59771345Sgshapiro	EXITIT(EX_OK);
59864562Sgshapiro}
59964562Sgshapiro
60064562Sgshapiro/*
60164562Sgshapiro** NSEARCH --
60264562Sgshapiro**	do a nice, slow, search of a string for a substring.
60364562Sgshapiro**
60464562Sgshapiro**	Parameters:
60564562Sgshapiro**		name -- name to search.
60664562Sgshapiro**		str -- string in which to search.
60764562Sgshapiro**
60864562Sgshapiro**	Returns:
60964562Sgshapiro**		is name a substring of str?
61064562Sgshapiro**
61164562Sgshapiro*/
61277349Sgshapiro
61364562Sgshapirobool
61464562Sgshapironsearch(name, str)
61564562Sgshapiro	register char *name, *str;
61664562Sgshapiro{
61764562Sgshapiro	register size_t len;
61864562Sgshapiro	register char *s;
61964562Sgshapiro
62064562Sgshapiro	len = strlen(name);
62164562Sgshapiro
62264562Sgshapiro	for (s = str; *s != '\0'; ++s)
62364562Sgshapiro	{
62464562Sgshapiro		/*
62564562Sgshapiro		**  Check to make sure that the string matches and
62664562Sgshapiro		**  the previous character is not an alphanumeric and
62764562Sgshapiro		**  the next character after the match is not an alphanumeric.
62864562Sgshapiro		**
62964562Sgshapiro		**  This prevents matching "eric" to "derick" while still
63064562Sgshapiro		**  matching "eric" to "<eric+detail>".
63164562Sgshapiro		*/
63264562Sgshapiro
63364562Sgshapiro		if (tolower(*s) == tolower(*name) &&
63464562Sgshapiro		    strncasecmp(name, s, len) == 0 &&
63564562Sgshapiro		    (s == str || !isascii(*(s - 1)) || !isalnum(*(s - 1))) &&
63664562Sgshapiro		    (!isascii(*(s + len)) || !isalnum(*(s + len))))
63790792Sgshapiro			return true;
63864562Sgshapiro	}
63990792Sgshapiro	return false;
64064562Sgshapiro}
64164562Sgshapiro
64264562Sgshapiro/*
64364562Sgshapiro** JUNKMAIL --
64464562Sgshapiro**	read the header and return if automagic/junk/bulk/list mail
64564562Sgshapiro**
64664562Sgshapiro**	Parameters:
64764562Sgshapiro**		from -- sender address.
64864562Sgshapiro**
64964562Sgshapiro**	Returns:
65064562Sgshapiro**		is this some automated/junk/bulk/list mail?
65164562Sgshapiro**
65264562Sgshapiro*/
65371345Sgshapiro
65471345Sgshapirostruct ignore
65571345Sgshapiro{
65671345Sgshapiro	char	*name;
65771345Sgshapiro	size_t	len;
65871345Sgshapiro};
65971345Sgshapiro
66071345Sgshapirotypedef struct ignore IGNORE_T;
66171345Sgshapiro
66271345Sgshapiro#define MAX_USER_LEN 256	/* maximum length of local part (sender) */
66371345Sgshapiro
66471345Sgshapiro/* delimiters for the local part of an address */
66571345Sgshapiro#define isdelim(c)	((c) == '%' || (c) == '@' || (c) == '+')
66671345Sgshapiro
66764562Sgshapirobool
66864562Sgshapirojunkmail(from)
66964562Sgshapiro	char *from;
67064562Sgshapiro{
67171345Sgshapiro	bool quot;
67271345Sgshapiro	char *e;
67371345Sgshapiro	size_t len;
67471345Sgshapiro	IGNORE_T *cur;
67571345Sgshapiro	char sender[MAX_USER_LEN];
67671345Sgshapiro	static IGNORE_T ignore[] =
67764562Sgshapiro	{
67864562Sgshapiro		{ "postmaster",		10	},
67964562Sgshapiro		{ "uucp",		4	},
68064562Sgshapiro		{ "mailer-daemon",	13	},
68164562Sgshapiro		{ "mailer",		6	},
68271345Sgshapiro		{ NULL,			0	}
68371345Sgshapiro	};
68471345Sgshapiro
68571345Sgshapiro	static IGNORE_T ignorepost[] =
68671345Sgshapiro	{
68771345Sgshapiro		{ "-request",		8	},
68864562Sgshapiro		{ "-relay",		6	},
68971345Sgshapiro		{ "-owner",		6	},
69064562Sgshapiro		{ NULL,			0	}
69164562Sgshapiro	};
69264562Sgshapiro
69371345Sgshapiro	static IGNORE_T ignorepre[] =
69471345Sgshapiro	{
69571345Sgshapiro		{ "owner-",		6	},
69671345Sgshapiro		{ NULL,			0	}
69771345Sgshapiro	};
69871345Sgshapiro
69964562Sgshapiro	/*
70071345Sgshapiro	**  This is mildly amusing, and I'm not positive it's right; trying
70171345Sgshapiro	**  to find the "real" name of the sender, assuming that addresses
70271345Sgshapiro	**  will be some variant of:
70371345Sgshapiro	**
70471345Sgshapiro	**  From site!site!SENDER%site.domain%site.domain@site.domain
70571345Sgshapiro	*/
70671345Sgshapiro
70790792Sgshapiro	quot = false;
70871345Sgshapiro	e = from;
70971345Sgshapiro	len = 0;
71071345Sgshapiro	while (*e != '\0' && (quot || !isdelim(*e)))
71164562Sgshapiro	{
71271345Sgshapiro		if (*e == '"')
71371345Sgshapiro		{
71471345Sgshapiro			quot = !quot;
71571345Sgshapiro			++e;
71671345Sgshapiro			continue;
71771345Sgshapiro		}
71871345Sgshapiro		if (*e == '\\')
71971345Sgshapiro		{
72071345Sgshapiro			if (*(++e) == '\0')
72171345Sgshapiro			{
72271345Sgshapiro				/* '\\' at end of string? */
72371345Sgshapiro				break;
72471345Sgshapiro			}
72571345Sgshapiro			if (len < MAX_USER_LEN)
72671345Sgshapiro				sender[len++] = *e;
72771345Sgshapiro			++e;
72871345Sgshapiro			continue;
72971345Sgshapiro		}
73071345Sgshapiro		if (*e == '!' && !quot)
73171345Sgshapiro		{
73271345Sgshapiro			len = 0;
73371345Sgshapiro			sender[len] = '\0';
73471345Sgshapiro		}
73564562Sgshapiro		else
73671345Sgshapiro			if (len < MAX_USER_LEN)
73771345Sgshapiro				sender[len++] = *e;
73871345Sgshapiro		++e;
73964562Sgshapiro	}
74071345Sgshapiro	if (len < MAX_USER_LEN)
74171345Sgshapiro		sender[len] = '\0';
74271345Sgshapiro	else
74371345Sgshapiro		sender[MAX_USER_LEN - 1] = '\0';
74471345Sgshapiro
74571345Sgshapiro	if (len <= 0)
74690792Sgshapiro		return false;
74771345Sgshapiro#if 0
74871345Sgshapiro	if (quot)
74990792Sgshapiro		return false;	/* syntax error... */
75071345Sgshapiro#endif /* 0 */
75171345Sgshapiro
75271345Sgshapiro	/* test prefixes */
75371345Sgshapiro	for (cur = ignorepre; cur->name != NULL; ++cur)
75471345Sgshapiro	{
75571345Sgshapiro		if (len >= cur->len &&
75671345Sgshapiro		    strncasecmp(cur->name, sender, cur->len) == 0)
75790792Sgshapiro			return true;
75871345Sgshapiro	}
75971345Sgshapiro
76071345Sgshapiro	/*
76171345Sgshapiro	**  If the name is truncated, don't test the rest.
76271345Sgshapiro	**	We could extract the "tail" of the sender address and
76371345Sgshapiro	**	compare it it ignorepost, however, it seems not worth
76471345Sgshapiro	**	the effort.
76571345Sgshapiro	**	The address surely can't match any entry in ignore[]
76671345Sgshapiro	**	(as long as all of them are shorter than MAX_USER_LEN).
76771345Sgshapiro	*/
76871345Sgshapiro
76971345Sgshapiro	if (len > MAX_USER_LEN)
77090792Sgshapiro		return false;
77171345Sgshapiro
77271345Sgshapiro	/* test full local parts */
77364562Sgshapiro	for (cur = ignore; cur->name != NULL; ++cur)
77464562Sgshapiro	{
77571345Sgshapiro		if (len == cur->len &&
77671345Sgshapiro		    strncasecmp(cur->name, sender, cur->len) == 0)
77790792Sgshapiro			return true;
77871345Sgshapiro	}
77971345Sgshapiro
78071345Sgshapiro	/* test postfixes */
78171345Sgshapiro	for (cur = ignorepost; cur->name != NULL; ++cur)
78271345Sgshapiro	{
78364562Sgshapiro		if (len >= cur->len &&
78471345Sgshapiro		    strncasecmp(cur->name, e - cur->len - 1,
78571345Sgshapiro				cur->len) == 0)
78690792Sgshapiro			return true;
78764562Sgshapiro	}
78890792Sgshapiro	return false;
78964562Sgshapiro}
79064562Sgshapiro
79164562Sgshapiro#define	VIT	"__VACATION__INTERVAL__TIMER__"
79264562Sgshapiro
79364562Sgshapiro/*
79464562Sgshapiro** RECENT --
79564562Sgshapiro**	find out if user has gotten a vacation message recently.
79664562Sgshapiro**
79764562Sgshapiro**	Parameters:
79864562Sgshapiro**		none.
79964562Sgshapiro**
80064562Sgshapiro**	Returns:
80190792Sgshapiro**		true iff user has gotten a vacation message recently.
80264562Sgshapiro**
80364562Sgshapiro*/
80477349Sgshapiro
80564562Sgshapirobool
80664562Sgshapirorecent()
80764562Sgshapiro{
80864562Sgshapiro	SMDB_DBENT key, data;
80964562Sgshapiro	time_t then, next;
81090792Sgshapiro	bool trydomain = false;
81164562Sgshapiro	int st;
81264562Sgshapiro	char *domain;
81364562Sgshapiro
81464562Sgshapiro	memset(&key, '\0', sizeof key);
81564562Sgshapiro	memset(&data, '\0', sizeof data);
81664562Sgshapiro
81764562Sgshapiro	/* get interval time */
81871345Sgshapiro	key.data = VIT;
81971345Sgshapiro	key.size = sizeof(VIT);
82064562Sgshapiro
82164562Sgshapiro	st = Db->smdb_get(Db, &key, &data, 0);
82264562Sgshapiro	if (st != SMDBE_OK)
82364562Sgshapiro		next = SECSPERDAY * DAYSPERWEEK;
82464562Sgshapiro	else
82571345Sgshapiro		memmove(&next, data.data, sizeof(next));
82664562Sgshapiro
82764562Sgshapiro	memset(&data, '\0', sizeof data);
82864562Sgshapiro
82964562Sgshapiro	/* get record for this address */
83071345Sgshapiro	key.data = From;
83171345Sgshapiro	key.size = strlen(From);
83264562Sgshapiro
83364562Sgshapiro	do
83464562Sgshapiro	{
83564562Sgshapiro		st = Db->smdb_get(Db, &key, &data, 0);
83664562Sgshapiro		if (st == SMDBE_OK)
83764562Sgshapiro		{
83871345Sgshapiro			memmove(&then, data.data, sizeof(then));
83964562Sgshapiro			if (next == ONLY_ONCE || then == ONLY_ONCE ||
84064562Sgshapiro			    then + next > time(NULL))
84190792Sgshapiro				return true;
84264562Sgshapiro		}
84364562Sgshapiro		if ((trydomain = !trydomain) &&
84464562Sgshapiro		    (domain = strchr(From, '@')) != NULL)
84564562Sgshapiro		{
84671345Sgshapiro			key.data = domain;
84771345Sgshapiro			key.size = strlen(domain);
84864562Sgshapiro		}
84964562Sgshapiro	} while (trydomain);
85090792Sgshapiro	return false;
85164562Sgshapiro}
85264562Sgshapiro
85364562Sgshapiro/*
85464562Sgshapiro** SETINTERVAL --
85564562Sgshapiro**	store the reply interval
85664562Sgshapiro**
85764562Sgshapiro**	Parameters:
85864562Sgshapiro**		interval -- time interval for replies.
85964562Sgshapiro**
86064562Sgshapiro**	Returns:
86164562Sgshapiro**		nothing.
86264562Sgshapiro**
86364562Sgshapiro**	Side Effects:
86464562Sgshapiro**		stores the reply interval in database.
86564562Sgshapiro*/
86677349Sgshapiro
86764562Sgshapirovoid
86864562Sgshapirosetinterval(interval)
86964562Sgshapiro	time_t interval;
87064562Sgshapiro{
87164562Sgshapiro	SMDB_DBENT key, data;
87264562Sgshapiro
87364562Sgshapiro	memset(&key, '\0', sizeof key);
87464562Sgshapiro	memset(&data, '\0', sizeof data);
87564562Sgshapiro
87671345Sgshapiro	key.data = VIT;
87771345Sgshapiro	key.size = sizeof(VIT);
87871345Sgshapiro	data.data = (char*) &interval;
87971345Sgshapiro	data.size = sizeof(interval);
88071345Sgshapiro	(void) (Db->smdb_put)(Db, &key, &data, 0);
88164562Sgshapiro}
88264562Sgshapiro
88364562Sgshapiro/*
88464562Sgshapiro** SETREPLY --
88564562Sgshapiro**	store that this user knows about the vacation.
88664562Sgshapiro**
88764562Sgshapiro**	Parameters:
88864562Sgshapiro**		from -- sender address.
88964562Sgshapiro**		when -- last reply time.
89064562Sgshapiro**
89164562Sgshapiro**	Returns:
89264562Sgshapiro**		nothing.
89364562Sgshapiro**
89464562Sgshapiro**	Side Effects:
89564562Sgshapiro**		stores user/time in database.
89664562Sgshapiro*/
89777349Sgshapiro
89864562Sgshapirovoid
89964562Sgshapirosetreply(from, when)
90064562Sgshapiro	char *from;
90164562Sgshapiro	time_t when;
90264562Sgshapiro{
90364562Sgshapiro	SMDB_DBENT key, data;
90464562Sgshapiro
90564562Sgshapiro	memset(&key, '\0', sizeof key);
90664562Sgshapiro	memset(&data, '\0', sizeof data);
90764562Sgshapiro
90871345Sgshapiro	key.data = from;
90971345Sgshapiro	key.size = strlen(from);
91071345Sgshapiro	data.data = (char*) &when;
91171345Sgshapiro	data.size = sizeof(when);
91271345Sgshapiro	(void) (Db->smdb_put)(Db, &key, &data, 0);
91364562Sgshapiro}
91464562Sgshapiro
91564562Sgshapiro/*
91664562Sgshapiro** XCLUDE --
91764562Sgshapiro**	add users to vacation db so they don't get a reply.
91864562Sgshapiro**
91964562Sgshapiro**	Parameters:
92064562Sgshapiro**		f -- file pointer with list of address to exclude
92164562Sgshapiro**
92264562Sgshapiro**	Returns:
92364562Sgshapiro**		nothing.
92464562Sgshapiro**
92564562Sgshapiro**	Side Effects:
92664562Sgshapiro**		stores users in database.
92764562Sgshapiro*/
92877349Sgshapiro
92964562Sgshapirovoid
93064562Sgshapiroxclude(f)
93190792Sgshapiro	SM_FILE_T *f;
93264562Sgshapiro{
93364562Sgshapiro	char buf[MAXLINE], *p;
93464562Sgshapiro
93564562Sgshapiro	if (f == NULL)
93664562Sgshapiro		return;
93790792Sgshapiro	while (sm_io_fgets(f, SM_TIME_DEFAULT, buf, sizeof buf))
93864562Sgshapiro	{
93964562Sgshapiro		if ((p = strchr(buf, '\n')) != NULL)
94064562Sgshapiro			*p = '\0';
94164562Sgshapiro		setreply(buf, ONLY_ONCE);
94264562Sgshapiro	}
94364562Sgshapiro}
94464562Sgshapiro
94564562Sgshapiro/*
94664562Sgshapiro** SENDMESSAGE --
94764562Sgshapiro**	exec sendmail to send the vacation file to sender
94864562Sgshapiro**
94964562Sgshapiro**	Parameters:
95064562Sgshapiro**		myname -- user name.
95164562Sgshapiro**		msgfn -- name of file with vacation message.
95290792Sgshapiro**		sender -- use as sender address
95364562Sgshapiro**
95464562Sgshapiro**	Returns:
95564562Sgshapiro**		nothing.
95664562Sgshapiro**
95764562Sgshapiro**	Side Effects:
95864562Sgshapiro**		sends vacation reply.
95964562Sgshapiro*/
96077349Sgshapiro
96164562Sgshapirovoid
96290792Sgshapirosendmessage(myname, msgfn, sender)
96364562Sgshapiro	char *myname;
96464562Sgshapiro	char *msgfn;
96590792Sgshapiro	char *sender;
96664562Sgshapiro{
96790792Sgshapiro	SM_FILE_T *mfp, *sfp;
96864562Sgshapiro	int i;
96964562Sgshapiro	int pvect[2];
97077349Sgshapiro	char *pv[8];
97164562Sgshapiro	char buf[MAXLINE];
97264562Sgshapiro
97390792Sgshapiro	mfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, msgfn, SM_IO_RDONLY, NULL);
97464562Sgshapiro	if (mfp == NULL)
97564562Sgshapiro	{
97664562Sgshapiro		if (msgfn[0] == '/')
97764562Sgshapiro			msglog(LOG_NOTICE, "vacation: no %s file.\n", msgfn);
97864562Sgshapiro		else
97964562Sgshapiro			msglog(LOG_NOTICE, "vacation: no ~%s/%s file.\n",
98064562Sgshapiro			       myname, msgfn);
98164562Sgshapiro		exit(EX_NOINPUT);
98264562Sgshapiro	}
98364562Sgshapiro	if (pipe(pvect) < 0)
98464562Sgshapiro	{
98590792Sgshapiro		msglog(LOG_ERR, "vacation: pipe: %s", sm_errstring(errno));
98664562Sgshapiro		exit(EX_OSERR);
98764562Sgshapiro	}
98877349Sgshapiro	pv[0] = "sendmail";
98977349Sgshapiro	pv[1] = "-oi";
99077349Sgshapiro	pv[2] = "-f";
99190792Sgshapiro	if (sender != NULL)
99290792Sgshapiro		pv[3] = sender;
99377349Sgshapiro	else
99477349Sgshapiro		pv[3] = myname;
99577349Sgshapiro	pv[4] = "--";
99677349Sgshapiro	pv[5] = From;
99777349Sgshapiro	pv[6] = NULL;
99864562Sgshapiro	i = fork();
99964562Sgshapiro	if (i < 0)
100064562Sgshapiro	{
100190792Sgshapiro		msglog(LOG_ERR, "vacation: fork: %s", sm_errstring(errno));
100264562Sgshapiro		exit(EX_OSERR);
100364562Sgshapiro	}
100464562Sgshapiro	if (i == 0)
100564562Sgshapiro	{
100664562Sgshapiro		(void) dup2(pvect[0], 0);
100764562Sgshapiro		(void) close(pvect[0]);
100864562Sgshapiro		(void) close(pvect[1]);
100990792Sgshapiro		(void) sm_io_close(mfp, SM_TIME_DEFAULT);
101077349Sgshapiro		(void) execv(_PATH_SENDMAIL, pv);
101164562Sgshapiro		msglog(LOG_ERR, "vacation: can't exec %s: %s",
101290792Sgshapiro			_PATH_SENDMAIL, sm_errstring(errno));
101364562Sgshapiro		exit(EX_UNAVAILABLE);
101464562Sgshapiro	}
101564562Sgshapiro	/* check return status of the following calls? XXX */
101664562Sgshapiro	(void) close(pvect[0]);
101790792Sgshapiro	if ((sfp = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT,
101890792Sgshapiro			      (void *) &(pvect[1]),
101990792Sgshapiro			      SM_IO_WRONLY, NULL)) != NULL)
102064562Sgshapiro	{
102190792Sgshapiro		(void) sm_io_fprintf(sfp, SM_TIME_DEFAULT, "To: %s\n", From);
102290792Sgshapiro		(void) sm_io_fprintf(sfp, SM_TIME_DEFAULT,
102390792Sgshapiro				     "Auto-Submitted: auto-replied\n");
102490792Sgshapiro		while (sm_io_fgets(mfp, SM_TIME_DEFAULT, buf, sizeof buf))
102590792Sgshapiro			(void) sm_io_fputs(sfp, SM_TIME_DEFAULT, buf);
102690792Sgshapiro		(void) sm_io_close(mfp, SM_TIME_DEFAULT);
102790792Sgshapiro		(void) sm_io_close(sfp, SM_TIME_DEFAULT);
102864562Sgshapiro	}
102964562Sgshapiro	else
103064562Sgshapiro	{
103190792Sgshapiro		(void) sm_io_close(mfp, SM_TIME_DEFAULT);
103264562Sgshapiro		msglog(LOG_ERR, "vacation: can't open pipe to sendmail");
103364562Sgshapiro		exit(EX_UNAVAILABLE);
103464562Sgshapiro	}
103564562Sgshapiro}
103664562Sgshapiro
103764562Sgshapirovoid
103864562Sgshapirousage()
103964562Sgshapiro{
104090792Sgshapiro	char *retusage;
104190792Sgshapiro
104290792Sgshapiro#if _FFR_RETURN_ADDR
104390792Sgshapiro	retusage = "[-R returnaddr] ";
104490792Sgshapiro#else /* _FFR_RETURN_ADDR */
104590792Sgshapiro	retusage = "";
104690792Sgshapiro#endif /* _FFR_RETURN_ADDR */
104790792Sgshapiro
104877349Sgshapiro	msglog(LOG_NOTICE,
104990792Sgshapiro	       "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",
105090792Sgshapiro	       getuid(), retusage);
105164562Sgshapiro	exit(EX_USAGE);
105264562Sgshapiro}
105364562Sgshapiro
105464562Sgshapiro/*
105564562Sgshapiro** LISTDB -- list the contents of the vacation database
105664562Sgshapiro**
105764562Sgshapiro**	Parameters:
105864562Sgshapiro**		none.
105964562Sgshapiro**
106064562Sgshapiro**	Returns:
106164562Sgshapiro**		nothing.
106264562Sgshapiro*/
106364562Sgshapiro
106464562Sgshapirostatic void
106564562Sgshapirolistdb()
106664562Sgshapiro{
106764562Sgshapiro	int result;
106864562Sgshapiro	time_t t;
106964562Sgshapiro	SMDB_CURSOR *cursor = NULL;
107064562Sgshapiro	SMDB_DBENT db_key, db_value;
107164562Sgshapiro
107264562Sgshapiro	memset(&db_key, '\0', sizeof db_key);
107364562Sgshapiro	memset(&db_value, '\0', sizeof db_value);
107464562Sgshapiro
107564562Sgshapiro	result = Db->smdb_cursor(Db, &cursor, 0);
107664562Sgshapiro	if (result != SMDBE_OK)
107764562Sgshapiro	{
107890792Sgshapiro		sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
107990792Sgshapiro			      "vacation: set cursor: %s\n",
108090792Sgshapiro			      sm_errstring(result));
108164562Sgshapiro		return;
108264562Sgshapiro	}
108364562Sgshapiro
108464562Sgshapiro	while ((result = cursor->smdbc_get(cursor, &db_key, &db_value,
108564562Sgshapiro					   SMDB_CURSOR_GET_NEXT)) == SMDBE_OK)
108664562Sgshapiro	{
108764562Sgshapiro		/* skip magic VIT entry */
108890792Sgshapiro		if ((int)db_key.size - 1 == strlen(VIT) &&
108971345Sgshapiro		    strncmp((char *)db_key.data, VIT,
109071345Sgshapiro			    (int)db_key.size - 1) == 0)
109164562Sgshapiro			continue;
109264562Sgshapiro
109364562Sgshapiro		/* skip bogus values */
109471345Sgshapiro		if (db_value.size != sizeof t)
109564562Sgshapiro		{
109690792Sgshapiro			sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
109790792Sgshapiro				      "vacation: %.*s invalid time stamp\n",
109890792Sgshapiro				      (int) db_key.size, (char *) db_key.data);
109964562Sgshapiro			continue;
110064562Sgshapiro		}
110164562Sgshapiro
110271345Sgshapiro		memcpy(&t, db_value.data, sizeof t);
110364562Sgshapiro
110471345Sgshapiro		if (db_key.size > 40)
110571345Sgshapiro			db_key.size = 40;
110664562Sgshapiro
110790792Sgshapiro		sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%-40.*s %-10s",
110890792Sgshapiro			      (int) db_key.size, (char *) db_key.data,
110990792Sgshapiro			      ctime(&t));
111064562Sgshapiro
111164562Sgshapiro		memset(&db_key, '\0', sizeof db_key);
111264562Sgshapiro		memset(&db_value, '\0', sizeof db_value);
111364562Sgshapiro	}
111464562Sgshapiro
111564562Sgshapiro	if (result != SMDBE_OK && result != SMDBE_LAST_ENTRY)
111664562Sgshapiro	{
111790792Sgshapiro		sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
111890792Sgshapiro			      "vacation: get value at cursor: %s\n",
111990792Sgshapiro			      sm_errstring(result));
112064562Sgshapiro		if (cursor != NULL)
112164562Sgshapiro		{
112264562Sgshapiro			(void) cursor->smdbc_close(cursor);
112364562Sgshapiro			cursor = NULL;
112464562Sgshapiro		}
112564562Sgshapiro		return;
112664562Sgshapiro	}
112764562Sgshapiro	(void) cursor->smdbc_close(cursor);
112864562Sgshapiro	cursor = NULL;
112964562Sgshapiro}
113064562Sgshapiro
113164562Sgshapiro/*
113264562Sgshapiro** DEBUGLOG -- write message to standard error
113364562Sgshapiro**
113464562Sgshapiro**	Append a message to the standard error for the convenience of
113564562Sgshapiro**	end-users debugging without access to the syslog messages.
113664562Sgshapiro**
113764562Sgshapiro**	Parameters:
113864562Sgshapiro**		i -- syslog log level
113964562Sgshapiro**		fmt -- string format
114064562Sgshapiro**
114164562Sgshapiro**	Returns:
114264562Sgshapiro**		nothing.
114364562Sgshapiro*/
114464562Sgshapiro
114564562Sgshapiro/*VARARGS2*/
114690792Sgshapirostatic SYSLOG_RET_T
114764562Sgshapiro#ifdef __STDC__
114864562Sgshapirodebuglog(int i, const char *fmt, ...)
114964562Sgshapiro#else /* __STDC__ */
115064562Sgshapirodebuglog(i, fmt, va_alist)
115164562Sgshapiro	int i;
115264562Sgshapiro	const char *fmt;
115364562Sgshapiro	va_dcl
115464562Sgshapiro#endif /* __STDC__ */
115564562Sgshapiro
115664562Sgshapiro{
115790792Sgshapiro	SM_VA_LOCAL_DECL
115864562Sgshapiro
115990792Sgshapiro	SM_VA_START(ap, fmt);
116090792Sgshapiro	sm_io_vfprintf(smioerr, SM_TIME_DEFAULT, fmt, ap);
116190792Sgshapiro	SM_VA_END(ap);
116290792Sgshapiro	SYSLOG_RET;
116364562Sgshapiro}
1164