xref: /csrg-svn/usr.sbin/sendmail/src/readcf.c (revision 69453)
122709Sdist /*
268839Seric  * Copyright (c) 1983, 1995 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*69453Seric static char sccsid[] = "@(#)readcf.c	8.89 (Berkeley) 05/14/95";
1133731Sbostic #endif /* not lint */
1222709Sdist 
133313Seric # include "sendmail.h"
1464133Seric # include <grp.h>
1566334Seric #if NAMED_BIND
1657207Seric # include <resolv.h>
1757207Seric #endif
183308Seric 
193308Seric /*
203308Seric **  READCF -- read control file.
213308Seric **
223308Seric **	This routine reads the control file and builds the internal
233308Seric **	form.
243308Seric **
254432Seric **	The file is formatted as a sequence of lines, each taken
264432Seric **	atomically.  The first character of each line describes how
274432Seric **	the line is to be interpreted.  The lines are:
284432Seric **		Dxval		Define macro x to have value val.
294432Seric **		Cxword		Put word into class x.
304432Seric **		Fxfile [fmt]	Read file for lines to put into
314432Seric **				class x.  Use scanf string 'fmt'
324432Seric **				or "%s" if not present.  Fmt should
334432Seric **				only produce one string-valued result.
344432Seric **		Hname: value	Define header with field-name 'name'
354432Seric **				and value as specified; this will be
364432Seric **				macro expanded immediately before
374432Seric **				use.
384432Seric **		Sn		Use rewriting set n.
394432Seric **		Rlhs rhs	Rewrite addresses that match lhs to
404432Seric **				be rhs.
4124944Seric **		Mn arg=val...	Define mailer.  n is the internal name.
4224944Seric **				Args specify mailer parameters.
438252Seric **		Oxvalue		Set option x to value.
448252Seric **		Pname=value	Set precedence name to value.
4564718Seric **		Vversioncode[/vendorcode]
4664718Seric **				Version level/vendor name of
4764718Seric **				configuration syntax.
4853654Seric **		Kmapname mapclass arguments....
4953654Seric **				Define keyed lookup of a given class.
5053654Seric **				Arguments are class dependent.
514432Seric **
523308Seric **	Parameters:
533308Seric **		cfname -- control file name.
5454973Seric **		safe -- TRUE if this is the system config file;
5554973Seric **			FALSE otherwise.
5655012Seric **		e -- the main envelope.
573308Seric **
583308Seric **	Returns:
593308Seric **		none.
603308Seric **
613308Seric **	Side Effects:
623308Seric **		Builds several internal tables.
633308Seric */
643308Seric 
6555012Seric readcf(cfname, safe, e)
663308Seric 	char *cfname;
6754973Seric 	bool safe;
6855012Seric 	register ENVELOPE *e;
693308Seric {
703308Seric 	FILE *cf;
718547Seric 	int ruleset = 0;
7268481Seric 	int nextruleset = MAXRWSETS;
738547Seric 	char *q;
749350Seric 	struct rewrite *rwp = NULL;
7557135Seric 	char *bp;
7664718Seric 	auto char *ep;
7757589Seric 	int nfuzzy;
7864133Seric 	char *file;
7964133Seric 	bool optional;
8068481Seric 	int mid;
813308Seric 	char buf[MAXLINE];
823308Seric 	register char *p;
833308Seric 	extern char **copyplist();
8452647Seric 	struct stat statb;
855909Seric 	char exbuf[MAXLINE];
8665066Seric 	char pvpbuf[MAXLINE + MAXATOM];
8768481Seric 	static char *null_list[1] = { NULL };
8810709Seric 	extern char *munchstring();
8953654Seric 	extern void makemapentry();
903308Seric 
9152647Seric 	FileName = cfname;
9252647Seric 	LineNumber = 0;
9352647Seric 
943308Seric 	cf = fopen(cfname, "r");
953308Seric 	if (cf == NULL)
963308Seric 	{
9752647Seric 		syserr("cannot open");
983308Seric 		exit(EX_OSFILE);
993308Seric 	}
1003308Seric 
10152647Seric 	if (fstat(fileno(cf), &statb) < 0)
10252647Seric 	{
10352647Seric 		syserr("cannot fstat");
10452647Seric 		exit(EX_OSFILE);
10552647Seric 	}
10652647Seric 
10752647Seric 	if (!S_ISREG(statb.st_mode))
10852647Seric 	{
10952647Seric 		syserr("not a plain file");
11052647Seric 		exit(EX_OSFILE);
11152647Seric 	}
11252647Seric 
11352647Seric 	if (OpMode != MD_TEST && bitset(S_IWGRP|S_IWOTH, statb.st_mode))
11452647Seric 	{
11553037Seric 		if (OpMode == MD_DAEMON || OpMode == MD_FREEZE)
11653037Seric 			fprintf(stderr, "%s: WARNING: dangerous write permissions\n",
11753037Seric 				FileName);
11853037Seric #ifdef LOG
11953037Seric 		if (LogLevel > 0)
12053037Seric 			syslog(LOG_CRIT, "%s: WARNING: dangerous write permissions",
12153037Seric 				FileName);
12253037Seric #endif
12352647Seric 	}
12452647Seric 
12559254Seric #ifdef XLA
12659254Seric 	xla_zero();
12759254Seric #endif
12859254Seric 
12957135Seric 	while ((bp = fgetfolded(buf, sizeof buf, cf)) != NULL)
1303308Seric 	{
13157135Seric 		if (bp[0] == '#')
13257135Seric 		{
13357135Seric 			if (bp != buf)
13457135Seric 				free(bp);
13552637Seric 			continue;
13657135Seric 		}
13752637Seric 
13868481Seric 		/* do macro expansion mappings */
13957135Seric 		for (p = bp; *p != '\0'; p++)
14016157Seric 		{
14157135Seric 			if (*p == '#' && p > bp && ConfigLevel >= 3)
14252647Seric 			{
14352647Seric 				/* this is an on-line comment */
14452647Seric 				register char *e;
14552647Seric 
14658050Seric 				switch (*--p & 0377)
14752647Seric 				{
14858050Seric 				  case MACROEXPAND:
14952647Seric 					/* it's from $# -- let it go through */
15052647Seric 					p++;
15152647Seric 					break;
15252647Seric 
15352647Seric 				  case '\\':
15452647Seric 					/* it's backslash escaped */
15552647Seric 					(void) strcpy(p, p + 1);
15652647Seric 					break;
15752647Seric 
15852647Seric 				  default:
15952647Seric 					/* delete preceeding white space */
16058050Seric 					while (isascii(*p) && isspace(*p) && p > bp)
16152647Seric 						p--;
16256795Seric 					if ((e = strchr(++p, '\n')) != NULL)
16352647Seric 						(void) strcpy(p, e);
16452647Seric 					else
16552647Seric 						p[0] = p[1] = '\0';
16652647Seric 					break;
16752647Seric 				}
16852647Seric 				continue;
16952647Seric 			}
17052647Seric 
17168481Seric 			if (*p != '$' || p[1] == '\0')
17216157Seric 				continue;
17316157Seric 
17416157Seric 			if (p[1] == '$')
17516157Seric 			{
17616157Seric 				/* actual dollar sign.... */
17723111Seric 				(void) strcpy(p, p + 1);
17816157Seric 				continue;
17916157Seric 			}
18016157Seric 
18116157Seric 			/* convert to macro expansion character */
18268481Seric 			*p++ = MACROEXPAND;
18368481Seric 
18468481Seric 			/* convert macro name to code */
18568481Seric 			*p = macid(p, &ep);
18668481Seric 			if (ep != p)
18768481Seric 				strcpy(p + 1, ep);
18816157Seric 		}
18916157Seric 
19016157Seric 		/* interpret this line */
19164718Seric 		errno = 0;
19257135Seric 		switch (bp[0])
1933308Seric 		{
1943308Seric 		  case '\0':
1953308Seric 		  case '#':		/* comment */
1963308Seric 			break;
1973308Seric 
1983308Seric 		  case 'R':		/* rewriting rule */
19957135Seric 			for (p = &bp[1]; *p != '\0' && *p != '\t'; p++)
2003308Seric 				continue;
2013308Seric 
2023308Seric 			if (*p == '\0')
2035909Seric 			{
20465821Seric 				syserr("invalid rewrite line \"%s\" (tab expected)", bp);
2055909Seric 				break;
2065909Seric 			}
2075909Seric 
2085909Seric 			/* allocate space for the rule header */
2095909Seric 			if (rwp == NULL)
2105909Seric 			{
2115909Seric 				RewriteRules[ruleset] = rwp =
2125909Seric 					(struct rewrite *) xalloc(sizeof *rwp);
2135909Seric 			}
2143308Seric 			else
2153308Seric 			{
2165909Seric 				rwp->r_next = (struct rewrite *) xalloc(sizeof *rwp);
2175909Seric 				rwp = rwp->r_next;
2185909Seric 			}
2195909Seric 			rwp->r_next = NULL;
2203308Seric 
2215909Seric 			/* expand and save the LHS */
2225909Seric 			*p = '\0';
22368529Seric 			expand(&bp[1], exbuf, sizeof exbuf, e);
22465066Seric 			rwp->r_lhs = prescan(exbuf, '\t', pvpbuf,
22568711Seric 					     sizeof pvpbuf, NULL, NULL);
22657589Seric 			nfuzzy = 0;
2275909Seric 			if (rwp->r_lhs != NULL)
22857589Seric 			{
22957589Seric 				register char **ap;
23057589Seric 
2315909Seric 				rwp->r_lhs = copyplist(rwp->r_lhs, TRUE);
23257589Seric 
23357589Seric 				/* count the number of fuzzy matches in LHS */
23457589Seric 				for (ap = rwp->r_lhs; *ap != NULL; ap++)
23557589Seric 				{
23658148Seric 					char *botch;
23758148Seric 
23858148Seric 					botch = NULL;
23958050Seric 					switch (**ap & 0377)
24057589Seric 					{
24157589Seric 					  case MATCHZANY:
24257589Seric 					  case MATCHANY:
24357589Seric 					  case MATCHONE:
24457589Seric 					  case MATCHCLASS:
24557589Seric 					  case MATCHNCLASS:
24657589Seric 						nfuzzy++;
24758148Seric 						break;
24858148Seric 
24958148Seric 					  case MATCHREPL:
25058148Seric 						botch = "$0-$9";
25158148Seric 						break;
25258148Seric 
25358148Seric 					  case CANONNET:
25458148Seric 						botch = "$#";
25558148Seric 						break;
25658148Seric 
25758148Seric 					  case CANONUSER:
25858148Seric 						botch = "$:";
25958148Seric 						break;
26058148Seric 
26158148Seric 					  case CALLSUBR:
26258148Seric 						botch = "$>";
26358148Seric 						break;
26458148Seric 
26558148Seric 					  case CONDIF:
26658148Seric 						botch = "$?";
26758148Seric 						break;
26858148Seric 
26958148Seric 					  case CONDELSE:
27058148Seric 						botch = "$|";
27158148Seric 						break;
27258148Seric 
27358148Seric 					  case CONDFI:
27458148Seric 						botch = "$.";
27558148Seric 						break;
27658148Seric 
27758148Seric 					  case HOSTBEGIN:
27858148Seric 						botch = "$[";
27958148Seric 						break;
28058148Seric 
28158148Seric 					  case HOSTEND:
28258148Seric 						botch = "$]";
28358148Seric 						break;
28458148Seric 
28558148Seric 					  case LOOKUPBEGIN:
28658148Seric 						botch = "$(";
28758148Seric 						break;
28858148Seric 
28958148Seric 					  case LOOKUPEND:
29058148Seric 						botch = "$)";
29158148Seric 						break;
29257589Seric 					}
29358148Seric 					if (botch != NULL)
29458148Seric 						syserr("Inappropriate use of %s on LHS",
29558148Seric 							botch);
29657589Seric 				}
29757589Seric 			}
29856678Seric 			else
29968481Seric 			{
30056678Seric 				syserr("R line: null LHS");
30168481Seric 				rwp->r_lhs = null_list;
30268481Seric 			}
3035909Seric 
3045909Seric 			/* expand and save the RHS */
3055909Seric 			while (*++p == '\t')
3065909Seric 				continue;
3077231Seric 			q = p;
3087231Seric 			while (*p != '\0' && *p != '\t')
3097231Seric 				p++;
3107231Seric 			*p = '\0';
31168529Seric 			expand(q, exbuf, sizeof exbuf, e);
31265066Seric 			rwp->r_rhs = prescan(exbuf, '\t', pvpbuf,
31368711Seric 					     sizeof pvpbuf, NULL, NULL);
3145909Seric 			if (rwp->r_rhs != NULL)
31557589Seric 			{
31657589Seric 				register char **ap;
31757589Seric 
3185909Seric 				rwp->r_rhs = copyplist(rwp->r_rhs, TRUE);
31957589Seric 
32057589Seric 				/* check no out-of-bounds replacements */
32157589Seric 				nfuzzy += '0';
32257589Seric 				for (ap = rwp->r_rhs; *ap != NULL; ap++)
32357589Seric 				{
32458148Seric 					char *botch;
32558148Seric 
32658148Seric 					botch = NULL;
32758148Seric 					switch (**ap & 0377)
32857589Seric 					{
32958148Seric 					  case MATCHREPL:
33058148Seric 						if ((*ap)[1] <= '0' || (*ap)[1] > nfuzzy)
33158148Seric 						{
33258148Seric 							syserr("replacement $%c out of bounds",
33358148Seric 								(*ap)[1]);
33458148Seric 						}
33558148Seric 						break;
33658148Seric 
33758148Seric 					  case MATCHZANY:
33858148Seric 						botch = "$*";
33958148Seric 						break;
34058148Seric 
34158148Seric 					  case MATCHANY:
34258148Seric 						botch = "$+";
34358148Seric 						break;
34458148Seric 
34558148Seric 					  case MATCHONE:
34658148Seric 						botch = "$-";
34758148Seric 						break;
34858148Seric 
34958148Seric 					  case MATCHCLASS:
35058148Seric 						botch = "$=";
35158148Seric 						break;
35258148Seric 
35358148Seric 					  case MATCHNCLASS:
35458148Seric 						botch = "$~";
35558148Seric 						break;
35657589Seric 					}
35758148Seric 					if (botch != NULL)
35858148Seric 						syserr("Inappropriate use of %s on RHS",
35958148Seric 							botch);
36057589Seric 				}
36157589Seric 			}
36256678Seric 			else
36368481Seric 			{
36456678Seric 				syserr("R line: null RHS");
36568481Seric 				rwp->r_rhs = null_list;
36668481Seric 			}
3673308Seric 			break;
3683308Seric 
3694072Seric 		  case 'S':		/* select rewriting set */
37064440Seric 			for (p = &bp[1]; isascii(*p) && isspace(*p); p++)
37164440Seric 				continue;
37268481Seric 			if (!isascii(*p))
37364440Seric 			{
37464440Seric 				syserr("invalid argument to S line: \"%.20s\"",
37564440Seric 					&bp[1]);
37664440Seric 				break;
37764440Seric 			}
37868481Seric 			if (isdigit(*p))
3798056Seric 			{
38068481Seric 				ruleset = atoi(p);
38168481Seric 				if (ruleset >= MAXRWSETS / 2 || ruleset < 0)
38268481Seric 				{
38368481Seric 					syserr("bad ruleset %d (%d max)",
38468481Seric 						ruleset, MAXRWSETS / 2);
38568481Seric 					ruleset = 0;
38668481Seric 				}
3878056Seric 			}
38868481Seric 			else
38968481Seric 			{
39068481Seric 				STAB *s;
39168481Seric 				char delim;
39268481Seric 
39368481Seric 				q = p;
39468481Seric 				while (*p != '\0' && isascii(*p) &&
39568481Seric 				       (isalnum(*p) || strchr("-_$", *p) != NULL))
39668481Seric 					p++;
39768481Seric 				while (isascii(*p) && isspace(*p))
39868481Seric 					*p++ = '\0';
39968481Seric 				delim = *p;
40068481Seric 				if (delim != '\0')
40168481Seric 					*p++ = '\0';
40268481Seric 				s = stab(q, ST_RULESET, ST_ENTER);
40368481Seric 				if (s->s_ruleset != 0)
40468481Seric 					ruleset = s->s_ruleset;
40568481Seric 				else if (delim == '=')
40668481Seric 				{
40768481Seric 					ruleset = atoi(p);
40868481Seric 					if (ruleset >= MAXRWSETS / 2 || ruleset < 0)
40968481Seric 					{
41068481Seric 						syserr("bad ruleset %s = %d (%d max)",
41168481Seric 							q, ruleset, MAXRWSETS / 2);
41268481Seric 						ruleset = 0;
41368481Seric 					}
41468481Seric 				}
41568481Seric 				else if ((ruleset = --nextruleset) < MAXRWSETS / 2)
41668481Seric 				{
41768481Seric 					syserr("%s: too many named rulesets (%d max)",
41868481Seric 						q, MAXRWSETS / 2);
41968481Seric 					ruleset = 0;
42068481Seric 				}
42168481Seric 				s->s_ruleset = ruleset;
42268481Seric 			}
4234072Seric 			rwp = NULL;
4244072Seric 			break;
4254072Seric 
4263308Seric 		  case 'D':		/* macro definition */
42768481Seric 			mid = macid(&bp[1], &ep);
42868481Seric 			p = munchstring(ep, NULL);
42968481Seric 			define(mid, newstr(p), e);
4303308Seric 			break;
4313308Seric 
4323387Seric 		  case 'H':		/* required header line */
43368717Seric 			(void) chompheader(&bp[1], TRUE, NULL, e);
4343387Seric 			break;
4353387Seric 
4364061Seric 		  case 'C':		/* word class */
43768481Seric 		  case 'T':		/* trusted user (set class `t') */
43868481Seric 			if (bp[0] == 'C')
4394061Seric 			{
44068481Seric 				mid = macid(&bp[1], &ep);
44168529Seric 				expand(ep, exbuf, sizeof exbuf, e);
44268481Seric 				p = exbuf;
44368481Seric 			}
44468481Seric 			else
44568481Seric 			{
44668481Seric 				mid = 't';
44768481Seric 				p = &bp[1];
44868481Seric 			}
44968481Seric 			while (*p != '\0')
45068481Seric 			{
4514061Seric 				register char *wd;
4524061Seric 				char delim;
4534061Seric 
45458050Seric 				while (*p != '\0' && isascii(*p) && isspace(*p))
4554061Seric 					p++;
4564061Seric 				wd = p;
45758050Seric 				while (*p != '\0' && !(isascii(*p) && isspace(*p)))
4584061Seric 					p++;
4594061Seric 				delim = *p;
4604061Seric 				*p = '\0';
4614061Seric 				if (wd[0] != '\0')
46268481Seric 					setclass(mid, wd);
4634061Seric 				*p = delim;
4644061Seric 			}
4654061Seric 			break;
4664061Seric 
46759272Seric 		  case 'F':		/* word class from file */
46868481Seric 			mid = macid(&bp[1], &ep);
46968481Seric 			for (p = ep; isascii(*p) && isspace(*p); )
47064133Seric 				p++;
47164133Seric 			if (p[0] == '-' && p[1] == 'o')
47264133Seric 			{
47364133Seric 				optional = TRUE;
47464133Seric 				while (*p != '\0' && !(isascii(*p) && isspace(*p)))
47564133Seric 					p++;
47664133Seric 				while (isascii(*p) && isspace(*p))
47768481Seric 					p++;
47864133Seric 			}
47964133Seric 			else
48064133Seric 				optional = FALSE;
48164133Seric 			file = p;
48264133Seric 			while (*p != '\0' && !(isascii(*p) && isspace(*p)))
48364133Seric 				p++;
48459272Seric 			if (*p == '\0')
48559272Seric 				p = "%s";
48659272Seric 			else
48759272Seric 			{
48859272Seric 				*p = '\0';
48959272Seric 				while (isascii(*++p) && isspace(*p))
49059272Seric 					continue;
49159272Seric 			}
49264133Seric 			fileclass(bp[1], file, p, safe, optional);
49359272Seric 			break;
49459272Seric 
49559156Seric #ifdef XLA
49659156Seric 		  case 'L':		/* extended load average description */
49759156Seric 			xla_init(&bp[1]);
49859156Seric 			break;
49959156Seric #endif
50059156Seric 
5014096Seric 		  case 'M':		/* define mailer */
50257135Seric 			makemailer(&bp[1]);
5034096Seric 			break;
5044096Seric 
5058252Seric 		  case 'O':		/* set option */
50658734Seric 			setoption(bp[1], &bp[2], safe, FALSE, e);
5078252Seric 			break;
5088252Seric 
5098252Seric 		  case 'P':		/* set precedence */
5108252Seric 			if (NumPriorities >= MAXPRIORITIES)
5118252Seric 			{
5128547Seric 				toomany('P', MAXPRIORITIES);
5138252Seric 				break;
5148252Seric 			}
51557135Seric 			for (p = &bp[1]; *p != '\0' && *p != '=' && *p != '\t'; p++)
5168252Seric 				continue;
5178252Seric 			if (*p == '\0')
5188252Seric 				goto badline;
5198252Seric 			*p = '\0';
52057135Seric 			Priorities[NumPriorities].pri_name = newstr(&bp[1]);
5218252Seric 			Priorities[NumPriorities].pri_val = atoi(++p);
5228252Seric 			NumPriorities++;
5238252Seric 			break;
5248252Seric 
52552645Seric 		  case 'V':		/* configuration syntax version */
52664440Seric 			for (p = &bp[1]; isascii(*p) && isspace(*p); p++)
52764440Seric 				continue;
52864440Seric 			if (!isascii(*p) || !isdigit(*p))
52964440Seric 			{
53064440Seric 				syserr("invalid argument to V line: \"%.20s\"",
53164440Seric 					&bp[1]);
53264440Seric 				break;
53364440Seric 			}
53464718Seric 			ConfigLevel = strtol(p, &ep, 10);
53568805Seric 
53668805Seric 			/*
53768805Seric 			**  Do heuristic tweaking for back compatibility.
53868805Seric 			*/
53968805Seric 
54064279Seric 			if (ConfigLevel >= 5)
54164279Seric 			{
54264279Seric 				/* level 5 configs have short name in $w */
54364279Seric 				p = macvalue('w', e);
54464279Seric 				if (p != NULL && (p = strchr(p, '.')) != NULL)
54564279Seric 					*p = '\0';
54664279Seric 			}
54768805Seric 			if (ConfigLevel >= 6)
54868805Seric 			{
54968805Seric 				ColonOkInAddr = FALSE;
55068805Seric 			}
55168805Seric 
55268805Seric 			/*
55368805Seric 			**  Look for vendor code.
55468805Seric 			*/
55568805Seric 
55664718Seric 			if (*ep++ == '/')
55764718Seric 			{
55864718Seric 				/* extract vendor code */
55964718Seric 				for (p = ep; isascii(*p) && isalpha(*p); )
56064718Seric 					p++;
56164718Seric 				*p = '\0';
56264718Seric 
56364718Seric 				if (!setvendor(ep))
56464718Seric 					syserr("invalid V line vendor code: \"%s\"",
56564718Seric 						ep);
56664718Seric 			}
56752645Seric 			break;
56852645Seric 
56953654Seric 		  case 'K':
57057135Seric 			makemapentry(&bp[1]);
57153654Seric 			break;
57253654Seric 
5733308Seric 		  default:
5744061Seric 		  badline:
57557135Seric 			syserr("unknown control line \"%s\"", bp);
5763308Seric 		}
57757135Seric 		if (bp != buf)
57857135Seric 			free(bp);
5793308Seric 	}
58052637Seric 	if (ferror(cf))
58152637Seric 	{
58252647Seric 		syserr("I/O read error", cfname);
58352637Seric 		exit(EX_OSFILE);
58452637Seric 	}
58552637Seric 	fclose(cf);
5869381Seric 	FileName = NULL;
58756836Seric 
58868481Seric 	/* initialize host maps from local service tables */
58968481Seric 	inithostmaps();
59068481Seric 
59168481Seric 	/* determine if we need to do special name-server frotz */
59267905Seric 	{
59368481Seric 		int nmaps;
59468481Seric 		char *maptype[MAXMAPSTACK];
59568481Seric 		short mapreturn[MAXMAPACTIONS];
59668481Seric 
59768481Seric 		nmaps = switch_map_find("hosts", maptype, mapreturn);
59868481Seric 		UseNameServer = FALSE;
59968481Seric 		if (nmaps > 0 && nmaps <= MAXMAPSTACK)
60068481Seric 		{
60168481Seric 			register int mapno;
60268481Seric 
60368481Seric 			for (mapno = 0; mapno < nmaps && !UseNameServer; mapno++)
60468481Seric 			{
60568481Seric 				if (strcmp(maptype[mapno], "dns") == 0)
60668481Seric 					UseNameServer = TRUE;
60768481Seric 			}
60868481Seric 		}
60968481Seric 
61068481Seric #ifdef HESIOD
61168481Seric 		nmaps = switch_map_find("passwd", maptype, mapreturn);
61268481Seric 		UseHesiod = FALSE;
61368481Seric 		if (nmaps > 0 && nmaps <= MAXMAPSTACK)
61468481Seric 		{
61568481Seric 			register int mapno;
61668481Seric 
61768481Seric 			for (mapno = 0; mapno < nmaps && !UseHesiod; mapno++)
61868481Seric 			{
61968481Seric 				if (strcmp(maptype[mapno], "hesiod") == 0)
62068481Seric 					UseHesiod = TRUE;
62168481Seric 			}
62268481Seric 		}
62368204Seric #endif
62467905Seric 	}
6254096Seric }
6264096Seric /*
6278547Seric **  TOOMANY -- signal too many of some option
6288547Seric **
6298547Seric **	Parameters:
6308547Seric **		id -- the id of the error line
6318547Seric **		maxcnt -- the maximum possible values
6328547Seric **
6338547Seric **	Returns:
6348547Seric **		none.
6358547Seric **
6368547Seric **	Side Effects:
6378547Seric **		gives a syserr.
6388547Seric */
6398547Seric 
6408547Seric toomany(id, maxcnt)
6418547Seric 	char id;
6428547Seric 	int maxcnt;
6438547Seric {
6449381Seric 	syserr("too many %c lines, %d max", id, maxcnt);
6458547Seric }
6468547Seric /*
6474432Seric **  FILECLASS -- read members of a class from a file
6484432Seric **
6494432Seric **	Parameters:
6504432Seric **		class -- class to define.
6514432Seric **		filename -- name of file to read.
6524432Seric **		fmt -- scanf string to use for match.
65364133Seric **		safe -- if set, this is a safe read.
65464133Seric **		optional -- if set, it is not an error for the file to
65564133Seric **			not exist.
6564432Seric **
6574432Seric **	Returns:
6584432Seric **		none
6594432Seric **
6604432Seric **	Side Effects:
6614432Seric **
6624432Seric **		puts all lines in filename that match a scanf into
6634432Seric **			the named class.
6644432Seric */
6654432Seric 
66664133Seric fileclass(class, filename, fmt, safe, optional)
6674432Seric 	int class;
6684432Seric 	char *filename;
6694432Seric 	char *fmt;
67054973Seric 	bool safe;
67164133Seric 	bool optional;
6724432Seric {
67325808Seric 	FILE *f;
67468513Seric 	int sff;
675*69453Seric 	int pid;
676*69453Seric 	register char *p;
6774432Seric 	char buf[MAXLINE];
6784432Seric 
67966101Seric 	if (tTd(37, 2))
68066101Seric 		printf("fileclass(%s, fmt=%s)\n", filename, fmt);
68166101Seric 
68266031Seric 	if (filename[0] == '|')
68366031Seric 	{
684*69453Seric 		auto int fd;
685*69453Seric 		int i;
686*69453Seric 		char *argv[MAXPV + 1];
687*69453Seric 
688*69453Seric 		i = 0;
689*69453Seric 		for (p = strtok(&filename[1], " \t"); p != NULL; p = strtok(NULL, " \t"))
690*69453Seric 		{
691*69453Seric 			if (i >= MAXPV)
692*69453Seric 				break;
693*69453Seric 			argv[i++] = p;
694*69453Seric 		}
695*69453Seric 		argv[i] = NULL;
696*69453Seric 		pid = prog_open(argv, &fd, CurEnv);
697*69453Seric 		if (pid < 0)
698*69453Seric 			f = NULL;
699*69453Seric 		else
700*69453Seric 			f = fdopen(fd, "r");
70166031Seric 	}
702*69453Seric 	else
703*69453Seric 	{
704*69453Seric 		pid = -1;
705*69453Seric 		sff = SFF_REGONLY;
706*69453Seric 		if (safe)
707*69453Seric 			sff |= SFF_OPENASROOT;
708*69453Seric 		f = safefopen(filename, O_RDONLY, 0, sff);
709*69453Seric 	}
71068602Seric 	if (f == NULL)
71154973Seric 	{
71268602Seric 		if (!optional)
71368602Seric 			syserr("fileclass: cannot open %s", filename);
7144432Seric 		return;
7154432Seric 	}
7164432Seric 
7174432Seric 	while (fgets(buf, sizeof buf, f) != NULL)
7184432Seric 	{
7194432Seric 		register STAB *s;
72025808Seric 		register char *p;
72125808Seric # ifdef SCANF
7224432Seric 		char wordbuf[MAXNAME+1];
7234432Seric 
7244432Seric 		if (sscanf(buf, fmt, wordbuf) != 1)
7254432Seric 			continue;
72625808Seric 		p = wordbuf;
72756795Seric # else /* SCANF */
72825808Seric 		p = buf;
72956795Seric # endif /* SCANF */
73025808Seric 
73125808Seric 		/*
73225808Seric 		**  Break up the match into words.
73325808Seric 		*/
73425808Seric 
73525808Seric 		while (*p != '\0')
73625808Seric 		{
73725808Seric 			register char *q;
73825808Seric 
73925808Seric 			/* strip leading spaces */
74058050Seric 			while (isascii(*p) && isspace(*p))
74125808Seric 				p++;
74225808Seric 			if (*p == '\0')
74325808Seric 				break;
74425808Seric 
74525808Seric 			/* find the end of the word */
74625808Seric 			q = p;
74758050Seric 			while (*p != '\0' && !(isascii(*p) && isspace(*p)))
74825808Seric 				p++;
74925808Seric 			if (*p != '\0')
75025808Seric 				*p++ = '\0';
75125808Seric 
75225808Seric 			/* enter the word in the symbol table */
75366101Seric 			setclass(class, q);
75425808Seric 		}
7554432Seric 	}
7564432Seric 
75754973Seric 	(void) fclose(f);
758*69453Seric 	if (pid > 0)
759*69453Seric 		(void) waitfor(pid);
7604432Seric }
7614432Seric /*
7624096Seric **  MAKEMAILER -- define a new mailer.
7634096Seric **
7644096Seric **	Parameters:
76510327Seric **		line -- description of mailer.  This is in labeled
76610327Seric **			fields.  The fields are:
76768481Seric **			   A -- the argv for this mailer
76868481Seric **			   C -- the character set for MIME conversions
76968481Seric **			   D -- the directory to run in
77068481Seric **			   E -- the eol string
77168481Seric **			   F -- the flags associated with the mailer
77268481Seric **			   L -- the maximum line length
77368481Seric **			   M -- the maximum message size
77468816Seric **			   N -- the niceness at which to run
77568479Seric **			   P -- the path to the mailer
77668481Seric **			   R -- the recipient rewriting set
77768479Seric **			   S -- the sender rewriting set
77868481Seric **			   T -- the mailer type (for DSNs)
77968481Seric **			   U -- the uid to run as
78010327Seric **			The first word is the canonical name of the mailer.
7814096Seric **
7824096Seric **	Returns:
7834096Seric **		none.
7844096Seric **
7854096Seric **	Side Effects:
7864096Seric **		enters the mailer into the mailer table.
7874096Seric */
7883308Seric 
78921066Seric makemailer(line)
7904096Seric 	char *line;
7914096Seric {
7924096Seric 	register char *p;
7938067Seric 	register struct mailer *m;
7948067Seric 	register STAB *s;
7958067Seric 	int i;
79610327Seric 	char fcode;
79758020Seric 	auto char *endp;
7984096Seric 	extern int NextMailer;
79910327Seric 	extern char **makeargv();
80010327Seric 	extern char *munchstring();
8014096Seric 
80210327Seric 	/* allocate a mailer and set up defaults */
80310327Seric 	m = (struct mailer *) xalloc(sizeof *m);
80410327Seric 	bzero((char *) m, sizeof *m);
80510327Seric 	m->m_eol = "\n";
80668481Seric 	m->m_uid = m->m_gid = 0;
80710327Seric 
80810327Seric 	/* collect the mailer name */
80958050Seric 	for (p = line; *p != '\0' && *p != ',' && !(isascii(*p) && isspace(*p)); p++)
81010327Seric 		continue;
81110327Seric 	if (*p != '\0')
81210327Seric 		*p++ = '\0';
81310327Seric 	m->m_name = newstr(line);
81410327Seric 
81510327Seric 	/* now scan through and assign info from the fields */
81610327Seric 	while (*p != '\0')
81710327Seric 	{
81858333Seric 		auto char *delimptr;
81958333Seric 
82058050Seric 		while (*p != '\0' && (*p == ',' || (isascii(*p) && isspace(*p))))
82110327Seric 			p++;
82210327Seric 
82310327Seric 		/* p now points to field code */
82410327Seric 		fcode = *p;
82510327Seric 		while (*p != '\0' && *p != '=' && *p != ',')
82610327Seric 			p++;
82710327Seric 		if (*p++ != '=')
82810327Seric 		{
82952637Seric 			syserr("mailer %s: `=' expected", m->m_name);
83010327Seric 			return;
83110327Seric 		}
83258050Seric 		while (isascii(*p) && isspace(*p))
83310327Seric 			p++;
83410327Seric 
83510327Seric 		/* p now points to the field body */
83658333Seric 		p = munchstring(p, &delimptr);
83710327Seric 
83810327Seric 		/* install the field into the mailer struct */
83910327Seric 		switch (fcode)
84010327Seric 		{
84110327Seric 		  case 'P':		/* pathname */
84210327Seric 			m->m_mailer = newstr(p);
84310327Seric 			break;
84410327Seric 
84510327Seric 		  case 'F':		/* flags */
84610687Seric 			for (; *p != '\0'; p++)
84758050Seric 				if (!(isascii(*p) && isspace(*p)))
84852637Seric 					setbitn(*p, m->m_flags);
84910327Seric 			break;
85010327Seric 
85110327Seric 		  case 'S':		/* sender rewriting ruleset */
85210327Seric 		  case 'R':		/* recipient rewriting ruleset */
85358020Seric 			i = strtol(p, &endp, 10);
85410327Seric 			if (i < 0 || i >= MAXRWSETS)
85510327Seric 			{
85610327Seric 				syserr("invalid rewrite set, %d max", MAXRWSETS);
85710327Seric 				return;
85810327Seric 			}
85910327Seric 			if (fcode == 'S')
86058020Seric 				m->m_sh_rwset = m->m_se_rwset = i;
86110327Seric 			else
86258020Seric 				m->m_rh_rwset = m->m_re_rwset = i;
86358020Seric 
86458020Seric 			p = endp;
86559985Seric 			if (*p++ == '/')
86658020Seric 			{
86758020Seric 				i = strtol(p, NULL, 10);
86858020Seric 				if (i < 0 || i >= MAXRWSETS)
86958020Seric 				{
87058020Seric 					syserr("invalid rewrite set, %d max",
87158020Seric 						MAXRWSETS);
87258020Seric 					return;
87358020Seric 				}
87458020Seric 				if (fcode == 'S')
87558020Seric 					m->m_sh_rwset = i;
87658020Seric 				else
87758020Seric 					m->m_rh_rwset = i;
87858020Seric 			}
87910327Seric 			break;
88010327Seric 
88110327Seric 		  case 'E':		/* end of line string */
88210327Seric 			m->m_eol = newstr(p);
88310327Seric 			break;
88410327Seric 
88510327Seric 		  case 'A':		/* argument vector */
88610327Seric 			m->m_argv = makeargv(p);
88710327Seric 			break;
88810701Seric 
88910701Seric 		  case 'M':		/* maximum message size */
89010701Seric 			m->m_maxsize = atol(p);
89110701Seric 			break;
89252106Seric 
89352106Seric 		  case 'L':		/* maximum line length */
89452106Seric 			m->m_linelimit = atoi(p);
89552106Seric 			break;
89658935Seric 
89768816Seric 		  case 'N':		/* run niceness */
89868816Seric 			m->m_nice = atoi(p);
89968816Seric 			break;
90068816Seric 
90158935Seric 		  case 'D':		/* working directory */
90258935Seric 			m->m_execdir = newstr(p);
90358935Seric 			break;
90468481Seric 
90568481Seric 		  case 'C':		/* default charset */
90668481Seric 			m->m_defcharset = newstr(p);
90768481Seric 			break;
90868481Seric 
90968481Seric 		  case 'T':		/* MTA Type */
91068481Seric 			m->m_mtatype = newstr(p);
91168481Seric 			p = strchr(m->m_mtatype, '/');
91268481Seric 			if (p != NULL)
91368481Seric 			{
91468481Seric 				*p++ = '\0';
91568481Seric 				if (*p == '\0')
91668481Seric 					p = NULL;
91768481Seric 			}
91868481Seric 			if (p == NULL)
91968481Seric 				m->m_addrtype = m->m_mtatype;
92068481Seric 			else
92168481Seric 			{
92268481Seric 				m->m_addrtype = p;
92368481Seric 				p = strchr(p, '/');
92468481Seric 			}
92568481Seric 			if (p != NULL)
92668481Seric 			{
92768481Seric 				*p++ = '\0';
92868481Seric 				if (*p == '\0')
92968481Seric 					p = NULL;
93068481Seric 			}
93168481Seric 			if (p == NULL)
93268481Seric 				m->m_diagtype = m->m_mtatype;
93368481Seric 			else
93468481Seric 				m->m_diagtype = p;
93568481Seric 			break;
93668481Seric 
93768481Seric 		  case 'U':		/* user id */
93868481Seric 			if (isascii(*p) && !isdigit(*p))
93968481Seric 			{
94068481Seric 				char *q = p;
94168481Seric 				struct passwd *pw;
94268481Seric 
94368481Seric 				while (isascii(*p) && isalnum(*p))
94468481Seric 					p++;
94568481Seric 				while (isascii(*p) && isspace(*p))
94668481Seric 					*p++ = '\0';
94768481Seric 				if (*p != '\0')
94868481Seric 					*p++ = '\0';
94968693Seric 				pw = sm_getpwnam(q);
95068481Seric 				if (pw == NULL)
95168481Seric 					syserr("readcf: mailer U= flag: unknown user %s", q);
95268481Seric 				else
95368481Seric 				{
95468481Seric 					m->m_uid = pw->pw_uid;
95568481Seric 					m->m_gid = pw->pw_gid;
95668481Seric 				}
95768481Seric 			}
95868481Seric 			else
95968481Seric 			{
96068481Seric 				auto char *q;
96168481Seric 
96268481Seric 				m->m_uid = strtol(p, &q, 0);
96368481Seric 				p = q;
96468481Seric 			}
96568481Seric 			while (isascii(*p) && isspace(*p))
96668481Seric 				p++;
96768481Seric 			if (*p == '\0')
96868481Seric 				break;
96968481Seric 			if (isascii(*p) && !isdigit(*p))
97068481Seric 			{
97168481Seric 				char *q = p;
97268481Seric 				struct group *gr;
97368481Seric 
97468481Seric 				while (isascii(*p) && isalnum(*p))
97568481Seric 					p++;
97668481Seric 				*p++ = '\0';
97768481Seric 				gr = getgrnam(q);
97868481Seric 				if (gr == NULL)
97968481Seric 					syserr("readcf: mailer U= flag: unknown group %s", q);
98068481Seric 				else
98168481Seric 					m->m_gid = gr->gr_gid;
98268481Seric 			}
98368481Seric 			else
98468481Seric 			{
98568481Seric 				m->m_gid = strtol(p, NULL, 0);
98668481Seric 			}
98768481Seric 			break;
98810327Seric 		}
98910327Seric 
99058333Seric 		p = delimptr;
99110327Seric 	}
99210327Seric 
99358321Seric 	/* do some rationality checking */
99458321Seric 	if (m->m_argv == NULL)
99558321Seric 	{
99658321Seric 		syserr("M%s: A= argument required", m->m_name);
99758321Seric 		return;
99858321Seric 	}
99958321Seric 	if (m->m_mailer == NULL)
100058321Seric 	{
100158321Seric 		syserr("M%s: P= argument required", m->m_name);
100258321Seric 		return;
100358321Seric 	}
100458321Seric 
10054096Seric 	if (NextMailer >= MAXMAILERS)
10064096Seric 	{
10079381Seric 		syserr("too many mailers defined (%d max)", MAXMAILERS);
10084096Seric 		return;
10094096Seric 	}
101057402Seric 
101168481Seric 	/* do some heuristic cleanup for back compatibility */
101268481Seric 	if (bitnset(M_LIMITS, m->m_flags))
101368481Seric 	{
101468481Seric 		if (m->m_linelimit == 0)
101568481Seric 			m->m_linelimit = SMTPLINELIM;
101668481Seric 		if (ConfigLevel < 2)
101768481Seric 			setbitn(M_7BITS, m->m_flags);
101868481Seric 	}
101968481Seric 
102068481Seric 	if (ConfigLevel < 6 &&
102168481Seric 	    (strcmp(m->m_mailer, "[IPC]") == 0 ||
102268481Seric 	     strcmp(m->m_mailer, "[TCP]") == 0))
102368481Seric 	{
102468481Seric 		if (m->m_mtatype == NULL)
102568481Seric 			m->m_mtatype = "dns";
102668481Seric 		if (m->m_addrtype == NULL)
102768481Seric 			m->m_addrtype = "rfc822";
102868481Seric 		if (m->m_diagtype == NULL)
102968481Seric 			m->m_diagtype = "smtp";
103068481Seric 	}
103168481Seric 
103268481Seric 	/* enter the mailer into the symbol table */
103310327Seric 	s = stab(m->m_name, ST_MAILER, ST_ENTER);
103457402Seric 	if (s->s_mailer != NULL)
103557402Seric 	{
103657402Seric 		i = s->s_mailer->m_mno;
103757402Seric 		free(s->s_mailer);
103857402Seric 	}
103957402Seric 	else
104057402Seric 	{
104157402Seric 		i = NextMailer++;
104257402Seric 	}
104357402Seric 	Mailer[i] = s->s_mailer = m;
104457454Seric 	m->m_mno = i;
104510327Seric }
104610327Seric /*
104710327Seric **  MUNCHSTRING -- translate a string into internal form.
104810327Seric **
104910327Seric **	Parameters:
105010327Seric **		p -- the string to munch.
105158333Seric **		delimptr -- if non-NULL, set to the pointer of the
105258333Seric **			field delimiter character.
105310327Seric **
105410327Seric **	Returns:
105510327Seric **		the munched string.
105610327Seric */
10574096Seric 
105810327Seric char *
105958333Seric munchstring(p, delimptr)
106010327Seric 	register char *p;
106158333Seric 	char **delimptr;
106210327Seric {
106310327Seric 	register char *q;
106410327Seric 	bool backslash = FALSE;
106510327Seric 	bool quotemode = FALSE;
106610327Seric 	static char buf[MAXLINE];
10674096Seric 
106810327Seric 	for (q = buf; *p != '\0'; p++)
10694096Seric 	{
107010327Seric 		if (backslash)
107110327Seric 		{
107210327Seric 			/* everything is roughly literal */
107310357Seric 			backslash = FALSE;
107410327Seric 			switch (*p)
107510327Seric 			{
107610327Seric 			  case 'r':		/* carriage return */
107710327Seric 				*q++ = '\r';
107810327Seric 				continue;
107910327Seric 
108010327Seric 			  case 'n':		/* newline */
108110327Seric 				*q++ = '\n';
108210327Seric 				continue;
108310327Seric 
108410327Seric 			  case 'f':		/* form feed */
108510327Seric 				*q++ = '\f';
108610327Seric 				continue;
108710327Seric 
108810327Seric 			  case 'b':		/* backspace */
108910327Seric 				*q++ = '\b';
109010327Seric 				continue;
109110327Seric 			}
109210327Seric 			*q++ = *p;
109310327Seric 		}
109410327Seric 		else
109510327Seric 		{
109610327Seric 			if (*p == '\\')
109710327Seric 				backslash = TRUE;
109810327Seric 			else if (*p == '"')
109910327Seric 				quotemode = !quotemode;
110010327Seric 			else if (quotemode || *p != ',')
110110327Seric 				*q++ = *p;
110210327Seric 			else
110310327Seric 				break;
110410327Seric 		}
11054096Seric 	}
11064096Seric 
110758333Seric 	if (delimptr != NULL)
110858333Seric 		*delimptr = p;
110910327Seric 	*q++ = '\0';
111010327Seric 	return (buf);
111110327Seric }
111210327Seric /*
111310327Seric **  MAKEARGV -- break up a string into words
111410327Seric **
111510327Seric **	Parameters:
111610327Seric **		p -- the string to break up.
111710327Seric **
111810327Seric **	Returns:
111910327Seric **		a char **argv (dynamically allocated)
112010327Seric **
112110327Seric **	Side Effects:
112210327Seric **		munges p.
112310327Seric */
11244096Seric 
112510327Seric char **
112610327Seric makeargv(p)
112710327Seric 	register char *p;
112810327Seric {
112910327Seric 	char *q;
113010327Seric 	int i;
113110327Seric 	char **avp;
113210327Seric 	char *argv[MAXPV + 1];
113310327Seric 
113410327Seric 	/* take apart the words */
113510327Seric 	i = 0;
113610327Seric 	while (*p != '\0' && i < MAXPV)
11374096Seric 	{
113810327Seric 		q = p;
113958050Seric 		while (*p != '\0' && !(isascii(*p) && isspace(*p)))
114010327Seric 			p++;
114158050Seric 		while (isascii(*p) && isspace(*p))
114210327Seric 			*p++ = '\0';
114310327Seric 		argv[i++] = newstr(q);
11444096Seric 	}
114510327Seric 	argv[i++] = NULL;
11464096Seric 
114710327Seric 	/* now make a copy of the argv */
114810327Seric 	avp = (char **) xalloc(sizeof *avp * i);
114916893Seric 	bcopy((char *) argv, (char *) avp, sizeof *avp * i);
115010327Seric 
115110327Seric 	return (avp);
11523308Seric }
11533308Seric /*
11543308Seric **  PRINTRULES -- print rewrite rules (for debugging)
11553308Seric **
11563308Seric **	Parameters:
11573308Seric **		none.
11583308Seric **
11593308Seric **	Returns:
11603308Seric **		none.
11613308Seric **
11623308Seric **	Side Effects:
11633308Seric **		prints rewrite rules.
11643308Seric */
11653308Seric 
11663308Seric printrules()
11673308Seric {
11683308Seric 	register struct rewrite *rwp;
11694072Seric 	register int ruleset;
11703308Seric 
11714072Seric 	for (ruleset = 0; ruleset < 10; ruleset++)
11723308Seric 	{
11734072Seric 		if (RewriteRules[ruleset] == NULL)
11744072Seric 			continue;
11758067Seric 		printf("\n----Rule Set %d:", ruleset);
11763308Seric 
11774072Seric 		for (rwp = RewriteRules[ruleset]; rwp != NULL; rwp = rwp->r_next)
11783308Seric 		{
11798067Seric 			printf("\nLHS:");
11808067Seric 			printav(rwp->r_lhs);
11818067Seric 			printf("RHS:");
11828067Seric 			printav(rwp->r_rhs);
11833308Seric 		}
11843308Seric 	}
11853308Seric }
118668481Seric /*
118768481Seric **  PRINTMAILER -- print mailer structure (for debugging)
118868481Seric **
118968481Seric **	Parameters:
119068481Seric **		m -- the mailer to print
119168481Seric **
119268481Seric **	Returns:
119368481Seric **		none.
119468481Seric */
11954319Seric 
119668481Seric printmailer(m)
119768481Seric 	register MAILER *m;
119868481Seric {
119968481Seric 	int j;
120068481Seric 
120168481Seric 	printf("mailer %d (%s): P=%s S=%d/%d R=%d/%d M=%ld U=%d:%d F=",
120268481Seric 		m->m_mno, m->m_name,
120368481Seric 		m->m_mailer, m->m_se_rwset, m->m_sh_rwset,
120468481Seric 		m->m_re_rwset, m->m_rh_rwset, m->m_maxsize,
120568481Seric 		m->m_uid, m->m_gid);
120668481Seric 	for (j = '\0'; j <= '\177'; j++)
120768481Seric 		if (bitnset(j, m->m_flags))
120868481Seric 			(void) putchar(j);
120968481Seric 	printf(" L=%d E=", m->m_linelimit);
121068481Seric 	xputs(m->m_eol);
121168481Seric 	if (m->m_defcharset != NULL)
121268481Seric 		printf(" C=%s", m->m_defcharset);
121368481Seric 	printf(" T=%s/%s/%s",
121468481Seric 		m->m_mtatype == NULL ? "<undefined>" : m->m_mtatype,
121568481Seric 		m->m_addrtype == NULL ? "<undefined>" : m->m_addrtype,
121668481Seric 		m->m_diagtype == NULL ? "<undefined>" : m->m_diagtype);
121768481Seric 	if (m->m_argv != NULL)
121868481Seric 	{
121968481Seric 		char **a = m->m_argv;
122068481Seric 
122168481Seric 		printf(" A=");
122268481Seric 		while (*a != NULL)
122368481Seric 		{
122468481Seric 			if (a != m->m_argv)
122568481Seric 				printf(" ");
122668481Seric 			xputs(*a++);
122768481Seric 		}
122868481Seric 	}
122968481Seric 	printf("\n");
123068481Seric }
12314096Seric /*
12328256Seric **  SETOPTION -- set global processing option
12338256Seric **
12348256Seric **	Parameters:
12358256Seric **		opt -- option name.
12368256Seric **		val -- option value (as a text string).
123721755Seric **		safe -- set if this came from a configuration file.
123821755Seric **			Some options (if set from the command line) will
123921755Seric **			reset the user id to avoid security problems.
12408269Seric **		sticky -- if set, don't let other setoptions override
12418269Seric **			this value.
124258734Seric **		e -- the main envelope.
12438256Seric **
12448256Seric **	Returns:
12458256Seric **		none.
12468256Seric **
12478256Seric **	Side Effects:
12488256Seric **		Sets options as implied by the arguments.
12498256Seric */
12508256Seric 
125110687Seric static BITMAP	StickyOpt;		/* set if option is stuck */
12528269Seric 
125357207Seric 
125466334Seric #if NAMED_BIND
125557207Seric 
125657207Seric struct resolverflags
125757207Seric {
125857207Seric 	char	*rf_name;	/* name of the flag */
125957207Seric 	long	rf_bits;	/* bits to set/clear */
126057207Seric } ResolverFlags[] =
126157207Seric {
126257207Seric 	"debug",	RES_DEBUG,
126357207Seric 	"aaonly",	RES_AAONLY,
126457207Seric 	"usevc",	RES_USEVC,
126557207Seric 	"primary",	RES_PRIMARY,
126657207Seric 	"igntc",	RES_IGNTC,
126757207Seric 	"recurse",	RES_RECURSE,
126857207Seric 	"defnames",	RES_DEFNAMES,
126957207Seric 	"stayopen",	RES_STAYOPEN,
127057207Seric 	"dnsrch",	RES_DNSRCH,
127165583Seric 	"true",		0,		/* to avoid error on old syntax */
127257207Seric 	NULL,		0
127357207Seric };
127457207Seric 
127557207Seric #endif
127657207Seric 
127768481Seric struct optioninfo
127868481Seric {
127968481Seric 	char	*o_name;	/* long name of option */
128068481Seric 	u_char	o_code;		/* short name of option */
128168481Seric 	bool	o_safe;		/* safe for random people to use */
128268481Seric } OptionTab[] =
128368481Seric {
128468481Seric 	"SevenBitInput",	'7',		TRUE,
128568481Seric 	"EightBitMode",		'8',		TRUE,
128668481Seric 	"AliasFile",		'A',		FALSE,
128768481Seric 	"AliasWait",		'a',		FALSE,
128868481Seric 	"BlankSub",		'B',		FALSE,
128968481Seric 	"MinFreeBlocks",	'b',		TRUE,
129068481Seric 	"CheckpointInterval",	'C',		TRUE,
129168481Seric 	"HoldExpensive",	'c',		FALSE,
129268481Seric 	"AutoRebuildAliases",	'D',		FALSE,
129368481Seric 	"DeliveryMode",		'd',		TRUE,
129468481Seric 	"ErrorHeader",		'E',		FALSE,
129568481Seric 	"ErrorMode",		'e',		TRUE,
129668481Seric 	"TempFileMode",		'F',		FALSE,
129768481Seric 	"SaveFromLine",		'f',		FALSE,
129868481Seric 	"MatchGECOS",		'G',		FALSE,
129968481Seric 	"HelpFile",		'H',		FALSE,
130068481Seric 	"MaxHopCount",		'h',		FALSE,
130168569Seric 	"ResolverOptions",	'I',		FALSE,
130268481Seric 	"IgnoreDots",		'i',		TRUE,
130368481Seric 	"ForwardPath",		'J',		FALSE,
130468481Seric 	"SendMimeErrors",	'j',		TRUE,
130568481Seric 	"ConnectionCacheSize",	'k',		FALSE,
130668481Seric 	"ConnectionCacheTimeout", 'K',		FALSE,
130768481Seric 	"UseErrorsTo",		'l',		FALSE,
130868481Seric 	"LogLevel",		'L',		FALSE,
130968481Seric 	"MeToo",		'm',		TRUE,
131068481Seric 	"CheckAliases",		'n',		FALSE,
131168481Seric 	"OldStyleHeaders",	'o',		TRUE,
131268481Seric 	"DaemonPortOptions",	'O',		FALSE,
131368481Seric 	"PrivacyOptions",	'p',		TRUE,
131468481Seric 	"PostmasterCopy",	'P',		FALSE,
131568481Seric 	"QueueFactor",		'q',		FALSE,
131668481Seric 	"QueueDirectory",	'Q',		FALSE,
131768481Seric 	"DontPruneRoutes",	'R',		FALSE,
131868481Seric 	"Timeout",		'r',		TRUE,
131968481Seric 	"StatusFile",		'S',		FALSE,
132068481Seric 	"SuperSafe",		's',		TRUE,
132168481Seric 	"QueueTimeout",		'T',		FALSE,
132268481Seric 	"TimeZoneSpec",		't',		FALSE,
132368481Seric 	"UserDatabaseSpec",	'U',		FALSE,
132468481Seric 	"DefaultUser",		'u',		FALSE,
132568481Seric 	"FallbackMXhost",	'V',		FALSE,
132668481Seric 	"Verbose",		'v',		TRUE,
132768481Seric 	"TryNullMXList",	'w',		TRUE,
132868481Seric 	"QueueLA",		'x',		FALSE,
132968481Seric 	"RefuseLA",		'X',		FALSE,
133068481Seric 	"RecipientFactor",	'y',		FALSE,
133168569Seric 	"ForkEachJob",		'Y',		FALSE,
133268481Seric 	"ClassFactor",		'z',		FALSE,
133368569Seric 	"RetryFactor",		'Z',		FALSE,
133468481Seric #define O_QUEUESORTORD	0x81
133568481Seric 	"QueueSortOrder",	O_QUEUESORTORD,	TRUE,
133669401Seric #define O_HOSTSFILE	0x82
133769401Seric 	"HostsFile",		O_HOSTSFILE,	FALSE,
133868481Seric #define O_MQA		0x83
133968481Seric 	"MinQueueAge",		O_MQA,		TRUE,
134068481Seric #define O_MHSA		0x84
134168481Seric /*
134268481Seric 	"MaxHostStatAge",	O_MHSA,		TRUE,
134368481Seric */
134468481Seric #define O_DEFCHARSET	0x85
134568481Seric 	"DefaultCharSet",	O_DEFCHARSET,	TRUE,
134668481Seric #define O_SSFILE	0x86
134768481Seric 	"ServiceSwitchFile",	O_SSFILE,	FALSE,
134868481Seric #define O_DIALDELAY	0x87
134968481Seric 	"DialDelay",		O_DIALDELAY,	TRUE,
135068481Seric #define O_NORCPTACTION	0x88
135168481Seric 	"NoRecipientAction",	O_NORCPTACTION,	TRUE,
135268490Seric #define O_SAFEFILEENV	0x89
135368490Seric 	"SafeFileEnvironment",	O_SAFEFILEENV,	FALSE,
135468569Seric #define O_MAXMSGSIZE	0x8a
135568569Seric 	"MaxMessageSize",	O_MAXMSGSIZE,	FALSE,
135668756Seric #define O_COLONOKINADDR	0x8b
135768756Seric 	"ColonOkInAddr",	O_COLONOKINADDR, TRUE,
135868481Seric 
135968481Seric 	NULL,			'\0',		FALSE,
136068481Seric };
136168481Seric 
136268481Seric 
136368481Seric 
136458734Seric setoption(opt, val, safe, sticky, e)
136568481Seric 	u_char opt;
13668256Seric 	char *val;
136721755Seric 	bool safe;
13688269Seric 	bool sticky;
136958734Seric 	register ENVELOPE *e;
13708256Seric {
137157207Seric 	register char *p;
137268481Seric 	register struct optioninfo *o;
137368481Seric 	char *subopt;
13748265Seric 	extern bool atobool();
137512633Seric 	extern time_t convtime();
137614879Seric 	extern int QueueLA;
137714879Seric 	extern int RefuseLA;
137864718Seric 	extern bool Warn_Q_option;
13798256Seric 
138068481Seric 	errno = 0;
138168481Seric 	if (opt == ' ')
138268481Seric 	{
138368481Seric 		/* full word options */
138468481Seric 		struct optioninfo *sel;
138568481Seric 
138668481Seric 		p = strchr(val, '=');
138768481Seric 		if (p == NULL)
138868481Seric 			p = &val[strlen(val)];
138968481Seric 		while (*--p == ' ')
139068481Seric 			continue;
139168481Seric 		while (*++p == ' ')
139268481Seric 			*p = '\0';
139368481Seric 		if (p == val)
139468481Seric 		{
139568481Seric 			syserr("readcf: null option name");
139668481Seric 			return;
139768481Seric 		}
139868481Seric 		if (*p == '=')
139968481Seric 			*p++ = '\0';
140068481Seric 		while (*p == ' ')
140168481Seric 			p++;
140268481Seric 		subopt = strchr(val, '.');
140368481Seric 		if (subopt != NULL)
140468481Seric 			*subopt++ = '\0';
140568481Seric 		sel = NULL;
140668481Seric 		for (o = OptionTab; o->o_name != NULL; o++)
140768481Seric 		{
140868481Seric 			if (strncasecmp(o->o_name, val, strlen(val)) != 0)
140968481Seric 				continue;
141068481Seric 			if (strlen(o->o_name) == strlen(val))
141168481Seric 			{
141268481Seric 				/* completely specified -- this must be it */
141368481Seric 				sel = NULL;
141468481Seric 				break;
141568481Seric 			}
141668481Seric 			if (sel != NULL)
141768481Seric 				break;
141868481Seric 			sel = o;
141968481Seric 		}
142068481Seric 		if (sel != NULL && o->o_name == NULL)
142168481Seric 			o = sel;
142268481Seric 		else if (o->o_name == NULL)
142368481Seric 		{
142468481Seric 			syserr("readcf: unknown option name %s", val);
142568481Seric 			return;
142668481Seric 		}
142768481Seric 		else if (sel != NULL)
142868481Seric 		{
142968481Seric 			syserr("readcf: ambiguous option name %s (matches %s and %s)",
143068481Seric 				val, sel->o_name, o->o_name);
143168481Seric 			return;
143268481Seric 		}
143368481Seric 		if (strlen(val) != strlen(o->o_name))
143468481Seric 		{
143568481Seric 			bool oldVerbose = Verbose;
143668481Seric 
143768481Seric 			Verbose = TRUE;
143868481Seric 			message("Option %s used as abbreviation for %s",
143968481Seric 				val, o->o_name);
144068481Seric 			Verbose = oldVerbose;
144168481Seric 		}
144268481Seric 		opt = o->o_code;
144368481Seric 		val = p;
144468481Seric 	}
144568481Seric 	else
144668481Seric 	{
144768481Seric 		for (o = OptionTab; o->o_name != NULL; o++)
144868481Seric 		{
144968481Seric 			if (o->o_code == opt)
145068481Seric 				break;
145168481Seric 		}
145268481Seric 		subopt = NULL;
145368481Seric 	}
145468481Seric 
14558256Seric 	if (tTd(37, 1))
145668481Seric 	{
145768481Seric 		printf(isascii(opt) && isprint(opt) ?
145868481Seric 			    "setoption %s (%c).%s=%s" :
145968481Seric 			    "setoption %s (0x%x).%s=%s",
146068481Seric 			o->o_name == NULL ? "<unknown>" : o->o_name,
146168481Seric 			opt,
146268481Seric 			subopt == NULL ? "" : subopt,
146368481Seric 			val);
146468481Seric 	}
14658256Seric 
14668256Seric 	/*
14678269Seric 	**  See if this option is preset for us.
14688256Seric 	*/
14698256Seric 
147059731Seric 	if (!sticky && bitnset(opt, StickyOpt))
14718269Seric 	{
14729341Seric 		if (tTd(37, 1))
14739341Seric 			printf(" (ignored)\n");
14748269Seric 		return;
14758269Seric 	}
14768269Seric 
147721755Seric 	/*
147821755Seric 	**  Check to see if this option can be specified by this user.
147921755Seric 	*/
148021755Seric 
148163787Seric 	if (!safe && RealUid == 0)
148221755Seric 		safe = TRUE;
148368481Seric 	if (!safe && !o->o_safe)
148421755Seric 	{
148539111Srick 		if (opt != 'M' || (val[0] != 'r' && val[0] != 's'))
148621755Seric 		{
148736582Sbostic 			if (tTd(37, 1))
148836582Sbostic 				printf(" (unsafe)");
148963787Seric 			if (RealUid != geteuid())
149036582Sbostic 			{
149151210Seric 				if (tTd(37, 1))
149251210Seric 					printf("(Resetting uid)");
149363787Seric 				(void) setgid(RealGid);
149463787Seric 				(void) setuid(RealUid);
149536582Sbostic 			}
149621755Seric 		}
149721755Seric 	}
149851210Seric 	if (tTd(37, 1))
149917985Seric 		printf("\n");
15008269Seric 
150168481Seric 	switch (opt & 0xff)
15028256Seric 	{
150359709Seric 	  case '7':		/* force seven-bit input */
150468481Seric 		SevenBitInput = atobool(val);
150552106Seric 		break;
150652106Seric 
150768481Seric 	  case '8':		/* handling of 8-bit input */
150868481Seric 		switch (*val)
150968481Seric 		{
151068481Seric 		  case 'm':		/* convert 8-bit, convert MIME */
151168481Seric 			MimeMode = MM_CVTMIME|MM_MIME8BIT;
151268481Seric 			break;
151368481Seric 
151468481Seric 		  case 'p':		/* pass 8 bit, convert MIME */
151568856Seric 			MimeMode = MM_CVTMIME|MM_PASS8BIT;
151668481Seric 			break;
151768481Seric 
151868481Seric 		  case 's':		/* strict adherence */
151968481Seric 			MimeMode = MM_CVTMIME;
152068481Seric 			break;
152168481Seric 
152268856Seric #if 0
152368856Seric 		  case 'r':		/* reject 8-bit, don't convert MIME */
152468856Seric 			MimeMode = 0;
152568856Seric 			break;
152668856Seric 
152768856Seric 		  case 'j':		/* "just send 8" */
152868856Seric 			MimeMode = MM_PASS8BIT;
152968856Seric 			break;
153068856Seric 
153168481Seric 		  case 'a':		/* encode 8 bit if available */
153268481Seric 			MimeMode = MM_MIME8BIT|MM_PASS8BIT|MM_CVTMIME;
153368481Seric 			break;
153468481Seric 
153568481Seric 		  case 'c':		/* convert 8 bit to MIME, never 7 bit */
153668481Seric 			MimeMode = MM_MIME8BIT;
153768481Seric 			break;
153868856Seric #endif
153968481Seric 
154068481Seric 		  default:
154168481Seric 			syserr("Unknown 8-bit mode %c", *val);
154268481Seric 			exit(EX_USAGE);
154368481Seric 		}
154468481Seric 		break;
154568481Seric 
15468256Seric 	  case 'A':		/* set default alias file */
15479381Seric 		if (val[0] == '\0')
154859672Seric 			setalias("aliases");
15499381Seric 		else
155059672Seric 			setalias(val);
15518256Seric 		break;
15528256Seric 
155317474Seric 	  case 'a':		/* look N minutes for "@:@" in alias file */
155417474Seric 		if (val[0] == '\0')
155564796Seric 			SafeAlias = 5 * 60;		/* five minutes */
155617474Seric 		else
155764796Seric 			SafeAlias = convtime(val, 'm');
155817474Seric 		break;
155917474Seric 
156016843Seric 	  case 'B':		/* substitution for blank character */
156116843Seric 		SpaceSub = val[0];
156216843Seric 		if (SpaceSub == '\0')
156316843Seric 			SpaceSub = ' ';
156416843Seric 		break;
156516843Seric 
156659283Seric 	  case 'b':		/* min blocks free on queue fs/max msg size */
156759283Seric 		p = strchr(val, '/');
156859283Seric 		if (p != NULL)
156959283Seric 		{
157059283Seric 			*p++ = '\0';
157159283Seric 			MaxMessageSize = atol(p);
157259283Seric 		}
157358082Seric 		MinBlocksFree = atol(val);
157458082Seric 		break;
157558082Seric 
15769284Seric 	  case 'c':		/* don't connect to "expensive" mailers */
15779381Seric 		NoConnect = atobool(val);
15789284Seric 		break;
15799284Seric 
158051305Seric 	  case 'C':		/* checkpoint every N addresses */
158151305Seric 		CheckpointInterval = atoi(val);
158224944Seric 		break;
158324944Seric 
15849284Seric 	  case 'd':		/* delivery mode */
15859284Seric 		switch (*val)
15868269Seric 		{
15879284Seric 		  case '\0':
158858734Seric 			e->e_sendmode = SM_DELIVER;
15898269Seric 			break;
15908269Seric 
159110755Seric 		  case SM_QUEUE:	/* queue only */
159210755Seric #ifndef QUEUE
159310755Seric 			syserr("need QUEUE to set -odqueue");
159456795Seric #endif /* QUEUE */
159510755Seric 			/* fall through..... */
159610755Seric 
15979284Seric 		  case SM_DELIVER:	/* do everything */
15989284Seric 		  case SM_FORK:		/* fork after verification */
159958734Seric 			e->e_sendmode = *val;
16008269Seric 			break;
16018269Seric 
16028269Seric 		  default:
16039284Seric 			syserr("Unknown delivery mode %c", *val);
16048269Seric 			exit(EX_USAGE);
16058269Seric 		}
16068269Seric 		break;
16078269Seric 
16089146Seric 	  case 'D':		/* rebuild alias database as needed */
16099381Seric 		AutoRebuild = atobool(val);
16109146Seric 		break;
16119146Seric 
161255372Seric 	  case 'E':		/* error message header/header file */
161355379Seric 		if (*val != '\0')
161455379Seric 			ErrMsgFile = newstr(val);
161555372Seric 		break;
161655372Seric 
16178269Seric 	  case 'e':		/* set error processing mode */
16188269Seric 		switch (*val)
16198269Seric 		{
16209381Seric 		  case EM_QUIET:	/* be silent about it */
16219381Seric 		  case EM_MAIL:		/* mail back */
16229381Seric 		  case EM_BERKNET:	/* do berknet error processing */
16239381Seric 		  case EM_WRITE:	/* write back (or mail) */
16249381Seric 		  case EM_PRINT:	/* print errors normally (default) */
162558734Seric 			e->e_errormode = *val;
16268269Seric 			break;
16278269Seric 		}
16288269Seric 		break;
16298269Seric 
16309049Seric 	  case 'F':		/* file mode */
163117975Seric 		FileMode = atooct(val) & 0777;
16329049Seric 		break;
16339049Seric 
16348269Seric 	  case 'f':		/* save Unix-style From lines on front */
16359381Seric 		SaveFrom = atobool(val);
16368269Seric 		break;
16378269Seric 
163853735Seric 	  case 'G':		/* match recipients against GECOS field */
163953735Seric 		MatchGecos = atobool(val);
164053735Seric 		break;
164153735Seric 
16428256Seric 	  case 'g':		/* default gid */
164368481Seric   g_opt:
164464133Seric 		if (isascii(*val) && isdigit(*val))
164564133Seric 			DefGid = atoi(val);
164664133Seric 		else
164764133Seric 		{
164864133Seric 			register struct group *gr;
164964133Seric 
165064133Seric 			DefGid = -1;
165164133Seric 			gr = getgrnam(val);
165264133Seric 			if (gr == NULL)
165368481Seric 				syserr("readcf: option %c: unknown group %s",
165468481Seric 					opt, val);
165564133Seric 			else
165664133Seric 				DefGid = gr->gr_gid;
165764133Seric 		}
16588256Seric 		break;
16598256Seric 
16608256Seric 	  case 'H':		/* help file */
16619381Seric 		if (val[0] == '\0')
16628269Seric 			HelpFile = "sendmail.hf";
16639381Seric 		else
16649381Seric 			HelpFile = newstr(val);
16658256Seric 		break;
16668256Seric 
166751305Seric 	  case 'h':		/* maximum hop count */
166851305Seric 		MaxHopCount = atoi(val);
166951305Seric 		break;
167051305Seric 
167135651Seric 	  case 'I':		/* use internet domain name server */
167266334Seric #if NAMED_BIND
167357207Seric 		for (p = val; *p != 0; )
167457207Seric 		{
167557207Seric 			bool clearmode;
167657207Seric 			char *q;
167757207Seric 			struct resolverflags *rfp;
167857207Seric 
167957207Seric 			while (*p == ' ')
168057207Seric 				p++;
168157207Seric 			if (*p == '\0')
168257207Seric 				break;
168357207Seric 			clearmode = FALSE;
168457207Seric 			if (*p == '-')
168557207Seric 				clearmode = TRUE;
168657207Seric 			else if (*p != '+')
168757207Seric 				p--;
168857207Seric 			p++;
168957207Seric 			q = p;
169058050Seric 			while (*p != '\0' && !(isascii(*p) && isspace(*p)))
169157207Seric 				p++;
169257207Seric 			if (*p != '\0')
169357207Seric 				*p++ = '\0';
169468759Seric 			if (strcasecmp(q, "HasWildcardMX") == 0)
169568759Seric 			{
169668759Seric 				NoMXforCanon = !clearmode;
169768759Seric 				continue;
169868759Seric 			}
169957207Seric 			for (rfp = ResolverFlags; rfp->rf_name != NULL; rfp++)
170057207Seric 			{
170157207Seric 				if (strcasecmp(q, rfp->rf_name) == 0)
170257207Seric 					break;
170357207Seric 			}
170464923Seric 			if (rfp->rf_name == NULL)
170564923Seric 				syserr("readcf: I option value %s unrecognized", q);
170664923Seric 			else if (clearmode)
170757207Seric 				_res.options &= ~rfp->rf_bits;
170857207Seric 			else
170957207Seric 				_res.options |= rfp->rf_bits;
171057207Seric 		}
171157207Seric 		if (tTd(8, 2))
171268759Seric 			printf("_res.options = %x, HasWildcardMX = %d\n",
171368759Seric 				_res.options, !NoMXforCanon);
171457207Seric #else
171557207Seric 		usrerr("name server (I option) specified but BIND not compiled in");
171657207Seric #endif
171735651Seric 		break;
171835651Seric 
17198269Seric 	  case 'i':		/* ignore dot lines in message */
17209381Seric 		IgnrDot = atobool(val);
17218269Seric 		break;
17228269Seric 
172359730Seric 	  case 'j':		/* send errors in MIME (RFC 1341) format */
172459730Seric 		SendMIMEErrors = atobool(val);
172559730Seric 		break;
172659730Seric 
172757136Seric 	  case 'J':		/* .forward search path */
172857136Seric 		ForwardPath = newstr(val);
172957136Seric 		break;
173057136Seric 
173154967Seric 	  case 'k':		/* connection cache size */
173254967Seric 		MaxMciCache = atoi(val);
173356215Seric 		if (MaxMciCache < 0)
173456215Seric 			MaxMciCache = 0;
173554967Seric 		break;
173654967Seric 
173754967Seric 	  case 'K':		/* connection cache timeout */
173858796Seric 		MciCacheTimeout = convtime(val, 'm');
173954967Seric 		break;
174054967Seric 
174161104Seric 	  case 'l':		/* use Errors-To: header */
174261104Seric 		UseErrorsTo = atobool(val);
174361104Seric 		break;
174461104Seric 
17458256Seric 	  case 'L':		/* log level */
174664140Seric 		if (safe || LogLevel < atoi(val))
174764140Seric 			LogLevel = atoi(val);
17488256Seric 		break;
17498256Seric 
17508269Seric 	  case 'M':		/* define macro */
175168267Seric 		p = newstr(&val[1]);
175268267Seric 		if (!safe)
175368267Seric 			cleanstrcpy(p, p, MAXNAME);
175468267Seric 		define(val[0], p, CurEnv);
175516878Seric 		sticky = FALSE;
17568269Seric 		break;
17578269Seric 
17588269Seric 	  case 'm':		/* send to me too */
17599381Seric 		MeToo = atobool(val);
17608269Seric 		break;
17618269Seric 
176225820Seric 	  case 'n':		/* validate RHS in newaliases */
176325820Seric 		CheckAliases = atobool(val);
176425820Seric 		break;
176525820Seric 
176661104Seric 	    /* 'N' available -- was "net name" */
176761104Seric 
176858851Seric 	  case 'O':		/* daemon options */
176958851Seric 		setdaemonoptions(val);
177058851Seric 		break;
177158851Seric 
17728269Seric 	  case 'o':		/* assume old style headers */
17739381Seric 		if (atobool(val))
17749341Seric 			CurEnv->e_flags |= EF_OLDSTYLE;
17759341Seric 		else
17769341Seric 			CurEnv->e_flags &= ~EF_OLDSTYLE;
17778269Seric 		break;
17788269Seric 
177958082Seric 	  case 'p':		/* select privacy level */
178058082Seric 		p = val;
178158082Seric 		for (;;)
178258082Seric 		{
178358082Seric 			register struct prival *pv;
178458082Seric 			extern struct prival PrivacyValues[];
178558082Seric 
178658082Seric 			while (isascii(*p) && (isspace(*p) || ispunct(*p)))
178758082Seric 				p++;
178858082Seric 			if (*p == '\0')
178958082Seric 				break;
179058082Seric 			val = p;
179158082Seric 			while (isascii(*p) && isalnum(*p))
179258082Seric 				p++;
179358082Seric 			if (*p != '\0')
179458082Seric 				*p++ = '\0';
179558082Seric 
179658082Seric 			for (pv = PrivacyValues; pv->pv_name != NULL; pv++)
179758082Seric 			{
179858082Seric 				if (strcasecmp(val, pv->pv_name) == 0)
179958082Seric 					break;
180058082Seric 			}
180158886Seric 			if (pv->pv_name == NULL)
180258886Seric 				syserr("readcf: Op line: %s unrecognized", val);
180358082Seric 			PrivacyFlags |= pv->pv_flag;
180458082Seric 		}
180568479Seric 		sticky = FALSE;
180658082Seric 		break;
180758082Seric 
180824944Seric 	  case 'P':		/* postmaster copy address for returned mail */
180924944Seric 		PostMasterCopy = newstr(val);
181024944Seric 		break;
181124944Seric 
181224944Seric 	  case 'q':		/* slope of queue only function */
181324944Seric 		QueueFactor = atoi(val);
181424944Seric 		break;
181524944Seric 
18168256Seric 	  case 'Q':		/* queue directory */
18179381Seric 		if (val[0] == '\0')
18188269Seric 			QueueDir = "mqueue";
18199381Seric 		else
18209381Seric 			QueueDir = newstr(val);
182158789Seric 		if (RealUid != 0 && !safe)
182264718Seric 			Warn_Q_option = TRUE;
18238256Seric 		break;
18248256Seric 
182558148Seric 	  case 'R':		/* don't prune routes */
182658148Seric 		DontPruneRoutes = atobool(val);
182758148Seric 		break;
182858148Seric 
18298256Seric 	  case 'r':		/* read timeout */
183068481Seric 		if (subopt == NULL)
183168481Seric 			inittimeouts(val);
183268481Seric 		else
183368481Seric 			settimeout(subopt, val);
18348256Seric 		break;
18358256Seric 
18368256Seric 	  case 'S':		/* status file */
18379381Seric 		if (val[0] == '\0')
18388269Seric 			StatFile = "sendmail.st";
18399381Seric 		else
18409381Seric 			StatFile = newstr(val);
18418256Seric 		break;
18428256Seric 
18438265Seric 	  case 's':		/* be super safe, even if expensive */
18449381Seric 		SuperSafe = atobool(val);
18458256Seric 		break;
18468256Seric 
18478256Seric 	  case 'T':		/* queue timeout */
184858737Seric 		p = strchr(val, '/');
184958737Seric 		if (p != NULL)
185058737Seric 		{
185158737Seric 			*p++ = '\0';
185268481Seric 			settimeout("queuewarn", p);
185358737Seric 		}
185468481Seric 		settimeout("queuereturn", val);
185554967Seric 		break;
18568256Seric 
18578265Seric 	  case 't':		/* time zone name */
185852106Seric 		TimeZoneSpec = newstr(val);
18598265Seric 		break;
18608265Seric 
186150556Seric 	  case 'U':		/* location of user database */
186251360Seric 		UdbSpec = newstr(val);
186350556Seric 		break;
186450556Seric 
18658256Seric 	  case 'u':		/* set default uid */
186668481Seric 		for (p = val; *p != '\0'; p++)
186768481Seric 		{
186868481Seric 			if (*p == '.' || *p == '/' || *p == ':')
186968481Seric 			{
187068481Seric 				*p++ = '\0';
187168481Seric 				break;
187268481Seric 			}
187368481Seric 		}
187464133Seric 		if (isascii(*val) && isdigit(*val))
187564133Seric 			DefUid = atoi(val);
187664133Seric 		else
187764133Seric 		{
187864133Seric 			register struct passwd *pw;
187964133Seric 
188064133Seric 			DefUid = -1;
188168693Seric 			pw = sm_getpwnam(val);
188264133Seric 			if (pw == NULL)
188364133Seric 				syserr("readcf: option u: unknown user %s", val);
188464133Seric 			else
188568481Seric 			{
188664133Seric 				DefUid = pw->pw_uid;
188768481Seric 				DefGid = pw->pw_gid;
188868481Seric 			}
188964133Seric 		}
189040973Sbostic 		setdefuser();
18918256Seric 
189268481Seric 		/* handle the group if it is there */
189368481Seric 		if (*p == '\0')
189468481Seric 			break;
189568481Seric 		val = p;
189668481Seric 		goto g_opt;
189768481Seric 
189858851Seric 	  case 'V':		/* fallback MX host */
189958851Seric 		FallBackMX = newstr(val);
190058851Seric 		break;
190158851Seric 
19028269Seric 	  case 'v':		/* run in verbose mode */
19039381Seric 		Verbose = atobool(val);
19048256Seric 		break;
19058256Seric 
190663837Seric 	  case 'w':		/* if we are best MX, try host directly */
190763837Seric 		TryNullMXList = atobool(val);
190863837Seric 		break;
190961104Seric 
191061104Seric 	    /* 'W' available -- was wizard password */
191161104Seric 
191214879Seric 	  case 'x':		/* load avg at which to auto-queue msgs */
191314879Seric 		QueueLA = atoi(val);
191414879Seric 		break;
191514879Seric 
191614879Seric 	  case 'X':		/* load avg at which to auto-reject connections */
191714879Seric 		RefuseLA = atoi(val);
191814879Seric 		break;
191914879Seric 
192024981Seric 	  case 'y':		/* work recipient factor */
192124981Seric 		WkRecipFact = atoi(val);
192224981Seric 		break;
192324981Seric 
192424981Seric 	  case 'Y':		/* fork jobs during queue runs */
192524952Seric 		ForkQueueRuns = atobool(val);
192624952Seric 		break;
192724952Seric 
192824981Seric 	  case 'z':		/* work message class factor */
192924981Seric 		WkClassFact = atoi(val);
193024981Seric 		break;
193124981Seric 
193224981Seric 	  case 'Z':		/* work time factor */
193324981Seric 		WkTimeFact = atoi(val);
193424981Seric 		break;
193524981Seric 
193668481Seric 	  case O_QUEUESORTORD:	/* queue sorting order */
193768481Seric 		switch (*val)
193868481Seric 		{
193968481Seric 		  case 'h':	/* Host first */
194068481Seric 		  case 'H':
194168481Seric 			QueueSortOrder = QS_BYHOST;
194268481Seric 			break;
194368481Seric 
194468481Seric 		  case 'p':	/* Priority order */
194568481Seric 		  case 'P':
194668481Seric 			QueueSortOrder = QS_BYPRIORITY;
194768481Seric 			break;
194868481Seric 
194968481Seric 		  default:
195068481Seric 			syserr("Invalid queue sort order \"%s\"", val);
195168481Seric 		}
195268481Seric 		break;
195368481Seric 
195469401Seric 	  case O_HOSTSFILE:	/* pathname of /etc/hosts file */
195569401Seric 		HostsFile = newstr(val);
195669401Seric 		break;
195769401Seric 
195868481Seric 	  case O_MQA:		/* minimum queue age between deliveries */
195968481Seric 		MinQueueAge = convtime(val, 'm');
196068481Seric 		break;
196168481Seric 
196268481Seric 	  case O_MHSA:		/* maximum age of cached host status */
196368481Seric 		MaxHostStatAge = convtime(val, 'm');
196468481Seric 		break;
196568481Seric 
196668481Seric 	  case O_DEFCHARSET:	/* default character set for mimefying */
196768481Seric 		DefaultCharSet = newstr(denlstring(val, TRUE, TRUE));
196868481Seric 		break;
196968481Seric 
197068481Seric 	  case O_SSFILE:	/* service switch file */
197168481Seric 		ServiceSwitchFile = newstr(val);
197268481Seric 		break;
197368481Seric 
197468481Seric 	  case O_DIALDELAY:	/* delay for dial-on-demand operation */
197568481Seric 		DialDelay = convtime(val, 's');
197668481Seric 		break;
197768481Seric 
197868481Seric 	  case O_NORCPTACTION:	/* what to do if no recipient */
197968481Seric 		if (strcasecmp(val, "none") == 0)
198068481Seric 			NoRecipientAction = NRA_NO_ACTION;
198168481Seric 		else if (strcasecmp(val, "add-to") == 0)
198268481Seric 			NoRecipientAction = NRA_ADD_TO;
198368481Seric 		else if (strcasecmp(val, "add-apparently-to") == 0)
198468481Seric 			NoRecipientAction = NRA_ADD_APPARENTLY_TO;
198568481Seric 		else if (strcasecmp(val, "add-bcc") == 0)
198668481Seric 			NoRecipientAction = NRA_ADD_BCC;
198768481Seric 		else if (strcasecmp(val, "add-to-undisclosed") == 0)
198868481Seric 			NoRecipientAction = NRA_ADD_TO_UNDISCLOSED;
198968481Seric 		else
199068481Seric 			syserr("Invalid NoRecipientAction: %s", val);
199168481Seric 
199268490Seric 	  case O_SAFEFILEENV:	/* chroot() environ for writing to files */
199368490Seric 		SafeFileEnv = newstr(val);
199468490Seric 		break;
199568490Seric 
199668569Seric 	  case O_MAXMSGSIZE:	/* maximum message size */
199768569Seric 		MaxMessageSize = atol(p);
199868569Seric 		break;
199968569Seric 
200068756Seric 	  case O_COLONOKINADDR:	/* old style handling of colon addresses */
200168756Seric 		ColonOkInAddr = atobool(p);
200268756Seric 		break;
200368756Seric 
20048256Seric 	  default:
200568481Seric 		if (tTd(37, 1))
200668481Seric 		{
200768481Seric 			if (isascii(opt) && isprint(opt))
200868481Seric 				printf("Warning: option %c unknown\n", opt);
200968481Seric 			else
201068481Seric 				printf("Warning: option 0x%x unknown\n", opt);
201168481Seric 		}
20128256Seric 		break;
20138256Seric 	}
201416878Seric 	if (sticky)
201516878Seric 		setbitn(opt, StickyOpt);
20169188Seric 	return;
20178256Seric }
201810687Seric /*
201968481Seric **  SETCLASS -- set a string into a class
202010687Seric **
202110687Seric **	Parameters:
202268481Seric **		class -- the class to put the string in.
202368481Seric **		str -- the string to enter
202410687Seric **
202510687Seric **	Returns:
202610687Seric **		none.
202710687Seric **
202810687Seric **	Side Effects:
202910687Seric **		puts the word into the symbol table.
203010687Seric */
203110687Seric 
203268481Seric setclass(class, str)
203310687Seric 	int class;
203468481Seric 	char *str;
203510687Seric {
203610687Seric 	register STAB *s;
203710687Seric 
203857943Seric 	if (tTd(37, 8))
203968481Seric 		printf("setclass(%c, %s)\n", class, str);
204068481Seric 	s = stab(str, ST_CLASS, ST_ENTER);
204110687Seric 	setbitn(class, s->s_class);
204210687Seric }
204353654Seric /*
204453654Seric **  MAKEMAPENTRY -- create a map entry
204553654Seric **
204653654Seric **	Parameters:
204753654Seric **		line -- the config file line
204853654Seric **
204953654Seric **	Returns:
205053654Seric **		TRUE if it successfully entered the map entry.
205153654Seric **		FALSE otherwise (usually syntax error).
205253654Seric **
205353654Seric **	Side Effects:
205453654Seric **		Enters the map into the dictionary.
205553654Seric */
205653654Seric 
205753654Seric void
205853654Seric makemapentry(line)
205953654Seric 	char *line;
206053654Seric {
206153654Seric 	register char *p;
206253654Seric 	char *mapname;
206353654Seric 	char *classname;
206464078Seric 	register STAB *s;
206553654Seric 	STAB *class;
206653654Seric 
206758050Seric 	for (p = line; isascii(*p) && isspace(*p); p++)
206853654Seric 		continue;
206958050Seric 	if (!(isascii(*p) && isalnum(*p)))
207053654Seric 	{
207153654Seric 		syserr("readcf: config K line: no map name");
207253654Seric 		return;
207353654Seric 	}
207453654Seric 
207553654Seric 	mapname = p;
207668481Seric 	while ((isascii(*++p) && isalnum(*p)) || *p == '.')
207753654Seric 		continue;
207853654Seric 	if (*p != '\0')
207953654Seric 		*p++ = '\0';
208058050Seric 	while (isascii(*p) && isspace(*p))
208153654Seric 		p++;
208258050Seric 	if (!(isascii(*p) && isalnum(*p)))
208353654Seric 	{
208453654Seric 		syserr("readcf: config K line, map %s: no map class", mapname);
208553654Seric 		return;
208653654Seric 	}
208753654Seric 	classname = p;
208858050Seric 	while (isascii(*++p) && isalnum(*p))
208953654Seric 		continue;
209053654Seric 	if (*p != '\0')
209153654Seric 		*p++ = '\0';
209258050Seric 	while (isascii(*p) && isspace(*p))
209353654Seric 		p++;
209453654Seric 
209553654Seric 	/* look up the class */
209653654Seric 	class = stab(classname, ST_MAPCLASS, ST_FIND);
209753654Seric 	if (class == NULL)
209853654Seric 	{
209953654Seric 		syserr("readcf: map %s: class %s not available", mapname, classname);
210053654Seric 		return;
210153654Seric 	}
210253654Seric 
210353654Seric 	/* enter the map */
210464078Seric 	s = stab(mapname, ST_MAP, ST_ENTER);
210564078Seric 	s->s_map.map_class = &class->s_mapclass;
210664078Seric 	s->s_map.map_mname = newstr(mapname);
210753654Seric 
210864078Seric 	if (class->s_mapclass.map_parse(&s->s_map, p))
210964078Seric 		s->s_map.map_mflags |= MF_VALID;
211064078Seric 
211164078Seric 	if (tTd(37, 5))
211264078Seric 	{
211364078Seric 		printf("map %s, class %s, flags %x, file %s,\n",
211464078Seric 			s->s_map.map_mname, s->s_map.map_class->map_cname,
211564078Seric 			s->s_map.map_mflags,
211664078Seric 			s->s_map.map_file == NULL ? "(null)" : s->s_map.map_file);
211764078Seric 		printf("\tapp %s, domain %s, rebuild %s\n",
211864078Seric 			s->s_map.map_app == NULL ? "(null)" : s->s_map.map_app,
211964078Seric 			s->s_map.map_domain == NULL ? "(null)" : s->s_map.map_domain,
212064078Seric 			s->s_map.map_rebuild == NULL ? "(null)" : s->s_map.map_rebuild);
212164078Seric 	}
212253654Seric }
212358112Seric /*
212468481Seric **  INITTIMEOUTS -- parse and set timeout values
212558112Seric **
212658112Seric **	Parameters:
212758112Seric **		val -- a pointer to the values.  If NULL, do initial
212858112Seric **			settings.
212958112Seric **
213058112Seric **	Returns:
213158112Seric **		none.
213258112Seric **
213358112Seric **	Side Effects:
213458112Seric **		Initializes the TimeOuts structure
213558112Seric */
213658112Seric 
213764255Seric #define SECONDS
213858112Seric #define MINUTES	* 60
213958112Seric #define HOUR	* 3600
214058112Seric 
214168481Seric inittimeouts(val)
214258112Seric 	register char *val;
214358112Seric {
214458112Seric 	register char *p;
214558671Seric 	extern time_t convtime();
214658112Seric 
214758112Seric 	if (val == NULL)
214858112Seric 	{
214958112Seric 		TimeOuts.to_initial = (time_t) 5 MINUTES;
215058112Seric 		TimeOuts.to_helo = (time_t) 5 MINUTES;
215158112Seric 		TimeOuts.to_mail = (time_t) 10 MINUTES;
215258112Seric 		TimeOuts.to_rcpt = (time_t) 1 HOUR;
215358112Seric 		TimeOuts.to_datainit = (time_t) 5 MINUTES;
215458112Seric 		TimeOuts.to_datablock = (time_t) 1 HOUR;
215558112Seric 		TimeOuts.to_datafinal = (time_t) 1 HOUR;
215658112Seric 		TimeOuts.to_rset = (time_t) 5 MINUTES;
215758112Seric 		TimeOuts.to_quit = (time_t) 2 MINUTES;
215858112Seric 		TimeOuts.to_nextcommand = (time_t) 1 HOUR;
215958112Seric 		TimeOuts.to_miscshort = (time_t) 2 MINUTES;
216068481Seric #if IDENTPROTO
216164255Seric 		TimeOuts.to_ident = (time_t) 30 SECONDS;
216268481Seric #else
216368481Seric 		TimeOuts.to_ident = (time_t) 0 SECONDS;
216468481Seric #endif
216568481Seric 		TimeOuts.to_fileopen = (time_t) 60 SECONDS;
216658112Seric 		return;
216758112Seric 	}
216858112Seric 
216958112Seric 	for (;; val = p)
217058112Seric 	{
217158112Seric 		while (isascii(*val) && isspace(*val))
217258112Seric 			val++;
217358112Seric 		if (*val == '\0')
217458112Seric 			break;
217558112Seric 		for (p = val; *p != '\0' && *p != ','; p++)
217658112Seric 			continue;
217758112Seric 		if (*p != '\0')
217858112Seric 			*p++ = '\0';
217958112Seric 
218058112Seric 		if (isascii(*val) && isdigit(*val))
218158112Seric 		{
218258112Seric 			/* old syntax -- set everything */
218358796Seric 			TimeOuts.to_mail = convtime(val, 'm');
218458112Seric 			TimeOuts.to_rcpt = TimeOuts.to_mail;
218558112Seric 			TimeOuts.to_datainit = TimeOuts.to_mail;
218658112Seric 			TimeOuts.to_datablock = TimeOuts.to_mail;
218758112Seric 			TimeOuts.to_datafinal = TimeOuts.to_mail;
218858112Seric 			TimeOuts.to_nextcommand = TimeOuts.to_mail;
218958112Seric 			continue;
219058112Seric 		}
219158112Seric 		else
219258112Seric 		{
219368481Seric 			register char *q = strchr(val, ':');
219458112Seric 
219568481Seric 			if (q == NULL && (q = strchr(val, '=')) == NULL)
219658112Seric 			{
219758112Seric 				/* syntax error */
219858112Seric 				continue;
219958112Seric 			}
220058112Seric 			*q++ = '\0';
220168481Seric 			settimeout(val, q);
220268481Seric 		}
220368481Seric 	}
220468481Seric }
220568481Seric /*
220668481Seric **  SETTIMEOUT -- set an individual timeout
220768481Seric **
220868481Seric **	Parameters:
220968481Seric **		name -- the name of the timeout.
221068481Seric **		val -- the value of the timeout.
221168481Seric **
221268481Seric **	Returns:
221368481Seric **		none.
221468481Seric */
221558112Seric 
221668481Seric settimeout(name, val)
221768481Seric 	char *name;
221868481Seric 	char *val;
221968481Seric {
222068481Seric 	register char *p;
222168481Seric 	time_t to;
222268481Seric 	extern time_t convtime();
222368481Seric 
222468481Seric 	to = convtime(val, 'm');
222568481Seric 	p = strchr(name, '.');
222668481Seric 	if (p != NULL)
222768481Seric 		*p++ = '\0';
222868481Seric 
222968481Seric 	if (strcasecmp(name, "initial") == 0)
223068481Seric 		TimeOuts.to_initial = to;
223168481Seric 	else if (strcasecmp(name, "mail") == 0)
223268481Seric 		TimeOuts.to_mail = to;
223368481Seric 	else if (strcasecmp(name, "rcpt") == 0)
223468481Seric 		TimeOuts.to_rcpt = to;
223568481Seric 	else if (strcasecmp(name, "datainit") == 0)
223668481Seric 		TimeOuts.to_datainit = to;
223768481Seric 	else if (strcasecmp(name, "datablock") == 0)
223868481Seric 		TimeOuts.to_datablock = to;
223968481Seric 	else if (strcasecmp(name, "datafinal") == 0)
224068481Seric 		TimeOuts.to_datafinal = to;
224168481Seric 	else if (strcasecmp(name, "command") == 0)
224268481Seric 		TimeOuts.to_nextcommand = to;
224368481Seric 	else if (strcasecmp(name, "rset") == 0)
224468481Seric 		TimeOuts.to_rset = to;
224568481Seric 	else if (strcasecmp(name, "helo") == 0)
224668481Seric 		TimeOuts.to_helo = to;
224768481Seric 	else if (strcasecmp(name, "quit") == 0)
224868481Seric 		TimeOuts.to_quit = to;
224968481Seric 	else if (strcasecmp(name, "misc") == 0)
225068481Seric 		TimeOuts.to_miscshort = to;
225168481Seric 	else if (strcasecmp(name, "ident") == 0)
225268481Seric 		TimeOuts.to_ident = to;
225368481Seric 	else if (strcasecmp(name, "fileopen") == 0)
225468481Seric 		TimeOuts.to_fileopen = to;
225568481Seric 	else if (strcasecmp(name, "queuewarn") == 0)
225668481Seric 	{
225768481Seric 		to = convtime(val, 'h');
225868481Seric 		if (p == NULL || strcmp(p, "*") == 0)
225968481Seric 		{
226068481Seric 			TimeOuts.to_q_warning[TOC_NORMAL] = to;
226168481Seric 			TimeOuts.to_q_warning[TOC_URGENT] = to;
226268481Seric 			TimeOuts.to_q_warning[TOC_NONURGENT] = to;
226358112Seric 		}
226468481Seric 		else if (strcasecmp(p, "normal") == 0)
226568481Seric 			TimeOuts.to_q_warning[TOC_NORMAL] = to;
226668481Seric 		else if (strcasecmp(p, "urgent") == 0)
226768481Seric 			TimeOuts.to_q_warning[TOC_URGENT] = to;
226868481Seric 		else if (strcasecmp(p, "non-urgent") == 0)
226968481Seric 			TimeOuts.to_q_warning[TOC_NONURGENT] = to;
227068481Seric 		else
227168481Seric 			syserr("settimeout: invalid queuewarn subtimeout %s", p);
227258112Seric 	}
227368481Seric 	else if (strcasecmp(name, "queuereturn") == 0)
227468481Seric 	{
227568481Seric 		to = convtime(val, 'd');
227668481Seric 		if (p == NULL || strcmp(p, "*") == 0)
227768481Seric 		{
227868481Seric 			TimeOuts.to_q_return[TOC_NORMAL] = to;
227968481Seric 			TimeOuts.to_q_return[TOC_URGENT] = to;
228068481Seric 			TimeOuts.to_q_return[TOC_NONURGENT] = to;
228168481Seric 		}
228268481Seric 		else if (strcasecmp(p, "normal") == 0)
228368481Seric 			TimeOuts.to_q_return[TOC_NORMAL] = to;
228468481Seric 		else if (strcasecmp(p, "urgent") == 0)
228568481Seric 			TimeOuts.to_q_return[TOC_URGENT] = to;
228668481Seric 		else if (strcasecmp(p, "non-urgent") == 0)
228768481Seric 			TimeOuts.to_q_return[TOC_NONURGENT] = to;
228868481Seric 		else
228968481Seric 			syserr("settimeout: invalid queuereturn subtimeout %s", p);
229068481Seric 	}
229168481Seric 	else
229268481Seric 		syserr("settimeout: invalid timeout %s", name);
229358112Seric }
2294