xref: /csrg-svn/usr.sbin/sendmail/src/readcf.c (revision 69686)
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*69686Seric static char sccsid[] = "@(#)readcf.c	8.92 (Berkeley) 05/25/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.
5169476Seric **		Eenvar=value	Set the environment value to the given value.
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 	{
116*69686Seric 		if (OpMode == MD_DAEMON || OpMode == MD_INITALIAS)
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';
22468529Seric 			expand(&bp[1], exbuf, sizeof exbuf, e);
22565066Seric 			rwp->r_lhs = prescan(exbuf, '\t', pvpbuf,
22668711Seric 					     sizeof pvpbuf, NULL, 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';
31268529Seric 			expand(q, exbuf, sizeof exbuf, e);
31365066Seric 			rwp->r_rhs = prescan(exbuf, '\t', pvpbuf,
31468711Seric 					     sizeof pvpbuf, NULL, 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 */
43468717Seric 			(void) chompheader(&bp[1], TRUE, NULL, 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);
44268529Seric 				expand(ep, 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);
53668805Seric 
53768805Seric 			/*
53868805Seric 			**  Do heuristic tweaking for back compatibility.
53968805Seric 			*/
54068805Seric 
54164279Seric 			if (ConfigLevel >= 5)
54264279Seric 			{
54364279Seric 				/* level 5 configs have short name in $w */
54464279Seric 				p = macvalue('w', e);
54564279Seric 				if (p != NULL && (p = strchr(p, '.')) != NULL)
54664279Seric 					*p = '\0';
54764279Seric 			}
54868805Seric 			if (ConfigLevel >= 6)
54968805Seric 			{
55068805Seric 				ColonOkInAddr = FALSE;
55168805Seric 			}
55268805Seric 
55368805Seric 			/*
55468805Seric 			**  Look for vendor code.
55568805Seric 			*/
55668805Seric 
55764718Seric 			if (*ep++ == '/')
55864718Seric 			{
55964718Seric 				/* extract vendor code */
56064718Seric 				for (p = ep; isascii(*p) && isalpha(*p); )
56164718Seric 					p++;
56264718Seric 				*p = '\0';
56364718Seric 
56464718Seric 				if (!setvendor(ep))
56564718Seric 					syserr("invalid V line vendor code: \"%s\"",
56664718Seric 						ep);
56764718Seric 			}
56852645Seric 			break;
56952645Seric 
57053654Seric 		  case 'K':
57157135Seric 			makemapentry(&bp[1]);
57253654Seric 			break;
57353654Seric 
57469476Seric 		  case 'E':
57569476Seric 			p = strchr(bp, '=');
57669476Seric 			if (p != NULL)
57769476Seric 				*p++ = '\0';
57869476Seric 			setuserenv(&bp[1], p);
57969476Seric 			break;
58069476Seric 
5813308Seric 		  default:
5824061Seric 		  badline:
58357135Seric 			syserr("unknown control line \"%s\"", bp);
5843308Seric 		}
58557135Seric 		if (bp != buf)
58657135Seric 			free(bp);
5873308Seric 	}
58852637Seric 	if (ferror(cf))
58952637Seric 	{
59052647Seric 		syserr("I/O read error", cfname);
59152637Seric 		exit(EX_OSFILE);
59252637Seric 	}
59352637Seric 	fclose(cf);
5949381Seric 	FileName = NULL;
59556836Seric 
59668481Seric 	/* initialize host maps from local service tables */
59768481Seric 	inithostmaps();
59868481Seric 
59968481Seric 	/* determine if we need to do special name-server frotz */
60067905Seric 	{
60168481Seric 		int nmaps;
60268481Seric 		char *maptype[MAXMAPSTACK];
60368481Seric 		short mapreturn[MAXMAPACTIONS];
60468481Seric 
60568481Seric 		nmaps = switch_map_find("hosts", maptype, mapreturn);
60668481Seric 		UseNameServer = FALSE;
60768481Seric 		if (nmaps > 0 && nmaps <= MAXMAPSTACK)
60868481Seric 		{
60968481Seric 			register int mapno;
61068481Seric 
61168481Seric 			for (mapno = 0; mapno < nmaps && !UseNameServer; mapno++)
61268481Seric 			{
61368481Seric 				if (strcmp(maptype[mapno], "dns") == 0)
61468481Seric 					UseNameServer = TRUE;
61568481Seric 			}
61668481Seric 		}
61768481Seric 
61868481Seric #ifdef HESIOD
61968481Seric 		nmaps = switch_map_find("passwd", maptype, mapreturn);
62068481Seric 		UseHesiod = FALSE;
62168481Seric 		if (nmaps > 0 && nmaps <= MAXMAPSTACK)
62268481Seric 		{
62368481Seric 			register int mapno;
62468481Seric 
62568481Seric 			for (mapno = 0; mapno < nmaps && !UseHesiod; mapno++)
62668481Seric 			{
62768481Seric 				if (strcmp(maptype[mapno], "hesiod") == 0)
62868481Seric 					UseHesiod = TRUE;
62968481Seric 			}
63068481Seric 		}
63168204Seric #endif
63267905Seric 	}
6334096Seric }
6344096Seric /*
6358547Seric **  TOOMANY -- signal too many of some option
6368547Seric **
6378547Seric **	Parameters:
6388547Seric **		id -- the id of the error line
6398547Seric **		maxcnt -- the maximum possible values
6408547Seric **
6418547Seric **	Returns:
6428547Seric **		none.
6438547Seric **
6448547Seric **	Side Effects:
6458547Seric **		gives a syserr.
6468547Seric */
6478547Seric 
6488547Seric toomany(id, maxcnt)
6498547Seric 	char id;
6508547Seric 	int maxcnt;
6518547Seric {
6529381Seric 	syserr("too many %c lines, %d max", id, maxcnt);
6538547Seric }
6548547Seric /*
6554432Seric **  FILECLASS -- read members of a class from a file
6564432Seric **
6574432Seric **	Parameters:
6584432Seric **		class -- class to define.
6594432Seric **		filename -- name of file to read.
6604432Seric **		fmt -- scanf string to use for match.
66164133Seric **		safe -- if set, this is a safe read.
66264133Seric **		optional -- if set, it is not an error for the file to
66364133Seric **			not exist.
6644432Seric **
6654432Seric **	Returns:
6664432Seric **		none
6674432Seric **
6684432Seric **	Side Effects:
6694432Seric **
6704432Seric **		puts all lines in filename that match a scanf into
6714432Seric **			the named class.
6724432Seric */
6734432Seric 
67464133Seric fileclass(class, filename, fmt, safe, optional)
6754432Seric 	int class;
6764432Seric 	char *filename;
6774432Seric 	char *fmt;
67854973Seric 	bool safe;
67964133Seric 	bool optional;
6804432Seric {
68125808Seric 	FILE *f;
68268513Seric 	int sff;
68369453Seric 	int pid;
68469453Seric 	register char *p;
6854432Seric 	char buf[MAXLINE];
6864432Seric 
68766101Seric 	if (tTd(37, 2))
68866101Seric 		printf("fileclass(%s, fmt=%s)\n", filename, fmt);
68966101Seric 
69066031Seric 	if (filename[0] == '|')
69166031Seric 	{
69269453Seric 		auto int fd;
69369453Seric 		int i;
69469453Seric 		char *argv[MAXPV + 1];
69569453Seric 
69669453Seric 		i = 0;
69769453Seric 		for (p = strtok(&filename[1], " \t"); p != NULL; p = strtok(NULL, " \t"))
69869453Seric 		{
69969453Seric 			if (i >= MAXPV)
70069453Seric 				break;
70169453Seric 			argv[i++] = p;
70269453Seric 		}
70369453Seric 		argv[i] = NULL;
70469453Seric 		pid = prog_open(argv, &fd, CurEnv);
70569453Seric 		if (pid < 0)
70669453Seric 			f = NULL;
70769453Seric 		else
70869453Seric 			f = fdopen(fd, "r");
70966031Seric 	}
71069453Seric 	else
71169453Seric 	{
71269453Seric 		pid = -1;
71369453Seric 		sff = SFF_REGONLY;
71469453Seric 		if (safe)
71569453Seric 			sff |= SFF_OPENASROOT;
71669453Seric 		f = safefopen(filename, O_RDONLY, 0, sff);
71769453Seric 	}
71868602Seric 	if (f == NULL)
71954973Seric 	{
72068602Seric 		if (!optional)
72168602Seric 			syserr("fileclass: cannot open %s", filename);
7224432Seric 		return;
7234432Seric 	}
7244432Seric 
7254432Seric 	while (fgets(buf, sizeof buf, f) != NULL)
7264432Seric 	{
7274432Seric 		register STAB *s;
72825808Seric 		register char *p;
72925808Seric # ifdef SCANF
7304432Seric 		char wordbuf[MAXNAME+1];
7314432Seric 
7324432Seric 		if (sscanf(buf, fmt, wordbuf) != 1)
7334432Seric 			continue;
73425808Seric 		p = wordbuf;
73556795Seric # else /* SCANF */
73625808Seric 		p = buf;
73756795Seric # endif /* SCANF */
73825808Seric 
73925808Seric 		/*
74025808Seric 		**  Break up the match into words.
74125808Seric 		*/
74225808Seric 
74325808Seric 		while (*p != '\0')
74425808Seric 		{
74525808Seric 			register char *q;
74625808Seric 
74725808Seric 			/* strip leading spaces */
74858050Seric 			while (isascii(*p) && isspace(*p))
74925808Seric 				p++;
75025808Seric 			if (*p == '\0')
75125808Seric 				break;
75225808Seric 
75325808Seric 			/* find the end of the word */
75425808Seric 			q = p;
75558050Seric 			while (*p != '\0' && !(isascii(*p) && isspace(*p)))
75625808Seric 				p++;
75725808Seric 			if (*p != '\0')
75825808Seric 				*p++ = '\0';
75925808Seric 
76025808Seric 			/* enter the word in the symbol table */
76166101Seric 			setclass(class, q);
76225808Seric 		}
7634432Seric 	}
7644432Seric 
76554973Seric 	(void) fclose(f);
76669453Seric 	if (pid > 0)
76769453Seric 		(void) waitfor(pid);
7684432Seric }
7694432Seric /*
7704096Seric **  MAKEMAILER -- define a new mailer.
7714096Seric **
7724096Seric **	Parameters:
77310327Seric **		line -- description of mailer.  This is in labeled
77410327Seric **			fields.  The fields are:
77568481Seric **			   A -- the argv for this mailer
77668481Seric **			   C -- the character set for MIME conversions
77768481Seric **			   D -- the directory to run in
77868481Seric **			   E -- the eol string
77968481Seric **			   F -- the flags associated with the mailer
78068481Seric **			   L -- the maximum line length
78168481Seric **			   M -- the maximum message size
78268816Seric **			   N -- the niceness at which to run
78368479Seric **			   P -- the path to the mailer
78468481Seric **			   R -- the recipient rewriting set
78568479Seric **			   S -- the sender rewriting set
78668481Seric **			   T -- the mailer type (for DSNs)
78768481Seric **			   U -- the uid to run as
78810327Seric **			The first word is the canonical name of the mailer.
7894096Seric **
7904096Seric **	Returns:
7914096Seric **		none.
7924096Seric **
7934096Seric **	Side Effects:
7944096Seric **		enters the mailer into the mailer table.
7954096Seric */
7963308Seric 
79721066Seric makemailer(line)
7984096Seric 	char *line;
7994096Seric {
8004096Seric 	register char *p;
8018067Seric 	register struct mailer *m;
8028067Seric 	register STAB *s;
8038067Seric 	int i;
80410327Seric 	char fcode;
80558020Seric 	auto char *endp;
8064096Seric 	extern int NextMailer;
80710327Seric 	extern char **makeargv();
80810327Seric 	extern char *munchstring();
8094096Seric 
81010327Seric 	/* allocate a mailer and set up defaults */
81110327Seric 	m = (struct mailer *) xalloc(sizeof *m);
81210327Seric 	bzero((char *) m, sizeof *m);
81310327Seric 	m->m_eol = "\n";
81468481Seric 	m->m_uid = m->m_gid = 0;
81510327Seric 
81610327Seric 	/* collect the mailer name */
81758050Seric 	for (p = line; *p != '\0' && *p != ',' && !(isascii(*p) && isspace(*p)); p++)
81810327Seric 		continue;
81910327Seric 	if (*p != '\0')
82010327Seric 		*p++ = '\0';
82110327Seric 	m->m_name = newstr(line);
82210327Seric 
82310327Seric 	/* now scan through and assign info from the fields */
82410327Seric 	while (*p != '\0')
82510327Seric 	{
82658333Seric 		auto char *delimptr;
82758333Seric 
82858050Seric 		while (*p != '\0' && (*p == ',' || (isascii(*p) && isspace(*p))))
82910327Seric 			p++;
83010327Seric 
83110327Seric 		/* p now points to field code */
83210327Seric 		fcode = *p;
83310327Seric 		while (*p != '\0' && *p != '=' && *p != ',')
83410327Seric 			p++;
83510327Seric 		if (*p++ != '=')
83610327Seric 		{
83752637Seric 			syserr("mailer %s: `=' expected", m->m_name);
83810327Seric 			return;
83910327Seric 		}
84058050Seric 		while (isascii(*p) && isspace(*p))
84110327Seric 			p++;
84210327Seric 
84310327Seric 		/* p now points to the field body */
84458333Seric 		p = munchstring(p, &delimptr);
84510327Seric 
84610327Seric 		/* install the field into the mailer struct */
84710327Seric 		switch (fcode)
84810327Seric 		{
84910327Seric 		  case 'P':		/* pathname */
85010327Seric 			m->m_mailer = newstr(p);
85110327Seric 			break;
85210327Seric 
85310327Seric 		  case 'F':		/* flags */
85410687Seric 			for (; *p != '\0'; p++)
85558050Seric 				if (!(isascii(*p) && isspace(*p)))
85652637Seric 					setbitn(*p, m->m_flags);
85710327Seric 			break;
85810327Seric 
85910327Seric 		  case 'S':		/* sender rewriting ruleset */
86010327Seric 		  case 'R':		/* recipient rewriting ruleset */
86158020Seric 			i = strtol(p, &endp, 10);
86210327Seric 			if (i < 0 || i >= MAXRWSETS)
86310327Seric 			{
86410327Seric 				syserr("invalid rewrite set, %d max", MAXRWSETS);
86510327Seric 				return;
86610327Seric 			}
86710327Seric 			if (fcode == 'S')
86858020Seric 				m->m_sh_rwset = m->m_se_rwset = i;
86910327Seric 			else
87058020Seric 				m->m_rh_rwset = m->m_re_rwset = i;
87158020Seric 
87258020Seric 			p = endp;
87359985Seric 			if (*p++ == '/')
87458020Seric 			{
87558020Seric 				i = strtol(p, NULL, 10);
87658020Seric 				if (i < 0 || i >= MAXRWSETS)
87758020Seric 				{
87858020Seric 					syserr("invalid rewrite set, %d max",
87958020Seric 						MAXRWSETS);
88058020Seric 					return;
88158020Seric 				}
88258020Seric 				if (fcode == 'S')
88358020Seric 					m->m_sh_rwset = i;
88458020Seric 				else
88558020Seric 					m->m_rh_rwset = i;
88658020Seric 			}
88710327Seric 			break;
88810327Seric 
88910327Seric 		  case 'E':		/* end of line string */
89010327Seric 			m->m_eol = newstr(p);
89110327Seric 			break;
89210327Seric 
89310327Seric 		  case 'A':		/* argument vector */
89410327Seric 			m->m_argv = makeargv(p);
89510327Seric 			break;
89610701Seric 
89710701Seric 		  case 'M':		/* maximum message size */
89810701Seric 			m->m_maxsize = atol(p);
89910701Seric 			break;
90052106Seric 
90152106Seric 		  case 'L':		/* maximum line length */
90252106Seric 			m->m_linelimit = atoi(p);
90352106Seric 			break;
90458935Seric 
90568816Seric 		  case 'N':		/* run niceness */
90668816Seric 			m->m_nice = atoi(p);
90768816Seric 			break;
90868816Seric 
90958935Seric 		  case 'D':		/* working directory */
91058935Seric 			m->m_execdir = newstr(p);
91158935Seric 			break;
91268481Seric 
91368481Seric 		  case 'C':		/* default charset */
91468481Seric 			m->m_defcharset = newstr(p);
91568481Seric 			break;
91668481Seric 
91768481Seric 		  case 'T':		/* MTA Type */
91868481Seric 			m->m_mtatype = newstr(p);
91968481Seric 			p = strchr(m->m_mtatype, '/');
92068481Seric 			if (p != NULL)
92168481Seric 			{
92268481Seric 				*p++ = '\0';
92368481Seric 				if (*p == '\0')
92468481Seric 					p = NULL;
92568481Seric 			}
92668481Seric 			if (p == NULL)
92768481Seric 				m->m_addrtype = m->m_mtatype;
92868481Seric 			else
92968481Seric 			{
93068481Seric 				m->m_addrtype = p;
93168481Seric 				p = strchr(p, '/');
93268481Seric 			}
93368481Seric 			if (p != NULL)
93468481Seric 			{
93568481Seric 				*p++ = '\0';
93668481Seric 				if (*p == '\0')
93768481Seric 					p = NULL;
93868481Seric 			}
93968481Seric 			if (p == NULL)
94068481Seric 				m->m_diagtype = m->m_mtatype;
94168481Seric 			else
94268481Seric 				m->m_diagtype = p;
94368481Seric 			break;
94468481Seric 
94568481Seric 		  case 'U':		/* user id */
94668481Seric 			if (isascii(*p) && !isdigit(*p))
94768481Seric 			{
94868481Seric 				char *q = p;
94968481Seric 				struct passwd *pw;
95068481Seric 
95168481Seric 				while (isascii(*p) && isalnum(*p))
95268481Seric 					p++;
95368481Seric 				while (isascii(*p) && isspace(*p))
95468481Seric 					*p++ = '\0';
95568481Seric 				if (*p != '\0')
95668481Seric 					*p++ = '\0';
95768693Seric 				pw = sm_getpwnam(q);
95868481Seric 				if (pw == NULL)
95968481Seric 					syserr("readcf: mailer U= flag: unknown user %s", q);
96068481Seric 				else
96168481Seric 				{
96268481Seric 					m->m_uid = pw->pw_uid;
96368481Seric 					m->m_gid = pw->pw_gid;
96468481Seric 				}
96568481Seric 			}
96668481Seric 			else
96768481Seric 			{
96868481Seric 				auto char *q;
96968481Seric 
97068481Seric 				m->m_uid = strtol(p, &q, 0);
97168481Seric 				p = q;
97268481Seric 			}
97368481Seric 			while (isascii(*p) && isspace(*p))
97468481Seric 				p++;
97568481Seric 			if (*p == '\0')
97668481Seric 				break;
97768481Seric 			if (isascii(*p) && !isdigit(*p))
97868481Seric 			{
97968481Seric 				char *q = p;
98068481Seric 				struct group *gr;
98168481Seric 
98268481Seric 				while (isascii(*p) && isalnum(*p))
98368481Seric 					p++;
98468481Seric 				*p++ = '\0';
98568481Seric 				gr = getgrnam(q);
98668481Seric 				if (gr == NULL)
98768481Seric 					syserr("readcf: mailer U= flag: unknown group %s", q);
98868481Seric 				else
98968481Seric 					m->m_gid = gr->gr_gid;
99068481Seric 			}
99168481Seric 			else
99268481Seric 			{
99368481Seric 				m->m_gid = strtol(p, NULL, 0);
99468481Seric 			}
99568481Seric 			break;
99610327Seric 		}
99710327Seric 
99858333Seric 		p = delimptr;
99910327Seric 	}
100010327Seric 
100158321Seric 	/* do some rationality checking */
100258321Seric 	if (m->m_argv == NULL)
100358321Seric 	{
100458321Seric 		syserr("M%s: A= argument required", m->m_name);
100558321Seric 		return;
100658321Seric 	}
100758321Seric 	if (m->m_mailer == NULL)
100858321Seric 	{
100958321Seric 		syserr("M%s: P= argument required", m->m_name);
101058321Seric 		return;
101158321Seric 	}
101258321Seric 
10134096Seric 	if (NextMailer >= MAXMAILERS)
10144096Seric 	{
10159381Seric 		syserr("too many mailers defined (%d max)", MAXMAILERS);
10164096Seric 		return;
10174096Seric 	}
101857402Seric 
101968481Seric 	/* do some heuristic cleanup for back compatibility */
102068481Seric 	if (bitnset(M_LIMITS, m->m_flags))
102168481Seric 	{
102268481Seric 		if (m->m_linelimit == 0)
102368481Seric 			m->m_linelimit = SMTPLINELIM;
102468481Seric 		if (ConfigLevel < 2)
102568481Seric 			setbitn(M_7BITS, m->m_flags);
102668481Seric 	}
102768481Seric 
102868481Seric 	if (ConfigLevel < 6 &&
102968481Seric 	    (strcmp(m->m_mailer, "[IPC]") == 0 ||
103068481Seric 	     strcmp(m->m_mailer, "[TCP]") == 0))
103168481Seric 	{
103268481Seric 		if (m->m_mtatype == NULL)
103368481Seric 			m->m_mtatype = "dns";
103468481Seric 		if (m->m_addrtype == NULL)
103568481Seric 			m->m_addrtype = "rfc822";
103668481Seric 		if (m->m_diagtype == NULL)
103768481Seric 			m->m_diagtype = "smtp";
103868481Seric 	}
103968481Seric 
104068481Seric 	/* enter the mailer into the symbol table */
104110327Seric 	s = stab(m->m_name, ST_MAILER, ST_ENTER);
104257402Seric 	if (s->s_mailer != NULL)
104357402Seric 	{
104457402Seric 		i = s->s_mailer->m_mno;
104557402Seric 		free(s->s_mailer);
104657402Seric 	}
104757402Seric 	else
104857402Seric 	{
104957402Seric 		i = NextMailer++;
105057402Seric 	}
105157402Seric 	Mailer[i] = s->s_mailer = m;
105257454Seric 	m->m_mno = i;
105310327Seric }
105410327Seric /*
105510327Seric **  MUNCHSTRING -- translate a string into internal form.
105610327Seric **
105710327Seric **	Parameters:
105810327Seric **		p -- the string to munch.
105958333Seric **		delimptr -- if non-NULL, set to the pointer of the
106058333Seric **			field delimiter character.
106110327Seric **
106210327Seric **	Returns:
106310327Seric **		the munched string.
106410327Seric */
10654096Seric 
106610327Seric char *
106758333Seric munchstring(p, delimptr)
106810327Seric 	register char *p;
106958333Seric 	char **delimptr;
107010327Seric {
107110327Seric 	register char *q;
107210327Seric 	bool backslash = FALSE;
107310327Seric 	bool quotemode = FALSE;
107410327Seric 	static char buf[MAXLINE];
10754096Seric 
107610327Seric 	for (q = buf; *p != '\0'; p++)
10774096Seric 	{
107810327Seric 		if (backslash)
107910327Seric 		{
108010327Seric 			/* everything is roughly literal */
108110357Seric 			backslash = FALSE;
108210327Seric 			switch (*p)
108310327Seric 			{
108410327Seric 			  case 'r':		/* carriage return */
108510327Seric 				*q++ = '\r';
108610327Seric 				continue;
108710327Seric 
108810327Seric 			  case 'n':		/* newline */
108910327Seric 				*q++ = '\n';
109010327Seric 				continue;
109110327Seric 
109210327Seric 			  case 'f':		/* form feed */
109310327Seric 				*q++ = '\f';
109410327Seric 				continue;
109510327Seric 
109610327Seric 			  case 'b':		/* backspace */
109710327Seric 				*q++ = '\b';
109810327Seric 				continue;
109910327Seric 			}
110010327Seric 			*q++ = *p;
110110327Seric 		}
110210327Seric 		else
110310327Seric 		{
110410327Seric 			if (*p == '\\')
110510327Seric 				backslash = TRUE;
110610327Seric 			else if (*p == '"')
110710327Seric 				quotemode = !quotemode;
110810327Seric 			else if (quotemode || *p != ',')
110910327Seric 				*q++ = *p;
111010327Seric 			else
111110327Seric 				break;
111210327Seric 		}
11134096Seric 	}
11144096Seric 
111558333Seric 	if (delimptr != NULL)
111658333Seric 		*delimptr = p;
111710327Seric 	*q++ = '\0';
111810327Seric 	return (buf);
111910327Seric }
112010327Seric /*
112110327Seric **  MAKEARGV -- break up a string into words
112210327Seric **
112310327Seric **	Parameters:
112410327Seric **		p -- the string to break up.
112510327Seric **
112610327Seric **	Returns:
112710327Seric **		a char **argv (dynamically allocated)
112810327Seric **
112910327Seric **	Side Effects:
113010327Seric **		munges p.
113110327Seric */
11324096Seric 
113310327Seric char **
113410327Seric makeargv(p)
113510327Seric 	register char *p;
113610327Seric {
113710327Seric 	char *q;
113810327Seric 	int i;
113910327Seric 	char **avp;
114010327Seric 	char *argv[MAXPV + 1];
114110327Seric 
114210327Seric 	/* take apart the words */
114310327Seric 	i = 0;
114410327Seric 	while (*p != '\0' && i < MAXPV)
11454096Seric 	{
114610327Seric 		q = p;
114758050Seric 		while (*p != '\0' && !(isascii(*p) && isspace(*p)))
114810327Seric 			p++;
114958050Seric 		while (isascii(*p) && isspace(*p))
115010327Seric 			*p++ = '\0';
115110327Seric 		argv[i++] = newstr(q);
11524096Seric 	}
115310327Seric 	argv[i++] = NULL;
11544096Seric 
115510327Seric 	/* now make a copy of the argv */
115610327Seric 	avp = (char **) xalloc(sizeof *avp * i);
115716893Seric 	bcopy((char *) argv, (char *) avp, sizeof *avp * i);
115810327Seric 
115910327Seric 	return (avp);
11603308Seric }
11613308Seric /*
11623308Seric **  PRINTRULES -- print rewrite rules (for debugging)
11633308Seric **
11643308Seric **	Parameters:
11653308Seric **		none.
11663308Seric **
11673308Seric **	Returns:
11683308Seric **		none.
11693308Seric **
11703308Seric **	Side Effects:
11713308Seric **		prints rewrite rules.
11723308Seric */
11733308Seric 
11743308Seric printrules()
11753308Seric {
11763308Seric 	register struct rewrite *rwp;
11774072Seric 	register int ruleset;
11783308Seric 
11794072Seric 	for (ruleset = 0; ruleset < 10; ruleset++)
11803308Seric 	{
11814072Seric 		if (RewriteRules[ruleset] == NULL)
11824072Seric 			continue;
11838067Seric 		printf("\n----Rule Set %d:", ruleset);
11843308Seric 
11854072Seric 		for (rwp = RewriteRules[ruleset]; rwp != NULL; rwp = rwp->r_next)
11863308Seric 		{
11878067Seric 			printf("\nLHS:");
11888067Seric 			printav(rwp->r_lhs);
11898067Seric 			printf("RHS:");
11908067Seric 			printav(rwp->r_rhs);
11913308Seric 		}
11923308Seric 	}
11933308Seric }
119468481Seric /*
119568481Seric **  PRINTMAILER -- print mailer structure (for debugging)
119668481Seric **
119768481Seric **	Parameters:
119868481Seric **		m -- the mailer to print
119968481Seric **
120068481Seric **	Returns:
120168481Seric **		none.
120268481Seric */
12034319Seric 
120468481Seric printmailer(m)
120568481Seric 	register MAILER *m;
120668481Seric {
120768481Seric 	int j;
120868481Seric 
120968481Seric 	printf("mailer %d (%s): P=%s S=%d/%d R=%d/%d M=%ld U=%d:%d F=",
121068481Seric 		m->m_mno, m->m_name,
121168481Seric 		m->m_mailer, m->m_se_rwset, m->m_sh_rwset,
121268481Seric 		m->m_re_rwset, m->m_rh_rwset, m->m_maxsize,
121368481Seric 		m->m_uid, m->m_gid);
121468481Seric 	for (j = '\0'; j <= '\177'; j++)
121568481Seric 		if (bitnset(j, m->m_flags))
121668481Seric 			(void) putchar(j);
121768481Seric 	printf(" L=%d E=", m->m_linelimit);
121868481Seric 	xputs(m->m_eol);
121968481Seric 	if (m->m_defcharset != NULL)
122068481Seric 		printf(" C=%s", m->m_defcharset);
122168481Seric 	printf(" T=%s/%s/%s",
122268481Seric 		m->m_mtatype == NULL ? "<undefined>" : m->m_mtatype,
122368481Seric 		m->m_addrtype == NULL ? "<undefined>" : m->m_addrtype,
122468481Seric 		m->m_diagtype == NULL ? "<undefined>" : m->m_diagtype);
122568481Seric 	if (m->m_argv != NULL)
122668481Seric 	{
122768481Seric 		char **a = m->m_argv;
122868481Seric 
122968481Seric 		printf(" A=");
123068481Seric 		while (*a != NULL)
123168481Seric 		{
123268481Seric 			if (a != m->m_argv)
123368481Seric 				printf(" ");
123468481Seric 			xputs(*a++);
123568481Seric 		}
123668481Seric 	}
123768481Seric 	printf("\n");
123868481Seric }
12394096Seric /*
12408256Seric **  SETOPTION -- set global processing option
12418256Seric **
12428256Seric **	Parameters:
12438256Seric **		opt -- option name.
12448256Seric **		val -- option value (as a text string).
124521755Seric **		safe -- set if this came from a configuration file.
124621755Seric **			Some options (if set from the command line) will
124721755Seric **			reset the user id to avoid security problems.
12488269Seric **		sticky -- if set, don't let other setoptions override
12498269Seric **			this value.
125058734Seric **		e -- the main envelope.
12518256Seric **
12528256Seric **	Returns:
12538256Seric **		none.
12548256Seric **
12558256Seric **	Side Effects:
12568256Seric **		Sets options as implied by the arguments.
12578256Seric */
12588256Seric 
125910687Seric static BITMAP	StickyOpt;		/* set if option is stuck */
12608269Seric 
126157207Seric 
126266334Seric #if NAMED_BIND
126357207Seric 
126457207Seric struct resolverflags
126557207Seric {
126657207Seric 	char	*rf_name;	/* name of the flag */
126757207Seric 	long	rf_bits;	/* bits to set/clear */
126857207Seric } ResolverFlags[] =
126957207Seric {
127057207Seric 	"debug",	RES_DEBUG,
127157207Seric 	"aaonly",	RES_AAONLY,
127257207Seric 	"usevc",	RES_USEVC,
127357207Seric 	"primary",	RES_PRIMARY,
127457207Seric 	"igntc",	RES_IGNTC,
127557207Seric 	"recurse",	RES_RECURSE,
127657207Seric 	"defnames",	RES_DEFNAMES,
127757207Seric 	"stayopen",	RES_STAYOPEN,
127857207Seric 	"dnsrch",	RES_DNSRCH,
127965583Seric 	"true",		0,		/* to avoid error on old syntax */
128057207Seric 	NULL,		0
128157207Seric };
128257207Seric 
128357207Seric #endif
128457207Seric 
128568481Seric struct optioninfo
128668481Seric {
128768481Seric 	char	*o_name;	/* long name of option */
128868481Seric 	u_char	o_code;		/* short name of option */
128968481Seric 	bool	o_safe;		/* safe for random people to use */
129068481Seric } OptionTab[] =
129168481Seric {
129268481Seric 	"SevenBitInput",	'7',		TRUE,
129369480Seric #if MIME8TO7
129468481Seric 	"EightBitMode",		'8',		TRUE,
129569480Seric #endif
129668481Seric 	"AliasFile",		'A',		FALSE,
129768481Seric 	"AliasWait",		'a',		FALSE,
129868481Seric 	"BlankSub",		'B',		FALSE,
129968481Seric 	"MinFreeBlocks",	'b',		TRUE,
130068481Seric 	"CheckpointInterval",	'C',		TRUE,
130168481Seric 	"HoldExpensive",	'c',		FALSE,
130268481Seric 	"AutoRebuildAliases",	'D',		FALSE,
130368481Seric 	"DeliveryMode",		'd',		TRUE,
130468481Seric 	"ErrorHeader",		'E',		FALSE,
130568481Seric 	"ErrorMode",		'e',		TRUE,
130668481Seric 	"TempFileMode",		'F',		FALSE,
130768481Seric 	"SaveFromLine",		'f',		FALSE,
130868481Seric 	"MatchGECOS",		'G',		FALSE,
130968481Seric 	"HelpFile",		'H',		FALSE,
131068481Seric 	"MaxHopCount",		'h',		FALSE,
131168569Seric 	"ResolverOptions",	'I',		FALSE,
131268481Seric 	"IgnoreDots",		'i',		TRUE,
131368481Seric 	"ForwardPath",		'J',		FALSE,
131468481Seric 	"SendMimeErrors",	'j',		TRUE,
131568481Seric 	"ConnectionCacheSize",	'k',		FALSE,
131668481Seric 	"ConnectionCacheTimeout", 'K',		FALSE,
131768481Seric 	"UseErrorsTo",		'l',		FALSE,
131868481Seric 	"LogLevel",		'L',		FALSE,
131968481Seric 	"MeToo",		'm',		TRUE,
132068481Seric 	"CheckAliases",		'n',		FALSE,
132168481Seric 	"OldStyleHeaders",	'o',		TRUE,
132268481Seric 	"DaemonPortOptions",	'O',		FALSE,
132368481Seric 	"PrivacyOptions",	'p',		TRUE,
132468481Seric 	"PostmasterCopy",	'P',		FALSE,
132568481Seric 	"QueueFactor",		'q',		FALSE,
132668481Seric 	"QueueDirectory",	'Q',		FALSE,
132768481Seric 	"DontPruneRoutes",	'R',		FALSE,
132868481Seric 	"Timeout",		'r',		TRUE,
132968481Seric 	"StatusFile",		'S',		FALSE,
133068481Seric 	"SuperSafe",		's',		TRUE,
133168481Seric 	"QueueTimeout",		'T',		FALSE,
133268481Seric 	"TimeZoneSpec",		't',		FALSE,
133368481Seric 	"UserDatabaseSpec",	'U',		FALSE,
133468481Seric 	"DefaultUser",		'u',		FALSE,
133568481Seric 	"FallbackMXhost",	'V',		FALSE,
133668481Seric 	"Verbose",		'v',		TRUE,
133768481Seric 	"TryNullMXList",	'w',		TRUE,
133868481Seric 	"QueueLA",		'x',		FALSE,
133968481Seric 	"RefuseLA",		'X',		FALSE,
134068481Seric 	"RecipientFactor",	'y',		FALSE,
134168569Seric 	"ForkEachJob",		'Y',		FALSE,
134268481Seric 	"ClassFactor",		'z',		FALSE,
134368569Seric 	"RetryFactor",		'Z',		FALSE,
134468481Seric #define O_QUEUESORTORD	0x81
134568481Seric 	"QueueSortOrder",	O_QUEUESORTORD,	TRUE,
134669401Seric #define O_HOSTSFILE	0x82
134769401Seric 	"HostsFile",		O_HOSTSFILE,	FALSE,
134868481Seric #define O_MQA		0x83
134968481Seric 	"MinQueueAge",		O_MQA,		TRUE,
135068481Seric #define O_MHSA		0x84
135168481Seric /*
135268481Seric 	"MaxHostStatAge",	O_MHSA,		TRUE,
135368481Seric */
135468481Seric #define O_DEFCHARSET	0x85
135568481Seric 	"DefaultCharSet",	O_DEFCHARSET,	TRUE,
135668481Seric #define O_SSFILE	0x86
135768481Seric 	"ServiceSwitchFile",	O_SSFILE,	FALSE,
135868481Seric #define O_DIALDELAY	0x87
135968481Seric 	"DialDelay",		O_DIALDELAY,	TRUE,
136068481Seric #define O_NORCPTACTION	0x88
136168481Seric 	"NoRecipientAction",	O_NORCPTACTION,	TRUE,
136268490Seric #define O_SAFEFILEENV	0x89
136368490Seric 	"SafeFileEnvironment",	O_SAFEFILEENV,	FALSE,
136468569Seric #define O_MAXMSGSIZE	0x8a
136568569Seric 	"MaxMessageSize",	O_MAXMSGSIZE,	FALSE,
136668756Seric #define O_COLONOKINADDR	0x8b
136768756Seric 	"ColonOkInAddr",	O_COLONOKINADDR, TRUE,
136868481Seric 
136968481Seric 	NULL,			'\0',		FALSE,
137068481Seric };
137168481Seric 
137268481Seric 
137368481Seric 
137458734Seric setoption(opt, val, safe, sticky, e)
137568481Seric 	u_char opt;
13768256Seric 	char *val;
137721755Seric 	bool safe;
13788269Seric 	bool sticky;
137958734Seric 	register ENVELOPE *e;
13808256Seric {
138157207Seric 	register char *p;
138268481Seric 	register struct optioninfo *o;
138368481Seric 	char *subopt;
13848265Seric 	extern bool atobool();
138512633Seric 	extern time_t convtime();
138614879Seric 	extern int QueueLA;
138714879Seric 	extern int RefuseLA;
138864718Seric 	extern bool Warn_Q_option;
13898256Seric 
139068481Seric 	errno = 0;
139168481Seric 	if (opt == ' ')
139268481Seric 	{
139368481Seric 		/* full word options */
139468481Seric 		struct optioninfo *sel;
139568481Seric 
139668481Seric 		p = strchr(val, '=');
139768481Seric 		if (p == NULL)
139868481Seric 			p = &val[strlen(val)];
139968481Seric 		while (*--p == ' ')
140068481Seric 			continue;
140168481Seric 		while (*++p == ' ')
140268481Seric 			*p = '\0';
140368481Seric 		if (p == val)
140468481Seric 		{
140568481Seric 			syserr("readcf: null option name");
140668481Seric 			return;
140768481Seric 		}
140868481Seric 		if (*p == '=')
140968481Seric 			*p++ = '\0';
141068481Seric 		while (*p == ' ')
141168481Seric 			p++;
141268481Seric 		subopt = strchr(val, '.');
141368481Seric 		if (subopt != NULL)
141468481Seric 			*subopt++ = '\0';
141568481Seric 		sel = NULL;
141668481Seric 		for (o = OptionTab; o->o_name != NULL; o++)
141768481Seric 		{
141868481Seric 			if (strncasecmp(o->o_name, val, strlen(val)) != 0)
141968481Seric 				continue;
142068481Seric 			if (strlen(o->o_name) == strlen(val))
142168481Seric 			{
142268481Seric 				/* completely specified -- this must be it */
142368481Seric 				sel = NULL;
142468481Seric 				break;
142568481Seric 			}
142668481Seric 			if (sel != NULL)
142768481Seric 				break;
142868481Seric 			sel = o;
142968481Seric 		}
143068481Seric 		if (sel != NULL && o->o_name == NULL)
143168481Seric 			o = sel;
143268481Seric 		else if (o->o_name == NULL)
143368481Seric 		{
143468481Seric 			syserr("readcf: unknown option name %s", val);
143568481Seric 			return;
143668481Seric 		}
143768481Seric 		else if (sel != NULL)
143868481Seric 		{
143968481Seric 			syserr("readcf: ambiguous option name %s (matches %s and %s)",
144068481Seric 				val, sel->o_name, o->o_name);
144168481Seric 			return;
144268481Seric 		}
144368481Seric 		if (strlen(val) != strlen(o->o_name))
144468481Seric 		{
144568481Seric 			bool oldVerbose = Verbose;
144668481Seric 
144768481Seric 			Verbose = TRUE;
144868481Seric 			message("Option %s used as abbreviation for %s",
144968481Seric 				val, o->o_name);
145068481Seric 			Verbose = oldVerbose;
145168481Seric 		}
145268481Seric 		opt = o->o_code;
145368481Seric 		val = p;
145468481Seric 	}
145568481Seric 	else
145668481Seric 	{
145768481Seric 		for (o = OptionTab; o->o_name != NULL; o++)
145868481Seric 		{
145968481Seric 			if (o->o_code == opt)
146068481Seric 				break;
146168481Seric 		}
146268481Seric 		subopt = NULL;
146368481Seric 	}
146468481Seric 
14658256Seric 	if (tTd(37, 1))
146668481Seric 	{
146768481Seric 		printf(isascii(opt) && isprint(opt) ?
146868481Seric 			    "setoption %s (%c).%s=%s" :
146968481Seric 			    "setoption %s (0x%x).%s=%s",
147068481Seric 			o->o_name == NULL ? "<unknown>" : o->o_name,
147168481Seric 			opt,
147268481Seric 			subopt == NULL ? "" : subopt,
147368481Seric 			val);
147468481Seric 	}
14758256Seric 
14768256Seric 	/*
14778269Seric 	**  See if this option is preset for us.
14788256Seric 	*/
14798256Seric 
148059731Seric 	if (!sticky && bitnset(opt, StickyOpt))
14818269Seric 	{
14829341Seric 		if (tTd(37, 1))
14839341Seric 			printf(" (ignored)\n");
14848269Seric 		return;
14858269Seric 	}
14868269Seric 
148721755Seric 	/*
148821755Seric 	**  Check to see if this option can be specified by this user.
148921755Seric 	*/
149021755Seric 
149163787Seric 	if (!safe && RealUid == 0)
149221755Seric 		safe = TRUE;
149368481Seric 	if (!safe && !o->o_safe)
149421755Seric 	{
149539111Srick 		if (opt != 'M' || (val[0] != 'r' && val[0] != 's'))
149621755Seric 		{
149736582Sbostic 			if (tTd(37, 1))
149836582Sbostic 				printf(" (unsafe)");
149963787Seric 			if (RealUid != geteuid())
150036582Sbostic 			{
150151210Seric 				if (tTd(37, 1))
150251210Seric 					printf("(Resetting uid)");
150363787Seric 				(void) setgid(RealGid);
150463787Seric 				(void) setuid(RealUid);
150536582Sbostic 			}
150621755Seric 		}
150721755Seric 	}
150851210Seric 	if (tTd(37, 1))
150917985Seric 		printf("\n");
15108269Seric 
151168481Seric 	switch (opt & 0xff)
15128256Seric 	{
151359709Seric 	  case '7':		/* force seven-bit input */
151468481Seric 		SevenBitInput = atobool(val);
151552106Seric 		break;
151652106Seric 
151769480Seric #if MIME8TO7
151868481Seric 	  case '8':		/* handling of 8-bit input */
151968481Seric 		switch (*val)
152068481Seric 		{
152168481Seric 		  case 'm':		/* convert 8-bit, convert MIME */
152268481Seric 			MimeMode = MM_CVTMIME|MM_MIME8BIT;
152368481Seric 			break;
152468481Seric 
152568481Seric 		  case 'p':		/* pass 8 bit, convert MIME */
152668856Seric 			MimeMode = MM_CVTMIME|MM_PASS8BIT;
152768481Seric 			break;
152868481Seric 
152968481Seric 		  case 's':		/* strict adherence */
153068481Seric 			MimeMode = MM_CVTMIME;
153168481Seric 			break;
153268481Seric 
153368856Seric #if 0
153468856Seric 		  case 'r':		/* reject 8-bit, don't convert MIME */
153568856Seric 			MimeMode = 0;
153668856Seric 			break;
153768856Seric 
153868856Seric 		  case 'j':		/* "just send 8" */
153968856Seric 			MimeMode = MM_PASS8BIT;
154068856Seric 			break;
154168856Seric 
154268481Seric 		  case 'a':		/* encode 8 bit if available */
154368481Seric 			MimeMode = MM_MIME8BIT|MM_PASS8BIT|MM_CVTMIME;
154468481Seric 			break;
154568481Seric 
154668481Seric 		  case 'c':		/* convert 8 bit to MIME, never 7 bit */
154768481Seric 			MimeMode = MM_MIME8BIT;
154868481Seric 			break;
154968856Seric #endif
155068481Seric 
155168481Seric 		  default:
155268481Seric 			syserr("Unknown 8-bit mode %c", *val);
155368481Seric 			exit(EX_USAGE);
155468481Seric 		}
155568481Seric 		break;
155669480Seric #endif
155768481Seric 
15588256Seric 	  case 'A':		/* set default alias file */
15599381Seric 		if (val[0] == '\0')
156059672Seric 			setalias("aliases");
15619381Seric 		else
156259672Seric 			setalias(val);
15638256Seric 		break;
15648256Seric 
156517474Seric 	  case 'a':		/* look N minutes for "@:@" in alias file */
156617474Seric 		if (val[0] == '\0')
156764796Seric 			SafeAlias = 5 * 60;		/* five minutes */
156817474Seric 		else
156964796Seric 			SafeAlias = convtime(val, 'm');
157017474Seric 		break;
157117474Seric 
157216843Seric 	  case 'B':		/* substitution for blank character */
157316843Seric 		SpaceSub = val[0];
157416843Seric 		if (SpaceSub == '\0')
157516843Seric 			SpaceSub = ' ';
157616843Seric 		break;
157716843Seric 
157859283Seric 	  case 'b':		/* min blocks free on queue fs/max msg size */
157959283Seric 		p = strchr(val, '/');
158059283Seric 		if (p != NULL)
158159283Seric 		{
158259283Seric 			*p++ = '\0';
158359283Seric 			MaxMessageSize = atol(p);
158459283Seric 		}
158558082Seric 		MinBlocksFree = atol(val);
158658082Seric 		break;
158758082Seric 
15889284Seric 	  case 'c':		/* don't connect to "expensive" mailers */
15899381Seric 		NoConnect = atobool(val);
15909284Seric 		break;
15919284Seric 
159251305Seric 	  case 'C':		/* checkpoint every N addresses */
159351305Seric 		CheckpointInterval = atoi(val);
159424944Seric 		break;
159524944Seric 
15969284Seric 	  case 'd':		/* delivery mode */
15979284Seric 		switch (*val)
15988269Seric 		{
15999284Seric 		  case '\0':
160058734Seric 			e->e_sendmode = SM_DELIVER;
16018269Seric 			break;
16028269Seric 
160310755Seric 		  case SM_QUEUE:	/* queue only */
160410755Seric #ifndef QUEUE
160510755Seric 			syserr("need QUEUE to set -odqueue");
160656795Seric #endif /* QUEUE */
160710755Seric 			/* fall through..... */
160810755Seric 
16099284Seric 		  case SM_DELIVER:	/* do everything */
16109284Seric 		  case SM_FORK:		/* fork after verification */
161158734Seric 			e->e_sendmode = *val;
16128269Seric 			break;
16138269Seric 
16148269Seric 		  default:
16159284Seric 			syserr("Unknown delivery mode %c", *val);
16168269Seric 			exit(EX_USAGE);
16178269Seric 		}
16188269Seric 		break;
16198269Seric 
16209146Seric 	  case 'D':		/* rebuild alias database as needed */
16219381Seric 		AutoRebuild = atobool(val);
16229146Seric 		break;
16239146Seric 
162455372Seric 	  case 'E':		/* error message header/header file */
162555379Seric 		if (*val != '\0')
162655379Seric 			ErrMsgFile = newstr(val);
162755372Seric 		break;
162855372Seric 
16298269Seric 	  case 'e':		/* set error processing mode */
16308269Seric 		switch (*val)
16318269Seric 		{
16329381Seric 		  case EM_QUIET:	/* be silent about it */
16339381Seric 		  case EM_MAIL:		/* mail back */
16349381Seric 		  case EM_BERKNET:	/* do berknet error processing */
16359381Seric 		  case EM_WRITE:	/* write back (or mail) */
16369381Seric 		  case EM_PRINT:	/* print errors normally (default) */
163758734Seric 			e->e_errormode = *val;
16388269Seric 			break;
16398269Seric 		}
16408269Seric 		break;
16418269Seric 
16429049Seric 	  case 'F':		/* file mode */
164317975Seric 		FileMode = atooct(val) & 0777;
16449049Seric 		break;
16459049Seric 
16468269Seric 	  case 'f':		/* save Unix-style From lines on front */
16479381Seric 		SaveFrom = atobool(val);
16488269Seric 		break;
16498269Seric 
165053735Seric 	  case 'G':		/* match recipients against GECOS field */
165153735Seric 		MatchGecos = atobool(val);
165253735Seric 		break;
165353735Seric 
16548256Seric 	  case 'g':		/* default gid */
165568481Seric   g_opt:
165664133Seric 		if (isascii(*val) && isdigit(*val))
165764133Seric 			DefGid = atoi(val);
165864133Seric 		else
165964133Seric 		{
166064133Seric 			register struct group *gr;
166164133Seric 
166264133Seric 			DefGid = -1;
166364133Seric 			gr = getgrnam(val);
166464133Seric 			if (gr == NULL)
166568481Seric 				syserr("readcf: option %c: unknown group %s",
166668481Seric 					opt, val);
166764133Seric 			else
166864133Seric 				DefGid = gr->gr_gid;
166964133Seric 		}
16708256Seric 		break;
16718256Seric 
16728256Seric 	  case 'H':		/* help file */
16739381Seric 		if (val[0] == '\0')
16748269Seric 			HelpFile = "sendmail.hf";
16759381Seric 		else
16769381Seric 			HelpFile = newstr(val);
16778256Seric 		break;
16788256Seric 
167951305Seric 	  case 'h':		/* maximum hop count */
168051305Seric 		MaxHopCount = atoi(val);
168151305Seric 		break;
168251305Seric 
168335651Seric 	  case 'I':		/* use internet domain name server */
168466334Seric #if NAMED_BIND
168557207Seric 		for (p = val; *p != 0; )
168657207Seric 		{
168757207Seric 			bool clearmode;
168857207Seric 			char *q;
168957207Seric 			struct resolverflags *rfp;
169057207Seric 
169157207Seric 			while (*p == ' ')
169257207Seric 				p++;
169357207Seric 			if (*p == '\0')
169457207Seric 				break;
169557207Seric 			clearmode = FALSE;
169657207Seric 			if (*p == '-')
169757207Seric 				clearmode = TRUE;
169857207Seric 			else if (*p != '+')
169957207Seric 				p--;
170057207Seric 			p++;
170157207Seric 			q = p;
170258050Seric 			while (*p != '\0' && !(isascii(*p) && isspace(*p)))
170357207Seric 				p++;
170457207Seric 			if (*p != '\0')
170557207Seric 				*p++ = '\0';
170668759Seric 			if (strcasecmp(q, "HasWildcardMX") == 0)
170768759Seric 			{
170868759Seric 				NoMXforCanon = !clearmode;
170968759Seric 				continue;
171068759Seric 			}
171157207Seric 			for (rfp = ResolverFlags; rfp->rf_name != NULL; rfp++)
171257207Seric 			{
171357207Seric 				if (strcasecmp(q, rfp->rf_name) == 0)
171457207Seric 					break;
171557207Seric 			}
171664923Seric 			if (rfp->rf_name == NULL)
171764923Seric 				syserr("readcf: I option value %s unrecognized", q);
171864923Seric 			else if (clearmode)
171957207Seric 				_res.options &= ~rfp->rf_bits;
172057207Seric 			else
172157207Seric 				_res.options |= rfp->rf_bits;
172257207Seric 		}
172357207Seric 		if (tTd(8, 2))
172468759Seric 			printf("_res.options = %x, HasWildcardMX = %d\n",
172568759Seric 				_res.options, !NoMXforCanon);
172657207Seric #else
172757207Seric 		usrerr("name server (I option) specified but BIND not compiled in");
172857207Seric #endif
172935651Seric 		break;
173035651Seric 
17318269Seric 	  case 'i':		/* ignore dot lines in message */
17329381Seric 		IgnrDot = atobool(val);
17338269Seric 		break;
17348269Seric 
173559730Seric 	  case 'j':		/* send errors in MIME (RFC 1341) format */
173659730Seric 		SendMIMEErrors = atobool(val);
173759730Seric 		break;
173859730Seric 
173957136Seric 	  case 'J':		/* .forward search path */
174057136Seric 		ForwardPath = newstr(val);
174157136Seric 		break;
174257136Seric 
174354967Seric 	  case 'k':		/* connection cache size */
174454967Seric 		MaxMciCache = atoi(val);
174556215Seric 		if (MaxMciCache < 0)
174656215Seric 			MaxMciCache = 0;
174754967Seric 		break;
174854967Seric 
174954967Seric 	  case 'K':		/* connection cache timeout */
175058796Seric 		MciCacheTimeout = convtime(val, 'm');
175154967Seric 		break;
175254967Seric 
175361104Seric 	  case 'l':		/* use Errors-To: header */
175461104Seric 		UseErrorsTo = atobool(val);
175561104Seric 		break;
175661104Seric 
17578256Seric 	  case 'L':		/* log level */
175864140Seric 		if (safe || LogLevel < atoi(val))
175964140Seric 			LogLevel = atoi(val);
17608256Seric 		break;
17618256Seric 
17628269Seric 	  case 'M':		/* define macro */
176368267Seric 		p = newstr(&val[1]);
176468267Seric 		if (!safe)
176568267Seric 			cleanstrcpy(p, p, MAXNAME);
176668267Seric 		define(val[0], p, CurEnv);
176716878Seric 		sticky = FALSE;
17688269Seric 		break;
17698269Seric 
17708269Seric 	  case 'm':		/* send to me too */
17719381Seric 		MeToo = atobool(val);
17728269Seric 		break;
17738269Seric 
177425820Seric 	  case 'n':		/* validate RHS in newaliases */
177525820Seric 		CheckAliases = atobool(val);
177625820Seric 		break;
177725820Seric 
177861104Seric 	    /* 'N' available -- was "net name" */
177961104Seric 
178058851Seric 	  case 'O':		/* daemon options */
178158851Seric 		setdaemonoptions(val);
178258851Seric 		break;
178358851Seric 
17848269Seric 	  case 'o':		/* assume old style headers */
17859381Seric 		if (atobool(val))
17869341Seric 			CurEnv->e_flags |= EF_OLDSTYLE;
17879341Seric 		else
17889341Seric 			CurEnv->e_flags &= ~EF_OLDSTYLE;
17898269Seric 		break;
17908269Seric 
179158082Seric 	  case 'p':		/* select privacy level */
179258082Seric 		p = val;
179358082Seric 		for (;;)
179458082Seric 		{
179558082Seric 			register struct prival *pv;
179658082Seric 			extern struct prival PrivacyValues[];
179758082Seric 
179858082Seric 			while (isascii(*p) && (isspace(*p) || ispunct(*p)))
179958082Seric 				p++;
180058082Seric 			if (*p == '\0')
180158082Seric 				break;
180258082Seric 			val = p;
180358082Seric 			while (isascii(*p) && isalnum(*p))
180458082Seric 				p++;
180558082Seric 			if (*p != '\0')
180658082Seric 				*p++ = '\0';
180758082Seric 
180858082Seric 			for (pv = PrivacyValues; pv->pv_name != NULL; pv++)
180958082Seric 			{
181058082Seric 				if (strcasecmp(val, pv->pv_name) == 0)
181158082Seric 					break;
181258082Seric 			}
181358886Seric 			if (pv->pv_name == NULL)
181458886Seric 				syserr("readcf: Op line: %s unrecognized", val);
181558082Seric 			PrivacyFlags |= pv->pv_flag;
181658082Seric 		}
181768479Seric 		sticky = FALSE;
181858082Seric 		break;
181958082Seric 
182024944Seric 	  case 'P':		/* postmaster copy address for returned mail */
182124944Seric 		PostMasterCopy = newstr(val);
182224944Seric 		break;
182324944Seric 
182424944Seric 	  case 'q':		/* slope of queue only function */
182524944Seric 		QueueFactor = atoi(val);
182624944Seric 		break;
182724944Seric 
18288256Seric 	  case 'Q':		/* queue directory */
18299381Seric 		if (val[0] == '\0')
18308269Seric 			QueueDir = "mqueue";
18319381Seric 		else
18329381Seric 			QueueDir = newstr(val);
183358789Seric 		if (RealUid != 0 && !safe)
183464718Seric 			Warn_Q_option = TRUE;
18358256Seric 		break;
18368256Seric 
183758148Seric 	  case 'R':		/* don't prune routes */
183858148Seric 		DontPruneRoutes = atobool(val);
183958148Seric 		break;
184058148Seric 
18418256Seric 	  case 'r':		/* read timeout */
184268481Seric 		if (subopt == NULL)
184368481Seric 			inittimeouts(val);
184468481Seric 		else
184568481Seric 			settimeout(subopt, val);
18468256Seric 		break;
18478256Seric 
18488256Seric 	  case 'S':		/* status file */
18499381Seric 		if (val[0] == '\0')
18508269Seric 			StatFile = "sendmail.st";
18519381Seric 		else
18529381Seric 			StatFile = newstr(val);
18538256Seric 		break;
18548256Seric 
18558265Seric 	  case 's':		/* be super safe, even if expensive */
18569381Seric 		SuperSafe = atobool(val);
18578256Seric 		break;
18588256Seric 
18598256Seric 	  case 'T':		/* queue timeout */
186058737Seric 		p = strchr(val, '/');
186158737Seric 		if (p != NULL)
186258737Seric 		{
186358737Seric 			*p++ = '\0';
186468481Seric 			settimeout("queuewarn", p);
186558737Seric 		}
186668481Seric 		settimeout("queuereturn", val);
186754967Seric 		break;
18688256Seric 
18698265Seric 	  case 't':		/* time zone name */
187052106Seric 		TimeZoneSpec = newstr(val);
18718265Seric 		break;
18728265Seric 
187350556Seric 	  case 'U':		/* location of user database */
187451360Seric 		UdbSpec = newstr(val);
187550556Seric 		break;
187650556Seric 
18778256Seric 	  case 'u':		/* set default uid */
187868481Seric 		for (p = val; *p != '\0'; p++)
187968481Seric 		{
188068481Seric 			if (*p == '.' || *p == '/' || *p == ':')
188168481Seric 			{
188268481Seric 				*p++ = '\0';
188368481Seric 				break;
188468481Seric 			}
188568481Seric 		}
188664133Seric 		if (isascii(*val) && isdigit(*val))
188764133Seric 			DefUid = atoi(val);
188864133Seric 		else
188964133Seric 		{
189064133Seric 			register struct passwd *pw;
189164133Seric 
189264133Seric 			DefUid = -1;
189368693Seric 			pw = sm_getpwnam(val);
189464133Seric 			if (pw == NULL)
189564133Seric 				syserr("readcf: option u: unknown user %s", val);
189664133Seric 			else
189768481Seric 			{
189864133Seric 				DefUid = pw->pw_uid;
189968481Seric 				DefGid = pw->pw_gid;
190068481Seric 			}
190164133Seric 		}
190240973Sbostic 		setdefuser();
19038256Seric 
190468481Seric 		/* handle the group if it is there */
190568481Seric 		if (*p == '\0')
190668481Seric 			break;
190768481Seric 		val = p;
190868481Seric 		goto g_opt;
190968481Seric 
191058851Seric 	  case 'V':		/* fallback MX host */
191158851Seric 		FallBackMX = newstr(val);
191258851Seric 		break;
191358851Seric 
19148269Seric 	  case 'v':		/* run in verbose mode */
19159381Seric 		Verbose = atobool(val);
19168256Seric 		break;
19178256Seric 
191863837Seric 	  case 'w':		/* if we are best MX, try host directly */
191963837Seric 		TryNullMXList = atobool(val);
192063837Seric 		break;
192161104Seric 
192261104Seric 	    /* 'W' available -- was wizard password */
192361104Seric 
192414879Seric 	  case 'x':		/* load avg at which to auto-queue msgs */
192514879Seric 		QueueLA = atoi(val);
192614879Seric 		break;
192714879Seric 
192814879Seric 	  case 'X':		/* load avg at which to auto-reject connections */
192914879Seric 		RefuseLA = atoi(val);
193014879Seric 		break;
193114879Seric 
193224981Seric 	  case 'y':		/* work recipient factor */
193324981Seric 		WkRecipFact = atoi(val);
193424981Seric 		break;
193524981Seric 
193624981Seric 	  case 'Y':		/* fork jobs during queue runs */
193724952Seric 		ForkQueueRuns = atobool(val);
193824952Seric 		break;
193924952Seric 
194024981Seric 	  case 'z':		/* work message class factor */
194124981Seric 		WkClassFact = atoi(val);
194224981Seric 		break;
194324981Seric 
194424981Seric 	  case 'Z':		/* work time factor */
194524981Seric 		WkTimeFact = atoi(val);
194624981Seric 		break;
194724981Seric 
194868481Seric 	  case O_QUEUESORTORD:	/* queue sorting order */
194968481Seric 		switch (*val)
195068481Seric 		{
195168481Seric 		  case 'h':	/* Host first */
195268481Seric 		  case 'H':
195368481Seric 			QueueSortOrder = QS_BYHOST;
195468481Seric 			break;
195568481Seric 
195668481Seric 		  case 'p':	/* Priority order */
195768481Seric 		  case 'P':
195868481Seric 			QueueSortOrder = QS_BYPRIORITY;
195968481Seric 			break;
196068481Seric 
196168481Seric 		  default:
196268481Seric 			syserr("Invalid queue sort order \"%s\"", val);
196368481Seric 		}
196468481Seric 		break;
196568481Seric 
196669401Seric 	  case O_HOSTSFILE:	/* pathname of /etc/hosts file */
196769401Seric 		HostsFile = newstr(val);
196869401Seric 		break;
196969401Seric 
197068481Seric 	  case O_MQA:		/* minimum queue age between deliveries */
197168481Seric 		MinQueueAge = convtime(val, 'm');
197268481Seric 		break;
197368481Seric 
197468481Seric 	  case O_MHSA:		/* maximum age of cached host status */
197568481Seric 		MaxHostStatAge = convtime(val, 'm');
197668481Seric 		break;
197768481Seric 
197868481Seric 	  case O_DEFCHARSET:	/* default character set for mimefying */
197968481Seric 		DefaultCharSet = newstr(denlstring(val, TRUE, TRUE));
198068481Seric 		break;
198168481Seric 
198268481Seric 	  case O_SSFILE:	/* service switch file */
198368481Seric 		ServiceSwitchFile = newstr(val);
198468481Seric 		break;
198568481Seric 
198668481Seric 	  case O_DIALDELAY:	/* delay for dial-on-demand operation */
198768481Seric 		DialDelay = convtime(val, 's');
198868481Seric 		break;
198968481Seric 
199068481Seric 	  case O_NORCPTACTION:	/* what to do if no recipient */
199168481Seric 		if (strcasecmp(val, "none") == 0)
199268481Seric 			NoRecipientAction = NRA_NO_ACTION;
199368481Seric 		else if (strcasecmp(val, "add-to") == 0)
199468481Seric 			NoRecipientAction = NRA_ADD_TO;
199568481Seric 		else if (strcasecmp(val, "add-apparently-to") == 0)
199668481Seric 			NoRecipientAction = NRA_ADD_APPARENTLY_TO;
199768481Seric 		else if (strcasecmp(val, "add-bcc") == 0)
199868481Seric 			NoRecipientAction = NRA_ADD_BCC;
199968481Seric 		else if (strcasecmp(val, "add-to-undisclosed") == 0)
200068481Seric 			NoRecipientAction = NRA_ADD_TO_UNDISCLOSED;
200168481Seric 		else
200268481Seric 			syserr("Invalid NoRecipientAction: %s", val);
200368481Seric 
200468490Seric 	  case O_SAFEFILEENV:	/* chroot() environ for writing to files */
200568490Seric 		SafeFileEnv = newstr(val);
200668490Seric 		break;
200768490Seric 
200868569Seric 	  case O_MAXMSGSIZE:	/* maximum message size */
200968569Seric 		MaxMessageSize = atol(p);
201068569Seric 		break;
201168569Seric 
201268756Seric 	  case O_COLONOKINADDR:	/* old style handling of colon addresses */
201368756Seric 		ColonOkInAddr = atobool(p);
201468756Seric 		break;
201568756Seric 
20168256Seric 	  default:
201768481Seric 		if (tTd(37, 1))
201868481Seric 		{
201968481Seric 			if (isascii(opt) && isprint(opt))
202068481Seric 				printf("Warning: option %c unknown\n", opt);
202168481Seric 			else
202268481Seric 				printf("Warning: option 0x%x unknown\n", opt);
202368481Seric 		}
20248256Seric 		break;
20258256Seric 	}
202616878Seric 	if (sticky)
202716878Seric 		setbitn(opt, StickyOpt);
20289188Seric 	return;
20298256Seric }
203010687Seric /*
203168481Seric **  SETCLASS -- set a string into a class
203210687Seric **
203310687Seric **	Parameters:
203468481Seric **		class -- the class to put the string in.
203568481Seric **		str -- the string to enter
203610687Seric **
203710687Seric **	Returns:
203810687Seric **		none.
203910687Seric **
204010687Seric **	Side Effects:
204110687Seric **		puts the word into the symbol table.
204210687Seric */
204310687Seric 
204468481Seric setclass(class, str)
204510687Seric 	int class;
204668481Seric 	char *str;
204710687Seric {
204810687Seric 	register STAB *s;
204910687Seric 
205057943Seric 	if (tTd(37, 8))
205168481Seric 		printf("setclass(%c, %s)\n", class, str);
205268481Seric 	s = stab(str, ST_CLASS, ST_ENTER);
205310687Seric 	setbitn(class, s->s_class);
205410687Seric }
205553654Seric /*
205653654Seric **  MAKEMAPENTRY -- create a map entry
205753654Seric **
205853654Seric **	Parameters:
205953654Seric **		line -- the config file line
206053654Seric **
206153654Seric **	Returns:
206253654Seric **		TRUE if it successfully entered the map entry.
206353654Seric **		FALSE otherwise (usually syntax error).
206453654Seric **
206553654Seric **	Side Effects:
206653654Seric **		Enters the map into the dictionary.
206753654Seric */
206853654Seric 
206953654Seric void
207053654Seric makemapentry(line)
207153654Seric 	char *line;
207253654Seric {
207353654Seric 	register char *p;
207453654Seric 	char *mapname;
207553654Seric 	char *classname;
207664078Seric 	register STAB *s;
207753654Seric 	STAB *class;
207853654Seric 
207958050Seric 	for (p = line; isascii(*p) && isspace(*p); p++)
208053654Seric 		continue;
208158050Seric 	if (!(isascii(*p) && isalnum(*p)))
208253654Seric 	{
208353654Seric 		syserr("readcf: config K line: no map name");
208453654Seric 		return;
208553654Seric 	}
208653654Seric 
208753654Seric 	mapname = p;
208868481Seric 	while ((isascii(*++p) && isalnum(*p)) || *p == '.')
208953654Seric 		continue;
209053654Seric 	if (*p != '\0')
209153654Seric 		*p++ = '\0';
209258050Seric 	while (isascii(*p) && isspace(*p))
209353654Seric 		p++;
209458050Seric 	if (!(isascii(*p) && isalnum(*p)))
209553654Seric 	{
209653654Seric 		syserr("readcf: config K line, map %s: no map class", mapname);
209753654Seric 		return;
209853654Seric 	}
209953654Seric 	classname = p;
210058050Seric 	while (isascii(*++p) && isalnum(*p))
210153654Seric 		continue;
210253654Seric 	if (*p != '\0')
210353654Seric 		*p++ = '\0';
210458050Seric 	while (isascii(*p) && isspace(*p))
210553654Seric 		p++;
210653654Seric 
210753654Seric 	/* look up the class */
210853654Seric 	class = stab(classname, ST_MAPCLASS, ST_FIND);
210953654Seric 	if (class == NULL)
211053654Seric 	{
211153654Seric 		syserr("readcf: map %s: class %s not available", mapname, classname);
211253654Seric 		return;
211353654Seric 	}
211453654Seric 
211553654Seric 	/* enter the map */
211664078Seric 	s = stab(mapname, ST_MAP, ST_ENTER);
211764078Seric 	s->s_map.map_class = &class->s_mapclass;
211864078Seric 	s->s_map.map_mname = newstr(mapname);
211953654Seric 
212064078Seric 	if (class->s_mapclass.map_parse(&s->s_map, p))
212164078Seric 		s->s_map.map_mflags |= MF_VALID;
212264078Seric 
212364078Seric 	if (tTd(37, 5))
212464078Seric 	{
212564078Seric 		printf("map %s, class %s, flags %x, file %s,\n",
212664078Seric 			s->s_map.map_mname, s->s_map.map_class->map_cname,
212764078Seric 			s->s_map.map_mflags,
212864078Seric 			s->s_map.map_file == NULL ? "(null)" : s->s_map.map_file);
212964078Seric 		printf("\tapp %s, domain %s, rebuild %s\n",
213064078Seric 			s->s_map.map_app == NULL ? "(null)" : s->s_map.map_app,
213164078Seric 			s->s_map.map_domain == NULL ? "(null)" : s->s_map.map_domain,
213264078Seric 			s->s_map.map_rebuild == NULL ? "(null)" : s->s_map.map_rebuild);
213364078Seric 	}
213453654Seric }
213558112Seric /*
213668481Seric **  INITTIMEOUTS -- parse and set timeout values
213758112Seric **
213858112Seric **	Parameters:
213958112Seric **		val -- a pointer to the values.  If NULL, do initial
214058112Seric **			settings.
214158112Seric **
214258112Seric **	Returns:
214358112Seric **		none.
214458112Seric **
214558112Seric **	Side Effects:
214658112Seric **		Initializes the TimeOuts structure
214758112Seric */
214858112Seric 
214964255Seric #define SECONDS
215058112Seric #define MINUTES	* 60
215158112Seric #define HOUR	* 3600
215258112Seric 
215368481Seric inittimeouts(val)
215458112Seric 	register char *val;
215558112Seric {
215658112Seric 	register char *p;
215758671Seric 	extern time_t convtime();
215858112Seric 
215958112Seric 	if (val == NULL)
216058112Seric 	{
216158112Seric 		TimeOuts.to_initial = (time_t) 5 MINUTES;
216258112Seric 		TimeOuts.to_helo = (time_t) 5 MINUTES;
216358112Seric 		TimeOuts.to_mail = (time_t) 10 MINUTES;
216458112Seric 		TimeOuts.to_rcpt = (time_t) 1 HOUR;
216558112Seric 		TimeOuts.to_datainit = (time_t) 5 MINUTES;
216658112Seric 		TimeOuts.to_datablock = (time_t) 1 HOUR;
216758112Seric 		TimeOuts.to_datafinal = (time_t) 1 HOUR;
216858112Seric 		TimeOuts.to_rset = (time_t) 5 MINUTES;
216958112Seric 		TimeOuts.to_quit = (time_t) 2 MINUTES;
217058112Seric 		TimeOuts.to_nextcommand = (time_t) 1 HOUR;
217158112Seric 		TimeOuts.to_miscshort = (time_t) 2 MINUTES;
217268481Seric #if IDENTPROTO
217364255Seric 		TimeOuts.to_ident = (time_t) 30 SECONDS;
217468481Seric #else
217568481Seric 		TimeOuts.to_ident = (time_t) 0 SECONDS;
217668481Seric #endif
217768481Seric 		TimeOuts.to_fileopen = (time_t) 60 SECONDS;
217858112Seric 		return;
217958112Seric 	}
218058112Seric 
218158112Seric 	for (;; val = p)
218258112Seric 	{
218358112Seric 		while (isascii(*val) && isspace(*val))
218458112Seric 			val++;
218558112Seric 		if (*val == '\0')
218658112Seric 			break;
218758112Seric 		for (p = val; *p != '\0' && *p != ','; p++)
218858112Seric 			continue;
218958112Seric 		if (*p != '\0')
219058112Seric 			*p++ = '\0';
219158112Seric 
219258112Seric 		if (isascii(*val) && isdigit(*val))
219358112Seric 		{
219458112Seric 			/* old syntax -- set everything */
219558796Seric 			TimeOuts.to_mail = convtime(val, 'm');
219658112Seric 			TimeOuts.to_rcpt = TimeOuts.to_mail;
219758112Seric 			TimeOuts.to_datainit = TimeOuts.to_mail;
219858112Seric 			TimeOuts.to_datablock = TimeOuts.to_mail;
219958112Seric 			TimeOuts.to_datafinal = TimeOuts.to_mail;
220058112Seric 			TimeOuts.to_nextcommand = TimeOuts.to_mail;
220158112Seric 			continue;
220258112Seric 		}
220358112Seric 		else
220458112Seric 		{
220568481Seric 			register char *q = strchr(val, ':');
220658112Seric 
220768481Seric 			if (q == NULL && (q = strchr(val, '=')) == NULL)
220858112Seric 			{
220958112Seric 				/* syntax error */
221058112Seric 				continue;
221158112Seric 			}
221258112Seric 			*q++ = '\0';
221368481Seric 			settimeout(val, q);
221468481Seric 		}
221568481Seric 	}
221668481Seric }
221768481Seric /*
221868481Seric **  SETTIMEOUT -- set an individual timeout
221968481Seric **
222068481Seric **	Parameters:
222168481Seric **		name -- the name of the timeout.
222268481Seric **		val -- the value of the timeout.
222368481Seric **
222468481Seric **	Returns:
222568481Seric **		none.
222668481Seric */
222758112Seric 
222868481Seric settimeout(name, val)
222968481Seric 	char *name;
223068481Seric 	char *val;
223168481Seric {
223268481Seric 	register char *p;
223368481Seric 	time_t to;
223468481Seric 	extern time_t convtime();
223568481Seric 
223668481Seric 	to = convtime(val, 'm');
223768481Seric 	p = strchr(name, '.');
223868481Seric 	if (p != NULL)
223968481Seric 		*p++ = '\0';
224068481Seric 
224168481Seric 	if (strcasecmp(name, "initial") == 0)
224268481Seric 		TimeOuts.to_initial = to;
224368481Seric 	else if (strcasecmp(name, "mail") == 0)
224468481Seric 		TimeOuts.to_mail = to;
224568481Seric 	else if (strcasecmp(name, "rcpt") == 0)
224668481Seric 		TimeOuts.to_rcpt = to;
224768481Seric 	else if (strcasecmp(name, "datainit") == 0)
224868481Seric 		TimeOuts.to_datainit = to;
224968481Seric 	else if (strcasecmp(name, "datablock") == 0)
225068481Seric 		TimeOuts.to_datablock = to;
225168481Seric 	else if (strcasecmp(name, "datafinal") == 0)
225268481Seric 		TimeOuts.to_datafinal = to;
225368481Seric 	else if (strcasecmp(name, "command") == 0)
225468481Seric 		TimeOuts.to_nextcommand = to;
225568481Seric 	else if (strcasecmp(name, "rset") == 0)
225668481Seric 		TimeOuts.to_rset = to;
225768481Seric 	else if (strcasecmp(name, "helo") == 0)
225868481Seric 		TimeOuts.to_helo = to;
225968481Seric 	else if (strcasecmp(name, "quit") == 0)
226068481Seric 		TimeOuts.to_quit = to;
226168481Seric 	else if (strcasecmp(name, "misc") == 0)
226268481Seric 		TimeOuts.to_miscshort = to;
226368481Seric 	else if (strcasecmp(name, "ident") == 0)
226468481Seric 		TimeOuts.to_ident = to;
226568481Seric 	else if (strcasecmp(name, "fileopen") == 0)
226668481Seric 		TimeOuts.to_fileopen = to;
226768481Seric 	else if (strcasecmp(name, "queuewarn") == 0)
226868481Seric 	{
226968481Seric 		to = convtime(val, 'h');
227068481Seric 		if (p == NULL || strcmp(p, "*") == 0)
227168481Seric 		{
227268481Seric 			TimeOuts.to_q_warning[TOC_NORMAL] = to;
227368481Seric 			TimeOuts.to_q_warning[TOC_URGENT] = to;
227468481Seric 			TimeOuts.to_q_warning[TOC_NONURGENT] = to;
227558112Seric 		}
227668481Seric 		else if (strcasecmp(p, "normal") == 0)
227768481Seric 			TimeOuts.to_q_warning[TOC_NORMAL] = to;
227868481Seric 		else if (strcasecmp(p, "urgent") == 0)
227968481Seric 			TimeOuts.to_q_warning[TOC_URGENT] = to;
228068481Seric 		else if (strcasecmp(p, "non-urgent") == 0)
228168481Seric 			TimeOuts.to_q_warning[TOC_NONURGENT] = to;
228268481Seric 		else
228368481Seric 			syserr("settimeout: invalid queuewarn subtimeout %s", p);
228458112Seric 	}
228568481Seric 	else if (strcasecmp(name, "queuereturn") == 0)
228668481Seric 	{
228768481Seric 		to = convtime(val, 'd');
228868481Seric 		if (p == NULL || strcmp(p, "*") == 0)
228968481Seric 		{
229068481Seric 			TimeOuts.to_q_return[TOC_NORMAL] = to;
229168481Seric 			TimeOuts.to_q_return[TOC_URGENT] = to;
229268481Seric 			TimeOuts.to_q_return[TOC_NONURGENT] = to;
229368481Seric 		}
229468481Seric 		else if (strcasecmp(p, "normal") == 0)
229568481Seric 			TimeOuts.to_q_return[TOC_NORMAL] = to;
229668481Seric 		else if (strcasecmp(p, "urgent") == 0)
229768481Seric 			TimeOuts.to_q_return[TOC_URGENT] = to;
229868481Seric 		else if (strcasecmp(p, "non-urgent") == 0)
229968481Seric 			TimeOuts.to_q_return[TOC_NONURGENT] = to;
230068481Seric 		else
230168481Seric 			syserr("settimeout: invalid queuereturn subtimeout %s", p);
230268481Seric 	}
230368481Seric 	else
230468481Seric 		syserr("settimeout: invalid timeout %s", name);
230558112Seric }
2306