vacation.c revision 141858
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
23141858SgshapiroSM_IDSTR(id, "@(#)$Id: vacation.c,v 8.142 2004/11/02 18:25:33 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];
81141858Sgshapirobool CloseMBDB = false;
8264562Sgshapiro
8390792Sgshapiro#if defined(__hpux) || defined(__osf__)
8490792Sgshapiro# ifndef SM_CONF_SYSLOG_INT
8590792Sgshapiro#  define SM_CONF_SYSLOG_INT	1
8690792Sgshapiro# endif /* SM_CONF_SYSLOG_INT */
8790792Sgshapiro#endif /* defined(__hpux) || defined(__osf__) */
8864562Sgshapiro
8990792Sgshapiro#if SM_CONF_SYSLOG_INT
9090792Sgshapiro# define SYSLOG_RET_T	int
9190792Sgshapiro# define SYSLOG_RET	return 0
9290792Sgshapiro#else /* SM_CONF_SYSLOG_INT */
9390792Sgshapiro# define SYSLOG_RET_T	void
9490792Sgshapiro# define SYSLOG_RET
9590792Sgshapiro#endif /* SM_CONF_SYSLOG_INT */
9690792Sgshapiro
9790792Sgshapirotypedef SYSLOG_RET_T SYSLOG_T __P((int, const char *, ...));
9890792SgshapiroSYSLOG_T *msglog = syslog;
9990792Sgshapirostatic SYSLOG_RET_T debuglog __P((int, const char *, ...));
10066494Sgshapirostatic void eatmsg __P((void));
10190792Sgshapirostatic void listdb __P((void));
10266494Sgshapiro
10366494Sgshapiro/* exit after reading input */
104141858Sgshapiro#define EXITIT(excode)			\
105141858Sgshapiro{					\
106141858Sgshapiro	eatmsg();			\
107141858Sgshapiro	if (CloseMBDB)			\
108141858Sgshapiro	{				\
109141858Sgshapiro		sm_mbdb_terminate();	\
110141858Sgshapiro		CloseMBDB = false;	\
111141858Sgshapiro	}				\
112141858Sgshapiro	return excode;			\
11390792Sgshapiro}
11477349Sgshapiro
115141858Sgshapiro#define EXITM(excode)			\
116141858Sgshapiro{					\
117141858Sgshapiro	if (!initdb && !list)		\
118141858Sgshapiro		eatmsg();		\
119141858Sgshapiro	if (CloseMBDB)			\
120141858Sgshapiro	{				\
121141858Sgshapiro		sm_mbdb_terminate();	\
122141858Sgshapiro		CloseMBDB = false;	\
123141858Sgshapiro	}				\
124141858Sgshapiro	exit(excode);			\
12590792Sgshapiro}
12690792Sgshapiro
12764562Sgshapiroint
12864562Sgshapiromain(argc, argv)
12964562Sgshapiro	int argc;
13064562Sgshapiro	char **argv;
13164562Sgshapiro{
13298121Sgshapiro	bool alwaysrespond = false;
13398121Sgshapiro	bool initdb, exclude;
13490792Sgshapiro	bool runasuser = false;
13598121Sgshapiro	bool list = false;
13664562Sgshapiro	int mfail = 0, ufail = 0;
13764562Sgshapiro	int ch;
13864562Sgshapiro	int result;
13966494Sgshapiro	long sff;
14064562Sgshapiro	time_t interval;
14164562Sgshapiro	struct passwd *pw;
14264562Sgshapiro	ALIAS *cur;
14377349Sgshapiro	char *dbfilename = NULL;
14477349Sgshapiro	char *msgfilename = NULL;
14590792Sgshapiro	char *cfpath = NULL;
14664562Sgshapiro	char *name;
14790792Sgshapiro	char *returnaddr = NULL;
14864562Sgshapiro	SMDB_USER_INFO user_info;
14964562Sgshapiro	static char rnamebuf[MAXNAME];
15064562Sgshapiro	extern int optind, opterr;
15164562Sgshapiro	extern char *optarg;
15264562Sgshapiro	extern void usage __P((void));
15364562Sgshapiro	extern void setinterval __P((time_t));
15498121Sgshapiro	extern int readheaders __P((bool));
15564562Sgshapiro	extern bool recent __P((void));
15664562Sgshapiro	extern void setreply __P((char *, time_t));
15790792Sgshapiro	extern void sendmessage __P((char *, char *, char *));
15890792Sgshapiro	extern void xclude __P((SM_FILE_T *));
15964562Sgshapiro
16064562Sgshapiro	/* Vars needed to link with smutil */
16164562Sgshapiro	clrbitmap(DontBlameSendmail);
16264562Sgshapiro	RunAsUid = RealUid = getuid();
16364562Sgshapiro	RunAsGid = RealGid = getgid();
16464562Sgshapiro	pw = getpwuid(RealUid);
16564562Sgshapiro	if (pw != NULL)
16664562Sgshapiro	{
16764562Sgshapiro		if (strlen(pw->pw_name) > MAXNAME - 1)
16864562Sgshapiro			pw->pw_name[MAXNAME] = '\0';
16990792Sgshapiro		sm_snprintf(rnamebuf, sizeof rnamebuf, "%s", pw->pw_name);
17064562Sgshapiro	}
17164562Sgshapiro	else
17290792Sgshapiro		sm_snprintf(rnamebuf, sizeof rnamebuf,
17390792Sgshapiro			    "Unknown UID %d", (int) RealUid);
17464562Sgshapiro	RunAsUserName = RealUserName = rnamebuf;
17564562Sgshapiro
17677349Sgshapiro# ifdef LOG_MAIL
17764562Sgshapiro	openlog("vacation", LOG_PID, LOG_MAIL);
17877349Sgshapiro# else /* LOG_MAIL */
17964562Sgshapiro	openlog("vacation", LOG_PID);
18077349Sgshapiro# endif /* LOG_MAIL */
18164562Sgshapiro
18264562Sgshapiro	opterr = 0;
18398121Sgshapiro	initdb = false;
18490792Sgshapiro	exclude = false;
18564562Sgshapiro	interval = INTERVAL_UNDEF;
18664562Sgshapiro	*From = '\0';
18764562Sgshapiro
18864562Sgshapiro
18998121Sgshapiro#define OPTIONS	"a:C:df:Iijlm:R:r:s:t:Uxz"
19090792Sgshapiro
19164562Sgshapiro	while (mfail == 0 && ufail == 0 &&
19264562Sgshapiro	       (ch = getopt(argc, argv, OPTIONS)) != -1)
19364562Sgshapiro	{
19464562Sgshapiro		switch((char)ch)
19564562Sgshapiro		{
19664562Sgshapiro		  case 'a':			/* alias */
19790792Sgshapiro			cur = (ALIAS *) malloc((unsigned int) sizeof(ALIAS));
19864562Sgshapiro			if (cur == NULL)
19964562Sgshapiro			{
20064562Sgshapiro				mfail++;
20164562Sgshapiro				break;
20264562Sgshapiro			}
20364562Sgshapiro			cur->name = optarg;
20464562Sgshapiro			cur->next = Names;
20564562Sgshapiro			Names = cur;
20664562Sgshapiro			break;
20764562Sgshapiro
20890792Sgshapiro		  case 'C':
20990792Sgshapiro			cfpath = optarg;
21090792Sgshapiro			break;
21190792Sgshapiro
21277349Sgshapiro		  case 'd':			/* debug mode */
21390792Sgshapiro			msglog = debuglog;
21464562Sgshapiro			break;
21564562Sgshapiro
21664562Sgshapiro		  case 'f':		/* alternate database */
21764562Sgshapiro			dbfilename = optarg;
21864562Sgshapiro			break;
21964562Sgshapiro
22064562Sgshapiro		  case 'I':			/* backward compatible */
22164562Sgshapiro		  case 'i':			/* init the database */
22298121Sgshapiro			initdb = true;
22364562Sgshapiro			break;
22464562Sgshapiro
22598121Sgshapiro		  case 'j':
22698121Sgshapiro			alwaysrespond = true;
22798121Sgshapiro			break;
22898121Sgshapiro
22964562Sgshapiro		  case 'l':
23098121Sgshapiro			list = true;		/* list the database */
23164562Sgshapiro			break;
23264562Sgshapiro
23364562Sgshapiro		  case 'm':		/* alternate message file */
23464562Sgshapiro			msgfilename = optarg;
23564562Sgshapiro			break;
23664562Sgshapiro
23790792Sgshapiro		  case 'R':
23890792Sgshapiro			returnaddr = optarg;
23990792Sgshapiro			break;
24090792Sgshapiro
24164562Sgshapiro		  case 'r':
24264562Sgshapiro			if (isascii(*optarg) && isdigit(*optarg))
24364562Sgshapiro			{
24464562Sgshapiro				interval = atol(optarg) * SECSPERDAY;
24564562Sgshapiro				if (interval < 0)
24664562Sgshapiro					ufail++;
24764562Sgshapiro			}
24864562Sgshapiro			else
24964562Sgshapiro				interval = ONLY_ONCE;
25064562Sgshapiro			break;
25164562Sgshapiro
25264562Sgshapiro		  case 's':		/* alternate sender name */
25390792Sgshapiro			(void) sm_strlcpy(From, optarg, sizeof From);
25464562Sgshapiro			break;
25564562Sgshapiro
25664562Sgshapiro		  case 't':		/* SunOS: -t1d (default expire) */
25764562Sgshapiro			break;
25864562Sgshapiro
25977349Sgshapiro		  case 'U':		/* run as single user mode */
26090792Sgshapiro			runasuser = true;
26177349Sgshapiro			break;
26277349Sgshapiro
26364562Sgshapiro		  case 'x':
26490792Sgshapiro			exclude = true;
26564562Sgshapiro			break;
26664562Sgshapiro
26764562Sgshapiro		  case 'z':
26890792Sgshapiro			returnaddr = "<>";
26964562Sgshapiro			break;
27064562Sgshapiro
27164562Sgshapiro		  case '?':
27264562Sgshapiro		  default:
27364562Sgshapiro			ufail++;
27464562Sgshapiro			break;
27564562Sgshapiro		}
27664562Sgshapiro	}
27764562Sgshapiro	argc -= optind;
27864562Sgshapiro	argv += optind;
27964562Sgshapiro
28064562Sgshapiro	if (mfail != 0)
28164562Sgshapiro	{
28264562Sgshapiro		msglog(LOG_NOTICE,
28364562Sgshapiro		       "vacation: can't allocate memory for alias.\n");
28466494Sgshapiro		EXITM(EX_TEMPFAIL);
28564562Sgshapiro	}
28664562Sgshapiro	if (ufail != 0)
28764562Sgshapiro		usage();
28864562Sgshapiro
28964562Sgshapiro	if (argc != 1)
29064562Sgshapiro	{
29198121Sgshapiro		if (!initdb && !list && !exclude)
29264562Sgshapiro			usage();
29364562Sgshapiro		if ((pw = getpwuid(getuid())) == NULL)
29464562Sgshapiro		{
29564562Sgshapiro			msglog(LOG_ERR,
29664562Sgshapiro			       "vacation: no such user uid %u.\n", getuid());
29766494Sgshapiro			EXITM(EX_NOUSER);
29864562Sgshapiro		}
29977349Sgshapiro		name = pw->pw_name;
30077349Sgshapiro		user_info.smdbu_id = pw->pw_uid;
30177349Sgshapiro		user_info.smdbu_group_id = pw->pw_gid;
30290792Sgshapiro		(void) sm_strlcpy(user_info.smdbu_name, pw->pw_name,
30390792Sgshapiro				  SMDB_MAX_USER_NAME_LEN);
30477349Sgshapiro		if (chdir(pw->pw_dir) != 0)
30577349Sgshapiro		{
30690792Sgshapiro			msglog(LOG_NOTICE,
30790792Sgshapiro			       "vacation: no such directory %s.\n",
30877349Sgshapiro			       pw->pw_dir);
30977349Sgshapiro			EXITM(EX_NOINPUT);
31077349Sgshapiro		}
31164562Sgshapiro	}
31277349Sgshapiro	else if (runasuser)
31377349Sgshapiro	{
31477349Sgshapiro		name = *argv;
31577349Sgshapiro		if (dbfilename == NULL || msgfilename == NULL)
31677349Sgshapiro		{
31777349Sgshapiro			msglog(LOG_NOTICE,
31877349Sgshapiro			       "vacation: -U requires setting both -f and -m\n");
31977349Sgshapiro			EXITM(EX_NOINPUT);
32077349Sgshapiro		}
32177349Sgshapiro		user_info.smdbu_id = pw->pw_uid;
32277349Sgshapiro		user_info.smdbu_group_id = pw->pw_gid;
32390792Sgshapiro		(void) sm_strlcpy(user_info.smdbu_name, pw->pw_name,
32477349Sgshapiro			       SMDB_MAX_USER_NAME_LEN);
32577349Sgshapiro	}
32677349Sgshapiro	else
32764562Sgshapiro	{
32890792Sgshapiro		int err;
32990792Sgshapiro		SM_CF_OPT_T mbdbname;
33090792Sgshapiro		SM_MBDB_T user;
33190792Sgshapiro
33294334Sgshapiro		cfpath = getcfname(0, 0, SM_GET_SENDMAIL_CF, cfpath);
33390792Sgshapiro		mbdbname.opt_name = "MailboxDatabase";
33490792Sgshapiro		mbdbname.opt_val = "pw";
33590792Sgshapiro		(void) sm_cf_getopt(cfpath, 1, &mbdbname);
33690792Sgshapiro		err = sm_mbdb_initialize(mbdbname.opt_val);
33790792Sgshapiro		if (err != EX_OK)
33877349Sgshapiro		{
33990792Sgshapiro			msglog(LOG_ERR,
34090792Sgshapiro			       "vacation: can't open mailbox database: %s.\n",
34190792Sgshapiro			       sm_strexit(err));
34290792Sgshapiro			EXITM(err);
34390792Sgshapiro		}
344141858Sgshapiro		CloseMBDB = true;
34590792Sgshapiro		err = sm_mbdb_lookup(*argv, &user);
34690792Sgshapiro		if (err == EX_NOUSER)
34790792Sgshapiro		{
34890792Sgshapiro			msglog(LOG_ERR, "vacation: no such user %s.\n", *argv);
34990792Sgshapiro			EXITM(EX_NOUSER);
35090792Sgshapiro		}
35190792Sgshapiro		if (err != EX_OK)
35290792Sgshapiro		{
35390792Sgshapiro			msglog(LOG_ERR,
35490792Sgshapiro			       "vacation: can't read mailbox database: %s.\n",
35590792Sgshapiro			       sm_strexit(err));
35690792Sgshapiro			EXITM(err);
35790792Sgshapiro		}
35890792Sgshapiro		name = user.mbdb_name;
35990792Sgshapiro		if (chdir(user.mbdb_homedir) != 0)
36090792Sgshapiro		{
36190792Sgshapiro			msglog(LOG_NOTICE,
36290792Sgshapiro			       "vacation: no such directory %s.\n",
36390792Sgshapiro			       user.mbdb_homedir);
36477349Sgshapiro			EXITM(EX_NOINPUT);
36577349Sgshapiro		}
36690792Sgshapiro		user_info.smdbu_id = user.mbdb_uid;
36790792Sgshapiro		user_info.smdbu_group_id = user.mbdb_gid;
36890792Sgshapiro		(void) sm_strlcpy(user_info.smdbu_name, user.mbdb_name,
36977349Sgshapiro			       SMDB_MAX_USER_NAME_LEN);
37064562Sgshapiro	}
37164562Sgshapiro
37277349Sgshapiro	if (dbfilename == NULL)
37377349Sgshapiro		dbfilename = VDB;
37477349Sgshapiro	if (msgfilename == NULL)
37577349Sgshapiro		msgfilename = VMSG;
37677349Sgshapiro
37766494Sgshapiro	sff = SFF_CREAT;
37866494Sgshapiro	if (getegid() != getgid())
37977349Sgshapiro	{
38090792Sgshapiro		/* Allow a set-group-ID vacation binary */
38166494Sgshapiro		RunAsGid = user_info.smdbu_group_id = getegid();
38290792Sgshapiro		sff |= SFF_OPENASROOT;
38377349Sgshapiro	}
38494334Sgshapiro	if (getuid() == 0)
38594334Sgshapiro	{
38694334Sgshapiro		/* Allow root to initialize user's vacation databases */
38794334Sgshapiro		sff |= SFF_OPENASROOT|SFF_ROOTOK;
38866494Sgshapiro
38994334Sgshapiro		/* ... safely */
39094334Sgshapiro		sff |= SFF_NOSLINK|SFF_NOHLINK|SFF_REGONLY;
39194334Sgshapiro	}
39294334Sgshapiro
39394334Sgshapiro
39464562Sgshapiro	result = smdb_open_database(&Db, dbfilename,
39598121Sgshapiro				    O_CREAT|O_RDWR | (initdb ? O_TRUNC : 0),
39666494Sgshapiro				    S_IRUSR|S_IWUSR, sff,
39764562Sgshapiro				    SMDB_TYPE_DEFAULT, &user_info, NULL);
39864562Sgshapiro	if (result != SMDBE_OK)
39964562Sgshapiro	{
40064562Sgshapiro		msglog(LOG_NOTICE, "vacation: %s: %s\n", dbfilename,
40190792Sgshapiro		       sm_errstring(result));
40266494Sgshapiro		EXITM(EX_DATAERR);
40364562Sgshapiro	}
40464562Sgshapiro
40598121Sgshapiro	if (list)
40664562Sgshapiro	{
40764562Sgshapiro		listdb();
40871345Sgshapiro		(void) Db->smdb_close(Db);
40964562Sgshapiro		exit(EX_OK);
41064562Sgshapiro	}
41164562Sgshapiro
41264562Sgshapiro	if (interval != INTERVAL_UNDEF)
41364562Sgshapiro		setinterval(interval);
41464562Sgshapiro
41598121Sgshapiro	if (initdb && !exclude)
41664562Sgshapiro	{
41771345Sgshapiro		(void) Db->smdb_close(Db);
41871345Sgshapiro		exit(EX_OK);
41964562Sgshapiro	}
42064562Sgshapiro
42164562Sgshapiro	if (exclude)
42264562Sgshapiro	{
42390792Sgshapiro		xclude(smioin);
42471345Sgshapiro		(void) Db->smdb_close(Db);
42566494Sgshapiro		EXITM(EX_OK);
42664562Sgshapiro	}
42764562Sgshapiro
42890792Sgshapiro	if ((cur = (ALIAS *) malloc((unsigned int) sizeof(ALIAS))) == NULL)
42964562Sgshapiro	{
43064562Sgshapiro		msglog(LOG_NOTICE,
43164562Sgshapiro		       "vacation: can't allocate memory for username.\n");
43271345Sgshapiro		(void) Db->smdb_close(Db);
43366494Sgshapiro		EXITM(EX_OSERR);
43464562Sgshapiro	}
43564562Sgshapiro	cur->name = name;
43664562Sgshapiro	cur->next = Names;
43764562Sgshapiro	Names = cur;
43864562Sgshapiro
43998121Sgshapiro	result = readheaders(alwaysrespond);
44071345Sgshapiro	if (result == EX_OK && !recent())
44164562Sgshapiro	{
44264562Sgshapiro		time_t now;
44364562Sgshapiro
44464562Sgshapiro		(void) time(&now);
44564562Sgshapiro		setreply(From, now);
44671345Sgshapiro		(void) Db->smdb_close(Db);
44790792Sgshapiro		sendmessage(name, msgfilename, returnaddr);
44864562Sgshapiro	}
44964562Sgshapiro	else
45071345Sgshapiro		(void) Db->smdb_close(Db);
45171345Sgshapiro	if (result == EX_NOUSER)
45271345Sgshapiro		result = EX_OK;
45371345Sgshapiro	exit(result);
45464562Sgshapiro}
45564562Sgshapiro
45664562Sgshapiro/*
45766494Sgshapiro** EATMSG -- read stdin till EOF
45866494Sgshapiro**
45966494Sgshapiro**	Parameters:
46066494Sgshapiro**		none.
46166494Sgshapiro**
46266494Sgshapiro**	Returns:
46366494Sgshapiro**		nothing.
46466494Sgshapiro**
46566494Sgshapiro*/
46677349Sgshapiro
46766494Sgshapirostatic void
46866494Sgshapiroeatmsg()
46966494Sgshapiro{
47066494Sgshapiro	/*
47166494Sgshapiro	**  read the rest of the e-mail and ignore it to avoid problems
47266494Sgshapiro	**  with EPIPE in sendmail
47366494Sgshapiro	*/
47466494Sgshapiro	while (getc(stdin) != EOF)
47566494Sgshapiro		continue;
47666494Sgshapiro}
47766494Sgshapiro
47866494Sgshapiro/*
47964562Sgshapiro** READHEADERS -- read mail headers
48064562Sgshapiro**
48164562Sgshapiro**	Parameters:
48298121Sgshapiro**		alwaysrespond -- respond regardless of whether msg is to me
48364562Sgshapiro**
48464562Sgshapiro**	Returns:
48571345Sgshapiro**		a exit code: NOUSER if no reply, OK if reply, * if error
48664562Sgshapiro**
48766494Sgshapiro**	Side Effects:
48866494Sgshapiro**		may exit().
48966494Sgshapiro**
49064562Sgshapiro*/
49171345Sgshapiro
49271345Sgshapiroint
49398121Sgshapiroreadheaders(alwaysrespond)
49498121Sgshapiro	bool alwaysrespond;
49564562Sgshapiro{
49664562Sgshapiro	bool tome, cont;
49764562Sgshapiro	register char *p;
49864562Sgshapiro	register ALIAS *cur;
49964562Sgshapiro	char buf[MAXLINE];
50064562Sgshapiro	extern bool junkmail __P((char *));
50164562Sgshapiro	extern bool nsearch __P((char *, char *));
50264562Sgshapiro
50398121Sgshapiro	cont = false;
50498121Sgshapiro	tome = alwaysrespond;
50590792Sgshapiro	while (sm_io_fgets(smioin, SM_TIME_DEFAULT, buf, sizeof(buf)) &&
50690792Sgshapiro	       *buf != '\n')
50764562Sgshapiro	{
50864562Sgshapiro		switch(*buf)
50964562Sgshapiro		{
51064562Sgshapiro		  case 'F':		/* "From " */
51190792Sgshapiro			cont = false;
51264562Sgshapiro			if (strncmp(buf, "From ", 5) == 0)
51364562Sgshapiro			{
51490792Sgshapiro				bool quoted = false;
51564562Sgshapiro
51664562Sgshapiro				p = buf + 5;
51764562Sgshapiro				while (*p != '\0')
51864562Sgshapiro				{
51964562Sgshapiro					/* escaped character */
52064562Sgshapiro					if (*p == '\\')
52164562Sgshapiro					{
52264562Sgshapiro						p++;
52364562Sgshapiro						if (*p == '\0')
52464562Sgshapiro						{
52564562Sgshapiro							msglog(LOG_NOTICE,
52664562Sgshapiro							       "vacation: badly formatted \"From \" line.\n");
52766494Sgshapiro							EXITIT(EX_DATAERR);
52864562Sgshapiro						}
52964562Sgshapiro					}
53064562Sgshapiro					else if (*p == '"')
53164562Sgshapiro						quoted = !quoted;
53264562Sgshapiro					else if (*p == '\r' || *p == '\n')
53364562Sgshapiro						break;
53464562Sgshapiro					else if (*p == ' ' && !quoted)
53564562Sgshapiro						break;
53664562Sgshapiro					p++;
53764562Sgshapiro				}
53864562Sgshapiro				if (quoted)
53964562Sgshapiro				{
54064562Sgshapiro					msglog(LOG_NOTICE,
54164562Sgshapiro					       "vacation: badly formatted \"From \" line.\n");
54266494Sgshapiro					EXITIT(EX_DATAERR);
54364562Sgshapiro				}
54464562Sgshapiro				*p = '\0';
54564562Sgshapiro
54664562Sgshapiro				/* ok since both strings have MAXLINE length */
54764562Sgshapiro				if (*From == '\0')
54890792Sgshapiro					(void) sm_strlcpy(From, buf + 5,
54990792Sgshapiro							  sizeof From);
55064562Sgshapiro				if ((p = strchr(buf + 5, '\n')) != NULL)
55164562Sgshapiro					*p = '\0';
55264562Sgshapiro				if (junkmail(buf + 5))
55371345Sgshapiro					EXITIT(EX_NOUSER);
55464562Sgshapiro			}
55564562Sgshapiro			break;
55664562Sgshapiro
55764562Sgshapiro		  case 'P':		/* "Precedence:" */
55864562Sgshapiro		  case 'p':
55990792Sgshapiro			cont = false;
56064562Sgshapiro			if (strlen(buf) <= 10 ||
56164562Sgshapiro			    strncasecmp(buf, "Precedence", 10) != 0 ||
56264562Sgshapiro			    (buf[10] != ':' && buf[10] != ' ' &&
56364562Sgshapiro			     buf[10] != '\t'))
56464562Sgshapiro				break;
56564562Sgshapiro			if ((p = strchr(buf, ':')) == NULL)
56664562Sgshapiro				break;
56764562Sgshapiro			while (*++p != '\0' && isascii(*p) && isspace(*p));
56864562Sgshapiro			if (*p == '\0')
56964562Sgshapiro				break;
57064562Sgshapiro			if (strncasecmp(p, "junk", 4) == 0 ||
57164562Sgshapiro			    strncasecmp(p, "bulk", 4) == 0 ||
57264562Sgshapiro			    strncasecmp(p, "list", 4) == 0)
57371345Sgshapiro				EXITIT(EX_NOUSER);
57464562Sgshapiro			break;
57564562Sgshapiro
57664562Sgshapiro		  case 'C':		/* "Cc:" */
57764562Sgshapiro		  case 'c':
57864562Sgshapiro			if (strncasecmp(buf, "Cc:", 3) != 0)
57964562Sgshapiro				break;
58090792Sgshapiro			cont = true;
58164562Sgshapiro			goto findme;
58264562Sgshapiro
58364562Sgshapiro		  case 'T':		/* "To:" */
58464562Sgshapiro		  case 't':
58564562Sgshapiro			if (strncasecmp(buf, "To:", 3) != 0)
58664562Sgshapiro				break;
58790792Sgshapiro			cont = true;
58864562Sgshapiro			goto findme;
58964562Sgshapiro
59064562Sgshapiro		  default:
59164562Sgshapiro			if (!isascii(*buf) || !isspace(*buf) || !cont || tome)
59264562Sgshapiro			{
59390792Sgshapiro				cont = false;
59464562Sgshapiro				break;
59564562Sgshapiro			}
59664562Sgshapirofindme:
59764562Sgshapiro			for (cur = Names;
59864562Sgshapiro			     !tome && cur != NULL;
59964562Sgshapiro			     cur = cur->next)
60064562Sgshapiro				tome = nsearch(cur->name, buf);
60164562Sgshapiro		}
60264562Sgshapiro	}
60364562Sgshapiro	if (!tome)
60471345Sgshapiro		EXITIT(EX_NOUSER);
60564562Sgshapiro	if (*From == '\0')
60664562Sgshapiro	{
60764562Sgshapiro		msglog(LOG_NOTICE, "vacation: no initial \"From \" line.\n");
60866494Sgshapiro		EXITIT(EX_DATAERR);
60964562Sgshapiro	}
61071345Sgshapiro	EXITIT(EX_OK);
61164562Sgshapiro}
61264562Sgshapiro
61364562Sgshapiro/*
61464562Sgshapiro** NSEARCH --
61564562Sgshapiro**	do a nice, slow, search of a string for a substring.
61664562Sgshapiro**
61764562Sgshapiro**	Parameters:
61864562Sgshapiro**		name -- name to search.
61964562Sgshapiro**		str -- string in which to search.
62064562Sgshapiro**
62164562Sgshapiro**	Returns:
62264562Sgshapiro**		is name a substring of str?
62364562Sgshapiro**
62464562Sgshapiro*/
62577349Sgshapiro
62664562Sgshapirobool
62764562Sgshapironsearch(name, str)
62864562Sgshapiro	register char *name, *str;
62964562Sgshapiro{
63064562Sgshapiro	register size_t len;
63164562Sgshapiro	register char *s;
63264562Sgshapiro
63364562Sgshapiro	len = strlen(name);
63464562Sgshapiro
63564562Sgshapiro	for (s = str; *s != '\0'; ++s)
63664562Sgshapiro	{
63764562Sgshapiro		/*
63864562Sgshapiro		**  Check to make sure that the string matches and
63964562Sgshapiro		**  the previous character is not an alphanumeric and
64064562Sgshapiro		**  the next character after the match is not an alphanumeric.
64164562Sgshapiro		**
64264562Sgshapiro		**  This prevents matching "eric" to "derick" while still
64364562Sgshapiro		**  matching "eric" to "<eric+detail>".
64464562Sgshapiro		*/
64564562Sgshapiro
64664562Sgshapiro		if (tolower(*s) == tolower(*name) &&
64764562Sgshapiro		    strncasecmp(name, s, len) == 0 &&
64864562Sgshapiro		    (s == str || !isascii(*(s - 1)) || !isalnum(*(s - 1))) &&
64964562Sgshapiro		    (!isascii(*(s + len)) || !isalnum(*(s + len))))
65090792Sgshapiro			return true;
65164562Sgshapiro	}
65290792Sgshapiro	return false;
65364562Sgshapiro}
65464562Sgshapiro
65564562Sgshapiro/*
65664562Sgshapiro** JUNKMAIL --
65764562Sgshapiro**	read the header and return if automagic/junk/bulk/list mail
65864562Sgshapiro**
65964562Sgshapiro**	Parameters:
66064562Sgshapiro**		from -- sender address.
66164562Sgshapiro**
66264562Sgshapiro**	Returns:
66364562Sgshapiro**		is this some automated/junk/bulk/list mail?
66464562Sgshapiro**
66564562Sgshapiro*/
66671345Sgshapiro
66771345Sgshapirostruct ignore
66871345Sgshapiro{
66971345Sgshapiro	char	*name;
67071345Sgshapiro	size_t	len;
67171345Sgshapiro};
67271345Sgshapiro
67371345Sgshapirotypedef struct ignore IGNORE_T;
67471345Sgshapiro
67571345Sgshapiro#define MAX_USER_LEN 256	/* maximum length of local part (sender) */
67671345Sgshapiro
67771345Sgshapiro/* delimiters for the local part of an address */
67871345Sgshapiro#define isdelim(c)	((c) == '%' || (c) == '@' || (c) == '+')
67971345Sgshapiro
68064562Sgshapirobool
68164562Sgshapirojunkmail(from)
68264562Sgshapiro	char *from;
68364562Sgshapiro{
68471345Sgshapiro	bool quot;
68571345Sgshapiro	char *e;
68671345Sgshapiro	size_t len;
68771345Sgshapiro	IGNORE_T *cur;
68871345Sgshapiro	char sender[MAX_USER_LEN];
68971345Sgshapiro	static IGNORE_T ignore[] =
69064562Sgshapiro	{
69164562Sgshapiro		{ "postmaster",		10	},
69264562Sgshapiro		{ "uucp",		4	},
69364562Sgshapiro		{ "mailer-daemon",	13	},
69464562Sgshapiro		{ "mailer",		6	},
69571345Sgshapiro		{ NULL,			0	}
69671345Sgshapiro	};
69771345Sgshapiro
69871345Sgshapiro	static IGNORE_T ignorepost[] =
69971345Sgshapiro	{
70071345Sgshapiro		{ "-request",		8	},
70164562Sgshapiro		{ "-relay",		6	},
70271345Sgshapiro		{ "-owner",		6	},
70364562Sgshapiro		{ NULL,			0	}
70464562Sgshapiro	};
70564562Sgshapiro
70671345Sgshapiro	static IGNORE_T ignorepre[] =
70771345Sgshapiro	{
70871345Sgshapiro		{ "owner-",		6	},
70971345Sgshapiro		{ NULL,			0	}
71071345Sgshapiro	};
71171345Sgshapiro
71264562Sgshapiro	/*
71371345Sgshapiro	**  This is mildly amusing, and I'm not positive it's right; trying
71471345Sgshapiro	**  to find the "real" name of the sender, assuming that addresses
71571345Sgshapiro	**  will be some variant of:
71671345Sgshapiro	**
71771345Sgshapiro	**  From site!site!SENDER%site.domain%site.domain@site.domain
71871345Sgshapiro	*/
71971345Sgshapiro
72090792Sgshapiro	quot = false;
72171345Sgshapiro	e = from;
72271345Sgshapiro	len = 0;
72371345Sgshapiro	while (*e != '\0' && (quot || !isdelim(*e)))
72464562Sgshapiro	{
72571345Sgshapiro		if (*e == '"')
72671345Sgshapiro		{
72771345Sgshapiro			quot = !quot;
72871345Sgshapiro			++e;
72971345Sgshapiro			continue;
73071345Sgshapiro		}
73171345Sgshapiro		if (*e == '\\')
73271345Sgshapiro		{
73371345Sgshapiro			if (*(++e) == '\0')
73471345Sgshapiro			{
73571345Sgshapiro				/* '\\' at end of string? */
73671345Sgshapiro				break;
73771345Sgshapiro			}
73871345Sgshapiro			if (len < MAX_USER_LEN)
73971345Sgshapiro				sender[len++] = *e;
74071345Sgshapiro			++e;
74171345Sgshapiro			continue;
74271345Sgshapiro		}
74371345Sgshapiro		if (*e == '!' && !quot)
74471345Sgshapiro		{
74571345Sgshapiro			len = 0;
74671345Sgshapiro			sender[len] = '\0';
74771345Sgshapiro		}
74864562Sgshapiro		else
74971345Sgshapiro			if (len < MAX_USER_LEN)
75071345Sgshapiro				sender[len++] = *e;
75171345Sgshapiro		++e;
75264562Sgshapiro	}
75371345Sgshapiro	if (len < MAX_USER_LEN)
75471345Sgshapiro		sender[len] = '\0';
75571345Sgshapiro	else
75671345Sgshapiro		sender[MAX_USER_LEN - 1] = '\0';
75771345Sgshapiro
75871345Sgshapiro	if (len <= 0)
75990792Sgshapiro		return false;
76071345Sgshapiro#if 0
76171345Sgshapiro	if (quot)
76290792Sgshapiro		return false;	/* syntax error... */
76371345Sgshapiro#endif /* 0 */
76471345Sgshapiro
76571345Sgshapiro	/* test prefixes */
76671345Sgshapiro	for (cur = ignorepre; cur->name != NULL; ++cur)
76771345Sgshapiro	{
76871345Sgshapiro		if (len >= cur->len &&
76971345Sgshapiro		    strncasecmp(cur->name, sender, cur->len) == 0)
77090792Sgshapiro			return true;
77171345Sgshapiro	}
77271345Sgshapiro
77371345Sgshapiro	/*
77471345Sgshapiro	**  If the name is truncated, don't test the rest.
77571345Sgshapiro	**	We could extract the "tail" of the sender address and
77671345Sgshapiro	**	compare it it ignorepost, however, it seems not worth
77771345Sgshapiro	**	the effort.
77871345Sgshapiro	**	The address surely can't match any entry in ignore[]
77971345Sgshapiro	**	(as long as all of them are shorter than MAX_USER_LEN).
78071345Sgshapiro	*/
78171345Sgshapiro
78271345Sgshapiro	if (len > MAX_USER_LEN)
78390792Sgshapiro		return false;
78471345Sgshapiro
78571345Sgshapiro	/* test full local parts */
78664562Sgshapiro	for (cur = ignore; cur->name != NULL; ++cur)
78764562Sgshapiro	{
78871345Sgshapiro		if (len == cur->len &&
78971345Sgshapiro		    strncasecmp(cur->name, sender, cur->len) == 0)
79090792Sgshapiro			return true;
79171345Sgshapiro	}
79271345Sgshapiro
79371345Sgshapiro	/* test postfixes */
79471345Sgshapiro	for (cur = ignorepost; cur->name != NULL; ++cur)
79571345Sgshapiro	{
79664562Sgshapiro		if (len >= cur->len &&
79771345Sgshapiro		    strncasecmp(cur->name, e - cur->len - 1,
79871345Sgshapiro				cur->len) == 0)
79990792Sgshapiro			return true;
80064562Sgshapiro	}
80190792Sgshapiro	return false;
80264562Sgshapiro}
80364562Sgshapiro
80464562Sgshapiro#define	VIT	"__VACATION__INTERVAL__TIMER__"
80564562Sgshapiro
80664562Sgshapiro/*
80764562Sgshapiro** RECENT --
80864562Sgshapiro**	find out if user has gotten a vacation message recently.
80964562Sgshapiro**
81064562Sgshapiro**	Parameters:
81164562Sgshapiro**		none.
81264562Sgshapiro**
81364562Sgshapiro**	Returns:
81490792Sgshapiro**		true iff user has gotten a vacation message recently.
81564562Sgshapiro**
81664562Sgshapiro*/
81777349Sgshapiro
81864562Sgshapirobool
81964562Sgshapirorecent()
82064562Sgshapiro{
82164562Sgshapiro	SMDB_DBENT key, data;
82264562Sgshapiro	time_t then, next;
82390792Sgshapiro	bool trydomain = false;
82464562Sgshapiro	int st;
82564562Sgshapiro	char *domain;
82664562Sgshapiro
82764562Sgshapiro	memset(&key, '\0', sizeof key);
82864562Sgshapiro	memset(&data, '\0', sizeof data);
82964562Sgshapiro
83064562Sgshapiro	/* get interval time */
83171345Sgshapiro	key.data = VIT;
83271345Sgshapiro	key.size = sizeof(VIT);
83364562Sgshapiro
83464562Sgshapiro	st = Db->smdb_get(Db, &key, &data, 0);
83564562Sgshapiro	if (st != SMDBE_OK)
83664562Sgshapiro		next = SECSPERDAY * DAYSPERWEEK;
83764562Sgshapiro	else
83871345Sgshapiro		memmove(&next, data.data, sizeof(next));
83964562Sgshapiro
84064562Sgshapiro	memset(&data, '\0', sizeof data);
84164562Sgshapiro
84264562Sgshapiro	/* get record for this address */
84371345Sgshapiro	key.data = From;
84471345Sgshapiro	key.size = strlen(From);
84564562Sgshapiro
84664562Sgshapiro	do
84764562Sgshapiro	{
84864562Sgshapiro		st = Db->smdb_get(Db, &key, &data, 0);
84964562Sgshapiro		if (st == SMDBE_OK)
85064562Sgshapiro		{
85171345Sgshapiro			memmove(&then, data.data, sizeof(then));
85264562Sgshapiro			if (next == ONLY_ONCE || then == ONLY_ONCE ||
85364562Sgshapiro			    then + next > time(NULL))
85490792Sgshapiro				return true;
85564562Sgshapiro		}
85664562Sgshapiro		if ((trydomain = !trydomain) &&
85764562Sgshapiro		    (domain = strchr(From, '@')) != NULL)
85864562Sgshapiro		{
85971345Sgshapiro			key.data = domain;
86071345Sgshapiro			key.size = strlen(domain);
86164562Sgshapiro		}
86264562Sgshapiro	} while (trydomain);
86390792Sgshapiro	return false;
86464562Sgshapiro}
86564562Sgshapiro
86664562Sgshapiro/*
86764562Sgshapiro** SETINTERVAL --
86864562Sgshapiro**	store the reply interval
86964562Sgshapiro**
87064562Sgshapiro**	Parameters:
87164562Sgshapiro**		interval -- time interval for replies.
87264562Sgshapiro**
87364562Sgshapiro**	Returns:
87464562Sgshapiro**		nothing.
87564562Sgshapiro**
87664562Sgshapiro**	Side Effects:
87764562Sgshapiro**		stores the reply interval in database.
87864562Sgshapiro*/
87977349Sgshapiro
88064562Sgshapirovoid
88164562Sgshapirosetinterval(interval)
88264562Sgshapiro	time_t interval;
88364562Sgshapiro{
88464562Sgshapiro	SMDB_DBENT key, data;
88564562Sgshapiro
88664562Sgshapiro	memset(&key, '\0', sizeof key);
88764562Sgshapiro	memset(&data, '\0', sizeof data);
88864562Sgshapiro
88971345Sgshapiro	key.data = VIT;
89071345Sgshapiro	key.size = sizeof(VIT);
89171345Sgshapiro	data.data = (char*) &interval;
89271345Sgshapiro	data.size = sizeof(interval);
89371345Sgshapiro	(void) (Db->smdb_put)(Db, &key, &data, 0);
89464562Sgshapiro}
89564562Sgshapiro
89664562Sgshapiro/*
89764562Sgshapiro** SETREPLY --
89864562Sgshapiro**	store that this user knows about the vacation.
89964562Sgshapiro**
90064562Sgshapiro**	Parameters:
90164562Sgshapiro**		from -- sender address.
90264562Sgshapiro**		when -- last reply time.
90364562Sgshapiro**
90464562Sgshapiro**	Returns:
90564562Sgshapiro**		nothing.
90664562Sgshapiro**
90764562Sgshapiro**	Side Effects:
90864562Sgshapiro**		stores user/time in database.
90964562Sgshapiro*/
91077349Sgshapiro
91164562Sgshapirovoid
91264562Sgshapirosetreply(from, when)
91364562Sgshapiro	char *from;
91464562Sgshapiro	time_t when;
91564562Sgshapiro{
91664562Sgshapiro	SMDB_DBENT key, data;
91764562Sgshapiro
91864562Sgshapiro	memset(&key, '\0', sizeof key);
91964562Sgshapiro	memset(&data, '\0', sizeof data);
92064562Sgshapiro
92171345Sgshapiro	key.data = from;
92271345Sgshapiro	key.size = strlen(from);
92371345Sgshapiro	data.data = (char*) &when;
92471345Sgshapiro	data.size = sizeof(when);
92571345Sgshapiro	(void) (Db->smdb_put)(Db, &key, &data, 0);
92664562Sgshapiro}
92764562Sgshapiro
92864562Sgshapiro/*
92964562Sgshapiro** XCLUDE --
93064562Sgshapiro**	add users to vacation db so they don't get a reply.
93164562Sgshapiro**
93264562Sgshapiro**	Parameters:
93364562Sgshapiro**		f -- file pointer with list of address to exclude
93464562Sgshapiro**
93564562Sgshapiro**	Returns:
93664562Sgshapiro**		nothing.
93764562Sgshapiro**
93864562Sgshapiro**	Side Effects:
93964562Sgshapiro**		stores users in database.
94064562Sgshapiro*/
94177349Sgshapiro
94264562Sgshapirovoid
94364562Sgshapiroxclude(f)
94490792Sgshapiro	SM_FILE_T *f;
94564562Sgshapiro{
94664562Sgshapiro	char buf[MAXLINE], *p;
94764562Sgshapiro
94864562Sgshapiro	if (f == NULL)
94964562Sgshapiro		return;
95090792Sgshapiro	while (sm_io_fgets(f, SM_TIME_DEFAULT, buf, sizeof buf))
95164562Sgshapiro	{
95264562Sgshapiro		if ((p = strchr(buf, '\n')) != NULL)
95364562Sgshapiro			*p = '\0';
95464562Sgshapiro		setreply(buf, ONLY_ONCE);
95564562Sgshapiro	}
95664562Sgshapiro}
95764562Sgshapiro
95864562Sgshapiro/*
95964562Sgshapiro** SENDMESSAGE --
96064562Sgshapiro**	exec sendmail to send the vacation file to sender
96164562Sgshapiro**
96264562Sgshapiro**	Parameters:
96364562Sgshapiro**		myname -- user name.
96464562Sgshapiro**		msgfn -- name of file with vacation message.
96590792Sgshapiro**		sender -- use as sender address
96664562Sgshapiro**
96764562Sgshapiro**	Returns:
96864562Sgshapiro**		nothing.
96964562Sgshapiro**
97064562Sgshapiro**	Side Effects:
97164562Sgshapiro**		sends vacation reply.
97264562Sgshapiro*/
97377349Sgshapiro
97464562Sgshapirovoid
97590792Sgshapirosendmessage(myname, msgfn, sender)
97664562Sgshapiro	char *myname;
97764562Sgshapiro	char *msgfn;
97890792Sgshapiro	char *sender;
97964562Sgshapiro{
98090792Sgshapiro	SM_FILE_T *mfp, *sfp;
98164562Sgshapiro	int i;
98264562Sgshapiro	int pvect[2];
98377349Sgshapiro	char *pv[8];
98464562Sgshapiro	char buf[MAXLINE];
98564562Sgshapiro
98690792Sgshapiro	mfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, msgfn, SM_IO_RDONLY, NULL);
98764562Sgshapiro	if (mfp == NULL)
98864562Sgshapiro	{
98964562Sgshapiro		if (msgfn[0] == '/')
99064562Sgshapiro			msglog(LOG_NOTICE, "vacation: no %s file.\n", msgfn);
99164562Sgshapiro		else
99264562Sgshapiro			msglog(LOG_NOTICE, "vacation: no ~%s/%s file.\n",
99364562Sgshapiro			       myname, msgfn);
99464562Sgshapiro		exit(EX_NOINPUT);
99564562Sgshapiro	}
99664562Sgshapiro	if (pipe(pvect) < 0)
99764562Sgshapiro	{
99890792Sgshapiro		msglog(LOG_ERR, "vacation: pipe: %s", sm_errstring(errno));
99964562Sgshapiro		exit(EX_OSERR);
100064562Sgshapiro	}
100177349Sgshapiro	pv[0] = "sendmail";
100277349Sgshapiro	pv[1] = "-oi";
100377349Sgshapiro	pv[2] = "-f";
100490792Sgshapiro	if (sender != NULL)
100590792Sgshapiro		pv[3] = sender;
100677349Sgshapiro	else
100777349Sgshapiro		pv[3] = myname;
100877349Sgshapiro	pv[4] = "--";
100977349Sgshapiro	pv[5] = From;
101077349Sgshapiro	pv[6] = NULL;
101164562Sgshapiro	i = fork();
101264562Sgshapiro	if (i < 0)
101364562Sgshapiro	{
101490792Sgshapiro		msglog(LOG_ERR, "vacation: fork: %s", sm_errstring(errno));
101564562Sgshapiro		exit(EX_OSERR);
101664562Sgshapiro	}
101764562Sgshapiro	if (i == 0)
101864562Sgshapiro	{
101964562Sgshapiro		(void) dup2(pvect[0], 0);
102064562Sgshapiro		(void) close(pvect[0]);
102164562Sgshapiro		(void) close(pvect[1]);
102290792Sgshapiro		(void) sm_io_close(mfp, SM_TIME_DEFAULT);
102377349Sgshapiro		(void) execv(_PATH_SENDMAIL, pv);
102464562Sgshapiro		msglog(LOG_ERR, "vacation: can't exec %s: %s",
102590792Sgshapiro			_PATH_SENDMAIL, sm_errstring(errno));
102664562Sgshapiro		exit(EX_UNAVAILABLE);
102764562Sgshapiro	}
102864562Sgshapiro	/* check return status of the following calls? XXX */
102964562Sgshapiro	(void) close(pvect[0]);
103090792Sgshapiro	if ((sfp = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT,
103190792Sgshapiro			      (void *) &(pvect[1]),
103290792Sgshapiro			      SM_IO_WRONLY, NULL)) != NULL)
103364562Sgshapiro	{
103490792Sgshapiro		(void) sm_io_fprintf(sfp, SM_TIME_DEFAULT, "To: %s\n", From);
103590792Sgshapiro		(void) sm_io_fprintf(sfp, SM_TIME_DEFAULT,
103690792Sgshapiro				     "Auto-Submitted: auto-replied\n");
103790792Sgshapiro		while (sm_io_fgets(mfp, SM_TIME_DEFAULT, buf, sizeof buf))
103890792Sgshapiro			(void) sm_io_fputs(sfp, SM_TIME_DEFAULT, buf);
103990792Sgshapiro		(void) sm_io_close(mfp, SM_TIME_DEFAULT);
104090792Sgshapiro		(void) sm_io_close(sfp, SM_TIME_DEFAULT);
104164562Sgshapiro	}
104264562Sgshapiro	else
104364562Sgshapiro	{
104490792Sgshapiro		(void) sm_io_close(mfp, SM_TIME_DEFAULT);
104564562Sgshapiro		msglog(LOG_ERR, "vacation: can't open pipe to sendmail");
104664562Sgshapiro		exit(EX_UNAVAILABLE);
104764562Sgshapiro	}
104864562Sgshapiro}
104964562Sgshapiro
105064562Sgshapirovoid
105164562Sgshapirousage()
105264562Sgshapiro{
105377349Sgshapiro	msglog(LOG_NOTICE,
1054132943Sgshapiro	       "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",
1055132943Sgshapiro	       getuid());
105664562Sgshapiro	exit(EX_USAGE);
105764562Sgshapiro}
105864562Sgshapiro
105964562Sgshapiro/*
106064562Sgshapiro** LISTDB -- list the contents of the vacation database
106164562Sgshapiro**
106264562Sgshapiro**	Parameters:
106364562Sgshapiro**		none.
106464562Sgshapiro**
106564562Sgshapiro**	Returns:
106664562Sgshapiro**		nothing.
106764562Sgshapiro*/
106864562Sgshapiro
106964562Sgshapirostatic void
107064562Sgshapirolistdb()
107164562Sgshapiro{
107264562Sgshapiro	int result;
107364562Sgshapiro	time_t t;
107464562Sgshapiro	SMDB_CURSOR *cursor = NULL;
107564562Sgshapiro	SMDB_DBENT db_key, db_value;
107664562Sgshapiro
107764562Sgshapiro	memset(&db_key, '\0', sizeof db_key);
107864562Sgshapiro	memset(&db_value, '\0', sizeof db_value);
107964562Sgshapiro
108064562Sgshapiro	result = Db->smdb_cursor(Db, &cursor, 0);
108164562Sgshapiro	if (result != SMDBE_OK)
108264562Sgshapiro	{
108390792Sgshapiro		sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
108490792Sgshapiro			      "vacation: set cursor: %s\n",
108590792Sgshapiro			      sm_errstring(result));
108664562Sgshapiro		return;
108764562Sgshapiro	}
108864562Sgshapiro
108964562Sgshapiro	while ((result = cursor->smdbc_get(cursor, &db_key, &db_value,
109064562Sgshapiro					   SMDB_CURSOR_GET_NEXT)) == SMDBE_OK)
109164562Sgshapiro	{
109298121Sgshapiro		char *timestamp;
109398121Sgshapiro
109464562Sgshapiro		/* skip magic VIT entry */
1095110560Sgshapiro		if (db_key.size == strlen(VIT) + 1 &&
109671345Sgshapiro		    strncmp((char *)db_key.data, VIT,
109771345Sgshapiro			    (int)db_key.size - 1) == 0)
109864562Sgshapiro			continue;
109964562Sgshapiro
110064562Sgshapiro		/* skip bogus values */
110171345Sgshapiro		if (db_value.size != sizeof t)
110264562Sgshapiro		{
110390792Sgshapiro			sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
110490792Sgshapiro				      "vacation: %.*s invalid time stamp\n",
110590792Sgshapiro				      (int) db_key.size, (char *) db_key.data);
110664562Sgshapiro			continue;
110764562Sgshapiro		}
110864562Sgshapiro
110971345Sgshapiro		memcpy(&t, db_value.data, sizeof t);
111064562Sgshapiro
111171345Sgshapiro		if (db_key.size > 40)
111271345Sgshapiro			db_key.size = 40;
111364562Sgshapiro
111498121Sgshapiro		if (t <= 0)
111598121Sgshapiro		{
111698121Sgshapiro			/* must be an exclude */
111798121Sgshapiro			timestamp = "(exclusion)\n";
111898121Sgshapiro		}
111998121Sgshapiro		else
112098121Sgshapiro		{
112198121Sgshapiro			timestamp = ctime(&t);
112298121Sgshapiro		}
112390792Sgshapiro		sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%-40.*s %-10s",
112490792Sgshapiro			      (int) db_key.size, (char *) db_key.data,
112598121Sgshapiro			      timestamp);
112664562Sgshapiro
112764562Sgshapiro		memset(&db_key, '\0', sizeof db_key);
112864562Sgshapiro		memset(&db_value, '\0', sizeof db_value);
112964562Sgshapiro	}
113064562Sgshapiro
113164562Sgshapiro	if (result != SMDBE_OK && result != SMDBE_LAST_ENTRY)
113264562Sgshapiro	{
113390792Sgshapiro		sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
113490792Sgshapiro			      "vacation: get value at cursor: %s\n",
113590792Sgshapiro			      sm_errstring(result));
113664562Sgshapiro		if (cursor != NULL)
113764562Sgshapiro		{
113864562Sgshapiro			(void) cursor->smdbc_close(cursor);
113964562Sgshapiro			cursor = NULL;
114064562Sgshapiro		}
114164562Sgshapiro		return;
114264562Sgshapiro	}
114364562Sgshapiro	(void) cursor->smdbc_close(cursor);
114464562Sgshapiro	cursor = NULL;
114564562Sgshapiro}
114664562Sgshapiro
114764562Sgshapiro/*
114864562Sgshapiro** DEBUGLOG -- write message to standard error
114964562Sgshapiro**
115064562Sgshapiro**	Append a message to the standard error for the convenience of
115164562Sgshapiro**	end-users debugging without access to the syslog messages.
115264562Sgshapiro**
115364562Sgshapiro**	Parameters:
115464562Sgshapiro**		i -- syslog log level
115564562Sgshapiro**		fmt -- string format
115664562Sgshapiro**
115764562Sgshapiro**	Returns:
115864562Sgshapiro**		nothing.
115964562Sgshapiro*/
116064562Sgshapiro
116164562Sgshapiro/*VARARGS2*/
116290792Sgshapirostatic SYSLOG_RET_T
116364562Sgshapiro#ifdef __STDC__
116464562Sgshapirodebuglog(int i, const char *fmt, ...)
116564562Sgshapiro#else /* __STDC__ */
116664562Sgshapirodebuglog(i, fmt, va_alist)
116764562Sgshapiro	int i;
116864562Sgshapiro	const char *fmt;
116964562Sgshapiro	va_dcl
117064562Sgshapiro#endif /* __STDC__ */
117164562Sgshapiro
117264562Sgshapiro{
117390792Sgshapiro	SM_VA_LOCAL_DECL
117464562Sgshapiro
117590792Sgshapiro	SM_VA_START(ap, fmt);
117690792Sgshapiro	sm_io_vfprintf(smioerr, SM_TIME_DEFAULT, fmt, ap);
117790792Sgshapiro	SM_VA_END(ap);
117890792Sgshapiro	SYSLOG_RET;
117964562Sgshapiro}
1180