164562Sgshapiro/*
2261370Sgshapiro * Copyright (c) 1999-2002, 2009 Proofpoint, 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,
17261370Sgshapiro"@(#) Copyright (c) 1999-2002, 2009 Proofpoint, 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
23266711SgshapiroSM_IDSTR(id, "@(#)$Id: vacation.c,v 8.148 2013-11-22 20:52:02 ca Exp $")
2464562Sgshapiro
2577349Sgshapiro
2664562Sgshapiro#include <ctype.h>
2764562Sgshapiro#include <stdlib.h>
2864562Sgshapiro#include <syslog.h>
2964562Sgshapiro#include <time.h>
3064562Sgshapiro#include <unistd.h>
3164562Sgshapiro#ifdef EX_OK
3264562Sgshapiro# undef EX_OK		/* unistd.h may have another use for this */
3364562Sgshapiro#endif /* EX_OK */
3490792Sgshapiro#include <sm/sysexits.h>
3564562Sgshapiro
3690792Sgshapiro#include <sm/cf.h>
3790792Sgshapiro#include <sm/mbdb.h>
3864562Sgshapiro#include "sendmail/sendmail.h"
3990792Sgshapiro#include <sendmail/pathnames.h>
4064562Sgshapiro#include "libsmdb/smdb.h"
4164562Sgshapiro
4264562Sgshapiro#define ONLY_ONCE	((time_t) 0)	/* send at most one reply */
4364562Sgshapiro#define INTERVAL_UNDEF	((time_t) (-1))	/* no value given */
4464562Sgshapiro
4564562Sgshapirouid_t	RealUid;
4664562Sgshapirogid_t	RealGid;
4764562Sgshapirochar	*RealUserName;
4864562Sgshapirouid_t	RunAsUid;
49173340Sgshapirogid_t	RunAsGid;
5064562Sgshapirochar	*RunAsUserName;
5164562Sgshapiroint	Verbose = 2;
5290792Sgshapirobool	DontInitGroups = false;
5364562Sgshapirouid_t	TrustedUid = 0;
5464562SgshapiroBITMAP256 DontBlameSendmail;
5564562Sgshapiro
56168515Sgshapirostatic int readheaders __P((bool));
57168515Sgshapirostatic bool junkmail __P((char *));
58168515Sgshapirostatic bool nsearch __P((char *, char *));
59168515Sgshapirostatic void usage __P((void));
60168515Sgshapirostatic void setinterval __P((time_t));
61168515Sgshapirostatic bool recent __P((void));
62168515Sgshapirostatic void setreply __P((char *, time_t));
63168515Sgshapirostatic void sendmessage __P((char *, char *, char *));
64168515Sgshapirostatic void xclude __P((SM_FILE_T *));
65168515Sgshapiro
6664562Sgshapiro/*
6764562Sgshapiro**  VACATION -- return a message to the sender when on vacation.
6864562Sgshapiro**
6964562Sgshapiro**	This program is invoked as a message receiver.  It returns a
7064562Sgshapiro**	message specified by the user to whomever sent the mail, taking
7164562Sgshapiro**	care not to return a message too often to prevent "I am on
7264562Sgshapiro**	vacation" loops.
7364562Sgshapiro*/
7464562Sgshapiro
7564562Sgshapiro#define	VDB	".vacation"		/* vacation database */
7664562Sgshapiro#define	VMSG	".vacation.msg"		/* vacation message */
7764562Sgshapiro#define SECSPERDAY	(60 * 60 * 24)
7864562Sgshapiro#define DAYSPERWEEK	7
7964562Sgshapiro
8064562Sgshapirotypedef struct alias
8164562Sgshapiro{
8264562Sgshapiro	char *name;
8364562Sgshapiro	struct alias *next;
8464562Sgshapiro} ALIAS;
8564562Sgshapiro
8664562SgshapiroALIAS *Names = NULL;
8764562Sgshapiro
8864562SgshapiroSMDB_DATABASE *Db;
8964562Sgshapiro
9064562Sgshapirochar From[MAXLINE];
91141858Sgshapirobool CloseMBDB = false;
9264562Sgshapiro
9390792Sgshapiro#if defined(__hpux) || defined(__osf__)
9490792Sgshapiro# ifndef SM_CONF_SYSLOG_INT
9590792Sgshapiro#  define SM_CONF_SYSLOG_INT	1
9690792Sgshapiro# endif /* SM_CONF_SYSLOG_INT */
9790792Sgshapiro#endif /* defined(__hpux) || defined(__osf__) */
9864562Sgshapiro
9990792Sgshapiro#if SM_CONF_SYSLOG_INT
10090792Sgshapiro# define SYSLOG_RET_T	int
10190792Sgshapiro# define SYSLOG_RET	return 0
10290792Sgshapiro#else /* SM_CONF_SYSLOG_INT */
10390792Sgshapiro# define SYSLOG_RET_T	void
10490792Sgshapiro# define SYSLOG_RET
10590792Sgshapiro#endif /* SM_CONF_SYSLOG_INT */
10690792Sgshapiro
10790792Sgshapirotypedef SYSLOG_RET_T SYSLOG_T __P((int, const char *, ...));
10890792SgshapiroSYSLOG_T *msglog = syslog;
10990792Sgshapirostatic SYSLOG_RET_T debuglog __P((int, const char *, ...));
11066494Sgshapirostatic void eatmsg __P((void));
11190792Sgshapirostatic void listdb __P((void));
11266494Sgshapiro
11366494Sgshapiro/* exit after reading input */
114141858Sgshapiro#define EXITIT(excode)			\
115141858Sgshapiro{					\
116141858Sgshapiro	eatmsg();			\
117141858Sgshapiro	if (CloseMBDB)			\
118141858Sgshapiro	{				\
119141858Sgshapiro		sm_mbdb_terminate();	\
120141858Sgshapiro		CloseMBDB = false;	\
121141858Sgshapiro	}				\
122141858Sgshapiro	return excode;			\
12390792Sgshapiro}
12477349Sgshapiro
125141858Sgshapiro#define EXITM(excode)			\
126141858Sgshapiro{					\
127141858Sgshapiro	if (!initdb && !list)		\
128141858Sgshapiro		eatmsg();		\
129141858Sgshapiro	if (CloseMBDB)			\
130141858Sgshapiro	{				\
131141858Sgshapiro		sm_mbdb_terminate();	\
132141858Sgshapiro		CloseMBDB = false;	\
133141858Sgshapiro	}				\
134141858Sgshapiro	exit(excode);			\
13590792Sgshapiro}
13690792Sgshapiro
13764562Sgshapiroint
13864562Sgshapiromain(argc, argv)
13964562Sgshapiro	int argc;
14064562Sgshapiro	char **argv;
14164562Sgshapiro{
14298121Sgshapiro	bool alwaysrespond = false;
14398121Sgshapiro	bool initdb, exclude;
14490792Sgshapiro	bool runasuser = false;
14598121Sgshapiro	bool list = false;
14664562Sgshapiro	int mfail = 0, ufail = 0;
14764562Sgshapiro	int ch;
14864562Sgshapiro	int result;
14966494Sgshapiro	long sff;
15064562Sgshapiro	time_t interval;
15164562Sgshapiro	struct passwd *pw;
15264562Sgshapiro	ALIAS *cur;
15377349Sgshapiro	char *dbfilename = NULL;
15477349Sgshapiro	char *msgfilename = NULL;
15590792Sgshapiro	char *cfpath = NULL;
156203004Sgshapiro	char *name = NULL;
15790792Sgshapiro	char *returnaddr = NULL;
15864562Sgshapiro	SMDB_USER_INFO user_info;
15964562Sgshapiro	static char rnamebuf[MAXNAME];
16064562Sgshapiro	extern int optind, opterr;
16164562Sgshapiro	extern char *optarg;
16264562Sgshapiro
16364562Sgshapiro	/* Vars needed to link with smutil */
16464562Sgshapiro	clrbitmap(DontBlameSendmail);
16564562Sgshapiro	RunAsUid = RealUid = getuid();
16664562Sgshapiro	RunAsGid = RealGid = getgid();
16764562Sgshapiro	pw = getpwuid(RealUid);
16864562Sgshapiro	if (pw != NULL)
16964562Sgshapiro	{
17064562Sgshapiro		if (strlen(pw->pw_name) > MAXNAME - 1)
17164562Sgshapiro			pw->pw_name[MAXNAME] = '\0';
17290792Sgshapiro		sm_snprintf(rnamebuf, sizeof rnamebuf, "%s", pw->pw_name);
17364562Sgshapiro	}
17464562Sgshapiro	else
17590792Sgshapiro		sm_snprintf(rnamebuf, sizeof rnamebuf,
17690792Sgshapiro			    "Unknown UID %d", (int) RealUid);
17764562Sgshapiro	RunAsUserName = RealUserName = rnamebuf;
17864562Sgshapiro
17977349Sgshapiro# ifdef LOG_MAIL
18064562Sgshapiro	openlog("vacation", LOG_PID, LOG_MAIL);
18177349Sgshapiro# else /* LOG_MAIL */
18264562Sgshapiro	openlog("vacation", LOG_PID);
18377349Sgshapiro# endif /* LOG_MAIL */
18464562Sgshapiro
18564562Sgshapiro	opterr = 0;
18698121Sgshapiro	initdb = false;
18790792Sgshapiro	exclude = false;
18864562Sgshapiro	interval = INTERVAL_UNDEF;
18964562Sgshapiro	*From = '\0';
19064562Sgshapiro
19164562Sgshapiro
19298121Sgshapiro#define OPTIONS	"a:C:df:Iijlm:R:r:s:t:Uxz"
19390792Sgshapiro
19464562Sgshapiro	while (mfail == 0 && ufail == 0 &&
19564562Sgshapiro	       (ch = getopt(argc, argv, OPTIONS)) != -1)
19664562Sgshapiro	{
19764562Sgshapiro		switch((char)ch)
19864562Sgshapiro		{
19964562Sgshapiro		  case 'a':			/* alias */
20090792Sgshapiro			cur = (ALIAS *) malloc((unsigned int) sizeof(ALIAS));
20164562Sgshapiro			if (cur == NULL)
20264562Sgshapiro			{
20364562Sgshapiro				mfail++;
20464562Sgshapiro				break;
20564562Sgshapiro			}
20664562Sgshapiro			cur->name = optarg;
20764562Sgshapiro			cur->next = Names;
20864562Sgshapiro			Names = cur;
20964562Sgshapiro			break;
21064562Sgshapiro
21190792Sgshapiro		  case 'C':
21290792Sgshapiro			cfpath = optarg;
21390792Sgshapiro			break;
21490792Sgshapiro
21577349Sgshapiro		  case 'd':			/* debug mode */
21690792Sgshapiro			msglog = debuglog;
21764562Sgshapiro			break;
21864562Sgshapiro
21964562Sgshapiro		  case 'f':		/* alternate database */
22064562Sgshapiro			dbfilename = optarg;
22164562Sgshapiro			break;
22264562Sgshapiro
22364562Sgshapiro		  case 'I':			/* backward compatible */
22464562Sgshapiro		  case 'i':			/* init the database */
22598121Sgshapiro			initdb = true;
22664562Sgshapiro			break;
22764562Sgshapiro
22898121Sgshapiro		  case 'j':
22998121Sgshapiro			alwaysrespond = true;
23098121Sgshapiro			break;
23198121Sgshapiro
23264562Sgshapiro		  case 'l':
23398121Sgshapiro			list = true;		/* list the database */
23464562Sgshapiro			break;
23564562Sgshapiro
23664562Sgshapiro		  case 'm':		/* alternate message file */
23764562Sgshapiro			msgfilename = optarg;
23864562Sgshapiro			break;
23964562Sgshapiro
24090792Sgshapiro		  case 'R':
24190792Sgshapiro			returnaddr = optarg;
24290792Sgshapiro			break;
24390792Sgshapiro
24464562Sgshapiro		  case 'r':
24564562Sgshapiro			if (isascii(*optarg) && isdigit(*optarg))
24664562Sgshapiro			{
24764562Sgshapiro				interval = atol(optarg) * SECSPERDAY;
24864562Sgshapiro				if (interval < 0)
24964562Sgshapiro					ufail++;
25064562Sgshapiro			}
25164562Sgshapiro			else
25264562Sgshapiro				interval = ONLY_ONCE;
25364562Sgshapiro			break;
25464562Sgshapiro
25564562Sgshapiro		  case 's':		/* alternate sender name */
25690792Sgshapiro			(void) sm_strlcpy(From, optarg, sizeof From);
25764562Sgshapiro			break;
25864562Sgshapiro
25964562Sgshapiro		  case 't':		/* SunOS: -t1d (default expire) */
26064562Sgshapiro			break;
26164562Sgshapiro
26277349Sgshapiro		  case 'U':		/* run as single user mode */
26390792Sgshapiro			runasuser = true;
26477349Sgshapiro			break;
26577349Sgshapiro
26664562Sgshapiro		  case 'x':
26790792Sgshapiro			exclude = true;
26864562Sgshapiro			break;
26964562Sgshapiro
27064562Sgshapiro		  case 'z':
27190792Sgshapiro			returnaddr = "<>";
27264562Sgshapiro			break;
27364562Sgshapiro
27464562Sgshapiro		  case '?':
27564562Sgshapiro		  default:
27664562Sgshapiro			ufail++;
27764562Sgshapiro			break;
27864562Sgshapiro		}
27964562Sgshapiro	}
28064562Sgshapiro	argc -= optind;
28164562Sgshapiro	argv += optind;
28264562Sgshapiro
28364562Sgshapiro	if (mfail != 0)
28464562Sgshapiro	{
28564562Sgshapiro		msglog(LOG_NOTICE,
28664562Sgshapiro		       "vacation: can't allocate memory for alias.\n");
28766494Sgshapiro		EXITM(EX_TEMPFAIL);
28864562Sgshapiro	}
28964562Sgshapiro	if (ufail != 0)
29064562Sgshapiro		usage();
29164562Sgshapiro
29264562Sgshapiro	if (argc != 1)
29364562Sgshapiro	{
29498121Sgshapiro		if (!initdb && !list && !exclude)
29564562Sgshapiro			usage();
29664562Sgshapiro		if ((pw = getpwuid(getuid())) == NULL)
29764562Sgshapiro		{
29864562Sgshapiro			msglog(LOG_ERR,
29964562Sgshapiro			       "vacation: no such user uid %u.\n", getuid());
30066494Sgshapiro			EXITM(EX_NOUSER);
30164562Sgshapiro		}
302203004Sgshapiro		name = strdup(pw->pw_name);
30377349Sgshapiro		user_info.smdbu_id = pw->pw_uid;
30477349Sgshapiro		user_info.smdbu_group_id = pw->pw_gid;
30590792Sgshapiro		(void) sm_strlcpy(user_info.smdbu_name, pw->pw_name,
30690792Sgshapiro				  SMDB_MAX_USER_NAME_LEN);
30777349Sgshapiro		if (chdir(pw->pw_dir) != 0)
30877349Sgshapiro		{
30990792Sgshapiro			msglog(LOG_NOTICE,
31090792Sgshapiro			       "vacation: no such directory %s.\n",
31177349Sgshapiro			       pw->pw_dir);
31277349Sgshapiro			EXITM(EX_NOINPUT);
31377349Sgshapiro		}
31464562Sgshapiro	}
31577349Sgshapiro	else if (runasuser)
31677349Sgshapiro	{
317203004Sgshapiro		name = strdup(*argv);
31877349Sgshapiro		if (dbfilename == NULL || msgfilename == NULL)
31977349Sgshapiro		{
32077349Sgshapiro			msglog(LOG_NOTICE,
32177349Sgshapiro			       "vacation: -U requires setting both -f and -m\n");
32277349Sgshapiro			EXITM(EX_NOINPUT);
32377349Sgshapiro		}
32477349Sgshapiro		user_info.smdbu_id = pw->pw_uid;
32577349Sgshapiro		user_info.smdbu_group_id = pw->pw_gid;
32690792Sgshapiro		(void) sm_strlcpy(user_info.smdbu_name, pw->pw_name,
32777349Sgshapiro			       SMDB_MAX_USER_NAME_LEN);
32877349Sgshapiro	}
32977349Sgshapiro	else
33064562Sgshapiro	{
33190792Sgshapiro		int err;
33290792Sgshapiro		SM_CF_OPT_T mbdbname;
33390792Sgshapiro		SM_MBDB_T user;
33490792Sgshapiro
33594334Sgshapiro		cfpath = getcfname(0, 0, SM_GET_SENDMAIL_CF, cfpath);
33690792Sgshapiro		mbdbname.opt_name = "MailboxDatabase";
33790792Sgshapiro		mbdbname.opt_val = "pw";
33890792Sgshapiro		(void) sm_cf_getopt(cfpath, 1, &mbdbname);
33990792Sgshapiro		err = sm_mbdb_initialize(mbdbname.opt_val);
34090792Sgshapiro		if (err != EX_OK)
34177349Sgshapiro		{
34290792Sgshapiro			msglog(LOG_ERR,
34390792Sgshapiro			       "vacation: can't open mailbox database: %s.\n",
34490792Sgshapiro			       sm_strexit(err));
34590792Sgshapiro			EXITM(err);
34690792Sgshapiro		}
347141858Sgshapiro		CloseMBDB = true;
34890792Sgshapiro		err = sm_mbdb_lookup(*argv, &user);
34990792Sgshapiro		if (err == EX_NOUSER)
35090792Sgshapiro		{
35190792Sgshapiro			msglog(LOG_ERR, "vacation: no such user %s.\n", *argv);
35290792Sgshapiro			EXITM(EX_NOUSER);
35390792Sgshapiro		}
35490792Sgshapiro		if (err != EX_OK)
35590792Sgshapiro		{
35690792Sgshapiro			msglog(LOG_ERR,
35790792Sgshapiro			       "vacation: can't read mailbox database: %s.\n",
35890792Sgshapiro			       sm_strexit(err));
35990792Sgshapiro			EXITM(err);
36090792Sgshapiro		}
361203004Sgshapiro		name = strdup(user.mbdb_name);
36290792Sgshapiro		if (chdir(user.mbdb_homedir) != 0)
36390792Sgshapiro		{
36490792Sgshapiro			msglog(LOG_NOTICE,
36590792Sgshapiro			       "vacation: no such directory %s.\n",
36690792Sgshapiro			       user.mbdb_homedir);
36777349Sgshapiro			EXITM(EX_NOINPUT);
36877349Sgshapiro		}
36990792Sgshapiro		user_info.smdbu_id = user.mbdb_uid;
37090792Sgshapiro		user_info.smdbu_group_id = user.mbdb_gid;
37190792Sgshapiro		(void) sm_strlcpy(user_info.smdbu_name, user.mbdb_name,
37277349Sgshapiro			       SMDB_MAX_USER_NAME_LEN);
37364562Sgshapiro	}
374203004Sgshapiro	if (name == NULL)
375203004Sgshapiro	{
376203004Sgshapiro		msglog(LOG_ERR,
377203004Sgshapiro		       "vacation: can't allocate memory for username.\n");
378203004Sgshapiro		EXITM(EX_OSERR);
379203004Sgshapiro	}
38064562Sgshapiro
38177349Sgshapiro	if (dbfilename == NULL)
38277349Sgshapiro		dbfilename = VDB;
38377349Sgshapiro	if (msgfilename == NULL)
38477349Sgshapiro		msgfilename = VMSG;
38577349Sgshapiro
38666494Sgshapiro	sff = SFF_CREAT;
38766494Sgshapiro	if (getegid() != getgid())
38877349Sgshapiro	{
38990792Sgshapiro		/* Allow a set-group-ID vacation binary */
39066494Sgshapiro		RunAsGid = user_info.smdbu_group_id = getegid();
39190792Sgshapiro		sff |= SFF_OPENASROOT;
39277349Sgshapiro	}
39394334Sgshapiro	if (getuid() == 0)
39494334Sgshapiro	{
39594334Sgshapiro		/* Allow root to initialize user's vacation databases */
39694334Sgshapiro		sff |= SFF_OPENASROOT|SFF_ROOTOK;
39766494Sgshapiro
39894334Sgshapiro		/* ... safely */
39994334Sgshapiro		sff |= SFF_NOSLINK|SFF_NOHLINK|SFF_REGONLY;
40094334Sgshapiro	}
40194334Sgshapiro
40294334Sgshapiro
40364562Sgshapiro	result = smdb_open_database(&Db, dbfilename,
40498121Sgshapiro				    O_CREAT|O_RDWR | (initdb ? O_TRUNC : 0),
40566494Sgshapiro				    S_IRUSR|S_IWUSR, sff,
40664562Sgshapiro				    SMDB_TYPE_DEFAULT, &user_info, NULL);
40764562Sgshapiro	if (result != SMDBE_OK)
40864562Sgshapiro	{
40964562Sgshapiro		msglog(LOG_NOTICE, "vacation: %s: %s\n", dbfilename,
41090792Sgshapiro		       sm_errstring(result));
41166494Sgshapiro		EXITM(EX_DATAERR);
41264562Sgshapiro	}
41364562Sgshapiro
41498121Sgshapiro	if (list)
41564562Sgshapiro	{
41664562Sgshapiro		listdb();
41771345Sgshapiro		(void) Db->smdb_close(Db);
41864562Sgshapiro		exit(EX_OK);
41964562Sgshapiro	}
42064562Sgshapiro
42164562Sgshapiro	if (interval != INTERVAL_UNDEF)
42264562Sgshapiro		setinterval(interval);
42364562Sgshapiro
42498121Sgshapiro	if (initdb && !exclude)
42564562Sgshapiro	{
42671345Sgshapiro		(void) Db->smdb_close(Db);
42771345Sgshapiro		exit(EX_OK);
42864562Sgshapiro	}
42964562Sgshapiro
43064562Sgshapiro	if (exclude)
43164562Sgshapiro	{
43290792Sgshapiro		xclude(smioin);
43371345Sgshapiro		(void) Db->smdb_close(Db);
43466494Sgshapiro		EXITM(EX_OK);
43564562Sgshapiro	}
43664562Sgshapiro
43790792Sgshapiro	if ((cur = (ALIAS *) malloc((unsigned int) sizeof(ALIAS))) == NULL)
43864562Sgshapiro	{
43964562Sgshapiro		msglog(LOG_NOTICE,
44064562Sgshapiro		       "vacation: can't allocate memory for username.\n");
44171345Sgshapiro		(void) Db->smdb_close(Db);
44266494Sgshapiro		EXITM(EX_OSERR);
44364562Sgshapiro	}
44464562Sgshapiro	cur->name = name;
44564562Sgshapiro	cur->next = Names;
44664562Sgshapiro	Names = cur;
44764562Sgshapiro
44898121Sgshapiro	result = readheaders(alwaysrespond);
44971345Sgshapiro	if (result == EX_OK && !recent())
45064562Sgshapiro	{
45164562Sgshapiro		time_t now;
45264562Sgshapiro
45364562Sgshapiro		(void) time(&now);
45464562Sgshapiro		setreply(From, now);
45571345Sgshapiro		(void) Db->smdb_close(Db);
45690792Sgshapiro		sendmessage(name, msgfilename, returnaddr);
45764562Sgshapiro	}
45864562Sgshapiro	else
45971345Sgshapiro		(void) Db->smdb_close(Db);
46071345Sgshapiro	if (result == EX_NOUSER)
46171345Sgshapiro		result = EX_OK;
46271345Sgshapiro	exit(result);
46364562Sgshapiro}
46464562Sgshapiro
46564562Sgshapiro/*
46666494Sgshapiro** EATMSG -- read stdin till EOF
46766494Sgshapiro**
46866494Sgshapiro**	Parameters:
46966494Sgshapiro**		none.
47066494Sgshapiro**
47166494Sgshapiro**	Returns:
47266494Sgshapiro**		nothing.
47366494Sgshapiro**
47466494Sgshapiro*/
47577349Sgshapiro
47666494Sgshapirostatic void
47766494Sgshapiroeatmsg()
47866494Sgshapiro{
47966494Sgshapiro	/*
48066494Sgshapiro	**  read the rest of the e-mail and ignore it to avoid problems
48166494Sgshapiro	**  with EPIPE in sendmail
48266494Sgshapiro	*/
48366494Sgshapiro	while (getc(stdin) != EOF)
48466494Sgshapiro		continue;
48566494Sgshapiro}
48666494Sgshapiro
48766494Sgshapiro/*
48864562Sgshapiro** READHEADERS -- read mail headers
48964562Sgshapiro**
49064562Sgshapiro**	Parameters:
49198121Sgshapiro**		alwaysrespond -- respond regardless of whether msg is to me
49264562Sgshapiro**
49364562Sgshapiro**	Returns:
49471345Sgshapiro**		a exit code: NOUSER if no reply, OK if reply, * if error
49564562Sgshapiro**
49666494Sgshapiro**	Side Effects:
49766494Sgshapiro**		may exit().
49866494Sgshapiro**
49964562Sgshapiro*/
50071345Sgshapiro
501168515Sgshapirostatic int
50298121Sgshapiroreadheaders(alwaysrespond)
50398121Sgshapiro	bool alwaysrespond;
50464562Sgshapiro{
50564562Sgshapiro	bool tome, cont;
50664562Sgshapiro	register char *p;
50764562Sgshapiro	register ALIAS *cur;
50864562Sgshapiro	char buf[MAXLINE];
50964562Sgshapiro
51098121Sgshapiro	cont = false;
51198121Sgshapiro	tome = alwaysrespond;
512249865Sgshapiro	while (sm_io_fgets(smioin, SM_TIME_DEFAULT, buf, sizeof(buf)) >= 0 &&
51390792Sgshapiro	       *buf != '\n')
51464562Sgshapiro	{
51564562Sgshapiro		switch(*buf)
51664562Sgshapiro		{
51764562Sgshapiro		  case 'F':		/* "From " */
51890792Sgshapiro			cont = false;
51964562Sgshapiro			if (strncmp(buf, "From ", 5) == 0)
52064562Sgshapiro			{
52190792Sgshapiro				bool quoted = false;
52264562Sgshapiro
52364562Sgshapiro				p = buf + 5;
52464562Sgshapiro				while (*p != '\0')
52564562Sgshapiro				{
52664562Sgshapiro					/* escaped character */
52764562Sgshapiro					if (*p == '\\')
52864562Sgshapiro					{
52964562Sgshapiro						p++;
53064562Sgshapiro						if (*p == '\0')
53164562Sgshapiro						{
53264562Sgshapiro							msglog(LOG_NOTICE,
53364562Sgshapiro							       "vacation: badly formatted \"From \" line.\n");
53466494Sgshapiro							EXITIT(EX_DATAERR);
53564562Sgshapiro						}
53664562Sgshapiro					}
53764562Sgshapiro					else if (*p == '"')
53864562Sgshapiro						quoted = !quoted;
53964562Sgshapiro					else if (*p == '\r' || *p == '\n')
54064562Sgshapiro						break;
54164562Sgshapiro					else if (*p == ' ' && !quoted)
54264562Sgshapiro						break;
54364562Sgshapiro					p++;
54464562Sgshapiro				}
54564562Sgshapiro				if (quoted)
54664562Sgshapiro				{
54764562Sgshapiro					msglog(LOG_NOTICE,
54864562Sgshapiro					       "vacation: badly formatted \"From \" line.\n");
54966494Sgshapiro					EXITIT(EX_DATAERR);
55064562Sgshapiro				}
55164562Sgshapiro				*p = '\0';
55264562Sgshapiro
55364562Sgshapiro				/* ok since both strings have MAXLINE length */
55464562Sgshapiro				if (*From == '\0')
55590792Sgshapiro					(void) sm_strlcpy(From, buf + 5,
55690792Sgshapiro							  sizeof From);
55764562Sgshapiro				if ((p = strchr(buf + 5, '\n')) != NULL)
55864562Sgshapiro					*p = '\0';
55964562Sgshapiro				if (junkmail(buf + 5))
56071345Sgshapiro					EXITIT(EX_NOUSER);
56164562Sgshapiro			}
56264562Sgshapiro			break;
56364562Sgshapiro
56464562Sgshapiro		  case 'P':		/* "Precedence:" */
56564562Sgshapiro		  case 'p':
56690792Sgshapiro			cont = false;
56764562Sgshapiro			if (strlen(buf) <= 10 ||
56864562Sgshapiro			    strncasecmp(buf, "Precedence", 10) != 0 ||
56964562Sgshapiro			    (buf[10] != ':' && buf[10] != ' ' &&
57064562Sgshapiro			     buf[10] != '\t'))
57164562Sgshapiro				break;
57264562Sgshapiro			if ((p = strchr(buf, ':')) == NULL)
57364562Sgshapiro				break;
57464562Sgshapiro			while (*++p != '\0' && isascii(*p) && isspace(*p));
57564562Sgshapiro			if (*p == '\0')
57664562Sgshapiro				break;
57764562Sgshapiro			if (strncasecmp(p, "junk", 4) == 0 ||
57864562Sgshapiro			    strncasecmp(p, "bulk", 4) == 0 ||
57964562Sgshapiro			    strncasecmp(p, "list", 4) == 0)
58071345Sgshapiro				EXITIT(EX_NOUSER);
58164562Sgshapiro			break;
58264562Sgshapiro
58364562Sgshapiro		  case 'C':		/* "Cc:" */
58464562Sgshapiro		  case 'c':
58564562Sgshapiro			if (strncasecmp(buf, "Cc:", 3) != 0)
58664562Sgshapiro				break;
58790792Sgshapiro			cont = true;
58864562Sgshapiro			goto findme;
58964562Sgshapiro
59064562Sgshapiro		  case 'T':		/* "To:" */
59164562Sgshapiro		  case 't':
59264562Sgshapiro			if (strncasecmp(buf, "To:", 3) != 0)
59364562Sgshapiro				break;
59490792Sgshapiro			cont = true;
59564562Sgshapiro			goto findme;
59664562Sgshapiro
59764562Sgshapiro		  default:
59864562Sgshapiro			if (!isascii(*buf) || !isspace(*buf) || !cont || tome)
59964562Sgshapiro			{
60090792Sgshapiro				cont = false;
60164562Sgshapiro				break;
60264562Sgshapiro			}
60364562Sgshapirofindme:
60464562Sgshapiro			for (cur = Names;
60564562Sgshapiro			     !tome && cur != NULL;
60664562Sgshapiro			     cur = cur->next)
60764562Sgshapiro				tome = nsearch(cur->name, buf);
60864562Sgshapiro		}
60964562Sgshapiro	}
61064562Sgshapiro	if (!tome)
61171345Sgshapiro		EXITIT(EX_NOUSER);
61264562Sgshapiro	if (*From == '\0')
61364562Sgshapiro	{
61464562Sgshapiro		msglog(LOG_NOTICE, "vacation: no initial \"From \" line.\n");
61566494Sgshapiro		EXITIT(EX_DATAERR);
61664562Sgshapiro	}
61771345Sgshapiro	EXITIT(EX_OK);
61864562Sgshapiro}
61964562Sgshapiro
62064562Sgshapiro/*
62164562Sgshapiro** NSEARCH --
62264562Sgshapiro**	do a nice, slow, search of a string for a substring.
62364562Sgshapiro**
62464562Sgshapiro**	Parameters:
62564562Sgshapiro**		name -- name to search.
62664562Sgshapiro**		str -- string in which to search.
62764562Sgshapiro**
62864562Sgshapiro**	Returns:
62964562Sgshapiro**		is name a substring of str?
63064562Sgshapiro**
63164562Sgshapiro*/
63277349Sgshapiro
633168515Sgshapirostatic bool
63464562Sgshapironsearch(name, str)
63564562Sgshapiro	register char *name, *str;
63664562Sgshapiro{
63764562Sgshapiro	register size_t len;
63864562Sgshapiro	register char *s;
63964562Sgshapiro
64064562Sgshapiro	len = strlen(name);
64164562Sgshapiro
64264562Sgshapiro	for (s = str; *s != '\0'; ++s)
64364562Sgshapiro	{
64464562Sgshapiro		/*
64564562Sgshapiro		**  Check to make sure that the string matches and
64664562Sgshapiro		**  the previous character is not an alphanumeric and
64764562Sgshapiro		**  the next character after the match is not an alphanumeric.
64864562Sgshapiro		**
64964562Sgshapiro		**  This prevents matching "eric" to "derick" while still
65064562Sgshapiro		**  matching "eric" to "<eric+detail>".
65164562Sgshapiro		*/
65264562Sgshapiro
65364562Sgshapiro		if (tolower(*s) == tolower(*name) &&
65464562Sgshapiro		    strncasecmp(name, s, len) == 0 &&
65564562Sgshapiro		    (s == str || !isascii(*(s - 1)) || !isalnum(*(s - 1))) &&
65664562Sgshapiro		    (!isascii(*(s + len)) || !isalnum(*(s + len))))
65790792Sgshapiro			return true;
65864562Sgshapiro	}
65990792Sgshapiro	return false;
66064562Sgshapiro}
66164562Sgshapiro
66264562Sgshapiro/*
66364562Sgshapiro** JUNKMAIL --
66464562Sgshapiro**	read the header and return if automagic/junk/bulk/list mail
66564562Sgshapiro**
66664562Sgshapiro**	Parameters:
66764562Sgshapiro**		from -- sender address.
66864562Sgshapiro**
66964562Sgshapiro**	Returns:
67064562Sgshapiro**		is this some automated/junk/bulk/list mail?
67164562Sgshapiro**
67264562Sgshapiro*/
67371345Sgshapiro
67471345Sgshapirostruct ignore
67571345Sgshapiro{
67671345Sgshapiro	char	*name;
67771345Sgshapiro	size_t	len;
67871345Sgshapiro};
67971345Sgshapiro
68071345Sgshapirotypedef struct ignore IGNORE_T;
68171345Sgshapiro
68271345Sgshapiro#define MAX_USER_LEN 256	/* maximum length of local part (sender) */
68371345Sgshapiro
68471345Sgshapiro/* delimiters for the local part of an address */
68571345Sgshapiro#define isdelim(c)	((c) == '%' || (c) == '@' || (c) == '+')
68671345Sgshapiro
687168515Sgshapirostatic bool
68864562Sgshapirojunkmail(from)
68964562Sgshapiro	char *from;
69064562Sgshapiro{
69171345Sgshapiro	bool quot;
69271345Sgshapiro	char *e;
69371345Sgshapiro	size_t len;
69471345Sgshapiro	IGNORE_T *cur;
69571345Sgshapiro	char sender[MAX_USER_LEN];
69671345Sgshapiro	static IGNORE_T ignore[] =
69764562Sgshapiro	{
69864562Sgshapiro		{ "postmaster",		10	},
69964562Sgshapiro		{ "uucp",		4	},
70064562Sgshapiro		{ "mailer-daemon",	13	},
70164562Sgshapiro		{ "mailer",		6	},
70271345Sgshapiro		{ NULL,			0	}
70371345Sgshapiro	};
70471345Sgshapiro
70571345Sgshapiro	static IGNORE_T ignorepost[] =
70671345Sgshapiro	{
70771345Sgshapiro		{ "-request",		8	},
70864562Sgshapiro		{ "-relay",		6	},
70971345Sgshapiro		{ "-owner",		6	},
71064562Sgshapiro		{ NULL,			0	}
71164562Sgshapiro	};
71264562Sgshapiro
71371345Sgshapiro	static IGNORE_T ignorepre[] =
71471345Sgshapiro	{
71571345Sgshapiro		{ "owner-",		6	},
71671345Sgshapiro		{ NULL,			0	}
71771345Sgshapiro	};
71871345Sgshapiro
71964562Sgshapiro	/*
72071345Sgshapiro	**  This is mildly amusing, and I'm not positive it's right; trying
72171345Sgshapiro	**  to find the "real" name of the sender, assuming that addresses
72271345Sgshapiro	**  will be some variant of:
72371345Sgshapiro	**
72471345Sgshapiro	**  From site!site!SENDER%site.domain%site.domain@site.domain
72571345Sgshapiro	*/
72671345Sgshapiro
72790792Sgshapiro	quot = false;
72871345Sgshapiro	e = from;
72971345Sgshapiro	len = 0;
73071345Sgshapiro	while (*e != '\0' && (quot || !isdelim(*e)))
73164562Sgshapiro	{
73271345Sgshapiro		if (*e == '"')
73371345Sgshapiro		{
73471345Sgshapiro			quot = !quot;
73571345Sgshapiro			++e;
73671345Sgshapiro			continue;
73771345Sgshapiro		}
73871345Sgshapiro		if (*e == '\\')
73971345Sgshapiro		{
74071345Sgshapiro			if (*(++e) == '\0')
74171345Sgshapiro			{
74271345Sgshapiro				/* '\\' at end of string? */
74371345Sgshapiro				break;
74471345Sgshapiro			}
74571345Sgshapiro			if (len < MAX_USER_LEN)
74671345Sgshapiro				sender[len++] = *e;
74771345Sgshapiro			++e;
74871345Sgshapiro			continue;
74971345Sgshapiro		}
75071345Sgshapiro		if (*e == '!' && !quot)
75171345Sgshapiro		{
75271345Sgshapiro			len = 0;
75371345Sgshapiro			sender[len] = '\0';
75471345Sgshapiro		}
75564562Sgshapiro		else
75671345Sgshapiro			if (len < MAX_USER_LEN)
75771345Sgshapiro				sender[len++] = *e;
75871345Sgshapiro		++e;
75964562Sgshapiro	}
76071345Sgshapiro	if (len < MAX_USER_LEN)
76171345Sgshapiro		sender[len] = '\0';
76271345Sgshapiro	else
76371345Sgshapiro		sender[MAX_USER_LEN - 1] = '\0';
76471345Sgshapiro
76571345Sgshapiro	if (len <= 0)
76690792Sgshapiro		return false;
76771345Sgshapiro#if 0
76871345Sgshapiro	if (quot)
76990792Sgshapiro		return false;	/* syntax error... */
77071345Sgshapiro#endif /* 0 */
77171345Sgshapiro
77271345Sgshapiro	/* test prefixes */
77371345Sgshapiro	for (cur = ignorepre; cur->name != NULL; ++cur)
77471345Sgshapiro	{
77571345Sgshapiro		if (len >= cur->len &&
77671345Sgshapiro		    strncasecmp(cur->name, sender, cur->len) == 0)
77790792Sgshapiro			return true;
77871345Sgshapiro	}
77971345Sgshapiro
78071345Sgshapiro	/*
78171345Sgshapiro	**  If the name is truncated, don't test the rest.
78271345Sgshapiro	**	We could extract the "tail" of the sender address and
78371345Sgshapiro	**	compare it it ignorepost, however, it seems not worth
78471345Sgshapiro	**	the effort.
78571345Sgshapiro	**	The address surely can't match any entry in ignore[]
78671345Sgshapiro	**	(as long as all of them are shorter than MAX_USER_LEN).
78771345Sgshapiro	*/
78871345Sgshapiro
78971345Sgshapiro	if (len > MAX_USER_LEN)
79090792Sgshapiro		return false;
79171345Sgshapiro
79271345Sgshapiro	/* test full local parts */
79364562Sgshapiro	for (cur = ignore; cur->name != NULL; ++cur)
79464562Sgshapiro	{
79571345Sgshapiro		if (len == cur->len &&
79671345Sgshapiro		    strncasecmp(cur->name, sender, cur->len) == 0)
79790792Sgshapiro			return true;
79871345Sgshapiro	}
79971345Sgshapiro
80071345Sgshapiro	/* test postfixes */
80171345Sgshapiro	for (cur = ignorepost; cur->name != NULL; ++cur)
80271345Sgshapiro	{
80364562Sgshapiro		if (len >= cur->len &&
80471345Sgshapiro		    strncasecmp(cur->name, e - cur->len - 1,
80571345Sgshapiro				cur->len) == 0)
80690792Sgshapiro			return true;
80764562Sgshapiro	}
80890792Sgshapiro	return false;
80964562Sgshapiro}
81064562Sgshapiro
81164562Sgshapiro#define	VIT	"__VACATION__INTERVAL__TIMER__"
81264562Sgshapiro
81364562Sgshapiro/*
81464562Sgshapiro** RECENT --
81564562Sgshapiro**	find out if user has gotten a vacation message recently.
81664562Sgshapiro**
81764562Sgshapiro**	Parameters:
81864562Sgshapiro**		none.
81964562Sgshapiro**
82064562Sgshapiro**	Returns:
82190792Sgshapiro**		true iff user has gotten a vacation message recently.
82264562Sgshapiro**
82364562Sgshapiro*/
82477349Sgshapiro
825168515Sgshapirostatic bool
82664562Sgshapirorecent()
82764562Sgshapiro{
82864562Sgshapiro	SMDB_DBENT key, data;
82964562Sgshapiro	time_t then, next;
83090792Sgshapiro	bool trydomain = false;
83164562Sgshapiro	int st;
83264562Sgshapiro	char *domain;
83364562Sgshapiro
83464562Sgshapiro	memset(&key, '\0', sizeof key);
83564562Sgshapiro	memset(&data, '\0', sizeof data);
83664562Sgshapiro
83764562Sgshapiro	/* get interval time */
83871345Sgshapiro	key.data = VIT;
83971345Sgshapiro	key.size = sizeof(VIT);
84064562Sgshapiro
84164562Sgshapiro	st = Db->smdb_get(Db, &key, &data, 0);
84264562Sgshapiro	if (st != SMDBE_OK)
84364562Sgshapiro		next = SECSPERDAY * DAYSPERWEEK;
84464562Sgshapiro	else
84571345Sgshapiro		memmove(&next, data.data, sizeof(next));
84664562Sgshapiro
84764562Sgshapiro	memset(&data, '\0', sizeof data);
84864562Sgshapiro
84964562Sgshapiro	/* get record for this address */
85071345Sgshapiro	key.data = From;
85171345Sgshapiro	key.size = strlen(From);
85264562Sgshapiro
85364562Sgshapiro	do
85464562Sgshapiro	{
85564562Sgshapiro		st = Db->smdb_get(Db, &key, &data, 0);
85664562Sgshapiro		if (st == SMDBE_OK)
85764562Sgshapiro		{
85871345Sgshapiro			memmove(&then, data.data, sizeof(then));
85964562Sgshapiro			if (next == ONLY_ONCE || then == ONLY_ONCE ||
86064562Sgshapiro			    then + next > time(NULL))
86190792Sgshapiro				return true;
86264562Sgshapiro		}
86364562Sgshapiro		if ((trydomain = !trydomain) &&
86464562Sgshapiro		    (domain = strchr(From, '@')) != NULL)
86564562Sgshapiro		{
86671345Sgshapiro			key.data = domain;
86771345Sgshapiro			key.size = strlen(domain);
86864562Sgshapiro		}
86964562Sgshapiro	} while (trydomain);
87090792Sgshapiro	return false;
87164562Sgshapiro}
87264562Sgshapiro
87364562Sgshapiro/*
87464562Sgshapiro** SETINTERVAL --
87564562Sgshapiro**	store the reply interval
87664562Sgshapiro**
87764562Sgshapiro**	Parameters:
87864562Sgshapiro**		interval -- time interval for replies.
87964562Sgshapiro**
88064562Sgshapiro**	Returns:
88164562Sgshapiro**		nothing.
88264562Sgshapiro**
88364562Sgshapiro**	Side Effects:
88464562Sgshapiro**		stores the reply interval in database.
88564562Sgshapiro*/
88677349Sgshapiro
887168515Sgshapirostatic void
88864562Sgshapirosetinterval(interval)
88964562Sgshapiro	time_t interval;
89064562Sgshapiro{
89164562Sgshapiro	SMDB_DBENT key, data;
89264562Sgshapiro
89364562Sgshapiro	memset(&key, '\0', sizeof key);
89464562Sgshapiro	memset(&data, '\0', sizeof data);
89564562Sgshapiro
89671345Sgshapiro	key.data = VIT;
89771345Sgshapiro	key.size = sizeof(VIT);
89871345Sgshapiro	data.data = (char*) &interval;
89971345Sgshapiro	data.size = sizeof(interval);
90071345Sgshapiro	(void) (Db->smdb_put)(Db, &key, &data, 0);
90164562Sgshapiro}
90264562Sgshapiro
90364562Sgshapiro/*
90464562Sgshapiro** SETREPLY --
90564562Sgshapiro**	store that this user knows about the vacation.
90664562Sgshapiro**
90764562Sgshapiro**	Parameters:
90864562Sgshapiro**		from -- sender address.
90964562Sgshapiro**		when -- last reply time.
91064562Sgshapiro**
91164562Sgshapiro**	Returns:
91264562Sgshapiro**		nothing.
91364562Sgshapiro**
91464562Sgshapiro**	Side Effects:
91564562Sgshapiro**		stores user/time in database.
91664562Sgshapiro*/
91777349Sgshapiro
918168515Sgshapirostatic void
91964562Sgshapirosetreply(from, when)
92064562Sgshapiro	char *from;
92164562Sgshapiro	time_t when;
92264562Sgshapiro{
92364562Sgshapiro	SMDB_DBENT key, data;
92464562Sgshapiro
92564562Sgshapiro	memset(&key, '\0', sizeof key);
92664562Sgshapiro	memset(&data, '\0', sizeof data);
92764562Sgshapiro
92871345Sgshapiro	key.data = from;
92971345Sgshapiro	key.size = strlen(from);
93071345Sgshapiro	data.data = (char*) &when;
93171345Sgshapiro	data.size = sizeof(when);
93271345Sgshapiro	(void) (Db->smdb_put)(Db, &key, &data, 0);
93364562Sgshapiro}
93464562Sgshapiro
93564562Sgshapiro/*
93664562Sgshapiro** XCLUDE --
93764562Sgshapiro**	add users to vacation db so they don't get a reply.
93864562Sgshapiro**
93964562Sgshapiro**	Parameters:
94064562Sgshapiro**		f -- file pointer with list of address to exclude
94164562Sgshapiro**
94264562Sgshapiro**	Returns:
94364562Sgshapiro**		nothing.
94464562Sgshapiro**
94564562Sgshapiro**	Side Effects:
94664562Sgshapiro**		stores users in database.
94764562Sgshapiro*/
94877349Sgshapiro
949168515Sgshapirostatic void
95064562Sgshapiroxclude(f)
95190792Sgshapiro	SM_FILE_T *f;
95264562Sgshapiro{
95364562Sgshapiro	char buf[MAXLINE], *p;
95464562Sgshapiro
95564562Sgshapiro	if (f == NULL)
95664562Sgshapiro		return;
957249865Sgshapiro	while (sm_io_fgets(f, SM_TIME_DEFAULT, buf, sizeof buf) >= 0)
95864562Sgshapiro	{
95964562Sgshapiro		if ((p = strchr(buf, '\n')) != NULL)
96064562Sgshapiro			*p = '\0';
96164562Sgshapiro		setreply(buf, ONLY_ONCE);
96264562Sgshapiro	}
96364562Sgshapiro}
96464562Sgshapiro
96564562Sgshapiro/*
96664562Sgshapiro** SENDMESSAGE --
96764562Sgshapiro**	exec sendmail to send the vacation file to sender
96864562Sgshapiro**
96964562Sgshapiro**	Parameters:
97064562Sgshapiro**		myname -- user name.
97164562Sgshapiro**		msgfn -- name of file with vacation message.
97290792Sgshapiro**		sender -- use as sender address
97364562Sgshapiro**
97464562Sgshapiro**	Returns:
97564562Sgshapiro**		nothing.
97664562Sgshapiro**
97764562Sgshapiro**	Side Effects:
97864562Sgshapiro**		sends vacation reply.
97964562Sgshapiro*/
98077349Sgshapiro
981168515Sgshapirostatic void
98290792Sgshapirosendmessage(myname, msgfn, sender)
98364562Sgshapiro	char *myname;
98464562Sgshapiro	char *msgfn;
98590792Sgshapiro	char *sender;
98664562Sgshapiro{
98790792Sgshapiro	SM_FILE_T *mfp, *sfp;
98864562Sgshapiro	int i;
98964562Sgshapiro	int pvect[2];
99077349Sgshapiro	char *pv[8];
99164562Sgshapiro	char buf[MAXLINE];
99264562Sgshapiro
99390792Sgshapiro	mfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, msgfn, SM_IO_RDONLY, NULL);
99464562Sgshapiro	if (mfp == NULL)
99564562Sgshapiro	{
99664562Sgshapiro		if (msgfn[0] == '/')
99764562Sgshapiro			msglog(LOG_NOTICE, "vacation: no %s file.\n", msgfn);
99864562Sgshapiro		else
99964562Sgshapiro			msglog(LOG_NOTICE, "vacation: no ~%s/%s file.\n",
100064562Sgshapiro			       myname, msgfn);
100164562Sgshapiro		exit(EX_NOINPUT);
100264562Sgshapiro	}
100364562Sgshapiro	if (pipe(pvect) < 0)
100464562Sgshapiro	{
100590792Sgshapiro		msglog(LOG_ERR, "vacation: pipe: %s", sm_errstring(errno));
100664562Sgshapiro		exit(EX_OSERR);
100764562Sgshapiro	}
100877349Sgshapiro	pv[0] = "sendmail";
100977349Sgshapiro	pv[1] = "-oi";
101077349Sgshapiro	pv[2] = "-f";
101190792Sgshapiro	if (sender != NULL)
101290792Sgshapiro		pv[3] = sender;
101377349Sgshapiro	else
101477349Sgshapiro		pv[3] = myname;
101577349Sgshapiro	pv[4] = "--";
101677349Sgshapiro	pv[5] = From;
101777349Sgshapiro	pv[6] = NULL;
101864562Sgshapiro	i = fork();
101964562Sgshapiro	if (i < 0)
102064562Sgshapiro	{
102190792Sgshapiro		msglog(LOG_ERR, "vacation: fork: %s", sm_errstring(errno));
102264562Sgshapiro		exit(EX_OSERR);
102364562Sgshapiro	}
102464562Sgshapiro	if (i == 0)
102564562Sgshapiro	{
102664562Sgshapiro		(void) dup2(pvect[0], 0);
102764562Sgshapiro		(void) close(pvect[0]);
102864562Sgshapiro		(void) close(pvect[1]);
102990792Sgshapiro		(void) sm_io_close(mfp, SM_TIME_DEFAULT);
103077349Sgshapiro		(void) execv(_PATH_SENDMAIL, pv);
103164562Sgshapiro		msglog(LOG_ERR, "vacation: can't exec %s: %s",
103290792Sgshapiro			_PATH_SENDMAIL, sm_errstring(errno));
103364562Sgshapiro		exit(EX_UNAVAILABLE);
103464562Sgshapiro	}
103564562Sgshapiro	/* check return status of the following calls? XXX */
103664562Sgshapiro	(void) close(pvect[0]);
103790792Sgshapiro	if ((sfp = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT,
103890792Sgshapiro			      (void *) &(pvect[1]),
103990792Sgshapiro			      SM_IO_WRONLY, NULL)) != NULL)
104064562Sgshapiro	{
1041203004Sgshapiro#if _FFR_VAC_WAIT4SM
1042203004Sgshapiro# ifdef WAITUNION
1043203004Sgshapiro		union wait st;
1044203004Sgshapiro# else /* WAITUNION */
1045203004Sgshapiro		auto int st;
1046203004Sgshapiro# endif /* WAITUNION */
1047203004Sgshapiro#endif /* _FFR_VAC_WAIT4SM */
1048203004Sgshapiro
104990792Sgshapiro		(void) sm_io_fprintf(sfp, SM_TIME_DEFAULT, "To: %s\n", From);
105090792Sgshapiro		(void) sm_io_fprintf(sfp, SM_TIME_DEFAULT,
105190792Sgshapiro				     "Auto-Submitted: auto-replied\n");
1052249865Sgshapiro		while (sm_io_fgets(mfp, SM_TIME_DEFAULT, buf, sizeof buf) >= 0)
105390792Sgshapiro			(void) sm_io_fputs(sfp, SM_TIME_DEFAULT, buf);
105490792Sgshapiro		(void) sm_io_close(mfp, SM_TIME_DEFAULT);
105590792Sgshapiro		(void) sm_io_close(sfp, SM_TIME_DEFAULT);
1056203004Sgshapiro#if _FFR_VAC_WAIT4SM
1057203004Sgshapiro		(void) wait(&st);
1058203004Sgshapiro#endif /* _FFR_VAC_WAIT4SM */
105964562Sgshapiro	}
106064562Sgshapiro	else
106164562Sgshapiro	{
106290792Sgshapiro		(void) sm_io_close(mfp, SM_TIME_DEFAULT);
106364562Sgshapiro		msglog(LOG_ERR, "vacation: can't open pipe to sendmail");
106464562Sgshapiro		exit(EX_UNAVAILABLE);
106564562Sgshapiro	}
106664562Sgshapiro}
106764562Sgshapiro
1068168515Sgshapirostatic void
106964562Sgshapirousage()
107064562Sgshapiro{
107177349Sgshapiro	msglog(LOG_NOTICE,
1072132943Sgshapiro	       "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",
1073132943Sgshapiro	       getuid());
107464562Sgshapiro	exit(EX_USAGE);
107564562Sgshapiro}
107664562Sgshapiro
107764562Sgshapiro/*
107864562Sgshapiro** LISTDB -- list the contents of the vacation database
107964562Sgshapiro**
108064562Sgshapiro**	Parameters:
108164562Sgshapiro**		none.
108264562Sgshapiro**
108364562Sgshapiro**	Returns:
108464562Sgshapiro**		nothing.
108564562Sgshapiro*/
108664562Sgshapiro
108764562Sgshapirostatic void
108864562Sgshapirolistdb()
108964562Sgshapiro{
109064562Sgshapiro	int result;
109164562Sgshapiro	time_t t;
109264562Sgshapiro	SMDB_CURSOR *cursor = NULL;
109364562Sgshapiro	SMDB_DBENT db_key, db_value;
109464562Sgshapiro
109564562Sgshapiro	memset(&db_key, '\0', sizeof db_key);
109664562Sgshapiro	memset(&db_value, '\0', sizeof db_value);
109764562Sgshapiro
109864562Sgshapiro	result = Db->smdb_cursor(Db, &cursor, 0);
109964562Sgshapiro	if (result != SMDBE_OK)
110064562Sgshapiro	{
110190792Sgshapiro		sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
110290792Sgshapiro			      "vacation: set cursor: %s\n",
110390792Sgshapiro			      sm_errstring(result));
110464562Sgshapiro		return;
110564562Sgshapiro	}
110664562Sgshapiro
110764562Sgshapiro	while ((result = cursor->smdbc_get(cursor, &db_key, &db_value,
110864562Sgshapiro					   SMDB_CURSOR_GET_NEXT)) == SMDBE_OK)
110964562Sgshapiro	{
111098121Sgshapiro		char *timestamp;
111198121Sgshapiro
111264562Sgshapiro		/* skip magic VIT entry */
1113110560Sgshapiro		if (db_key.size == strlen(VIT) + 1 &&
111471345Sgshapiro		    strncmp((char *)db_key.data, VIT,
111571345Sgshapiro			    (int)db_key.size - 1) == 0)
111664562Sgshapiro			continue;
111764562Sgshapiro
111864562Sgshapiro		/* skip bogus values */
111971345Sgshapiro		if (db_value.size != sizeof t)
112064562Sgshapiro		{
112190792Sgshapiro			sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
112290792Sgshapiro				      "vacation: %.*s invalid time stamp\n",
112390792Sgshapiro				      (int) db_key.size, (char *) db_key.data);
112464562Sgshapiro			continue;
112564562Sgshapiro		}
112664562Sgshapiro
112771345Sgshapiro		memcpy(&t, db_value.data, sizeof t);
112864562Sgshapiro
112971345Sgshapiro		if (db_key.size > 40)
113071345Sgshapiro			db_key.size = 40;
113164562Sgshapiro
113298121Sgshapiro		if (t <= 0)
113398121Sgshapiro		{
113498121Sgshapiro			/* must be an exclude */
113598121Sgshapiro			timestamp = "(exclusion)\n";
113698121Sgshapiro		}
113798121Sgshapiro		else
113898121Sgshapiro		{
113998121Sgshapiro			timestamp = ctime(&t);
114098121Sgshapiro		}
114190792Sgshapiro		sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%-40.*s %-10s",
114290792Sgshapiro			      (int) db_key.size, (char *) db_key.data,
114398121Sgshapiro			      timestamp);
114464562Sgshapiro
114564562Sgshapiro		memset(&db_key, '\0', sizeof db_key);
114664562Sgshapiro		memset(&db_value, '\0', sizeof db_value);
114764562Sgshapiro	}
114864562Sgshapiro
114964562Sgshapiro	if (result != SMDBE_OK && result != SMDBE_LAST_ENTRY)
115064562Sgshapiro	{
115190792Sgshapiro		sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
115290792Sgshapiro			      "vacation: get value at cursor: %s\n",
115390792Sgshapiro			      sm_errstring(result));
115464562Sgshapiro		if (cursor != NULL)
115564562Sgshapiro		{
115664562Sgshapiro			(void) cursor->smdbc_close(cursor);
115764562Sgshapiro			cursor = NULL;
115864562Sgshapiro		}
115964562Sgshapiro		return;
116064562Sgshapiro	}
116164562Sgshapiro	(void) cursor->smdbc_close(cursor);
116264562Sgshapiro	cursor = NULL;
116364562Sgshapiro}
116464562Sgshapiro
116564562Sgshapiro/*
116664562Sgshapiro** DEBUGLOG -- write message to standard error
116764562Sgshapiro**
116864562Sgshapiro**	Append a message to the standard error for the convenience of
116964562Sgshapiro**	end-users debugging without access to the syslog messages.
117064562Sgshapiro**
117164562Sgshapiro**	Parameters:
117264562Sgshapiro**		i -- syslog log level
117364562Sgshapiro**		fmt -- string format
117464562Sgshapiro**
117564562Sgshapiro**	Returns:
117664562Sgshapiro**		nothing.
117764562Sgshapiro*/
117864562Sgshapiro
117964562Sgshapiro/*VARARGS2*/
118090792Sgshapirostatic SYSLOG_RET_T
118164562Sgshapiro#ifdef __STDC__
118264562Sgshapirodebuglog(int i, const char *fmt, ...)
118364562Sgshapiro#else /* __STDC__ */
118464562Sgshapirodebuglog(i, fmt, va_alist)
118564562Sgshapiro	int i;
118664562Sgshapiro	const char *fmt;
118764562Sgshapiro	va_dcl
118864562Sgshapiro#endif /* __STDC__ */
118964562Sgshapiro
119064562Sgshapiro{
119190792Sgshapiro	SM_VA_LOCAL_DECL
119264562Sgshapiro
119390792Sgshapiro	SM_VA_START(ap, fmt);
119490792Sgshapiro	sm_io_vfprintf(smioerr, SM_TIME_DEFAULT, fmt, ap);
119590792Sgshapiro	SM_VA_END(ap);
119690792Sgshapiro	SYSLOG_RET;
119764562Sgshapiro}
1198