xref: /csrg-svn/usr.sbin/sendmail/src/readcf.c (revision 68490)
122709Sdist /*
234921Sbostic  * Copyright (c) 1983 Eric P. Allman
362530Sbostic  * Copyright (c) 1988, 1993
462530Sbostic  *	The Regents of the University of California.  All rights reserved.
533731Sbostic  *
642829Sbostic  * %sccs.include.redist.c%
733731Sbostic  */
822709Sdist 
922709Sdist #ifndef lint
10*68490Seric static char sccsid[] = "@(#)readcf.c	8.72 (Berkeley) 03/05/95";
1133731Sbostic #endif /* not lint */
1222709Sdist 
133313Seric # include "sendmail.h"
1464133Seric # include <pwd.h>
1564133Seric # include <grp.h>
1666334Seric #if NAMED_BIND
1757207Seric # include <resolv.h>
1857207Seric #endif
193308Seric 
203308Seric /*
213308Seric **  READCF -- read control file.
223308Seric **
233308Seric **	This routine reads the control file and builds the internal
243308Seric **	form.
253308Seric **
264432Seric **	The file is formatted as a sequence of lines, each taken
274432Seric **	atomically.  The first character of each line describes how
284432Seric **	the line is to be interpreted.  The lines are:
294432Seric **		Dxval		Define macro x to have value val.
304432Seric **		Cxword		Put word into class x.
314432Seric **		Fxfile [fmt]	Read file for lines to put into
324432Seric **				class x.  Use scanf string 'fmt'
334432Seric **				or "%s" if not present.  Fmt should
344432Seric **				only produce one string-valued result.
354432Seric **		Hname: value	Define header with field-name 'name'
364432Seric **				and value as specified; this will be
374432Seric **				macro expanded immediately before
384432Seric **				use.
394432Seric **		Sn		Use rewriting set n.
404432Seric **		Rlhs rhs	Rewrite addresses that match lhs to
414432Seric **				be rhs.
4224944Seric **		Mn arg=val...	Define mailer.  n is the internal name.
4324944Seric **				Args specify mailer parameters.
448252Seric **		Oxvalue		Set option x to value.
458252Seric **		Pname=value	Set precedence name to value.
4664718Seric **		Vversioncode[/vendorcode]
4764718Seric **				Version level/vendor name of
4864718Seric **				configuration syntax.
4953654Seric **		Kmapname mapclass arguments....
5053654Seric **				Define keyed lookup of a given class.
5153654Seric **				Arguments are class dependent.
524432Seric **
533308Seric **	Parameters:
543308Seric **		cfname -- control file name.
5554973Seric **		safe -- TRUE if this is the system config file;
5654973Seric **			FALSE otherwise.
5755012Seric **		e -- the main envelope.
583308Seric **
593308Seric **	Returns:
603308Seric **		none.
613308Seric **
623308Seric **	Side Effects:
633308Seric **		Builds several internal tables.
643308Seric */
653308Seric 
6655012Seric readcf(cfname, safe, e)
673308Seric 	char *cfname;
6854973Seric 	bool safe;
6955012Seric 	register ENVELOPE *e;
703308Seric {
713308Seric 	FILE *cf;
728547Seric 	int ruleset = 0;
7368481Seric 	int nextruleset = MAXRWSETS;
748547Seric 	char *q;
759350Seric 	struct rewrite *rwp = NULL;
7657135Seric 	char *bp;
7764718Seric 	auto char *ep;
7857589Seric 	int nfuzzy;
7964133Seric 	char *file;
8064133Seric 	bool optional;
8168481Seric 	int mid;
823308Seric 	char buf[MAXLINE];
833308Seric 	register char *p;
843308Seric 	extern char **copyplist();
8552647Seric 	struct stat statb;
865909Seric 	char exbuf[MAXLINE];
8765066Seric 	char pvpbuf[MAXLINE + MAXATOM];
8868481Seric 	static char *null_list[1] = { NULL };
8910709Seric 	extern char *munchstring();
9053654Seric 	extern void makemapentry();
913308Seric 
9252647Seric 	FileName = cfname;
9352647Seric 	LineNumber = 0;
9452647Seric 
953308Seric 	cf = fopen(cfname, "r");
963308Seric 	if (cf == NULL)
973308Seric 	{
9852647Seric 		syserr("cannot open");
993308Seric 		exit(EX_OSFILE);
1003308Seric 	}
1013308Seric 
10252647Seric 	if (fstat(fileno(cf), &statb) < 0)
10352647Seric 	{
10452647Seric 		syserr("cannot fstat");
10552647Seric 		exit(EX_OSFILE);
10652647Seric 	}
10752647Seric 
10852647Seric 	if (!S_ISREG(statb.st_mode))
10952647Seric 	{
11052647Seric 		syserr("not a plain file");
11152647Seric 		exit(EX_OSFILE);
11252647Seric 	}
11352647Seric 
11452647Seric 	if (OpMode != MD_TEST && bitset(S_IWGRP|S_IWOTH, statb.st_mode))
11552647Seric 	{
11653037Seric 		if (OpMode == MD_DAEMON || OpMode == MD_FREEZE)
11753037Seric 			fprintf(stderr, "%s: WARNING: dangerous write permissions\n",
11853037Seric 				FileName);
11953037Seric #ifdef LOG
12053037Seric 		if (LogLevel > 0)
12153037Seric 			syslog(LOG_CRIT, "%s: WARNING: dangerous write permissions",
12253037Seric 				FileName);
12353037Seric #endif
12452647Seric 	}
12552647Seric 
12659254Seric #ifdef XLA
12759254Seric 	xla_zero();
12859254Seric #endif
12959254Seric 
13057135Seric 	while ((bp = fgetfolded(buf, sizeof buf, cf)) != NULL)
1313308Seric 	{
13257135Seric 		if (bp[0] == '#')
13357135Seric 		{
13457135Seric 			if (bp != buf)
13557135Seric 				free(bp);
13652637Seric 			continue;
13757135Seric 		}
13852637Seric 
13968481Seric 		/* do macro expansion mappings */
14057135Seric 		for (p = bp; *p != '\0'; p++)
14116157Seric 		{
14257135Seric 			if (*p == '#' && p > bp && ConfigLevel >= 3)
14352647Seric 			{
14452647Seric 				/* this is an on-line comment */
14552647Seric 				register char *e;
14652647Seric 
14758050Seric 				switch (*--p & 0377)
14852647Seric 				{
14958050Seric 				  case MACROEXPAND:
15052647Seric 					/* it's from $# -- let it go through */
15152647Seric 					p++;
15252647Seric 					break;
15352647Seric 
15452647Seric 				  case '\\':
15552647Seric 					/* it's backslash escaped */
15652647Seric 					(void) strcpy(p, p + 1);
15752647Seric 					break;
15852647Seric 
15952647Seric 				  default:
16052647Seric 					/* delete preceeding white space */
16158050Seric 					while (isascii(*p) && isspace(*p) && p > bp)
16252647Seric 						p--;
16356795Seric 					if ((e = strchr(++p, '\n')) != NULL)
16452647Seric 						(void) strcpy(p, e);
16552647Seric 					else
16652647Seric 						p[0] = p[1] = '\0';
16752647Seric 					break;
16852647Seric 				}
16952647Seric 				continue;
17052647Seric 			}
17152647Seric 
17268481Seric 			if (*p != '$' || p[1] == '\0')
17316157Seric 				continue;
17416157Seric 
17516157Seric 			if (p[1] == '$')
17616157Seric 			{
17716157Seric 				/* actual dollar sign.... */
17823111Seric 				(void) strcpy(p, p + 1);
17916157Seric 				continue;
18016157Seric 			}
18116157Seric 
18216157Seric 			/* convert to macro expansion character */
18368481Seric 			*p++ = MACROEXPAND;
18468481Seric 
18568481Seric 			/* convert macro name to code */
18668481Seric 			*p = macid(p, &ep);
18768481Seric 			if (ep != p)
18868481Seric 				strcpy(p + 1, ep);
18916157Seric 		}
19016157Seric 
19116157Seric 		/* interpret this line */
19264718Seric 		errno = 0;
19357135Seric 		switch (bp[0])
1943308Seric 		{
1953308Seric 		  case '\0':
1963308Seric 		  case '#':		/* comment */
1973308Seric 			break;
1983308Seric 
1993308Seric 		  case 'R':		/* rewriting rule */
20057135Seric 			for (p = &bp[1]; *p != '\0' && *p != '\t'; p++)
2013308Seric 				continue;
2023308Seric 
2033308Seric 			if (*p == '\0')
2045909Seric 			{
20565821Seric 				syserr("invalid rewrite line \"%s\" (tab expected)", bp);
2065909Seric 				break;
2075909Seric 			}
2085909Seric 
2095909Seric 			/* allocate space for the rule header */
2105909Seric 			if (rwp == NULL)
2115909Seric 			{
2125909Seric 				RewriteRules[ruleset] = rwp =
2135909Seric 					(struct rewrite *) xalloc(sizeof *rwp);
2145909Seric 			}
2153308Seric 			else
2163308Seric 			{
2175909Seric 				rwp->r_next = (struct rewrite *) xalloc(sizeof *rwp);
2185909Seric 				rwp = rwp->r_next;
2195909Seric 			}
2205909Seric 			rwp->r_next = NULL;
2213308Seric 
2225909Seric 			/* expand and save the LHS */
2235909Seric 			*p = '\0';
22457135Seric 			expand(&bp[1], exbuf, &exbuf[sizeof exbuf], e);
22565066Seric 			rwp->r_lhs = prescan(exbuf, '\t', pvpbuf,
22665066Seric 					     sizeof pvpbuf, NULL);
22757589Seric 			nfuzzy = 0;
2285909Seric 			if (rwp->r_lhs != NULL)
22957589Seric 			{
23057589Seric 				register char **ap;
23157589Seric 
2325909Seric 				rwp->r_lhs = copyplist(rwp->r_lhs, TRUE);
23357589Seric 
23457589Seric 				/* count the number of fuzzy matches in LHS */
23557589Seric 				for (ap = rwp->r_lhs; *ap != NULL; ap++)
23657589Seric 				{
23758148Seric 					char *botch;
23858148Seric 
23958148Seric 					botch = NULL;
24058050Seric 					switch (**ap & 0377)
24157589Seric 					{
24257589Seric 					  case MATCHZANY:
24357589Seric 					  case MATCHANY:
24457589Seric 					  case MATCHONE:
24557589Seric 					  case MATCHCLASS:
24657589Seric 					  case MATCHNCLASS:
24757589Seric 						nfuzzy++;
24858148Seric 						break;
24958148Seric 
25058148Seric 					  case MATCHREPL:
25158148Seric 						botch = "$0-$9";
25258148Seric 						break;
25358148Seric 
25458148Seric 					  case CANONNET:
25558148Seric 						botch = "$#";
25658148Seric 						break;
25758148Seric 
25858148Seric 					  case CANONUSER:
25958148Seric 						botch = "$:";
26058148Seric 						break;
26158148Seric 
26258148Seric 					  case CALLSUBR:
26358148Seric 						botch = "$>";
26458148Seric 						break;
26558148Seric 
26658148Seric 					  case CONDIF:
26758148Seric 						botch = "$?";
26858148Seric 						break;
26958148Seric 
27058148Seric 					  case CONDELSE:
27158148Seric 						botch = "$|";
27258148Seric 						break;
27358148Seric 
27458148Seric 					  case CONDFI:
27558148Seric 						botch = "$.";
27658148Seric 						break;
27758148Seric 
27858148Seric 					  case HOSTBEGIN:
27958148Seric 						botch = "$[";
28058148Seric 						break;
28158148Seric 
28258148Seric 					  case HOSTEND:
28358148Seric 						botch = "$]";
28458148Seric 						break;
28558148Seric 
28658148Seric 					  case LOOKUPBEGIN:
28758148Seric 						botch = "$(";
28858148Seric 						break;
28958148Seric 
29058148Seric 					  case LOOKUPEND:
29158148Seric 						botch = "$)";
29258148Seric 						break;
29357589Seric 					}
29458148Seric 					if (botch != NULL)
29558148Seric 						syserr("Inappropriate use of %s on LHS",
29658148Seric 							botch);
29757589Seric 				}
29857589Seric 			}
29956678Seric 			else
30068481Seric 			{
30156678Seric 				syserr("R line: null LHS");
30268481Seric 				rwp->r_lhs = null_list;
30368481Seric 			}
3045909Seric 
3055909Seric 			/* expand and save the RHS */
3065909Seric 			while (*++p == '\t')
3075909Seric 				continue;
3087231Seric 			q = p;
3097231Seric 			while (*p != '\0' && *p != '\t')
3107231Seric 				p++;
3117231Seric 			*p = '\0';
31255012Seric 			expand(q, exbuf, &exbuf[sizeof exbuf], e);
31365066Seric 			rwp->r_rhs = prescan(exbuf, '\t', pvpbuf,
31465066Seric 					     sizeof pvpbuf, NULL);
3155909Seric 			if (rwp->r_rhs != NULL)
31657589Seric 			{
31757589Seric 				register char **ap;
31857589Seric 
3195909Seric 				rwp->r_rhs = copyplist(rwp->r_rhs, TRUE);
32057589Seric 
32157589Seric 				/* check no out-of-bounds replacements */
32257589Seric 				nfuzzy += '0';
32357589Seric 				for (ap = rwp->r_rhs; *ap != NULL; ap++)
32457589Seric 				{
32558148Seric 					char *botch;
32658148Seric 
32758148Seric 					botch = NULL;
32858148Seric 					switch (**ap & 0377)
32957589Seric 					{
33058148Seric 					  case MATCHREPL:
33158148Seric 						if ((*ap)[1] <= '0' || (*ap)[1] > nfuzzy)
33258148Seric 						{
33358148Seric 							syserr("replacement $%c out of bounds",
33458148Seric 								(*ap)[1]);
33558148Seric 						}
33658148Seric 						break;
33758148Seric 
33858148Seric 					  case MATCHZANY:
33958148Seric 						botch = "$*";
34058148Seric 						break;
34158148Seric 
34258148Seric 					  case MATCHANY:
34358148Seric 						botch = "$+";
34458148Seric 						break;
34558148Seric 
34658148Seric 					  case MATCHONE:
34758148Seric 						botch = "$-";
34858148Seric 						break;
34958148Seric 
35058148Seric 					  case MATCHCLASS:
35158148Seric 						botch = "$=";
35258148Seric 						break;
35358148Seric 
35458148Seric 					  case MATCHNCLASS:
35558148Seric 						botch = "$~";
35658148Seric 						break;
35757589Seric 					}
35858148Seric 					if (botch != NULL)
35958148Seric 						syserr("Inappropriate use of %s on RHS",
36058148Seric 							botch);
36157589Seric 				}
36257589Seric 			}
36356678Seric 			else
36468481Seric 			{
36556678Seric 				syserr("R line: null RHS");
36668481Seric 				rwp->r_rhs = null_list;
36768481Seric 			}
3683308Seric 			break;
3693308Seric 
3704072Seric 		  case 'S':		/* select rewriting set */
37164440Seric 			for (p = &bp[1]; isascii(*p) && isspace(*p); p++)
37264440Seric 				continue;
37368481Seric 			if (!isascii(*p))
37464440Seric 			{
37564440Seric 				syserr("invalid argument to S line: \"%.20s\"",
37664440Seric 					&bp[1]);
37764440Seric 				break;
37864440Seric 			}
37968481Seric 			if (isdigit(*p))
3808056Seric 			{
38168481Seric 				ruleset = atoi(p);
38268481Seric 				if (ruleset >= MAXRWSETS / 2 || ruleset < 0)
38368481Seric 				{
38468481Seric 					syserr("bad ruleset %d (%d max)",
38568481Seric 						ruleset, MAXRWSETS / 2);
38668481Seric 					ruleset = 0;
38768481Seric 				}
3888056Seric 			}
38968481Seric 			else
39068481Seric 			{
39168481Seric 				STAB *s;
39268481Seric 				char delim;
39368481Seric 
39468481Seric 				q = p;
39568481Seric 				while (*p != '\0' && isascii(*p) &&
39668481Seric 				       (isalnum(*p) || strchr("-_$", *p) != NULL))
39768481Seric 					p++;
39868481Seric 				while (isascii(*p) && isspace(*p))
39968481Seric 					*p++ = '\0';
40068481Seric 				delim = *p;
40168481Seric 				if (delim != '\0')
40268481Seric 					*p++ = '\0';
40368481Seric 				s = stab(q, ST_RULESET, ST_ENTER);
40468481Seric 				if (s->s_ruleset != 0)
40568481Seric 					ruleset = s->s_ruleset;
40668481Seric 				else if (delim == '=')
40768481Seric 				{
40868481Seric 					ruleset = atoi(p);
40968481Seric 					if (ruleset >= MAXRWSETS / 2 || ruleset < 0)
41068481Seric 					{
41168481Seric 						syserr("bad ruleset %s = %d (%d max)",
41268481Seric 							q, ruleset, MAXRWSETS / 2);
41368481Seric 						ruleset = 0;
41468481Seric 					}
41568481Seric 				}
41668481Seric 				else if ((ruleset = --nextruleset) < MAXRWSETS / 2)
41768481Seric 				{
41868481Seric 					syserr("%s: too many named rulesets (%d max)",
41968481Seric 						q, MAXRWSETS / 2);
42068481Seric 					ruleset = 0;
42168481Seric 				}
42268481Seric 				s->s_ruleset = ruleset;
42368481Seric 			}
4244072Seric 			rwp = NULL;
4254072Seric 			break;
4264072Seric 
4273308Seric 		  case 'D':		/* macro definition */
42868481Seric 			mid = macid(&bp[1], &ep);
42968481Seric 			p = munchstring(ep, NULL);
43068481Seric 			define(mid, newstr(p), e);
4313308Seric 			break;
4323308Seric 
4333387Seric 		  case 'H':		/* required header line */
43457135Seric 			(void) chompheader(&bp[1], TRUE, e);
4353387Seric 			break;
4363387Seric 
4374061Seric 		  case 'C':		/* word class */
43868481Seric 		  case 'T':		/* trusted user (set class `t') */
43968481Seric 			if (bp[0] == 'C')
4404061Seric 			{
44168481Seric 				mid = macid(&bp[1], &ep);
44268481Seric 				expand(ep, exbuf, &exbuf[sizeof exbuf], e);
44368481Seric 				p = exbuf;
44468481Seric 			}
44568481Seric 			else
44668481Seric 			{
44768481Seric 				mid = 't';
44868481Seric 				p = &bp[1];
44968481Seric 			}
45068481Seric 			while (*p != '\0')
45168481Seric 			{
4524061Seric 				register char *wd;
4534061Seric 				char delim;
4544061Seric 
45558050Seric 				while (*p != '\0' && isascii(*p) && isspace(*p))
4564061Seric 					p++;
4574061Seric 				wd = p;
45858050Seric 				while (*p != '\0' && !(isascii(*p) && isspace(*p)))
4594061Seric 					p++;
4604061Seric 				delim = *p;
4614061Seric 				*p = '\0';
4624061Seric 				if (wd[0] != '\0')
46368481Seric 					setclass(mid, wd);
4644061Seric 				*p = delim;
4654061Seric 			}
4664061Seric 			break;
4674061Seric 
46859272Seric 		  case 'F':		/* word class from file */
46968481Seric 			mid = macid(&bp[1], &ep);
47068481Seric 			for (p = ep; isascii(*p) && isspace(*p); )
47164133Seric 				p++;
47264133Seric 			if (p[0] == '-' && p[1] == 'o')
47364133Seric 			{
47464133Seric 				optional = TRUE;
47564133Seric 				while (*p != '\0' && !(isascii(*p) && isspace(*p)))
47664133Seric 					p++;
47764133Seric 				while (isascii(*p) && isspace(*p))
47868481Seric 					p++;
47964133Seric 			}
48064133Seric 			else
48164133Seric 				optional = FALSE;
48264133Seric 			file = p;
48364133Seric 			while (*p != '\0' && !(isascii(*p) && isspace(*p)))
48464133Seric 				p++;
48559272Seric 			if (*p == '\0')
48659272Seric 				p = "%s";
48759272Seric 			else
48859272Seric 			{
48959272Seric 				*p = '\0';
49059272Seric 				while (isascii(*++p) && isspace(*p))
49159272Seric 					continue;
49259272Seric 			}
49364133Seric 			fileclass(bp[1], file, p, safe, optional);
49459272Seric 			break;
49559272Seric 
49659156Seric #ifdef XLA
49759156Seric 		  case 'L':		/* extended load average description */
49859156Seric 			xla_init(&bp[1]);
49959156Seric 			break;
50059156Seric #endif
50159156Seric 
5024096Seric 		  case 'M':		/* define mailer */
50357135Seric 			makemailer(&bp[1]);
5044096Seric 			break;
5054096Seric 
5068252Seric 		  case 'O':		/* set option */
50758734Seric 			setoption(bp[1], &bp[2], safe, FALSE, e);
5088252Seric 			break;
5098252Seric 
5108252Seric 		  case 'P':		/* set precedence */
5118252Seric 			if (NumPriorities >= MAXPRIORITIES)
5128252Seric 			{
5138547Seric 				toomany('P', MAXPRIORITIES);
5148252Seric 				break;
5158252Seric 			}
51657135Seric 			for (p = &bp[1]; *p != '\0' && *p != '=' && *p != '\t'; p++)
5178252Seric 				continue;
5188252Seric 			if (*p == '\0')
5198252Seric 				goto badline;
5208252Seric 			*p = '\0';
52157135Seric 			Priorities[NumPriorities].pri_name = newstr(&bp[1]);
5228252Seric 			Priorities[NumPriorities].pri_val = atoi(++p);
5238252Seric 			NumPriorities++;
5248252Seric 			break;
5258252Seric 
52652645Seric 		  case 'V':		/* configuration syntax version */
52764440Seric 			for (p = &bp[1]; isascii(*p) && isspace(*p); p++)
52864440Seric 				continue;
52964440Seric 			if (!isascii(*p) || !isdigit(*p))
53064440Seric 			{
53164440Seric 				syserr("invalid argument to V line: \"%.20s\"",
53264440Seric 					&bp[1]);
53364440Seric 				break;
53464440Seric 			}
53564718Seric 			ConfigLevel = strtol(p, &ep, 10);
53664279Seric 			if (ConfigLevel >= 5)
53764279Seric 			{
53864279Seric 				/* level 5 configs have short name in $w */
53964279Seric 				p = macvalue('w', e);
54064279Seric 				if (p != NULL && (p = strchr(p, '.')) != NULL)
54164279Seric 					*p = '\0';
54264279Seric 			}
54364718Seric 			if (*ep++ == '/')
54464718Seric 			{
54564718Seric 				/* extract vendor code */
54664718Seric 				for (p = ep; isascii(*p) && isalpha(*p); )
54764718Seric 					p++;
54864718Seric 				*p = '\0';
54964718Seric 
55064718Seric 				if (!setvendor(ep))
55164718Seric 					syserr("invalid V line vendor code: \"%s\"",
55264718Seric 						ep);
55364718Seric 			}
55452645Seric 			break;
55552645Seric 
55653654Seric 		  case 'K':
55757135Seric 			makemapentry(&bp[1]);
55853654Seric 			break;
55953654Seric 
5603308Seric 		  default:
5614061Seric 		  badline:
56257135Seric 			syserr("unknown control line \"%s\"", bp);
5633308Seric 		}
56457135Seric 		if (bp != buf)
56557135Seric 			free(bp);
5663308Seric 	}
56752637Seric 	if (ferror(cf))
56852637Seric 	{
56952647Seric 		syserr("I/O read error", cfname);
57052637Seric 		exit(EX_OSFILE);
57152637Seric 	}
57252637Seric 	fclose(cf);
5739381Seric 	FileName = NULL;
57456836Seric 
57568481Seric 	/* initialize host maps from local service tables */
57668481Seric 	inithostmaps();
57768481Seric 
57868481Seric 	/* determine if we need to do special name-server frotz */
57967905Seric 	{
58068481Seric 		int nmaps;
58168481Seric 		char *maptype[MAXMAPSTACK];
58268481Seric 		short mapreturn[MAXMAPACTIONS];
58368481Seric 
58468481Seric 		nmaps = switch_map_find("hosts", maptype, mapreturn);
58568481Seric 		UseNameServer = FALSE;
58668481Seric 		if (nmaps > 0 && nmaps <= MAXMAPSTACK)
58768481Seric 		{
58868481Seric 			register int mapno;
58968481Seric 
59068481Seric 			for (mapno = 0; mapno < nmaps && !UseNameServer; mapno++)
59168481Seric 			{
59268481Seric 				if (strcmp(maptype[mapno], "dns") == 0)
59368481Seric 					UseNameServer = TRUE;
59468481Seric 			}
59568481Seric 		}
59668481Seric 
59768481Seric #ifdef HESIOD
59868481Seric 		nmaps = switch_map_find("passwd", maptype, mapreturn);
59968481Seric 		UseHesiod = FALSE;
60068481Seric 		if (nmaps > 0 && nmaps <= MAXMAPSTACK)
60168481Seric 		{
60268481Seric 			register int mapno;
60368481Seric 
60468481Seric 			for (mapno = 0; mapno < nmaps && !UseHesiod; mapno++)
60568481Seric 			{
60668481Seric 				if (strcmp(maptype[mapno], "hesiod") == 0)
60768481Seric 					UseHesiod = TRUE;
60868481Seric 			}
60968481Seric 		}
61068204Seric #endif
61167905Seric 	}
6124096Seric }
6134096Seric /*
6148547Seric **  TOOMANY -- signal too many of some option
6158547Seric **
6168547Seric **	Parameters:
6178547Seric **		id -- the id of the error line
6188547Seric **		maxcnt -- the maximum possible values
6198547Seric **
6208547Seric **	Returns:
6218547Seric **		none.
6228547Seric **
6238547Seric **	Side Effects:
6248547Seric **		gives a syserr.
6258547Seric */
6268547Seric 
6278547Seric toomany(id, maxcnt)
6288547Seric 	char id;
6298547Seric 	int maxcnt;
6308547Seric {
6319381Seric 	syserr("too many %c lines, %d max", id, maxcnt);
6328547Seric }
6338547Seric /*
6344432Seric **  FILECLASS -- read members of a class from a file
6354432Seric **
6364432Seric **	Parameters:
6374432Seric **		class -- class to define.
6384432Seric **		filename -- name of file to read.
6394432Seric **		fmt -- scanf string to use for match.
64064133Seric **		safe -- if set, this is a safe read.
64164133Seric **		optional -- if set, it is not an error for the file to
64264133Seric **			not exist.
6434432Seric **
6444432Seric **	Returns:
6454432Seric **		none
6464432Seric **
6474432Seric **	Side Effects:
6484432Seric **
6494432Seric **		puts all lines in filename that match a scanf into
6504432Seric **			the named class.
6514432Seric */
6524432Seric 
65364133Seric fileclass(class, filename, fmt, safe, optional)
6544432Seric 	int class;
6554432Seric 	char *filename;
6564432Seric 	char *fmt;
65754973Seric 	bool safe;
65864133Seric 	bool optional;
6594432Seric {
66025808Seric 	FILE *f;
66154973Seric 	struct stat stbuf;
6624432Seric 	char buf[MAXLINE];
6634432Seric 
66466101Seric 	if (tTd(37, 2))
66566101Seric 		printf("fileclass(%s, fmt=%s)\n", filename, fmt);
66666101Seric 
66766031Seric 	if (filename[0] == '|')
66866031Seric 	{
66966031Seric 		syserr("fileclass: pipes (F%c%s) not supported due to security problems",
67066031Seric 			class, filename);
67166031Seric 		return;
67266031Seric 	}
67354973Seric 	if (stat(filename, &stbuf) < 0)
67454973Seric 	{
67566101Seric 		if (tTd(37, 2))
67666101Seric 			printf("  cannot stat (%s)\n", errstring(errno));
67764133Seric 		if (!optional)
67864133Seric 			syserr("fileclass: cannot stat %s", filename);
67954973Seric 		return;
68054973Seric 	}
68154973Seric 	if (!S_ISREG(stbuf.st_mode))
68254973Seric 	{
68354973Seric 		syserr("fileclass: %s not a regular file", filename);
68454973Seric 		return;
68554973Seric 	}
68654973Seric 	if (!safe && access(filename, R_OK) < 0)
68754973Seric 	{
68854973Seric 		syserr("fileclass: access denied on %s", filename);
68954973Seric 		return;
69054973Seric 	}
69154973Seric 	f = fopen(filename, "r");
6924432Seric 	if (f == NULL)
6934432Seric 	{
69454973Seric 		syserr("fileclass: cannot open %s", filename);
6954432Seric 		return;
6964432Seric 	}
6974432Seric 
6984432Seric 	while (fgets(buf, sizeof buf, f) != NULL)
6994432Seric 	{
7004432Seric 		register STAB *s;
70125808Seric 		register char *p;
70225808Seric # ifdef SCANF
7034432Seric 		char wordbuf[MAXNAME+1];
7044432Seric 
7054432Seric 		if (sscanf(buf, fmt, wordbuf) != 1)
7064432Seric 			continue;
70725808Seric 		p = wordbuf;
70856795Seric # else /* SCANF */
70925808Seric 		p = buf;
71056795Seric # endif /* SCANF */
71125808Seric 
71225808Seric 		/*
71325808Seric 		**  Break up the match into words.
71425808Seric 		*/
71525808Seric 
71625808Seric 		while (*p != '\0')
71725808Seric 		{
71825808Seric 			register char *q;
71925808Seric 
72025808Seric 			/* strip leading spaces */
72158050Seric 			while (isascii(*p) && isspace(*p))
72225808Seric 				p++;
72325808Seric 			if (*p == '\0')
72425808Seric 				break;
72525808Seric 
72625808Seric 			/* find the end of the word */
72725808Seric 			q = p;
72858050Seric 			while (*p != '\0' && !(isascii(*p) && isspace(*p)))
72925808Seric 				p++;
73025808Seric 			if (*p != '\0')
73125808Seric 				*p++ = '\0';
73225808Seric 
73325808Seric 			/* enter the word in the symbol table */
73466101Seric 			setclass(class, q);
73525808Seric 		}
7364432Seric 	}
7374432Seric 
73854973Seric 	(void) fclose(f);
7394432Seric }
7404432Seric /*
7414096Seric **  MAKEMAILER -- define a new mailer.
7424096Seric **
7434096Seric **	Parameters:
74410327Seric **		line -- description of mailer.  This is in labeled
74510327Seric **			fields.  The fields are:
74668481Seric **			   A -- the argv for this mailer
74768481Seric **			   C -- the character set for MIME conversions
74868481Seric **			   D -- the directory to run in
74968481Seric **			   E -- the eol string
75068481Seric **			   F -- the flags associated with the mailer
75168481Seric **			   L -- the maximum line length
75268481Seric **			   M -- the maximum message size
75368479Seric **			   P -- the path to the mailer
75468481Seric **			   R -- the recipient rewriting set
75568479Seric **			   S -- the sender rewriting set
75668481Seric **			   T -- the mailer type (for DSNs)
75768481Seric **			   U -- the uid to run as
75810327Seric **			The first word is the canonical name of the mailer.
7594096Seric **
7604096Seric **	Returns:
7614096Seric **		none.
7624096Seric **
7634096Seric **	Side Effects:
7644096Seric **		enters the mailer into the mailer table.
7654096Seric */
7663308Seric 
76721066Seric makemailer(line)
7684096Seric 	char *line;
7694096Seric {
7704096Seric 	register char *p;
7718067Seric 	register struct mailer *m;
7728067Seric 	register STAB *s;
7738067Seric 	int i;
77410327Seric 	char fcode;
77558020Seric 	auto char *endp;
7764096Seric 	extern int NextMailer;
77710327Seric 	extern char **makeargv();
77810327Seric 	extern char *munchstring();
77910701Seric 	extern long atol();
7804096Seric 
78110327Seric 	/* allocate a mailer and set up defaults */
78210327Seric 	m = (struct mailer *) xalloc(sizeof *m);
78310327Seric 	bzero((char *) m, sizeof *m);
78410327Seric 	m->m_eol = "\n";
78568481Seric 	m->m_uid = m->m_gid = 0;
78610327Seric 
78710327Seric 	/* collect the mailer name */
78858050Seric 	for (p = line; *p != '\0' && *p != ',' && !(isascii(*p) && isspace(*p)); p++)
78910327Seric 		continue;
79010327Seric 	if (*p != '\0')
79110327Seric 		*p++ = '\0';
79210327Seric 	m->m_name = newstr(line);
79310327Seric 
79410327Seric 	/* now scan through and assign info from the fields */
79510327Seric 	while (*p != '\0')
79610327Seric 	{
79758333Seric 		auto char *delimptr;
79858333Seric 
79958050Seric 		while (*p != '\0' && (*p == ',' || (isascii(*p) && isspace(*p))))
80010327Seric 			p++;
80110327Seric 
80210327Seric 		/* p now points to field code */
80310327Seric 		fcode = *p;
80410327Seric 		while (*p != '\0' && *p != '=' && *p != ',')
80510327Seric 			p++;
80610327Seric 		if (*p++ != '=')
80710327Seric 		{
80852637Seric 			syserr("mailer %s: `=' expected", m->m_name);
80910327Seric 			return;
81010327Seric 		}
81158050Seric 		while (isascii(*p) && isspace(*p))
81210327Seric 			p++;
81310327Seric 
81410327Seric 		/* p now points to the field body */
81558333Seric 		p = munchstring(p, &delimptr);
81610327Seric 
81710327Seric 		/* install the field into the mailer struct */
81810327Seric 		switch (fcode)
81910327Seric 		{
82010327Seric 		  case 'P':		/* pathname */
82110327Seric 			m->m_mailer = newstr(p);
82210327Seric 			break;
82310327Seric 
82410327Seric 		  case 'F':		/* flags */
82510687Seric 			for (; *p != '\0'; p++)
82658050Seric 				if (!(isascii(*p) && isspace(*p)))
82752637Seric 					setbitn(*p, m->m_flags);
82810327Seric 			break;
82910327Seric 
83010327Seric 		  case 'S':		/* sender rewriting ruleset */
83110327Seric 		  case 'R':		/* recipient rewriting ruleset */
83258020Seric 			i = strtol(p, &endp, 10);
83310327Seric 			if (i < 0 || i >= MAXRWSETS)
83410327Seric 			{
83510327Seric 				syserr("invalid rewrite set, %d max", MAXRWSETS);
83610327Seric 				return;
83710327Seric 			}
83810327Seric 			if (fcode == 'S')
83958020Seric 				m->m_sh_rwset = m->m_se_rwset = i;
84010327Seric 			else
84158020Seric 				m->m_rh_rwset = m->m_re_rwset = i;
84258020Seric 
84358020Seric 			p = endp;
84459985Seric 			if (*p++ == '/')
84558020Seric 			{
84658020Seric 				i = strtol(p, NULL, 10);
84758020Seric 				if (i < 0 || i >= MAXRWSETS)
84858020Seric 				{
84958020Seric 					syserr("invalid rewrite set, %d max",
85058020Seric 						MAXRWSETS);
85158020Seric 					return;
85258020Seric 				}
85358020Seric 				if (fcode == 'S')
85458020Seric 					m->m_sh_rwset = i;
85558020Seric 				else
85658020Seric 					m->m_rh_rwset = i;
85758020Seric 			}
85810327Seric 			break;
85910327Seric 
86010327Seric 		  case 'E':		/* end of line string */
86110327Seric 			m->m_eol = newstr(p);
86210327Seric 			break;
86310327Seric 
86410327Seric 		  case 'A':		/* argument vector */
86510327Seric 			m->m_argv = makeargv(p);
86610327Seric 			break;
86710701Seric 
86810701Seric 		  case 'M':		/* maximum message size */
86910701Seric 			m->m_maxsize = atol(p);
87010701Seric 			break;
87152106Seric 
87252106Seric 		  case 'L':		/* maximum line length */
87352106Seric 			m->m_linelimit = atoi(p);
87452106Seric 			break;
87558935Seric 
87658935Seric 		  case 'D':		/* working directory */
87758935Seric 			m->m_execdir = newstr(p);
87858935Seric 			break;
87968481Seric 
88068481Seric 		  case 'C':		/* default charset */
88168481Seric 			m->m_defcharset = newstr(p);
88268481Seric 			break;
88368481Seric 
88468481Seric 		  case 'T':		/* MTA Type */
88568481Seric 			m->m_mtatype = newstr(p);
88668481Seric 			p = strchr(m->m_mtatype, '/');
88768481Seric 			if (p != NULL)
88868481Seric 			{
88968481Seric 				*p++ = '\0';
89068481Seric 				if (*p == '\0')
89168481Seric 					p = NULL;
89268481Seric 			}
89368481Seric 			if (p == NULL)
89468481Seric 				m->m_addrtype = m->m_mtatype;
89568481Seric 			else
89668481Seric 			{
89768481Seric 				m->m_addrtype = p;
89868481Seric 				p = strchr(p, '/');
89968481Seric 			}
90068481Seric 			if (p != NULL)
90168481Seric 			{
90268481Seric 				*p++ = '\0';
90368481Seric 				if (*p == '\0')
90468481Seric 					p = NULL;
90568481Seric 			}
90668481Seric 			if (p == NULL)
90768481Seric 				m->m_diagtype = m->m_mtatype;
90868481Seric 			else
90968481Seric 				m->m_diagtype = p;
91068481Seric 			break;
91168481Seric 
91268481Seric 		  case 'U':		/* user id */
91368481Seric 			if (isascii(*p) && !isdigit(*p))
91468481Seric 			{
91568481Seric 				char *q = p;
91668481Seric 				struct passwd *pw;
91768481Seric 
91868481Seric 				while (isascii(*p) && isalnum(*p))
91968481Seric 					p++;
92068481Seric 				while (isascii(*p) && isspace(*p))
92168481Seric 					*p++ = '\0';
92268481Seric 				if (*p != '\0')
92368481Seric 					*p++ = '\0';
92468481Seric 				pw = getpwnam(q);
92568481Seric 				if (pw == NULL)
92668481Seric 					syserr("readcf: mailer U= flag: unknown user %s", q);
92768481Seric 				else
92868481Seric 				{
92968481Seric 					m->m_uid = pw->pw_uid;
93068481Seric 					m->m_gid = pw->pw_gid;
93168481Seric 				}
93268481Seric 			}
93368481Seric 			else
93468481Seric 			{
93568481Seric 				auto char *q;
93668481Seric 
93768481Seric 				m->m_uid = strtol(p, &q, 0);
93868481Seric 				p = q;
93968481Seric 			}
94068481Seric 			while (isascii(*p) && isspace(*p))
94168481Seric 				p++;
94268481Seric 			if (*p == '\0')
94368481Seric 				break;
94468481Seric 			if (isascii(*p) && !isdigit(*p))
94568481Seric 			{
94668481Seric 				char *q = p;
94768481Seric 				struct group *gr;
94868481Seric 
94968481Seric 				while (isascii(*p) && isalnum(*p))
95068481Seric 					p++;
95168481Seric 				*p++ = '\0';
95268481Seric 				gr = getgrnam(q);
95368481Seric 				if (gr == NULL)
95468481Seric 					syserr("readcf: mailer U= flag: unknown group %s", q);
95568481Seric 				else
95668481Seric 					m->m_gid = gr->gr_gid;
95768481Seric 			}
95868481Seric 			else
95968481Seric 			{
96068481Seric 				m->m_gid = strtol(p, NULL, 0);
96168481Seric 			}
96268481Seric 			break;
96310327Seric 		}
96410327Seric 
96558333Seric 		p = delimptr;
96610327Seric 	}
96710327Seric 
96858321Seric 	/* do some rationality checking */
96958321Seric 	if (m->m_argv == NULL)
97058321Seric 	{
97158321Seric 		syserr("M%s: A= argument required", m->m_name);
97258321Seric 		return;
97358321Seric 	}
97458321Seric 	if (m->m_mailer == NULL)
97558321Seric 	{
97658321Seric 		syserr("M%s: P= argument required", m->m_name);
97758321Seric 		return;
97858321Seric 	}
97958321Seric 
9804096Seric 	if (NextMailer >= MAXMAILERS)
9814096Seric 	{
9829381Seric 		syserr("too many mailers defined (%d max)", MAXMAILERS);
9834096Seric 		return;
9844096Seric 	}
98557402Seric 
98668481Seric 	/* do some heuristic cleanup for back compatibility */
98768481Seric 	if (bitnset(M_LIMITS, m->m_flags))
98868481Seric 	{
98968481Seric 		if (m->m_linelimit == 0)
99068481Seric 			m->m_linelimit = SMTPLINELIM;
99168481Seric 		if (ConfigLevel < 2)
99268481Seric 			setbitn(M_7BITS, m->m_flags);
99368481Seric 	}
99468481Seric 
99568481Seric 	if (ConfigLevel < 6 &&
99668481Seric 	    (strcmp(m->m_mailer, "[IPC]") == 0 ||
99768481Seric 	     strcmp(m->m_mailer, "[TCP]") == 0))
99868481Seric 	{
99968481Seric 		if (m->m_mtatype == NULL)
100068481Seric 			m->m_mtatype = "dns";
100168481Seric 		if (m->m_addrtype == NULL)
100268481Seric 			m->m_addrtype = "rfc822";
100368481Seric 		if (m->m_diagtype == NULL)
100468481Seric 			m->m_diagtype = "smtp";
100568481Seric 	}
100668481Seric 
100768481Seric 	/* enter the mailer into the symbol table */
100810327Seric 	s = stab(m->m_name, ST_MAILER, ST_ENTER);
100957402Seric 	if (s->s_mailer != NULL)
101057402Seric 	{
101157402Seric 		i = s->s_mailer->m_mno;
101257402Seric 		free(s->s_mailer);
101357402Seric 	}
101457402Seric 	else
101557402Seric 	{
101657402Seric 		i = NextMailer++;
101757402Seric 	}
101857402Seric 	Mailer[i] = s->s_mailer = m;
101957454Seric 	m->m_mno = i;
102010327Seric }
102110327Seric /*
102210327Seric **  MUNCHSTRING -- translate a string into internal form.
102310327Seric **
102410327Seric **	Parameters:
102510327Seric **		p -- the string to munch.
102658333Seric **		delimptr -- if non-NULL, set to the pointer of the
102758333Seric **			field delimiter character.
102810327Seric **
102910327Seric **	Returns:
103010327Seric **		the munched string.
103110327Seric */
10324096Seric 
103310327Seric char *
103458333Seric munchstring(p, delimptr)
103510327Seric 	register char *p;
103658333Seric 	char **delimptr;
103710327Seric {
103810327Seric 	register char *q;
103910327Seric 	bool backslash = FALSE;
104010327Seric 	bool quotemode = FALSE;
104110327Seric 	static char buf[MAXLINE];
10424096Seric 
104310327Seric 	for (q = buf; *p != '\0'; p++)
10444096Seric 	{
104510327Seric 		if (backslash)
104610327Seric 		{
104710327Seric 			/* everything is roughly literal */
104810357Seric 			backslash = FALSE;
104910327Seric 			switch (*p)
105010327Seric 			{
105110327Seric 			  case 'r':		/* carriage return */
105210327Seric 				*q++ = '\r';
105310327Seric 				continue;
105410327Seric 
105510327Seric 			  case 'n':		/* newline */
105610327Seric 				*q++ = '\n';
105710327Seric 				continue;
105810327Seric 
105910327Seric 			  case 'f':		/* form feed */
106010327Seric 				*q++ = '\f';
106110327Seric 				continue;
106210327Seric 
106310327Seric 			  case 'b':		/* backspace */
106410327Seric 				*q++ = '\b';
106510327Seric 				continue;
106610327Seric 			}
106710327Seric 			*q++ = *p;
106810327Seric 		}
106910327Seric 		else
107010327Seric 		{
107110327Seric 			if (*p == '\\')
107210327Seric 				backslash = TRUE;
107310327Seric 			else if (*p == '"')
107410327Seric 				quotemode = !quotemode;
107510327Seric 			else if (quotemode || *p != ',')
107610327Seric 				*q++ = *p;
107710327Seric 			else
107810327Seric 				break;
107910327Seric 		}
10804096Seric 	}
10814096Seric 
108258333Seric 	if (delimptr != NULL)
108358333Seric 		*delimptr = p;
108410327Seric 	*q++ = '\0';
108510327Seric 	return (buf);
108610327Seric }
108710327Seric /*
108810327Seric **  MAKEARGV -- break up a string into words
108910327Seric **
109010327Seric **	Parameters:
109110327Seric **		p -- the string to break up.
109210327Seric **
109310327Seric **	Returns:
109410327Seric **		a char **argv (dynamically allocated)
109510327Seric **
109610327Seric **	Side Effects:
109710327Seric **		munges p.
109810327Seric */
10994096Seric 
110010327Seric char **
110110327Seric makeargv(p)
110210327Seric 	register char *p;
110310327Seric {
110410327Seric 	char *q;
110510327Seric 	int i;
110610327Seric 	char **avp;
110710327Seric 	char *argv[MAXPV + 1];
110810327Seric 
110910327Seric 	/* take apart the words */
111010327Seric 	i = 0;
111110327Seric 	while (*p != '\0' && i < MAXPV)
11124096Seric 	{
111310327Seric 		q = p;
111458050Seric 		while (*p != '\0' && !(isascii(*p) && isspace(*p)))
111510327Seric 			p++;
111658050Seric 		while (isascii(*p) && isspace(*p))
111710327Seric 			*p++ = '\0';
111810327Seric 		argv[i++] = newstr(q);
11194096Seric 	}
112010327Seric 	argv[i++] = NULL;
11214096Seric 
112210327Seric 	/* now make a copy of the argv */
112310327Seric 	avp = (char **) xalloc(sizeof *avp * i);
112416893Seric 	bcopy((char *) argv, (char *) avp, sizeof *avp * i);
112510327Seric 
112610327Seric 	return (avp);
11273308Seric }
11283308Seric /*
11293308Seric **  PRINTRULES -- print rewrite rules (for debugging)
11303308Seric **
11313308Seric **	Parameters:
11323308Seric **		none.
11333308Seric **
11343308Seric **	Returns:
11353308Seric **		none.
11363308Seric **
11373308Seric **	Side Effects:
11383308Seric **		prints rewrite rules.
11393308Seric */
11403308Seric 
11413308Seric printrules()
11423308Seric {
11433308Seric 	register struct rewrite *rwp;
11444072Seric 	register int ruleset;
11453308Seric 
11464072Seric 	for (ruleset = 0; ruleset < 10; ruleset++)
11473308Seric 	{
11484072Seric 		if (RewriteRules[ruleset] == NULL)
11494072Seric 			continue;
11508067Seric 		printf("\n----Rule Set %d:", ruleset);
11513308Seric 
11524072Seric 		for (rwp = RewriteRules[ruleset]; rwp != NULL; rwp = rwp->r_next)
11533308Seric 		{
11548067Seric 			printf("\nLHS:");
11558067Seric 			printav(rwp->r_lhs);
11568067Seric 			printf("RHS:");
11578067Seric 			printav(rwp->r_rhs);
11583308Seric 		}
11593308Seric 	}
11603308Seric }
116168481Seric /*
116268481Seric **  PRINTMAILER -- print mailer structure (for debugging)
116368481Seric **
116468481Seric **	Parameters:
116568481Seric **		m -- the mailer to print
116668481Seric **
116768481Seric **	Returns:
116868481Seric **		none.
116968481Seric */
11704319Seric 
117168481Seric printmailer(m)
117268481Seric 	register MAILER *m;
117368481Seric {
117468481Seric 	int j;
117568481Seric 
117668481Seric 	printf("mailer %d (%s): P=%s S=%d/%d R=%d/%d M=%ld U=%d:%d F=",
117768481Seric 		m->m_mno, m->m_name,
117868481Seric 		m->m_mailer, m->m_se_rwset, m->m_sh_rwset,
117968481Seric 		m->m_re_rwset, m->m_rh_rwset, m->m_maxsize,
118068481Seric 		m->m_uid, m->m_gid);
118168481Seric 	for (j = '\0'; j <= '\177'; j++)
118268481Seric 		if (bitnset(j, m->m_flags))
118368481Seric 			(void) putchar(j);
118468481Seric 	printf(" L=%d E=", m->m_linelimit);
118568481Seric 	xputs(m->m_eol);
118668481Seric 	if (m->m_defcharset != NULL)
118768481Seric 		printf(" C=%s", m->m_defcharset);
118868481Seric 	printf(" T=%s/%s/%s",
118968481Seric 		m->m_mtatype == NULL ? "<undefined>" : m->m_mtatype,
119068481Seric 		m->m_addrtype == NULL ? "<undefined>" : m->m_addrtype,
119168481Seric 		m->m_diagtype == NULL ? "<undefined>" : m->m_diagtype);
119268481Seric 	if (m->m_argv != NULL)
119368481Seric 	{
119468481Seric 		char **a = m->m_argv;
119568481Seric 
119668481Seric 		printf(" A=");
119768481Seric 		while (*a != NULL)
119868481Seric 		{
119968481Seric 			if (a != m->m_argv)
120068481Seric 				printf(" ");
120168481Seric 			xputs(*a++);
120268481Seric 		}
120368481Seric 	}
120468481Seric 	printf("\n");
120568481Seric }
12064096Seric /*
12078256Seric **  SETOPTION -- set global processing option
12088256Seric **
12098256Seric **	Parameters:
12108256Seric **		opt -- option name.
12118256Seric **		val -- option value (as a text string).
121221755Seric **		safe -- set if this came from a configuration file.
121321755Seric **			Some options (if set from the command line) will
121421755Seric **			reset the user id to avoid security problems.
12158269Seric **		sticky -- if set, don't let other setoptions override
12168269Seric **			this value.
121758734Seric **		e -- the main envelope.
12188256Seric **
12198256Seric **	Returns:
12208256Seric **		none.
12218256Seric **
12228256Seric **	Side Effects:
12238256Seric **		Sets options as implied by the arguments.
12248256Seric */
12258256Seric 
122610687Seric static BITMAP	StickyOpt;		/* set if option is stuck */
12278269Seric 
122857207Seric 
122966334Seric #if NAMED_BIND
123057207Seric 
123157207Seric struct resolverflags
123257207Seric {
123357207Seric 	char	*rf_name;	/* name of the flag */
123457207Seric 	long	rf_bits;	/* bits to set/clear */
123557207Seric } ResolverFlags[] =
123657207Seric {
123757207Seric 	"debug",	RES_DEBUG,
123857207Seric 	"aaonly",	RES_AAONLY,
123957207Seric 	"usevc",	RES_USEVC,
124057207Seric 	"primary",	RES_PRIMARY,
124157207Seric 	"igntc",	RES_IGNTC,
124257207Seric 	"recurse",	RES_RECURSE,
124357207Seric 	"defnames",	RES_DEFNAMES,
124457207Seric 	"stayopen",	RES_STAYOPEN,
124557207Seric 	"dnsrch",	RES_DNSRCH,
124665583Seric 	"true",		0,		/* to avoid error on old syntax */
124757207Seric 	NULL,		0
124857207Seric };
124957207Seric 
125057207Seric #endif
125157207Seric 
125268481Seric struct optioninfo
125368481Seric {
125468481Seric 	char	*o_name;	/* long name of option */
125568481Seric 	u_char	o_code;		/* short name of option */
125668481Seric 	bool	o_safe;		/* safe for random people to use */
125768481Seric } OptionTab[] =
125868481Seric {
125968481Seric 	"SevenBitInput",	'7',		TRUE,
126068481Seric 	"EightBitMode",		'8',		TRUE,
126168481Seric 	"AliasFile",		'A',		FALSE,
126268481Seric 	"AliasWait",		'a',		FALSE,
126368481Seric 	"BlankSub",		'B',		FALSE,
126468481Seric 	"MinFreeBlocks",	'b',		TRUE,
126568481Seric 	"CheckpointInterval",	'C',		TRUE,
126668481Seric 	"HoldExpensive",	'c',		FALSE,
126768481Seric 	"AutoRebuildAliases",	'D',		FALSE,
126868481Seric 	"DeliveryMode",		'd',		TRUE,
126968481Seric 	"ErrorHeader",		'E',		FALSE,
127068481Seric 	"ErrorMode",		'e',		TRUE,
127168481Seric 	"TempFileMode",		'F',		FALSE,
127268481Seric 	"SaveFromLine",		'f',		FALSE,
127368481Seric 	"MatchGECOS",		'G',		FALSE,
127468481Seric 	"HelpFile",		'H',		FALSE,
127568481Seric 	"MaxHopCount",		'h',		FALSE,
127668481Seric 	"NameServerOptions",	'I',		FALSE,
127768481Seric 	"IgnoreDots",		'i',		TRUE,
127868481Seric 	"ForwardPath",		'J',		FALSE,
127968481Seric 	"SendMimeErrors",	'j',		TRUE,
128068481Seric 	"ConnectionCacheSize",	'k',		FALSE,
128168481Seric 	"ConnectionCacheTimeout", 'K',		FALSE,
128268481Seric 	"UseErrorsTo",		'l',		FALSE,
128368481Seric 	"LogLevel",		'L',		FALSE,
128468481Seric 	"MeToo",		'm',		TRUE,
128568481Seric 	"CheckAliases",		'n',		FALSE,
128668481Seric 	"OldStyleHeaders",	'o',		TRUE,
128768481Seric 	"DaemonPortOptions",	'O',		FALSE,
128868481Seric 	"PrivacyOptions",	'p',		TRUE,
128968481Seric 	"PostmasterCopy",	'P',		FALSE,
129068481Seric 	"QueueFactor",		'q',		FALSE,
129168481Seric 	"QueueDirectory",	'Q',		FALSE,
129268481Seric 	"DontPruneRoutes",	'R',		FALSE,
129368481Seric 	"Timeout",		'r',		TRUE,
129468481Seric 	"StatusFile",		'S',		FALSE,
129568481Seric 	"SuperSafe",		's',		TRUE,
129668481Seric 	"QueueTimeout",		'T',		FALSE,
129768481Seric 	"TimeZoneSpec",		't',		FALSE,
129868481Seric 	"UserDatabaseSpec",	'U',		FALSE,
129968481Seric 	"DefaultUser",		'u',		FALSE,
130068481Seric 	"FallbackMXhost",	'V',		FALSE,
130168481Seric 	"Verbose",		'v',		TRUE,
130268481Seric 	"TryNullMXList",	'w',		TRUE,
130368481Seric 	"QueueLA",		'x',		FALSE,
130468481Seric 	"RefuseLA",		'X',		FALSE,
130568481Seric 	"RecipientFactor",	'y',		FALSE,
130668481Seric 	"ForkQueueRuns",	'Y',		FALSE,
130768481Seric 	"ClassFactor",		'z',		FALSE,
130868481Seric 	"TimeFactor",		'Z',		FALSE,
130968481Seric #define O_BSP		0x80
131068481Seric 	"BrokenSmtpPeers",	O_BSP,		TRUE,
131168481Seric #define O_QUEUESORTORD	0x81
131268481Seric 	"QueueSortOrder",	O_QUEUESORTORD,	TRUE,
131368481Seric #define O_MQA		0x83
131468481Seric 	"MinQueueAge",		O_MQA,		TRUE,
131568481Seric #define O_MHSA		0x84
131668481Seric /*
131768481Seric 	"MaxHostStatAge",	O_MHSA,		TRUE,
131868481Seric */
131968481Seric #define O_DEFCHARSET	0x85
132068481Seric 	"DefaultCharSet",	O_DEFCHARSET,	TRUE,
132168481Seric #define O_SSFILE	0x86
132268481Seric 	"ServiceSwitchFile",	O_SSFILE,	FALSE,
132368481Seric #define O_DIALDELAY	0x87
132468481Seric 	"DialDelay",		O_DIALDELAY,	TRUE,
132568481Seric #define O_NORCPTACTION	0x88
132668481Seric 	"NoRecipientAction",	O_NORCPTACTION,	TRUE,
1327*68490Seric #define O_SAFEFILEENV	0x89
1328*68490Seric 	"SafeFileEnvironment",	O_SAFEFILEENV,	FALSE,
132968481Seric 
133068481Seric 	NULL,			'\0',		FALSE,
133168481Seric };
133268481Seric 
133368481Seric 
133468481Seric 
133558734Seric setoption(opt, val, safe, sticky, e)
133668481Seric 	u_char opt;
13378256Seric 	char *val;
133821755Seric 	bool safe;
13398269Seric 	bool sticky;
134058734Seric 	register ENVELOPE *e;
13418256Seric {
134257207Seric 	register char *p;
134368481Seric 	register struct optioninfo *o;
134468481Seric 	char *subopt;
13458265Seric 	extern bool atobool();
134612633Seric 	extern time_t convtime();
134714879Seric 	extern int QueueLA;
134814879Seric 	extern int RefuseLA;
134964718Seric 	extern bool Warn_Q_option;
13508256Seric 
135168481Seric 	errno = 0;
135268481Seric 	if (opt == ' ')
135368481Seric 	{
135468481Seric 		/* full word options */
135568481Seric 		struct optioninfo *sel;
135668481Seric 
135768481Seric 		p = strchr(val, '=');
135868481Seric 		if (p == NULL)
135968481Seric 			p = &val[strlen(val)];
136068481Seric 		while (*--p == ' ')
136168481Seric 			continue;
136268481Seric 		while (*++p == ' ')
136368481Seric 			*p = '\0';
136468481Seric 		if (p == val)
136568481Seric 		{
136668481Seric 			syserr("readcf: null option name");
136768481Seric 			return;
136868481Seric 		}
136968481Seric 		if (*p == '=')
137068481Seric 			*p++ = '\0';
137168481Seric 		while (*p == ' ')
137268481Seric 			p++;
137368481Seric 		subopt = strchr(val, '.');
137468481Seric 		if (subopt != NULL)
137568481Seric 			*subopt++ = '\0';
137668481Seric 		sel = NULL;
137768481Seric 		for (o = OptionTab; o->o_name != NULL; o++)
137868481Seric 		{
137968481Seric 			if (strncasecmp(o->o_name, val, strlen(val)) != 0)
138068481Seric 				continue;
138168481Seric 			if (strlen(o->o_name) == strlen(val))
138268481Seric 			{
138368481Seric 				/* completely specified -- this must be it */
138468481Seric 				sel = NULL;
138568481Seric 				break;
138668481Seric 			}
138768481Seric 			if (sel != NULL)
138868481Seric 				break;
138968481Seric 			sel = o;
139068481Seric 		}
139168481Seric 		if (sel != NULL && o->o_name == NULL)
139268481Seric 			o = sel;
139368481Seric 		else if (o->o_name == NULL)
139468481Seric 		{
139568481Seric 			syserr("readcf: unknown option name %s", val);
139668481Seric 			return;
139768481Seric 		}
139868481Seric 		else if (sel != NULL)
139968481Seric 		{
140068481Seric 			syserr("readcf: ambiguous option name %s (matches %s and %s)",
140168481Seric 				val, sel->o_name, o->o_name);
140268481Seric 			return;
140368481Seric 		}
140468481Seric 		if (strlen(val) != strlen(o->o_name))
140568481Seric 		{
140668481Seric 			bool oldVerbose = Verbose;
140768481Seric 
140868481Seric 			Verbose = TRUE;
140968481Seric 			message("Option %s used as abbreviation for %s",
141068481Seric 				val, o->o_name);
141168481Seric 			Verbose = oldVerbose;
141268481Seric 		}
141368481Seric 		opt = o->o_code;
141468481Seric 		val = p;
141568481Seric 	}
141668481Seric 	else
141768481Seric 	{
141868481Seric 		for (o = OptionTab; o->o_name != NULL; o++)
141968481Seric 		{
142068481Seric 			if (o->o_code == opt)
142168481Seric 				break;
142268481Seric 		}
142368481Seric 		subopt = NULL;
142468481Seric 	}
142568481Seric 
14268256Seric 	if (tTd(37, 1))
142768481Seric 	{
142868481Seric 		printf(isascii(opt) && isprint(opt) ?
142968481Seric 			    "setoption %s (%c).%s=%s" :
143068481Seric 			    "setoption %s (0x%x).%s=%s",
143168481Seric 			o->o_name == NULL ? "<unknown>" : o->o_name,
143268481Seric 			opt,
143368481Seric 			subopt == NULL ? "" : subopt,
143468481Seric 			val);
143568481Seric 	}
14368256Seric 
14378256Seric 	/*
14388269Seric 	**  See if this option is preset for us.
14398256Seric 	*/
14408256Seric 
144159731Seric 	if (!sticky && bitnset(opt, StickyOpt))
14428269Seric 	{
14439341Seric 		if (tTd(37, 1))
14449341Seric 			printf(" (ignored)\n");
14458269Seric 		return;
14468269Seric 	}
14478269Seric 
144821755Seric 	/*
144921755Seric 	**  Check to see if this option can be specified by this user.
145021755Seric 	*/
145121755Seric 
145263787Seric 	if (!safe && RealUid == 0)
145321755Seric 		safe = TRUE;
145468481Seric 	if (!safe && !o->o_safe)
145521755Seric 	{
145639111Srick 		if (opt != 'M' || (val[0] != 'r' && val[0] != 's'))
145721755Seric 		{
145836582Sbostic 			if (tTd(37, 1))
145936582Sbostic 				printf(" (unsafe)");
146063787Seric 			if (RealUid != geteuid())
146136582Sbostic 			{
146251210Seric 				if (tTd(37, 1))
146351210Seric 					printf("(Resetting uid)");
146463787Seric 				(void) setgid(RealGid);
146563787Seric 				(void) setuid(RealUid);
146636582Sbostic 			}
146721755Seric 		}
146821755Seric 	}
146951210Seric 	if (tTd(37, 1))
147017985Seric 		printf("\n");
14718269Seric 
147268481Seric 	switch (opt & 0xff)
14738256Seric 	{
147459709Seric 	  case '7':		/* force seven-bit input */
147568481Seric 		SevenBitInput = atobool(val);
147652106Seric 		break;
147752106Seric 
147868481Seric 	  case '8':		/* handling of 8-bit input */
147968481Seric 		switch (*val)
148068481Seric 		{
148168481Seric 		  case 'r':		/* reject 8-bit, don't convert MIME */
148268481Seric 			MimeMode = 0;
148368481Seric 			break;
148468481Seric 
148568481Seric 		  case 'm':		/* convert 8-bit, convert MIME */
148668481Seric 			MimeMode = MM_CVTMIME|MM_MIME8BIT;
148768481Seric 			break;
148868481Seric 
148968481Seric 		  case 'j':		/* "just send 8" */
149068481Seric 			MimeMode = MM_PASS8BIT;
149168481Seric 			break;
149268481Seric 
149368481Seric 		  case 'p':		/* pass 8 bit, convert MIME */
149468481Seric 			MimeMode = MM_PASS8BIT|MM_CVTMIME;
149568481Seric 			break;
149668481Seric 
149768481Seric 		  case 's':		/* strict adherence */
149868481Seric 			MimeMode = MM_CVTMIME;
149968481Seric 			break;
150068481Seric 
150168481Seric 		  case 'a':		/* encode 8 bit if available */
150268481Seric 			MimeMode = MM_MIME8BIT|MM_PASS8BIT|MM_CVTMIME;
150368481Seric 			break;
150468481Seric 
150568481Seric 		  case 'c':		/* convert 8 bit to MIME, never 7 bit */
150668481Seric 			MimeMode = MM_MIME8BIT;
150768481Seric 			break;
150868481Seric 
150968481Seric 		  default:
151068481Seric 			syserr("Unknown 8-bit mode %c", *val);
151168481Seric 			exit(EX_USAGE);
151268481Seric 		}
151368481Seric 		break;
151468481Seric 
15158256Seric 	  case 'A':		/* set default alias file */
15169381Seric 		if (val[0] == '\0')
151759672Seric 			setalias("aliases");
15189381Seric 		else
151959672Seric 			setalias(val);
15208256Seric 		break;
15218256Seric 
152217474Seric 	  case 'a':		/* look N minutes for "@:@" in alias file */
152317474Seric 		if (val[0] == '\0')
152464796Seric 			SafeAlias = 5 * 60;		/* five minutes */
152517474Seric 		else
152664796Seric 			SafeAlias = convtime(val, 'm');
152717474Seric 		break;
152817474Seric 
152916843Seric 	  case 'B':		/* substitution for blank character */
153016843Seric 		SpaceSub = val[0];
153116843Seric 		if (SpaceSub == '\0')
153216843Seric 			SpaceSub = ' ';
153316843Seric 		break;
153416843Seric 
153559283Seric 	  case 'b':		/* min blocks free on queue fs/max msg size */
153659283Seric 		p = strchr(val, '/');
153759283Seric 		if (p != NULL)
153859283Seric 		{
153959283Seric 			*p++ = '\0';
154059283Seric 			MaxMessageSize = atol(p);
154159283Seric 		}
154258082Seric 		MinBlocksFree = atol(val);
154358082Seric 		break;
154458082Seric 
15459284Seric 	  case 'c':		/* don't connect to "expensive" mailers */
15469381Seric 		NoConnect = atobool(val);
15479284Seric 		break;
15489284Seric 
154951305Seric 	  case 'C':		/* checkpoint every N addresses */
155051305Seric 		CheckpointInterval = atoi(val);
155124944Seric 		break;
155224944Seric 
15539284Seric 	  case 'd':		/* delivery mode */
15549284Seric 		switch (*val)
15558269Seric 		{
15569284Seric 		  case '\0':
155758734Seric 			e->e_sendmode = SM_DELIVER;
15588269Seric 			break;
15598269Seric 
156010755Seric 		  case SM_QUEUE:	/* queue only */
156110755Seric #ifndef QUEUE
156210755Seric 			syserr("need QUEUE to set -odqueue");
156356795Seric #endif /* QUEUE */
156410755Seric 			/* fall through..... */
156510755Seric 
15669284Seric 		  case SM_DELIVER:	/* do everything */
15679284Seric 		  case SM_FORK:		/* fork after verification */
156858734Seric 			e->e_sendmode = *val;
15698269Seric 			break;
15708269Seric 
15718269Seric 		  default:
15729284Seric 			syserr("Unknown delivery mode %c", *val);
15738269Seric 			exit(EX_USAGE);
15748269Seric 		}
15758269Seric 		break;
15768269Seric 
15779146Seric 	  case 'D':		/* rebuild alias database as needed */
15789381Seric 		AutoRebuild = atobool(val);
15799146Seric 		break;
15809146Seric 
158155372Seric 	  case 'E':		/* error message header/header file */
158255379Seric 		if (*val != '\0')
158355379Seric 			ErrMsgFile = newstr(val);
158455372Seric 		break;
158555372Seric 
15868269Seric 	  case 'e':		/* set error processing mode */
15878269Seric 		switch (*val)
15888269Seric 		{
15899381Seric 		  case EM_QUIET:	/* be silent about it */
15909381Seric 		  case EM_MAIL:		/* mail back */
15919381Seric 		  case EM_BERKNET:	/* do berknet error processing */
15929381Seric 		  case EM_WRITE:	/* write back (or mail) */
15939381Seric 		  case EM_PRINT:	/* print errors normally (default) */
159458734Seric 			e->e_errormode = *val;
15958269Seric 			break;
15968269Seric 		}
15978269Seric 		break;
15988269Seric 
15999049Seric 	  case 'F':		/* file mode */
160017975Seric 		FileMode = atooct(val) & 0777;
16019049Seric 		break;
16029049Seric 
16038269Seric 	  case 'f':		/* save Unix-style From lines on front */
16049381Seric 		SaveFrom = atobool(val);
16058269Seric 		break;
16068269Seric 
160753735Seric 	  case 'G':		/* match recipients against GECOS field */
160853735Seric 		MatchGecos = atobool(val);
160953735Seric 		break;
161053735Seric 
16118256Seric 	  case 'g':		/* default gid */
161268481Seric   g_opt:
161364133Seric 		if (isascii(*val) && isdigit(*val))
161464133Seric 			DefGid = atoi(val);
161564133Seric 		else
161664133Seric 		{
161764133Seric 			register struct group *gr;
161864133Seric 
161964133Seric 			DefGid = -1;
162064133Seric 			gr = getgrnam(val);
162164133Seric 			if (gr == NULL)
162268481Seric 				syserr("readcf: option %c: unknown group %s",
162368481Seric 					opt, val);
162464133Seric 			else
162564133Seric 				DefGid = gr->gr_gid;
162664133Seric 		}
16278256Seric 		break;
16288256Seric 
16298256Seric 	  case 'H':		/* help file */
16309381Seric 		if (val[0] == '\0')
16318269Seric 			HelpFile = "sendmail.hf";
16329381Seric 		else
16339381Seric 			HelpFile = newstr(val);
16348256Seric 		break;
16358256Seric 
163651305Seric 	  case 'h':		/* maximum hop count */
163751305Seric 		MaxHopCount = atoi(val);
163851305Seric 		break;
163951305Seric 
164035651Seric 	  case 'I':		/* use internet domain name server */
164166334Seric #if NAMED_BIND
164257207Seric 		for (p = val; *p != 0; )
164357207Seric 		{
164457207Seric 			bool clearmode;
164557207Seric 			char *q;
164657207Seric 			struct resolverflags *rfp;
164757207Seric 
164857207Seric 			while (*p == ' ')
164957207Seric 				p++;
165057207Seric 			if (*p == '\0')
165157207Seric 				break;
165257207Seric 			clearmode = FALSE;
165357207Seric 			if (*p == '-')
165457207Seric 				clearmode = TRUE;
165557207Seric 			else if (*p != '+')
165657207Seric 				p--;
165757207Seric 			p++;
165857207Seric 			q = p;
165958050Seric 			while (*p != '\0' && !(isascii(*p) && isspace(*p)))
166057207Seric 				p++;
166157207Seric 			if (*p != '\0')
166257207Seric 				*p++ = '\0';
166357207Seric 			for (rfp = ResolverFlags; rfp->rf_name != NULL; rfp++)
166457207Seric 			{
166557207Seric 				if (strcasecmp(q, rfp->rf_name) == 0)
166657207Seric 					break;
166757207Seric 			}
166864923Seric 			if (rfp->rf_name == NULL)
166964923Seric 				syserr("readcf: I option value %s unrecognized", q);
167064923Seric 			else if (clearmode)
167157207Seric 				_res.options &= ~rfp->rf_bits;
167257207Seric 			else
167357207Seric 				_res.options |= rfp->rf_bits;
167457207Seric 		}
167557207Seric 		if (tTd(8, 2))
167657207Seric 			printf("_res.options = %x\n", _res.options);
167757207Seric #else
167857207Seric 		usrerr("name server (I option) specified but BIND not compiled in");
167957207Seric #endif
168035651Seric 		break;
168135651Seric 
16828269Seric 	  case 'i':		/* ignore dot lines in message */
16839381Seric 		IgnrDot = atobool(val);
16848269Seric 		break;
16858269Seric 
168659730Seric 	  case 'j':		/* send errors in MIME (RFC 1341) format */
168759730Seric 		SendMIMEErrors = atobool(val);
168859730Seric 		break;
168959730Seric 
169057136Seric 	  case 'J':		/* .forward search path */
169157136Seric 		ForwardPath = newstr(val);
169257136Seric 		break;
169357136Seric 
169454967Seric 	  case 'k':		/* connection cache size */
169554967Seric 		MaxMciCache = atoi(val);
169656215Seric 		if (MaxMciCache < 0)
169756215Seric 			MaxMciCache = 0;
169854967Seric 		break;
169954967Seric 
170054967Seric 	  case 'K':		/* connection cache timeout */
170158796Seric 		MciCacheTimeout = convtime(val, 'm');
170254967Seric 		break;
170354967Seric 
170461104Seric 	  case 'l':		/* use Errors-To: header */
170561104Seric 		UseErrorsTo = atobool(val);
170661104Seric 		break;
170761104Seric 
17088256Seric 	  case 'L':		/* log level */
170964140Seric 		if (safe || LogLevel < atoi(val))
171064140Seric 			LogLevel = atoi(val);
17118256Seric 		break;
17128256Seric 
17138269Seric 	  case 'M':		/* define macro */
171468267Seric 		p = newstr(&val[1]);
171568267Seric 		if (!safe)
171668267Seric 			cleanstrcpy(p, p, MAXNAME);
171768267Seric 		define(val[0], p, CurEnv);
171816878Seric 		sticky = FALSE;
17198269Seric 		break;
17208269Seric 
17218269Seric 	  case 'm':		/* send to me too */
17229381Seric 		MeToo = atobool(val);
17238269Seric 		break;
17248269Seric 
172525820Seric 	  case 'n':		/* validate RHS in newaliases */
172625820Seric 		CheckAliases = atobool(val);
172725820Seric 		break;
172825820Seric 
172961104Seric 	    /* 'N' available -- was "net name" */
173061104Seric 
173158851Seric 	  case 'O':		/* daemon options */
173258851Seric 		setdaemonoptions(val);
173358851Seric 		break;
173458851Seric 
17358269Seric 	  case 'o':		/* assume old style headers */
17369381Seric 		if (atobool(val))
17379341Seric 			CurEnv->e_flags |= EF_OLDSTYLE;
17389341Seric 		else
17399341Seric 			CurEnv->e_flags &= ~EF_OLDSTYLE;
17408269Seric 		break;
17418269Seric 
174258082Seric 	  case 'p':		/* select privacy level */
174358082Seric 		p = val;
174458082Seric 		for (;;)
174558082Seric 		{
174658082Seric 			register struct prival *pv;
174758082Seric 			extern struct prival PrivacyValues[];
174858082Seric 
174958082Seric 			while (isascii(*p) && (isspace(*p) || ispunct(*p)))
175058082Seric 				p++;
175158082Seric 			if (*p == '\0')
175258082Seric 				break;
175358082Seric 			val = p;
175458082Seric 			while (isascii(*p) && isalnum(*p))
175558082Seric 				p++;
175658082Seric 			if (*p != '\0')
175758082Seric 				*p++ = '\0';
175858082Seric 
175958082Seric 			for (pv = PrivacyValues; pv->pv_name != NULL; pv++)
176058082Seric 			{
176158082Seric 				if (strcasecmp(val, pv->pv_name) == 0)
176258082Seric 					break;
176358082Seric 			}
176458886Seric 			if (pv->pv_name == NULL)
176558886Seric 				syserr("readcf: Op line: %s unrecognized", val);
176658082Seric 			PrivacyFlags |= pv->pv_flag;
176758082Seric 		}
176868479Seric 		sticky = FALSE;
176958082Seric 		break;
177058082Seric 
177124944Seric 	  case 'P':		/* postmaster copy address for returned mail */
177224944Seric 		PostMasterCopy = newstr(val);
177324944Seric 		break;
177424944Seric 
177524944Seric 	  case 'q':		/* slope of queue only function */
177624944Seric 		QueueFactor = atoi(val);
177724944Seric 		break;
177824944Seric 
17798256Seric 	  case 'Q':		/* queue directory */
17809381Seric 		if (val[0] == '\0')
17818269Seric 			QueueDir = "mqueue";
17829381Seric 		else
17839381Seric 			QueueDir = newstr(val);
178458789Seric 		if (RealUid != 0 && !safe)
178564718Seric 			Warn_Q_option = TRUE;
17868256Seric 		break;
17878256Seric 
178858148Seric 	  case 'R':		/* don't prune routes */
178958148Seric 		DontPruneRoutes = atobool(val);
179058148Seric 		break;
179158148Seric 
17928256Seric 	  case 'r':		/* read timeout */
179368481Seric 		if (subopt == NULL)
179468481Seric 			inittimeouts(val);
179568481Seric 		else
179668481Seric 			settimeout(subopt, val);
17978256Seric 		break;
17988256Seric 
17998256Seric 	  case 'S':		/* status file */
18009381Seric 		if (val[0] == '\0')
18018269Seric 			StatFile = "sendmail.st";
18029381Seric 		else
18039381Seric 			StatFile = newstr(val);
18048256Seric 		break;
18058256Seric 
18068265Seric 	  case 's':		/* be super safe, even if expensive */
18079381Seric 		SuperSafe = atobool(val);
18088256Seric 		break;
18098256Seric 
18108256Seric 	  case 'T':		/* queue timeout */
181158737Seric 		p = strchr(val, '/');
181258737Seric 		if (p != NULL)
181358737Seric 		{
181458737Seric 			*p++ = '\0';
181568481Seric 			settimeout("queuewarn", p);
181658737Seric 		}
181768481Seric 		settimeout("queuereturn", val);
181854967Seric 		break;
18198256Seric 
18208265Seric 	  case 't':		/* time zone name */
182152106Seric 		TimeZoneSpec = newstr(val);
18228265Seric 		break;
18238265Seric 
182450556Seric 	  case 'U':		/* location of user database */
182551360Seric 		UdbSpec = newstr(val);
182650556Seric 		break;
182750556Seric 
18288256Seric 	  case 'u':		/* set default uid */
182968481Seric 		for (p = val; *p != '\0'; p++)
183068481Seric 		{
183168481Seric 			if (*p == '.' || *p == '/' || *p == ':')
183268481Seric 			{
183368481Seric 				*p++ = '\0';
183468481Seric 				break;
183568481Seric 			}
183668481Seric 		}
183764133Seric 		if (isascii(*val) && isdigit(*val))
183864133Seric 			DefUid = atoi(val);
183964133Seric 		else
184064133Seric 		{
184164133Seric 			register struct passwd *pw;
184264133Seric 
184364133Seric 			DefUid = -1;
184464133Seric 			pw = getpwnam(val);
184564133Seric 			if (pw == NULL)
184664133Seric 				syserr("readcf: option u: unknown user %s", val);
184764133Seric 			else
184868481Seric 			{
184964133Seric 				DefUid = pw->pw_uid;
185068481Seric 				DefGid = pw->pw_gid;
185168481Seric 			}
185264133Seric 		}
185340973Sbostic 		setdefuser();
18548256Seric 
185568481Seric 		/* handle the group if it is there */
185668481Seric 		if (*p == '\0')
185768481Seric 			break;
185868481Seric 		val = p;
185968481Seric 		goto g_opt;
186068481Seric 
186158851Seric 	  case 'V':		/* fallback MX host */
186258851Seric 		FallBackMX = newstr(val);
186358851Seric 		break;
186458851Seric 
18658269Seric 	  case 'v':		/* run in verbose mode */
18669381Seric 		Verbose = atobool(val);
18678256Seric 		break;
18688256Seric 
186963837Seric 	  case 'w':		/* if we are best MX, try host directly */
187063837Seric 		TryNullMXList = atobool(val);
187163837Seric 		break;
187261104Seric 
187361104Seric 	    /* 'W' available -- was wizard password */
187461104Seric 
187514879Seric 	  case 'x':		/* load avg at which to auto-queue msgs */
187614879Seric 		QueueLA = atoi(val);
187714879Seric 		break;
187814879Seric 
187914879Seric 	  case 'X':		/* load avg at which to auto-reject connections */
188014879Seric 		RefuseLA = atoi(val);
188114879Seric 		break;
188214879Seric 
188324981Seric 	  case 'y':		/* work recipient factor */
188424981Seric 		WkRecipFact = atoi(val);
188524981Seric 		break;
188624981Seric 
188724981Seric 	  case 'Y':		/* fork jobs during queue runs */
188824952Seric 		ForkQueueRuns = atobool(val);
188924952Seric 		break;
189024952Seric 
189124981Seric 	  case 'z':		/* work message class factor */
189224981Seric 		WkClassFact = atoi(val);
189324981Seric 		break;
189424981Seric 
189524981Seric 	  case 'Z':		/* work time factor */
189624981Seric 		WkTimeFact = atoi(val);
189724981Seric 		break;
189824981Seric 
189968481Seric 	  case O_BSP:		/* SMTP Peers can't handle 2-line greeting */
190068481Seric 		BrokenSmtpPeers = atobool(val);
190168481Seric 		break;
190268481Seric 
190368481Seric 	  case O_QUEUESORTORD:	/* queue sorting order */
190468481Seric 		switch (*val)
190568481Seric 		{
190668481Seric 		  case 'h':	/* Host first */
190768481Seric 		  case 'H':
190868481Seric 			QueueSortOrder = QS_BYHOST;
190968481Seric 			break;
191068481Seric 
191168481Seric 		  case 'p':	/* Priority order */
191268481Seric 		  case 'P':
191368481Seric 			QueueSortOrder = QS_BYPRIORITY;
191468481Seric 			break;
191568481Seric 
191668481Seric 		  default:
191768481Seric 			syserr("Invalid queue sort order \"%s\"", val);
191868481Seric 		}
191968481Seric 		break;
192068481Seric 
192168481Seric 	  case O_MQA:		/* minimum queue age between deliveries */
192268481Seric 		MinQueueAge = convtime(val, 'm');
192368481Seric 		break;
192468481Seric 
192568481Seric 	  case O_MHSA:		/* maximum age of cached host status */
192668481Seric 		MaxHostStatAge = convtime(val, 'm');
192768481Seric 		break;
192868481Seric 
192968481Seric 	  case O_DEFCHARSET:	/* default character set for mimefying */
193068481Seric 		DefaultCharSet = newstr(denlstring(val, TRUE, TRUE));
193168481Seric 		break;
193268481Seric 
193368481Seric 	  case O_SSFILE:	/* service switch file */
193468481Seric 		ServiceSwitchFile = newstr(val);
193568481Seric 		break;
193668481Seric 
193768481Seric 	  case O_DIALDELAY:	/* delay for dial-on-demand operation */
193868481Seric 		DialDelay = convtime(val, 's');
193968481Seric 		break;
194068481Seric 
194168481Seric 	  case O_NORCPTACTION:	/* what to do if no recipient */
194268481Seric 		if (strcasecmp(val, "none") == 0)
194368481Seric 			NoRecipientAction = NRA_NO_ACTION;
194468481Seric 		else if (strcasecmp(val, "add-to") == 0)
194568481Seric 			NoRecipientAction = NRA_ADD_TO;
194668481Seric 		else if (strcasecmp(val, "add-apparently-to") == 0)
194768481Seric 			NoRecipientAction = NRA_ADD_APPARENTLY_TO;
194868481Seric 		else if (strcasecmp(val, "add-bcc") == 0)
194968481Seric 			NoRecipientAction = NRA_ADD_BCC;
195068481Seric 		else if (strcasecmp(val, "add-to-undisclosed") == 0)
195168481Seric 			NoRecipientAction = NRA_ADD_TO_UNDISCLOSED;
195268481Seric 		else
195368481Seric 			syserr("Invalid NoRecipientAction: %s", val);
195468481Seric 
1955*68490Seric 	  case O_SAFEFILEENV:	/* chroot() environ for writing to files */
1956*68490Seric 		SafeFileEnv = newstr(val);
1957*68490Seric 		break;
1958*68490Seric 
19598256Seric 	  default:
196068481Seric 		if (tTd(37, 1))
196168481Seric 		{
196268481Seric 			if (isascii(opt) && isprint(opt))
196368481Seric 				printf("Warning: option %c unknown\n", opt);
196468481Seric 			else
196568481Seric 				printf("Warning: option 0x%x unknown\n", opt);
196668481Seric 		}
19678256Seric 		break;
19688256Seric 	}
196916878Seric 	if (sticky)
197016878Seric 		setbitn(opt, StickyOpt);
19719188Seric 	return;
19728256Seric }
197310687Seric /*
197468481Seric **  SETCLASS -- set a string into a class
197510687Seric **
197610687Seric **	Parameters:
197768481Seric **		class -- the class to put the string in.
197868481Seric **		str -- the string to enter
197910687Seric **
198010687Seric **	Returns:
198110687Seric **		none.
198210687Seric **
198310687Seric **	Side Effects:
198410687Seric **		puts the word into the symbol table.
198510687Seric */
198610687Seric 
198768481Seric setclass(class, str)
198810687Seric 	int class;
198968481Seric 	char *str;
199010687Seric {
199110687Seric 	register STAB *s;
199210687Seric 
199357943Seric 	if (tTd(37, 8))
199468481Seric 		printf("setclass(%c, %s)\n", class, str);
199568481Seric 	s = stab(str, ST_CLASS, ST_ENTER);
199610687Seric 	setbitn(class, s->s_class);
199710687Seric }
199853654Seric /*
199953654Seric **  MAKEMAPENTRY -- create a map entry
200053654Seric **
200153654Seric **	Parameters:
200253654Seric **		line -- the config file line
200353654Seric **
200453654Seric **	Returns:
200553654Seric **		TRUE if it successfully entered the map entry.
200653654Seric **		FALSE otherwise (usually syntax error).
200753654Seric **
200853654Seric **	Side Effects:
200953654Seric **		Enters the map into the dictionary.
201053654Seric */
201153654Seric 
201253654Seric void
201353654Seric makemapentry(line)
201453654Seric 	char *line;
201553654Seric {
201653654Seric 	register char *p;
201753654Seric 	char *mapname;
201853654Seric 	char *classname;
201964078Seric 	register STAB *s;
202053654Seric 	STAB *class;
202153654Seric 
202258050Seric 	for (p = line; isascii(*p) && isspace(*p); p++)
202353654Seric 		continue;
202458050Seric 	if (!(isascii(*p) && isalnum(*p)))
202553654Seric 	{
202653654Seric 		syserr("readcf: config K line: no map name");
202753654Seric 		return;
202853654Seric 	}
202953654Seric 
203053654Seric 	mapname = p;
203168481Seric 	while ((isascii(*++p) && isalnum(*p)) || *p == '.')
203253654Seric 		continue;
203353654Seric 	if (*p != '\0')
203453654Seric 		*p++ = '\0';
203558050Seric 	while (isascii(*p) && isspace(*p))
203653654Seric 		p++;
203758050Seric 	if (!(isascii(*p) && isalnum(*p)))
203853654Seric 	{
203953654Seric 		syserr("readcf: config K line, map %s: no map class", mapname);
204053654Seric 		return;
204153654Seric 	}
204253654Seric 	classname = p;
204358050Seric 	while (isascii(*++p) && isalnum(*p))
204453654Seric 		continue;
204553654Seric 	if (*p != '\0')
204653654Seric 		*p++ = '\0';
204758050Seric 	while (isascii(*p) && isspace(*p))
204853654Seric 		p++;
204953654Seric 
205053654Seric 	/* look up the class */
205153654Seric 	class = stab(classname, ST_MAPCLASS, ST_FIND);
205253654Seric 	if (class == NULL)
205353654Seric 	{
205453654Seric 		syserr("readcf: map %s: class %s not available", mapname, classname);
205553654Seric 		return;
205653654Seric 	}
205753654Seric 
205853654Seric 	/* enter the map */
205964078Seric 	s = stab(mapname, ST_MAP, ST_ENTER);
206064078Seric 	s->s_map.map_class = &class->s_mapclass;
206164078Seric 	s->s_map.map_mname = newstr(mapname);
206253654Seric 
206364078Seric 	if (class->s_mapclass.map_parse(&s->s_map, p))
206464078Seric 		s->s_map.map_mflags |= MF_VALID;
206564078Seric 
206664078Seric 	if (tTd(37, 5))
206764078Seric 	{
206864078Seric 		printf("map %s, class %s, flags %x, file %s,\n",
206964078Seric 			s->s_map.map_mname, s->s_map.map_class->map_cname,
207064078Seric 			s->s_map.map_mflags,
207164078Seric 			s->s_map.map_file == NULL ? "(null)" : s->s_map.map_file);
207264078Seric 		printf("\tapp %s, domain %s, rebuild %s\n",
207364078Seric 			s->s_map.map_app == NULL ? "(null)" : s->s_map.map_app,
207464078Seric 			s->s_map.map_domain == NULL ? "(null)" : s->s_map.map_domain,
207564078Seric 			s->s_map.map_rebuild == NULL ? "(null)" : s->s_map.map_rebuild);
207664078Seric 	}
207753654Seric }
207858112Seric /*
207968481Seric **  INITTIMEOUTS -- parse and set timeout values
208058112Seric **
208158112Seric **	Parameters:
208258112Seric **		val -- a pointer to the values.  If NULL, do initial
208358112Seric **			settings.
208458112Seric **
208558112Seric **	Returns:
208658112Seric **		none.
208758112Seric **
208858112Seric **	Side Effects:
208958112Seric **		Initializes the TimeOuts structure
209058112Seric */
209158112Seric 
209264255Seric #define SECONDS
209358112Seric #define MINUTES	* 60
209458112Seric #define HOUR	* 3600
209558112Seric 
209668481Seric inittimeouts(val)
209758112Seric 	register char *val;
209858112Seric {
209958112Seric 	register char *p;
210058671Seric 	extern time_t convtime();
210158112Seric 
210258112Seric 	if (val == NULL)
210358112Seric 	{
210458112Seric 		TimeOuts.to_initial = (time_t) 5 MINUTES;
210558112Seric 		TimeOuts.to_helo = (time_t) 5 MINUTES;
210658112Seric 		TimeOuts.to_mail = (time_t) 10 MINUTES;
210758112Seric 		TimeOuts.to_rcpt = (time_t) 1 HOUR;
210858112Seric 		TimeOuts.to_datainit = (time_t) 5 MINUTES;
210958112Seric 		TimeOuts.to_datablock = (time_t) 1 HOUR;
211058112Seric 		TimeOuts.to_datafinal = (time_t) 1 HOUR;
211158112Seric 		TimeOuts.to_rset = (time_t) 5 MINUTES;
211258112Seric 		TimeOuts.to_quit = (time_t) 2 MINUTES;
211358112Seric 		TimeOuts.to_nextcommand = (time_t) 1 HOUR;
211458112Seric 		TimeOuts.to_miscshort = (time_t) 2 MINUTES;
211568481Seric #if IDENTPROTO
211664255Seric 		TimeOuts.to_ident = (time_t) 30 SECONDS;
211768481Seric #else
211868481Seric 		TimeOuts.to_ident = (time_t) 0 SECONDS;
211968481Seric #endif
212068481Seric 		TimeOuts.to_fileopen = (time_t) 60 SECONDS;
212158112Seric 		return;
212258112Seric 	}
212358112Seric 
212458112Seric 	for (;; val = p)
212558112Seric 	{
212658112Seric 		while (isascii(*val) && isspace(*val))
212758112Seric 			val++;
212858112Seric 		if (*val == '\0')
212958112Seric 			break;
213058112Seric 		for (p = val; *p != '\0' && *p != ','; p++)
213158112Seric 			continue;
213258112Seric 		if (*p != '\0')
213358112Seric 			*p++ = '\0';
213458112Seric 
213558112Seric 		if (isascii(*val) && isdigit(*val))
213658112Seric 		{
213758112Seric 			/* old syntax -- set everything */
213858796Seric 			TimeOuts.to_mail = convtime(val, 'm');
213958112Seric 			TimeOuts.to_rcpt = TimeOuts.to_mail;
214058112Seric 			TimeOuts.to_datainit = TimeOuts.to_mail;
214158112Seric 			TimeOuts.to_datablock = TimeOuts.to_mail;
214258112Seric 			TimeOuts.to_datafinal = TimeOuts.to_mail;
214358112Seric 			TimeOuts.to_nextcommand = TimeOuts.to_mail;
214458112Seric 			continue;
214558112Seric 		}
214658112Seric 		else
214758112Seric 		{
214868481Seric 			register char *q = strchr(val, ':');
214958112Seric 
215068481Seric 			if (q == NULL && (q = strchr(val, '=')) == NULL)
215158112Seric 			{
215258112Seric 				/* syntax error */
215358112Seric 				continue;
215458112Seric 			}
215558112Seric 			*q++ = '\0';
215668481Seric 			settimeout(val, q);
215768481Seric 		}
215868481Seric 	}
215968481Seric }
216068481Seric /*
216168481Seric **  SETTIMEOUT -- set an individual timeout
216268481Seric **
216368481Seric **	Parameters:
216468481Seric **		name -- the name of the timeout.
216568481Seric **		val -- the value of the timeout.
216668481Seric **
216768481Seric **	Returns:
216868481Seric **		none.
216968481Seric */
217058112Seric 
217168481Seric settimeout(name, val)
217268481Seric 	char *name;
217368481Seric 	char *val;
217468481Seric {
217568481Seric 	register char *p;
217668481Seric 	time_t to;
217768481Seric 	extern time_t convtime();
217868481Seric 
217968481Seric 	to = convtime(val, 'm');
218068481Seric 	p = strchr(name, '.');
218168481Seric 	if (p != NULL)
218268481Seric 		*p++ = '\0';
218368481Seric 
218468481Seric 	if (strcasecmp(name, "initial") == 0)
218568481Seric 		TimeOuts.to_initial = to;
218668481Seric 	else if (strcasecmp(name, "mail") == 0)
218768481Seric 		TimeOuts.to_mail = to;
218868481Seric 	else if (strcasecmp(name, "rcpt") == 0)
218968481Seric 		TimeOuts.to_rcpt = to;
219068481Seric 	else if (strcasecmp(name, "datainit") == 0)
219168481Seric 		TimeOuts.to_datainit = to;
219268481Seric 	else if (strcasecmp(name, "datablock") == 0)
219368481Seric 		TimeOuts.to_datablock = to;
219468481Seric 	else if (strcasecmp(name, "datafinal") == 0)
219568481Seric 		TimeOuts.to_datafinal = to;
219668481Seric 	else if (strcasecmp(name, "command") == 0)
219768481Seric 		TimeOuts.to_nextcommand = to;
219868481Seric 	else if (strcasecmp(name, "rset") == 0)
219968481Seric 		TimeOuts.to_rset = to;
220068481Seric 	else if (strcasecmp(name, "helo") == 0)
220168481Seric 		TimeOuts.to_helo = to;
220268481Seric 	else if (strcasecmp(name, "quit") == 0)
220368481Seric 		TimeOuts.to_quit = to;
220468481Seric 	else if (strcasecmp(name, "misc") == 0)
220568481Seric 		TimeOuts.to_miscshort = to;
220668481Seric 	else if (strcasecmp(name, "ident") == 0)
220768481Seric 		TimeOuts.to_ident = to;
220868481Seric 	else if (strcasecmp(name, "fileopen") == 0)
220968481Seric 		TimeOuts.to_fileopen = to;
221068481Seric 	else if (strcasecmp(name, "queuewarn") == 0)
221168481Seric 	{
221268481Seric 		to = convtime(val, 'h');
221368481Seric 		if (p == NULL || strcmp(p, "*") == 0)
221468481Seric 		{
221568481Seric 			TimeOuts.to_q_warning[TOC_NORMAL] = to;
221668481Seric 			TimeOuts.to_q_warning[TOC_URGENT] = to;
221768481Seric 			TimeOuts.to_q_warning[TOC_NONURGENT] = to;
221858112Seric 		}
221968481Seric 		else if (strcasecmp(p, "normal") == 0)
222068481Seric 			TimeOuts.to_q_warning[TOC_NORMAL] = to;
222168481Seric 		else if (strcasecmp(p, "urgent") == 0)
222268481Seric 			TimeOuts.to_q_warning[TOC_URGENT] = to;
222368481Seric 		else if (strcasecmp(p, "non-urgent") == 0)
222468481Seric 			TimeOuts.to_q_warning[TOC_NONURGENT] = to;
222568481Seric 		else
222668481Seric 			syserr("settimeout: invalid queuewarn subtimeout %s", p);
222758112Seric 	}
222868481Seric 	else if (strcasecmp(name, "queuereturn") == 0)
222968481Seric 	{
223068481Seric 		to = convtime(val, 'd');
223168481Seric 		if (p == NULL || strcmp(p, "*") == 0)
223268481Seric 		{
223368481Seric 			TimeOuts.to_q_return[TOC_NORMAL] = to;
223468481Seric 			TimeOuts.to_q_return[TOC_URGENT] = to;
223568481Seric 			TimeOuts.to_q_return[TOC_NONURGENT] = to;
223668481Seric 		}
223768481Seric 		else if (strcasecmp(p, "normal") == 0)
223868481Seric 			TimeOuts.to_q_return[TOC_NORMAL] = to;
223968481Seric 		else if (strcasecmp(p, "urgent") == 0)
224068481Seric 			TimeOuts.to_q_return[TOC_URGENT] = to;
224168481Seric 		else if (strcasecmp(p, "non-urgent") == 0)
224268481Seric 			TimeOuts.to_q_return[TOC_NONURGENT] = to;
224368481Seric 		else
224468481Seric 			syserr("settimeout: invalid queuereturn subtimeout %s", p);
224568481Seric 	}
224668481Seric 	else
224768481Seric 		syserr("settimeout: invalid timeout %s", name);
224858112Seric }
2249