vacation.c revision 110560
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
23110560SgshapiroSM_IDSTR(id, "@(#)$Id: vacation.c,v 8.137.2.2 2002/11/01 16:48:55 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{ \
11198121Sgshapiro	if (!initdb && !list) \
11290792Sgshapiro		eatmsg(); \
11390792Sgshapiro	exit(excode); \
11490792Sgshapiro}
11590792Sgshapiro
11664562Sgshapiroint
11764562Sgshapiromain(argc, argv)
11864562Sgshapiro	int argc;
11964562Sgshapiro	char **argv;
12064562Sgshapiro{
12198121Sgshapiro	bool alwaysrespond = false;
12298121Sgshapiro	bool initdb, exclude;
12390792Sgshapiro	bool runasuser = false;
12498121Sgshapiro	bool list = false;
12564562Sgshapiro	int mfail = 0, ufail = 0;
12664562Sgshapiro	int ch;
12764562Sgshapiro	int result;
12866494Sgshapiro	long sff;
12964562Sgshapiro	time_t interval;
13064562Sgshapiro	struct passwd *pw;
13164562Sgshapiro	ALIAS *cur;
13277349Sgshapiro	char *dbfilename = NULL;
13377349Sgshapiro	char *msgfilename = NULL;
13490792Sgshapiro	char *cfpath = NULL;
13564562Sgshapiro	char *name;
13690792Sgshapiro	char *returnaddr = NULL;
13764562Sgshapiro	SMDB_USER_INFO user_info;
13864562Sgshapiro	static char rnamebuf[MAXNAME];
13964562Sgshapiro	extern int optind, opterr;
14064562Sgshapiro	extern char *optarg;
14164562Sgshapiro	extern void usage __P((void));
14264562Sgshapiro	extern void setinterval __P((time_t));
14398121Sgshapiro	extern int readheaders __P((bool));
14464562Sgshapiro	extern bool recent __P((void));
14564562Sgshapiro	extern void setreply __P((char *, time_t));
14690792Sgshapiro	extern void sendmessage __P((char *, char *, char *));
14790792Sgshapiro	extern void xclude __P((SM_FILE_T *));
14864562Sgshapiro
14964562Sgshapiro	/* Vars needed to link with smutil */
15064562Sgshapiro	clrbitmap(DontBlameSendmail);
15164562Sgshapiro	RunAsUid = RealUid = getuid();
15264562Sgshapiro	RunAsGid = RealGid = getgid();
15364562Sgshapiro	pw = getpwuid(RealUid);
15464562Sgshapiro	if (pw != NULL)
15564562Sgshapiro	{
15664562Sgshapiro		if (strlen(pw->pw_name) > MAXNAME - 1)
15764562Sgshapiro			pw->pw_name[MAXNAME] = '\0';
15890792Sgshapiro		sm_snprintf(rnamebuf, sizeof rnamebuf, "%s", pw->pw_name);
15964562Sgshapiro	}
16064562Sgshapiro	else
16190792Sgshapiro		sm_snprintf(rnamebuf, sizeof rnamebuf,
16290792Sgshapiro			    "Unknown UID %d", (int) RealUid);
16364562Sgshapiro	RunAsUserName = RealUserName = rnamebuf;
16464562Sgshapiro
16577349Sgshapiro# ifdef LOG_MAIL
16664562Sgshapiro	openlog("vacation", LOG_PID, LOG_MAIL);
16777349Sgshapiro# else /* LOG_MAIL */
16864562Sgshapiro	openlog("vacation", LOG_PID);
16977349Sgshapiro# endif /* LOG_MAIL */
17064562Sgshapiro
17164562Sgshapiro	opterr = 0;
17298121Sgshapiro	initdb = false;
17390792Sgshapiro	exclude = false;
17464562Sgshapiro	interval = INTERVAL_UNDEF;
17564562Sgshapiro	*From = '\0';
17664562Sgshapiro
17764562Sgshapiro
17898121Sgshapiro#define OPTIONS	"a:C:df:Iijlm:R:r:s:t:Uxz"
17990792Sgshapiro
18064562Sgshapiro	while (mfail == 0 && ufail == 0 &&
18164562Sgshapiro	       (ch = getopt(argc, argv, OPTIONS)) != -1)
18264562Sgshapiro	{
18364562Sgshapiro		switch((char)ch)
18464562Sgshapiro		{
18564562Sgshapiro		  case 'a':			/* alias */
18690792Sgshapiro			cur = (ALIAS *) malloc((unsigned int) sizeof(ALIAS));
18764562Sgshapiro			if (cur == NULL)
18864562Sgshapiro			{
18964562Sgshapiro				mfail++;
19064562Sgshapiro				break;
19164562Sgshapiro			}
19264562Sgshapiro			cur->name = optarg;
19364562Sgshapiro			cur->next = Names;
19464562Sgshapiro			Names = cur;
19564562Sgshapiro			break;
19664562Sgshapiro
19790792Sgshapiro		  case 'C':
19890792Sgshapiro			cfpath = optarg;
19990792Sgshapiro			break;
20090792Sgshapiro
20177349Sgshapiro		  case 'd':			/* debug mode */
20290792Sgshapiro			msglog = debuglog;
20364562Sgshapiro			break;
20464562Sgshapiro
20564562Sgshapiro		  case 'f':		/* alternate database */
20664562Sgshapiro			dbfilename = optarg;
20764562Sgshapiro			break;
20864562Sgshapiro
20964562Sgshapiro		  case 'I':			/* backward compatible */
21064562Sgshapiro		  case 'i':			/* init the database */
21198121Sgshapiro			initdb = true;
21264562Sgshapiro			break;
21364562Sgshapiro
21498121Sgshapiro#if _FFR_RESPOND_ALL
21598121Sgshapiro		  case 'j':
21698121Sgshapiro			alwaysrespond = true;
21798121Sgshapiro			break;
21898121Sgshapiro#endif /* _FFR_RESPOND_ALL */
21998121Sgshapiro
22064562Sgshapiro		  case 'l':
22198121Sgshapiro			list = true;		/* list the database */
22264562Sgshapiro			break;
22364562Sgshapiro
22464562Sgshapiro		  case 'm':		/* alternate message file */
22564562Sgshapiro			msgfilename = optarg;
22664562Sgshapiro			break;
22764562Sgshapiro
22890792Sgshapiro#if _FFR_RETURN_ADDR
22990792Sgshapiro		  case 'R':
23090792Sgshapiro			returnaddr = optarg;
23190792Sgshapiro			break;
23290792Sgshapiro#endif /* _FFR_RETURN_ADDR */
23390792Sgshapiro
23464562Sgshapiro		  case 'r':
23564562Sgshapiro			if (isascii(*optarg) && isdigit(*optarg))
23664562Sgshapiro			{
23764562Sgshapiro				interval = atol(optarg) * SECSPERDAY;
23864562Sgshapiro				if (interval < 0)
23964562Sgshapiro					ufail++;
24064562Sgshapiro			}
24164562Sgshapiro			else
24264562Sgshapiro				interval = ONLY_ONCE;
24364562Sgshapiro			break;
24464562Sgshapiro
24564562Sgshapiro		  case 's':		/* alternate sender name */
24690792Sgshapiro			(void) sm_strlcpy(From, optarg, sizeof From);
24764562Sgshapiro			break;
24864562Sgshapiro
24964562Sgshapiro		  case 't':		/* SunOS: -t1d (default expire) */
25064562Sgshapiro			break;
25164562Sgshapiro
25277349Sgshapiro		  case 'U':		/* run as single user mode */
25390792Sgshapiro			runasuser = true;
25477349Sgshapiro			break;
25577349Sgshapiro
25664562Sgshapiro		  case 'x':
25790792Sgshapiro			exclude = true;
25864562Sgshapiro			break;
25964562Sgshapiro
26064562Sgshapiro		  case 'z':
26190792Sgshapiro			returnaddr = "<>";
26264562Sgshapiro			break;
26364562Sgshapiro
26464562Sgshapiro		  case '?':
26564562Sgshapiro		  default:
26664562Sgshapiro			ufail++;
26764562Sgshapiro			break;
26864562Sgshapiro		}
26964562Sgshapiro	}
27064562Sgshapiro	argc -= optind;
27164562Sgshapiro	argv += optind;
27264562Sgshapiro
27364562Sgshapiro	if (mfail != 0)
27464562Sgshapiro	{
27564562Sgshapiro		msglog(LOG_NOTICE,
27664562Sgshapiro		       "vacation: can't allocate memory for alias.\n");
27766494Sgshapiro		EXITM(EX_TEMPFAIL);
27864562Sgshapiro	}
27964562Sgshapiro	if (ufail != 0)
28064562Sgshapiro		usage();
28164562Sgshapiro
28264562Sgshapiro	if (argc != 1)
28364562Sgshapiro	{
28498121Sgshapiro		if (!initdb && !list && !exclude)
28564562Sgshapiro			usage();
28664562Sgshapiro		if ((pw = getpwuid(getuid())) == NULL)
28764562Sgshapiro		{
28864562Sgshapiro			msglog(LOG_ERR,
28964562Sgshapiro			       "vacation: no such user uid %u.\n", getuid());
29066494Sgshapiro			EXITM(EX_NOUSER);
29164562Sgshapiro		}
29277349Sgshapiro		name = pw->pw_name;
29377349Sgshapiro		user_info.smdbu_id = pw->pw_uid;
29477349Sgshapiro		user_info.smdbu_group_id = pw->pw_gid;
29590792Sgshapiro		(void) sm_strlcpy(user_info.smdbu_name, pw->pw_name,
29690792Sgshapiro				  SMDB_MAX_USER_NAME_LEN);
29777349Sgshapiro		if (chdir(pw->pw_dir) != 0)
29877349Sgshapiro		{
29990792Sgshapiro			msglog(LOG_NOTICE,
30090792Sgshapiro			       "vacation: no such directory %s.\n",
30177349Sgshapiro			       pw->pw_dir);
30277349Sgshapiro			EXITM(EX_NOINPUT);
30377349Sgshapiro		}
30464562Sgshapiro	}
30577349Sgshapiro	else if (runasuser)
30677349Sgshapiro	{
30777349Sgshapiro		name = *argv;
30877349Sgshapiro		if (dbfilename == NULL || msgfilename == NULL)
30977349Sgshapiro		{
31077349Sgshapiro			msglog(LOG_NOTICE,
31177349Sgshapiro			       "vacation: -U requires setting both -f and -m\n");
31277349Sgshapiro			EXITM(EX_NOINPUT);
31377349Sgshapiro		}
31477349Sgshapiro		user_info.smdbu_id = pw->pw_uid;
31577349Sgshapiro		user_info.smdbu_group_id = pw->pw_gid;
31690792Sgshapiro		(void) sm_strlcpy(user_info.smdbu_name, pw->pw_name,
31777349Sgshapiro			       SMDB_MAX_USER_NAME_LEN);
31877349Sgshapiro	}
31977349Sgshapiro	else
32064562Sgshapiro	{
32190792Sgshapiro		int err;
32290792Sgshapiro		SM_CF_OPT_T mbdbname;
32390792Sgshapiro		SM_MBDB_T user;
32490792Sgshapiro
32594334Sgshapiro		cfpath = getcfname(0, 0, SM_GET_SENDMAIL_CF, cfpath);
32690792Sgshapiro		mbdbname.opt_name = "MailboxDatabase";
32790792Sgshapiro		mbdbname.opt_val = "pw";
32890792Sgshapiro		(void) sm_cf_getopt(cfpath, 1, &mbdbname);
32990792Sgshapiro		err = sm_mbdb_initialize(mbdbname.opt_val);
33090792Sgshapiro		if (err != EX_OK)
33177349Sgshapiro		{
33290792Sgshapiro			msglog(LOG_ERR,
33390792Sgshapiro			       "vacation: can't open mailbox database: %s.\n",
33490792Sgshapiro			       sm_strexit(err));
33590792Sgshapiro			EXITM(err);
33690792Sgshapiro		}
33790792Sgshapiro		err = sm_mbdb_lookup(*argv, &user);
33890792Sgshapiro		if (err == EX_NOUSER)
33990792Sgshapiro		{
34090792Sgshapiro			msglog(LOG_ERR, "vacation: no such user %s.\n", *argv);
34190792Sgshapiro			EXITM(EX_NOUSER);
34290792Sgshapiro		}
34390792Sgshapiro		if (err != EX_OK)
34490792Sgshapiro		{
34590792Sgshapiro			msglog(LOG_ERR,
34690792Sgshapiro			       "vacation: can't read mailbox database: %s.\n",
34790792Sgshapiro			       sm_strexit(err));
34890792Sgshapiro			EXITM(err);
34990792Sgshapiro		}
35090792Sgshapiro		name = user.mbdb_name;
35190792Sgshapiro		if (chdir(user.mbdb_homedir) != 0)
35290792Sgshapiro		{
35390792Sgshapiro			msglog(LOG_NOTICE,
35490792Sgshapiro			       "vacation: no such directory %s.\n",
35590792Sgshapiro			       user.mbdb_homedir);
35677349Sgshapiro			EXITM(EX_NOINPUT);
35777349Sgshapiro		}
35890792Sgshapiro		user_info.smdbu_id = user.mbdb_uid;
35990792Sgshapiro		user_info.smdbu_group_id = user.mbdb_gid;
36090792Sgshapiro		(void) sm_strlcpy(user_info.smdbu_name, user.mbdb_name,
36177349Sgshapiro			       SMDB_MAX_USER_NAME_LEN);
36264562Sgshapiro	}
36364562Sgshapiro
36477349Sgshapiro	if (dbfilename == NULL)
36577349Sgshapiro		dbfilename = VDB;
36677349Sgshapiro	if (msgfilename == NULL)
36777349Sgshapiro		msgfilename = VMSG;
36877349Sgshapiro
36966494Sgshapiro	sff = SFF_CREAT;
37066494Sgshapiro	if (getegid() != getgid())
37177349Sgshapiro	{
37290792Sgshapiro		/* Allow a set-group-ID vacation binary */
37366494Sgshapiro		RunAsGid = user_info.smdbu_group_id = getegid();
37490792Sgshapiro		sff |= SFF_OPENASROOT;
37577349Sgshapiro	}
37694334Sgshapiro	if (getuid() == 0)
37794334Sgshapiro	{
37894334Sgshapiro		/* Allow root to initialize user's vacation databases */
37994334Sgshapiro		sff |= SFF_OPENASROOT|SFF_ROOTOK;
38066494Sgshapiro
38194334Sgshapiro		/* ... safely */
38294334Sgshapiro		sff |= SFF_NOSLINK|SFF_NOHLINK|SFF_REGONLY;
38394334Sgshapiro	}
38494334Sgshapiro
38594334Sgshapiro
38664562Sgshapiro	result = smdb_open_database(&Db, dbfilename,
38798121Sgshapiro				    O_CREAT|O_RDWR | (initdb ? O_TRUNC : 0),
38866494Sgshapiro				    S_IRUSR|S_IWUSR, sff,
38964562Sgshapiro				    SMDB_TYPE_DEFAULT, &user_info, NULL);
39064562Sgshapiro	if (result != SMDBE_OK)
39164562Sgshapiro	{
39264562Sgshapiro		msglog(LOG_NOTICE, "vacation: %s: %s\n", dbfilename,
39390792Sgshapiro		       sm_errstring(result));
39466494Sgshapiro		EXITM(EX_DATAERR);
39564562Sgshapiro	}
39664562Sgshapiro
39798121Sgshapiro	if (list)
39864562Sgshapiro	{
39964562Sgshapiro		listdb();
40071345Sgshapiro		(void) Db->smdb_close(Db);
40164562Sgshapiro		exit(EX_OK);
40264562Sgshapiro	}
40364562Sgshapiro
40464562Sgshapiro	if (interval != INTERVAL_UNDEF)
40564562Sgshapiro		setinterval(interval);
40664562Sgshapiro
40798121Sgshapiro	if (initdb && !exclude)
40864562Sgshapiro	{
40971345Sgshapiro		(void) Db->smdb_close(Db);
41071345Sgshapiro		exit(EX_OK);
41164562Sgshapiro	}
41264562Sgshapiro
41364562Sgshapiro	if (exclude)
41464562Sgshapiro	{
41590792Sgshapiro		xclude(smioin);
41671345Sgshapiro		(void) Db->smdb_close(Db);
41766494Sgshapiro		EXITM(EX_OK);
41864562Sgshapiro	}
41964562Sgshapiro
42090792Sgshapiro	if ((cur = (ALIAS *) malloc((unsigned int) sizeof(ALIAS))) == NULL)
42164562Sgshapiro	{
42264562Sgshapiro		msglog(LOG_NOTICE,
42364562Sgshapiro		       "vacation: can't allocate memory for username.\n");
42471345Sgshapiro		(void) Db->smdb_close(Db);
42566494Sgshapiro		EXITM(EX_OSERR);
42664562Sgshapiro	}
42764562Sgshapiro	cur->name = name;
42864562Sgshapiro	cur->next = Names;
42964562Sgshapiro	Names = cur;
43064562Sgshapiro
43198121Sgshapiro	result = readheaders(alwaysrespond);
43271345Sgshapiro	if (result == EX_OK && !recent())
43364562Sgshapiro	{
43464562Sgshapiro		time_t now;
43564562Sgshapiro
43664562Sgshapiro		(void) time(&now);
43764562Sgshapiro		setreply(From, now);
43871345Sgshapiro		(void) Db->smdb_close(Db);
43990792Sgshapiro		sendmessage(name, msgfilename, returnaddr);
44064562Sgshapiro	}
44164562Sgshapiro	else
44271345Sgshapiro		(void) Db->smdb_close(Db);
44371345Sgshapiro	if (result == EX_NOUSER)
44471345Sgshapiro		result = EX_OK;
44571345Sgshapiro	exit(result);
44664562Sgshapiro}
44764562Sgshapiro
44864562Sgshapiro/*
44966494Sgshapiro** EATMSG -- read stdin till EOF
45066494Sgshapiro**
45166494Sgshapiro**	Parameters:
45266494Sgshapiro**		none.
45366494Sgshapiro**
45466494Sgshapiro**	Returns:
45566494Sgshapiro**		nothing.
45666494Sgshapiro**
45766494Sgshapiro*/
45877349Sgshapiro
45966494Sgshapirostatic void
46066494Sgshapiroeatmsg()
46166494Sgshapiro{
46266494Sgshapiro	/*
46366494Sgshapiro	**  read the rest of the e-mail and ignore it to avoid problems
46466494Sgshapiro	**  with EPIPE in sendmail
46566494Sgshapiro	*/
46666494Sgshapiro	while (getc(stdin) != EOF)
46766494Sgshapiro		continue;
46866494Sgshapiro}
46966494Sgshapiro
47066494Sgshapiro/*
47164562Sgshapiro** READHEADERS -- read mail headers
47264562Sgshapiro**
47364562Sgshapiro**	Parameters:
47498121Sgshapiro**		alwaysrespond -- respond regardless of whether msg is to me
47564562Sgshapiro**
47664562Sgshapiro**	Returns:
47771345Sgshapiro**		a exit code: NOUSER if no reply, OK if reply, * if error
47864562Sgshapiro**
47966494Sgshapiro**	Side Effects:
48066494Sgshapiro**		may exit().
48166494Sgshapiro**
48264562Sgshapiro*/
48371345Sgshapiro
48471345Sgshapiroint
48598121Sgshapiroreadheaders(alwaysrespond)
48698121Sgshapiro	bool alwaysrespond;
48764562Sgshapiro{
48864562Sgshapiro	bool tome, cont;
48964562Sgshapiro	register char *p;
49064562Sgshapiro	register ALIAS *cur;
49164562Sgshapiro	char buf[MAXLINE];
49264562Sgshapiro	extern bool junkmail __P((char *));
49364562Sgshapiro	extern bool nsearch __P((char *, char *));
49464562Sgshapiro
49598121Sgshapiro	cont = false;
49698121Sgshapiro	tome = alwaysrespond;
49790792Sgshapiro	while (sm_io_fgets(smioin, SM_TIME_DEFAULT, buf, sizeof(buf)) &&
49890792Sgshapiro	       *buf != '\n')
49964562Sgshapiro	{
50064562Sgshapiro		switch(*buf)
50164562Sgshapiro		{
50264562Sgshapiro		  case 'F':		/* "From " */
50390792Sgshapiro			cont = false;
50464562Sgshapiro			if (strncmp(buf, "From ", 5) == 0)
50564562Sgshapiro			{
50690792Sgshapiro				bool quoted = false;
50764562Sgshapiro
50864562Sgshapiro				p = buf + 5;
50964562Sgshapiro				while (*p != '\0')
51064562Sgshapiro				{
51164562Sgshapiro					/* escaped character */
51264562Sgshapiro					if (*p == '\\')
51364562Sgshapiro					{
51464562Sgshapiro						p++;
51564562Sgshapiro						if (*p == '\0')
51664562Sgshapiro						{
51764562Sgshapiro							msglog(LOG_NOTICE,
51864562Sgshapiro							       "vacation: badly formatted \"From \" line.\n");
51966494Sgshapiro							EXITIT(EX_DATAERR);
52064562Sgshapiro						}
52164562Sgshapiro					}
52264562Sgshapiro					else if (*p == '"')
52364562Sgshapiro						quoted = !quoted;
52464562Sgshapiro					else if (*p == '\r' || *p == '\n')
52564562Sgshapiro						break;
52664562Sgshapiro					else if (*p == ' ' && !quoted)
52764562Sgshapiro						break;
52864562Sgshapiro					p++;
52964562Sgshapiro				}
53064562Sgshapiro				if (quoted)
53164562Sgshapiro				{
53264562Sgshapiro					msglog(LOG_NOTICE,
53364562Sgshapiro					       "vacation: badly formatted \"From \" line.\n");
53466494Sgshapiro					EXITIT(EX_DATAERR);
53564562Sgshapiro				}
53664562Sgshapiro				*p = '\0';
53764562Sgshapiro
53864562Sgshapiro				/* ok since both strings have MAXLINE length */
53964562Sgshapiro				if (*From == '\0')
54090792Sgshapiro					(void) sm_strlcpy(From, buf + 5,
54190792Sgshapiro							  sizeof From);
54264562Sgshapiro				if ((p = strchr(buf + 5, '\n')) != NULL)
54364562Sgshapiro					*p = '\0';
54464562Sgshapiro				if (junkmail(buf + 5))
54571345Sgshapiro					EXITIT(EX_NOUSER);
54664562Sgshapiro			}
54764562Sgshapiro			break;
54864562Sgshapiro
54964562Sgshapiro		  case 'P':		/* "Precedence:" */
55064562Sgshapiro		  case 'p':
55190792Sgshapiro			cont = false;
55264562Sgshapiro			if (strlen(buf) <= 10 ||
55364562Sgshapiro			    strncasecmp(buf, "Precedence", 10) != 0 ||
55464562Sgshapiro			    (buf[10] != ':' && buf[10] != ' ' &&
55564562Sgshapiro			     buf[10] != '\t'))
55664562Sgshapiro				break;
55764562Sgshapiro			if ((p = strchr(buf, ':')) == NULL)
55864562Sgshapiro				break;
55964562Sgshapiro			while (*++p != '\0' && isascii(*p) && isspace(*p));
56064562Sgshapiro			if (*p == '\0')
56164562Sgshapiro				break;
56264562Sgshapiro			if (strncasecmp(p, "junk", 4) == 0 ||
56364562Sgshapiro			    strncasecmp(p, "bulk", 4) == 0 ||
56464562Sgshapiro			    strncasecmp(p, "list", 4) == 0)
56571345Sgshapiro				EXITIT(EX_NOUSER);
56664562Sgshapiro			break;
56764562Sgshapiro
56864562Sgshapiro		  case 'C':		/* "Cc:" */
56964562Sgshapiro		  case 'c':
57064562Sgshapiro			if (strncasecmp(buf, "Cc:", 3) != 0)
57164562Sgshapiro				break;
57290792Sgshapiro			cont = true;
57364562Sgshapiro			goto findme;
57464562Sgshapiro
57564562Sgshapiro		  case 'T':		/* "To:" */
57664562Sgshapiro		  case 't':
57764562Sgshapiro			if (strncasecmp(buf, "To:", 3) != 0)
57864562Sgshapiro				break;
57990792Sgshapiro			cont = true;
58064562Sgshapiro			goto findme;
58164562Sgshapiro
58264562Sgshapiro		  default:
58364562Sgshapiro			if (!isascii(*buf) || !isspace(*buf) || !cont || tome)
58464562Sgshapiro			{
58590792Sgshapiro				cont = false;
58664562Sgshapiro				break;
58764562Sgshapiro			}
58864562Sgshapirofindme:
58964562Sgshapiro			for (cur = Names;
59064562Sgshapiro			     !tome && cur != NULL;
59164562Sgshapiro			     cur = cur->next)
59264562Sgshapiro				tome = nsearch(cur->name, buf);
59364562Sgshapiro		}
59464562Sgshapiro	}
59564562Sgshapiro	if (!tome)
59671345Sgshapiro		EXITIT(EX_NOUSER);
59764562Sgshapiro	if (*From == '\0')
59864562Sgshapiro	{
59964562Sgshapiro		msglog(LOG_NOTICE, "vacation: no initial \"From \" line.\n");
60066494Sgshapiro		EXITIT(EX_DATAERR);
60164562Sgshapiro	}
60271345Sgshapiro	EXITIT(EX_OK);
60364562Sgshapiro}
60464562Sgshapiro
60564562Sgshapiro/*
60664562Sgshapiro** NSEARCH --
60764562Sgshapiro**	do a nice, slow, search of a string for a substring.
60864562Sgshapiro**
60964562Sgshapiro**	Parameters:
61064562Sgshapiro**		name -- name to search.
61164562Sgshapiro**		str -- string in which to search.
61264562Sgshapiro**
61364562Sgshapiro**	Returns:
61464562Sgshapiro**		is name a substring of str?
61564562Sgshapiro**
61664562Sgshapiro*/
61777349Sgshapiro
61864562Sgshapirobool
61964562Sgshapironsearch(name, str)
62064562Sgshapiro	register char *name, *str;
62164562Sgshapiro{
62264562Sgshapiro	register size_t len;
62364562Sgshapiro	register char *s;
62464562Sgshapiro
62564562Sgshapiro	len = strlen(name);
62664562Sgshapiro
62764562Sgshapiro	for (s = str; *s != '\0'; ++s)
62864562Sgshapiro	{
62964562Sgshapiro		/*
63064562Sgshapiro		**  Check to make sure that the string matches and
63164562Sgshapiro		**  the previous character is not an alphanumeric and
63264562Sgshapiro		**  the next character after the match is not an alphanumeric.
63364562Sgshapiro		**
63464562Sgshapiro		**  This prevents matching "eric" to "derick" while still
63564562Sgshapiro		**  matching "eric" to "<eric+detail>".
63664562Sgshapiro		*/
63764562Sgshapiro
63864562Sgshapiro		if (tolower(*s) == tolower(*name) &&
63964562Sgshapiro		    strncasecmp(name, s, len) == 0 &&
64064562Sgshapiro		    (s == str || !isascii(*(s - 1)) || !isalnum(*(s - 1))) &&
64164562Sgshapiro		    (!isascii(*(s + len)) || !isalnum(*(s + len))))
64290792Sgshapiro			return true;
64364562Sgshapiro	}
64490792Sgshapiro	return false;
64564562Sgshapiro}
64664562Sgshapiro
64764562Sgshapiro/*
64864562Sgshapiro** JUNKMAIL --
64964562Sgshapiro**	read the header and return if automagic/junk/bulk/list mail
65064562Sgshapiro**
65164562Sgshapiro**	Parameters:
65264562Sgshapiro**		from -- sender address.
65364562Sgshapiro**
65464562Sgshapiro**	Returns:
65564562Sgshapiro**		is this some automated/junk/bulk/list mail?
65664562Sgshapiro**
65764562Sgshapiro*/
65871345Sgshapiro
65971345Sgshapirostruct ignore
66071345Sgshapiro{
66171345Sgshapiro	char	*name;
66271345Sgshapiro	size_t	len;
66371345Sgshapiro};
66471345Sgshapiro
66571345Sgshapirotypedef struct ignore IGNORE_T;
66671345Sgshapiro
66771345Sgshapiro#define MAX_USER_LEN 256	/* maximum length of local part (sender) */
66871345Sgshapiro
66971345Sgshapiro/* delimiters for the local part of an address */
67071345Sgshapiro#define isdelim(c)	((c) == '%' || (c) == '@' || (c) == '+')
67171345Sgshapiro
67264562Sgshapirobool
67364562Sgshapirojunkmail(from)
67464562Sgshapiro	char *from;
67564562Sgshapiro{
67671345Sgshapiro	bool quot;
67771345Sgshapiro	char *e;
67871345Sgshapiro	size_t len;
67971345Sgshapiro	IGNORE_T *cur;
68071345Sgshapiro	char sender[MAX_USER_LEN];
68171345Sgshapiro	static IGNORE_T ignore[] =
68264562Sgshapiro	{
68364562Sgshapiro		{ "postmaster",		10	},
68464562Sgshapiro		{ "uucp",		4	},
68564562Sgshapiro		{ "mailer-daemon",	13	},
68664562Sgshapiro		{ "mailer",		6	},
68771345Sgshapiro		{ NULL,			0	}
68871345Sgshapiro	};
68971345Sgshapiro
69071345Sgshapiro	static IGNORE_T ignorepost[] =
69171345Sgshapiro	{
69271345Sgshapiro		{ "-request",		8	},
69364562Sgshapiro		{ "-relay",		6	},
69471345Sgshapiro		{ "-owner",		6	},
69564562Sgshapiro		{ NULL,			0	}
69664562Sgshapiro	};
69764562Sgshapiro
69871345Sgshapiro	static IGNORE_T ignorepre[] =
69971345Sgshapiro	{
70071345Sgshapiro		{ "owner-",		6	},
70171345Sgshapiro		{ NULL,			0	}
70271345Sgshapiro	};
70371345Sgshapiro
70464562Sgshapiro	/*
70571345Sgshapiro	**  This is mildly amusing, and I'm not positive it's right; trying
70671345Sgshapiro	**  to find the "real" name of the sender, assuming that addresses
70771345Sgshapiro	**  will be some variant of:
70871345Sgshapiro	**
70971345Sgshapiro	**  From site!site!SENDER%site.domain%site.domain@site.domain
71071345Sgshapiro	*/
71171345Sgshapiro
71290792Sgshapiro	quot = false;
71371345Sgshapiro	e = from;
71471345Sgshapiro	len = 0;
71571345Sgshapiro	while (*e != '\0' && (quot || !isdelim(*e)))
71664562Sgshapiro	{
71771345Sgshapiro		if (*e == '"')
71871345Sgshapiro		{
71971345Sgshapiro			quot = !quot;
72071345Sgshapiro			++e;
72171345Sgshapiro			continue;
72271345Sgshapiro		}
72371345Sgshapiro		if (*e == '\\')
72471345Sgshapiro		{
72571345Sgshapiro			if (*(++e) == '\0')
72671345Sgshapiro			{
72771345Sgshapiro				/* '\\' at end of string? */
72871345Sgshapiro				break;
72971345Sgshapiro			}
73071345Sgshapiro			if (len < MAX_USER_LEN)
73171345Sgshapiro				sender[len++] = *e;
73271345Sgshapiro			++e;
73371345Sgshapiro			continue;
73471345Sgshapiro		}
73571345Sgshapiro		if (*e == '!' && !quot)
73671345Sgshapiro		{
73771345Sgshapiro			len = 0;
73871345Sgshapiro			sender[len] = '\0';
73971345Sgshapiro		}
74064562Sgshapiro		else
74171345Sgshapiro			if (len < MAX_USER_LEN)
74271345Sgshapiro				sender[len++] = *e;
74371345Sgshapiro		++e;
74464562Sgshapiro	}
74571345Sgshapiro	if (len < MAX_USER_LEN)
74671345Sgshapiro		sender[len] = '\0';
74771345Sgshapiro	else
74871345Sgshapiro		sender[MAX_USER_LEN - 1] = '\0';
74971345Sgshapiro
75071345Sgshapiro	if (len <= 0)
75190792Sgshapiro		return false;
75271345Sgshapiro#if 0
75371345Sgshapiro	if (quot)
75490792Sgshapiro		return false;	/* syntax error... */
75571345Sgshapiro#endif /* 0 */
75671345Sgshapiro
75771345Sgshapiro	/* test prefixes */
75871345Sgshapiro	for (cur = ignorepre; cur->name != NULL; ++cur)
75971345Sgshapiro	{
76071345Sgshapiro		if (len >= cur->len &&
76171345Sgshapiro		    strncasecmp(cur->name, sender, cur->len) == 0)
76290792Sgshapiro			return true;
76371345Sgshapiro	}
76471345Sgshapiro
76571345Sgshapiro	/*
76671345Sgshapiro	**  If the name is truncated, don't test the rest.
76771345Sgshapiro	**	We could extract the "tail" of the sender address and
76871345Sgshapiro	**	compare it it ignorepost, however, it seems not worth
76971345Sgshapiro	**	the effort.
77071345Sgshapiro	**	The address surely can't match any entry in ignore[]
77171345Sgshapiro	**	(as long as all of them are shorter than MAX_USER_LEN).
77271345Sgshapiro	*/
77371345Sgshapiro
77471345Sgshapiro	if (len > MAX_USER_LEN)
77590792Sgshapiro		return false;
77671345Sgshapiro
77771345Sgshapiro	/* test full local parts */
77864562Sgshapiro	for (cur = ignore; cur->name != NULL; ++cur)
77964562Sgshapiro	{
78071345Sgshapiro		if (len == cur->len &&
78171345Sgshapiro		    strncasecmp(cur->name, sender, cur->len) == 0)
78290792Sgshapiro			return true;
78371345Sgshapiro	}
78471345Sgshapiro
78571345Sgshapiro	/* test postfixes */
78671345Sgshapiro	for (cur = ignorepost; cur->name != NULL; ++cur)
78771345Sgshapiro	{
78864562Sgshapiro		if (len >= cur->len &&
78971345Sgshapiro		    strncasecmp(cur->name, e - cur->len - 1,
79071345Sgshapiro				cur->len) == 0)
79190792Sgshapiro			return true;
79264562Sgshapiro	}
79390792Sgshapiro	return false;
79464562Sgshapiro}
79564562Sgshapiro
79664562Sgshapiro#define	VIT	"__VACATION__INTERVAL__TIMER__"
79764562Sgshapiro
79864562Sgshapiro/*
79964562Sgshapiro** RECENT --
80064562Sgshapiro**	find out if user has gotten a vacation message recently.
80164562Sgshapiro**
80264562Sgshapiro**	Parameters:
80364562Sgshapiro**		none.
80464562Sgshapiro**
80564562Sgshapiro**	Returns:
80690792Sgshapiro**		true iff user has gotten a vacation message recently.
80764562Sgshapiro**
80864562Sgshapiro*/
80977349Sgshapiro
81064562Sgshapirobool
81164562Sgshapirorecent()
81264562Sgshapiro{
81364562Sgshapiro	SMDB_DBENT key, data;
81464562Sgshapiro	time_t then, next;
81590792Sgshapiro	bool trydomain = false;
81664562Sgshapiro	int st;
81764562Sgshapiro	char *domain;
81864562Sgshapiro
81964562Sgshapiro	memset(&key, '\0', sizeof key);
82064562Sgshapiro	memset(&data, '\0', sizeof data);
82164562Sgshapiro
82264562Sgshapiro	/* get interval time */
82371345Sgshapiro	key.data = VIT;
82471345Sgshapiro	key.size = sizeof(VIT);
82564562Sgshapiro
82664562Sgshapiro	st = Db->smdb_get(Db, &key, &data, 0);
82764562Sgshapiro	if (st != SMDBE_OK)
82864562Sgshapiro		next = SECSPERDAY * DAYSPERWEEK;
82964562Sgshapiro	else
83071345Sgshapiro		memmove(&next, data.data, sizeof(next));
83164562Sgshapiro
83264562Sgshapiro	memset(&data, '\0', sizeof data);
83364562Sgshapiro
83464562Sgshapiro	/* get record for this address */
83571345Sgshapiro	key.data = From;
83671345Sgshapiro	key.size = strlen(From);
83764562Sgshapiro
83864562Sgshapiro	do
83964562Sgshapiro	{
84064562Sgshapiro		st = Db->smdb_get(Db, &key, &data, 0);
84164562Sgshapiro		if (st == SMDBE_OK)
84264562Sgshapiro		{
84371345Sgshapiro			memmove(&then, data.data, sizeof(then));
84464562Sgshapiro			if (next == ONLY_ONCE || then == ONLY_ONCE ||
84564562Sgshapiro			    then + next > time(NULL))
84690792Sgshapiro				return true;
84764562Sgshapiro		}
84864562Sgshapiro		if ((trydomain = !trydomain) &&
84964562Sgshapiro		    (domain = strchr(From, '@')) != NULL)
85064562Sgshapiro		{
85171345Sgshapiro			key.data = domain;
85271345Sgshapiro			key.size = strlen(domain);
85364562Sgshapiro		}
85464562Sgshapiro	} while (trydomain);
85590792Sgshapiro	return false;
85664562Sgshapiro}
85764562Sgshapiro
85864562Sgshapiro/*
85964562Sgshapiro** SETINTERVAL --
86064562Sgshapiro**	store the reply interval
86164562Sgshapiro**
86264562Sgshapiro**	Parameters:
86364562Sgshapiro**		interval -- time interval for replies.
86464562Sgshapiro**
86564562Sgshapiro**	Returns:
86664562Sgshapiro**		nothing.
86764562Sgshapiro**
86864562Sgshapiro**	Side Effects:
86964562Sgshapiro**		stores the reply interval in database.
87064562Sgshapiro*/
87177349Sgshapiro
87264562Sgshapirovoid
87364562Sgshapirosetinterval(interval)
87464562Sgshapiro	time_t interval;
87564562Sgshapiro{
87664562Sgshapiro	SMDB_DBENT key, data;
87764562Sgshapiro
87864562Sgshapiro	memset(&key, '\0', sizeof key);
87964562Sgshapiro	memset(&data, '\0', sizeof data);
88064562Sgshapiro
88171345Sgshapiro	key.data = VIT;
88271345Sgshapiro	key.size = sizeof(VIT);
88371345Sgshapiro	data.data = (char*) &interval;
88471345Sgshapiro	data.size = sizeof(interval);
88571345Sgshapiro	(void) (Db->smdb_put)(Db, &key, &data, 0);
88664562Sgshapiro}
88764562Sgshapiro
88864562Sgshapiro/*
88964562Sgshapiro** SETREPLY --
89064562Sgshapiro**	store that this user knows about the vacation.
89164562Sgshapiro**
89264562Sgshapiro**	Parameters:
89364562Sgshapiro**		from -- sender address.
89464562Sgshapiro**		when -- last reply time.
89564562Sgshapiro**
89664562Sgshapiro**	Returns:
89764562Sgshapiro**		nothing.
89864562Sgshapiro**
89964562Sgshapiro**	Side Effects:
90064562Sgshapiro**		stores user/time in database.
90164562Sgshapiro*/
90277349Sgshapiro
90364562Sgshapirovoid
90464562Sgshapirosetreply(from, when)
90564562Sgshapiro	char *from;
90664562Sgshapiro	time_t when;
90764562Sgshapiro{
90864562Sgshapiro	SMDB_DBENT key, data;
90964562Sgshapiro
91064562Sgshapiro	memset(&key, '\0', sizeof key);
91164562Sgshapiro	memset(&data, '\0', sizeof data);
91264562Sgshapiro
91371345Sgshapiro	key.data = from;
91471345Sgshapiro	key.size = strlen(from);
91571345Sgshapiro	data.data = (char*) &when;
91671345Sgshapiro	data.size = sizeof(when);
91771345Sgshapiro	(void) (Db->smdb_put)(Db, &key, &data, 0);
91864562Sgshapiro}
91964562Sgshapiro
92064562Sgshapiro/*
92164562Sgshapiro** XCLUDE --
92264562Sgshapiro**	add users to vacation db so they don't get a reply.
92364562Sgshapiro**
92464562Sgshapiro**	Parameters:
92564562Sgshapiro**		f -- file pointer with list of address to exclude
92664562Sgshapiro**
92764562Sgshapiro**	Returns:
92864562Sgshapiro**		nothing.
92964562Sgshapiro**
93064562Sgshapiro**	Side Effects:
93164562Sgshapiro**		stores users in database.
93264562Sgshapiro*/
93377349Sgshapiro
93464562Sgshapirovoid
93564562Sgshapiroxclude(f)
93690792Sgshapiro	SM_FILE_T *f;
93764562Sgshapiro{
93864562Sgshapiro	char buf[MAXLINE], *p;
93964562Sgshapiro
94064562Sgshapiro	if (f == NULL)
94164562Sgshapiro		return;
94290792Sgshapiro	while (sm_io_fgets(f, SM_TIME_DEFAULT, buf, sizeof buf))
94364562Sgshapiro	{
94464562Sgshapiro		if ((p = strchr(buf, '\n')) != NULL)
94564562Sgshapiro			*p = '\0';
94664562Sgshapiro		setreply(buf, ONLY_ONCE);
94764562Sgshapiro	}
94864562Sgshapiro}
94964562Sgshapiro
95064562Sgshapiro/*
95164562Sgshapiro** SENDMESSAGE --
95264562Sgshapiro**	exec sendmail to send the vacation file to sender
95364562Sgshapiro**
95464562Sgshapiro**	Parameters:
95564562Sgshapiro**		myname -- user name.
95664562Sgshapiro**		msgfn -- name of file with vacation message.
95790792Sgshapiro**		sender -- use as sender address
95864562Sgshapiro**
95964562Sgshapiro**	Returns:
96064562Sgshapiro**		nothing.
96164562Sgshapiro**
96264562Sgshapiro**	Side Effects:
96364562Sgshapiro**		sends vacation reply.
96464562Sgshapiro*/
96577349Sgshapiro
96664562Sgshapirovoid
96790792Sgshapirosendmessage(myname, msgfn, sender)
96864562Sgshapiro	char *myname;
96964562Sgshapiro	char *msgfn;
97090792Sgshapiro	char *sender;
97164562Sgshapiro{
97290792Sgshapiro	SM_FILE_T *mfp, *sfp;
97364562Sgshapiro	int i;
97464562Sgshapiro	int pvect[2];
97577349Sgshapiro	char *pv[8];
97664562Sgshapiro	char buf[MAXLINE];
97764562Sgshapiro
97890792Sgshapiro	mfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, msgfn, SM_IO_RDONLY, NULL);
97964562Sgshapiro	if (mfp == NULL)
98064562Sgshapiro	{
98164562Sgshapiro		if (msgfn[0] == '/')
98264562Sgshapiro			msglog(LOG_NOTICE, "vacation: no %s file.\n", msgfn);
98364562Sgshapiro		else
98464562Sgshapiro			msglog(LOG_NOTICE, "vacation: no ~%s/%s file.\n",
98564562Sgshapiro			       myname, msgfn);
98664562Sgshapiro		exit(EX_NOINPUT);
98764562Sgshapiro	}
98864562Sgshapiro	if (pipe(pvect) < 0)
98964562Sgshapiro	{
99090792Sgshapiro		msglog(LOG_ERR, "vacation: pipe: %s", sm_errstring(errno));
99164562Sgshapiro		exit(EX_OSERR);
99264562Sgshapiro	}
99377349Sgshapiro	pv[0] = "sendmail";
99477349Sgshapiro	pv[1] = "-oi";
99577349Sgshapiro	pv[2] = "-f";
99690792Sgshapiro	if (sender != NULL)
99790792Sgshapiro		pv[3] = sender;
99877349Sgshapiro	else
99977349Sgshapiro		pv[3] = myname;
100077349Sgshapiro	pv[4] = "--";
100177349Sgshapiro	pv[5] = From;
100277349Sgshapiro	pv[6] = NULL;
100364562Sgshapiro	i = fork();
100464562Sgshapiro	if (i < 0)
100564562Sgshapiro	{
100690792Sgshapiro		msglog(LOG_ERR, "vacation: fork: %s", sm_errstring(errno));
100764562Sgshapiro		exit(EX_OSERR);
100864562Sgshapiro	}
100964562Sgshapiro	if (i == 0)
101064562Sgshapiro	{
101164562Sgshapiro		(void) dup2(pvect[0], 0);
101264562Sgshapiro		(void) close(pvect[0]);
101364562Sgshapiro		(void) close(pvect[1]);
101490792Sgshapiro		(void) sm_io_close(mfp, SM_TIME_DEFAULT);
101577349Sgshapiro		(void) execv(_PATH_SENDMAIL, pv);
101664562Sgshapiro		msglog(LOG_ERR, "vacation: can't exec %s: %s",
101790792Sgshapiro			_PATH_SENDMAIL, sm_errstring(errno));
101864562Sgshapiro		exit(EX_UNAVAILABLE);
101964562Sgshapiro	}
102064562Sgshapiro	/* check return status of the following calls? XXX */
102164562Sgshapiro	(void) close(pvect[0]);
102290792Sgshapiro	if ((sfp = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT,
102390792Sgshapiro			      (void *) &(pvect[1]),
102490792Sgshapiro			      SM_IO_WRONLY, NULL)) != NULL)
102564562Sgshapiro	{
102690792Sgshapiro		(void) sm_io_fprintf(sfp, SM_TIME_DEFAULT, "To: %s\n", From);
102790792Sgshapiro		(void) sm_io_fprintf(sfp, SM_TIME_DEFAULT,
102890792Sgshapiro				     "Auto-Submitted: auto-replied\n");
102990792Sgshapiro		while (sm_io_fgets(mfp, SM_TIME_DEFAULT, buf, sizeof buf))
103090792Sgshapiro			(void) sm_io_fputs(sfp, SM_TIME_DEFAULT, buf);
103190792Sgshapiro		(void) sm_io_close(mfp, SM_TIME_DEFAULT);
103290792Sgshapiro		(void) sm_io_close(sfp, SM_TIME_DEFAULT);
103364562Sgshapiro	}
103464562Sgshapiro	else
103564562Sgshapiro	{
103690792Sgshapiro		(void) sm_io_close(mfp, SM_TIME_DEFAULT);
103764562Sgshapiro		msglog(LOG_ERR, "vacation: can't open pipe to sendmail");
103864562Sgshapiro		exit(EX_UNAVAILABLE);
103964562Sgshapiro	}
104064562Sgshapiro}
104164562Sgshapiro
104264562Sgshapirovoid
104364562Sgshapirousage()
104464562Sgshapiro{
104598121Sgshapiro	char *retusage = "";
104698121Sgshapiro	char *respusage = "";
104790792Sgshapiro
104890792Sgshapiro#if _FFR_RETURN_ADDR
104990792Sgshapiro	retusage = "[-R returnaddr] ";
105090792Sgshapiro#endif /* _FFR_RETURN_ADDR */
105190792Sgshapiro
105298121Sgshapiro#if _FFR_RESPOND_ALL
105398121Sgshapiro	respusage = "[-j] ";
105498121Sgshapiro#endif /* _FFR_RESPOND_ALL */
105598121Sgshapiro
105677349Sgshapiro	msglog(LOG_NOTICE,
105798121Sgshapiro	       "uid %u: usage: vacation [-a alias] [-C cfpath] [-d] [-f db] [-i] %s[-l] [-m msg] %s[-r interval] [-s sender] [-t time] [-U] [-x] [-z] login\n",
105898121Sgshapiro	       getuid(), respusage, retusage);
105964562Sgshapiro	exit(EX_USAGE);
106064562Sgshapiro}
106164562Sgshapiro
106264562Sgshapiro/*
106364562Sgshapiro** LISTDB -- list the contents of the vacation database
106464562Sgshapiro**
106564562Sgshapiro**	Parameters:
106664562Sgshapiro**		none.
106764562Sgshapiro**
106864562Sgshapiro**	Returns:
106964562Sgshapiro**		nothing.
107064562Sgshapiro*/
107164562Sgshapiro
107264562Sgshapirostatic void
107364562Sgshapirolistdb()
107464562Sgshapiro{
107564562Sgshapiro	int result;
107664562Sgshapiro	time_t t;
107764562Sgshapiro	SMDB_CURSOR *cursor = NULL;
107864562Sgshapiro	SMDB_DBENT db_key, db_value;
107964562Sgshapiro
108064562Sgshapiro	memset(&db_key, '\0', sizeof db_key);
108164562Sgshapiro	memset(&db_value, '\0', sizeof db_value);
108264562Sgshapiro
108364562Sgshapiro	result = Db->smdb_cursor(Db, &cursor, 0);
108464562Sgshapiro	if (result != SMDBE_OK)
108564562Sgshapiro	{
108690792Sgshapiro		sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
108790792Sgshapiro			      "vacation: set cursor: %s\n",
108890792Sgshapiro			      sm_errstring(result));
108964562Sgshapiro		return;
109064562Sgshapiro	}
109164562Sgshapiro
109264562Sgshapiro	while ((result = cursor->smdbc_get(cursor, &db_key, &db_value,
109364562Sgshapiro					   SMDB_CURSOR_GET_NEXT)) == SMDBE_OK)
109464562Sgshapiro	{
109598121Sgshapiro		char *timestamp;
109698121Sgshapiro
109764562Sgshapiro		/* skip magic VIT entry */
1098110560Sgshapiro		if (db_key.size == strlen(VIT) + 1 &&
109971345Sgshapiro		    strncmp((char *)db_key.data, VIT,
110071345Sgshapiro			    (int)db_key.size - 1) == 0)
110164562Sgshapiro			continue;
110264562Sgshapiro
110364562Sgshapiro		/* skip bogus values */
110471345Sgshapiro		if (db_value.size != sizeof t)
110564562Sgshapiro		{
110690792Sgshapiro			sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
110790792Sgshapiro				      "vacation: %.*s invalid time stamp\n",
110890792Sgshapiro				      (int) db_key.size, (char *) db_key.data);
110964562Sgshapiro			continue;
111064562Sgshapiro		}
111164562Sgshapiro
111271345Sgshapiro		memcpy(&t, db_value.data, sizeof t);
111364562Sgshapiro
111471345Sgshapiro		if (db_key.size > 40)
111571345Sgshapiro			db_key.size = 40;
111664562Sgshapiro
111798121Sgshapiro		if (t <= 0)
111898121Sgshapiro		{
111998121Sgshapiro			/* must be an exclude */
112098121Sgshapiro			timestamp = "(exclusion)\n";
112198121Sgshapiro		}
112298121Sgshapiro		else
112398121Sgshapiro		{
112498121Sgshapiro			timestamp = ctime(&t);
112598121Sgshapiro		}
112690792Sgshapiro		sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%-40.*s %-10s",
112790792Sgshapiro			      (int) db_key.size, (char *) db_key.data,
112898121Sgshapiro			      timestamp);
112964562Sgshapiro
113064562Sgshapiro		memset(&db_key, '\0', sizeof db_key);
113164562Sgshapiro		memset(&db_value, '\0', sizeof db_value);
113264562Sgshapiro	}
113364562Sgshapiro
113464562Sgshapiro	if (result != SMDBE_OK && result != SMDBE_LAST_ENTRY)
113564562Sgshapiro	{
113690792Sgshapiro		sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
113790792Sgshapiro			      "vacation: get value at cursor: %s\n",
113890792Sgshapiro			      sm_errstring(result));
113964562Sgshapiro		if (cursor != NULL)
114064562Sgshapiro		{
114164562Sgshapiro			(void) cursor->smdbc_close(cursor);
114264562Sgshapiro			cursor = NULL;
114364562Sgshapiro		}
114464562Sgshapiro		return;
114564562Sgshapiro	}
114664562Sgshapiro	(void) cursor->smdbc_close(cursor);
114764562Sgshapiro	cursor = NULL;
114864562Sgshapiro}
114964562Sgshapiro
115064562Sgshapiro/*
115164562Sgshapiro** DEBUGLOG -- write message to standard error
115264562Sgshapiro**
115364562Sgshapiro**	Append a message to the standard error for the convenience of
115464562Sgshapiro**	end-users debugging without access to the syslog messages.
115564562Sgshapiro**
115664562Sgshapiro**	Parameters:
115764562Sgshapiro**		i -- syslog log level
115864562Sgshapiro**		fmt -- string format
115964562Sgshapiro**
116064562Sgshapiro**	Returns:
116164562Sgshapiro**		nothing.
116264562Sgshapiro*/
116364562Sgshapiro
116464562Sgshapiro/*VARARGS2*/
116590792Sgshapirostatic SYSLOG_RET_T
116664562Sgshapiro#ifdef __STDC__
116764562Sgshapirodebuglog(int i, const char *fmt, ...)
116864562Sgshapiro#else /* __STDC__ */
116964562Sgshapirodebuglog(i, fmt, va_alist)
117064562Sgshapiro	int i;
117164562Sgshapiro	const char *fmt;
117264562Sgshapiro	va_dcl
117364562Sgshapiro#endif /* __STDC__ */
117464562Sgshapiro
117564562Sgshapiro{
117690792Sgshapiro	SM_VA_LOCAL_DECL
117764562Sgshapiro
117890792Sgshapiro	SM_VA_START(ap, fmt);
117990792Sgshapiro	sm_io_vfprintf(smioerr, SM_TIME_DEFAULT, fmt, ap);
118090792Sgshapiro	SM_VA_END(ap);
118190792Sgshapiro	SYSLOG_RET;
118264562Sgshapiro}
1183