xref: /csrg-svn/usr.sbin/sendmail/src/readcf.c (revision 69724)
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*69724Seric static char sccsid[] = "@(#)readcf.c	8.94 (Berkeley) 05/27/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 	{
11669686Seric 		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 
91769720Seric 		  case 'T':		/* MTA-Name/Address/Diagnostic types */
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,
1368*69724Seric #define O_MAXQUEUERUN	0x8c
1369*69724Seric 	"MaxQueueRunSize",	O_MAXQUEUERUN,	TRUE,
137068481Seric 
137168481Seric 	NULL,			'\0',		FALSE,
137268481Seric };
137368481Seric 
137468481Seric 
137568481Seric 
137658734Seric setoption(opt, val, safe, sticky, e)
137768481Seric 	u_char opt;
13788256Seric 	char *val;
137921755Seric 	bool safe;
13808269Seric 	bool sticky;
138158734Seric 	register ENVELOPE *e;
13828256Seric {
138357207Seric 	register char *p;
138468481Seric 	register struct optioninfo *o;
138568481Seric 	char *subopt;
13868265Seric 	extern bool atobool();
138712633Seric 	extern time_t convtime();
138814879Seric 	extern int QueueLA;
138914879Seric 	extern int RefuseLA;
139064718Seric 	extern bool Warn_Q_option;
13918256Seric 
139268481Seric 	errno = 0;
139368481Seric 	if (opt == ' ')
139468481Seric 	{
139568481Seric 		/* full word options */
139668481Seric 		struct optioninfo *sel;
139768481Seric 
139868481Seric 		p = strchr(val, '=');
139968481Seric 		if (p == NULL)
140068481Seric 			p = &val[strlen(val)];
140168481Seric 		while (*--p == ' ')
140268481Seric 			continue;
140368481Seric 		while (*++p == ' ')
140468481Seric 			*p = '\0';
140568481Seric 		if (p == val)
140668481Seric 		{
140768481Seric 			syserr("readcf: null option name");
140868481Seric 			return;
140968481Seric 		}
141068481Seric 		if (*p == '=')
141168481Seric 			*p++ = '\0';
141268481Seric 		while (*p == ' ')
141368481Seric 			p++;
141468481Seric 		subopt = strchr(val, '.');
141568481Seric 		if (subopt != NULL)
141668481Seric 			*subopt++ = '\0';
141768481Seric 		sel = NULL;
141868481Seric 		for (o = OptionTab; o->o_name != NULL; o++)
141968481Seric 		{
142068481Seric 			if (strncasecmp(o->o_name, val, strlen(val)) != 0)
142168481Seric 				continue;
142268481Seric 			if (strlen(o->o_name) == strlen(val))
142368481Seric 			{
142468481Seric 				/* completely specified -- this must be it */
142568481Seric 				sel = NULL;
142668481Seric 				break;
142768481Seric 			}
142868481Seric 			if (sel != NULL)
142968481Seric 				break;
143068481Seric 			sel = o;
143168481Seric 		}
143268481Seric 		if (sel != NULL && o->o_name == NULL)
143368481Seric 			o = sel;
143468481Seric 		else if (o->o_name == NULL)
143568481Seric 		{
143668481Seric 			syserr("readcf: unknown option name %s", val);
143768481Seric 			return;
143868481Seric 		}
143968481Seric 		else if (sel != NULL)
144068481Seric 		{
144168481Seric 			syserr("readcf: ambiguous option name %s (matches %s and %s)",
144268481Seric 				val, sel->o_name, o->o_name);
144368481Seric 			return;
144468481Seric 		}
144568481Seric 		if (strlen(val) != strlen(o->o_name))
144668481Seric 		{
144768481Seric 			bool oldVerbose = Verbose;
144868481Seric 
144968481Seric 			Verbose = TRUE;
145068481Seric 			message("Option %s used as abbreviation for %s",
145168481Seric 				val, o->o_name);
145268481Seric 			Verbose = oldVerbose;
145368481Seric 		}
145468481Seric 		opt = o->o_code;
145568481Seric 		val = p;
145668481Seric 	}
145768481Seric 	else
145868481Seric 	{
145968481Seric 		for (o = OptionTab; o->o_name != NULL; o++)
146068481Seric 		{
146168481Seric 			if (o->o_code == opt)
146268481Seric 				break;
146368481Seric 		}
146468481Seric 		subopt = NULL;
146568481Seric 	}
146668481Seric 
14678256Seric 	if (tTd(37, 1))
146868481Seric 	{
146968481Seric 		printf(isascii(opt) && isprint(opt) ?
147068481Seric 			    "setoption %s (%c).%s=%s" :
147168481Seric 			    "setoption %s (0x%x).%s=%s",
147268481Seric 			o->o_name == NULL ? "<unknown>" : o->o_name,
147368481Seric 			opt,
147468481Seric 			subopt == NULL ? "" : subopt,
147568481Seric 			val);
147668481Seric 	}
14778256Seric 
14788256Seric 	/*
14798269Seric 	**  See if this option is preset for us.
14808256Seric 	*/
14818256Seric 
148259731Seric 	if (!sticky && bitnset(opt, StickyOpt))
14838269Seric 	{
14849341Seric 		if (tTd(37, 1))
14859341Seric 			printf(" (ignored)\n");
14868269Seric 		return;
14878269Seric 	}
14888269Seric 
148921755Seric 	/*
149021755Seric 	**  Check to see if this option can be specified by this user.
149121755Seric 	*/
149221755Seric 
149363787Seric 	if (!safe && RealUid == 0)
149421755Seric 		safe = TRUE;
149568481Seric 	if (!safe && !o->o_safe)
149621755Seric 	{
149739111Srick 		if (opt != 'M' || (val[0] != 'r' && val[0] != 's'))
149821755Seric 		{
149936582Sbostic 			if (tTd(37, 1))
150036582Sbostic 				printf(" (unsafe)");
150163787Seric 			if (RealUid != geteuid())
150236582Sbostic 			{
150351210Seric 				if (tTd(37, 1))
150451210Seric 					printf("(Resetting uid)");
150563787Seric 				(void) setgid(RealGid);
150663787Seric 				(void) setuid(RealUid);
150736582Sbostic 			}
150821755Seric 		}
150921755Seric 	}
151051210Seric 	if (tTd(37, 1))
151117985Seric 		printf("\n");
15128269Seric 
151368481Seric 	switch (opt & 0xff)
15148256Seric 	{
151559709Seric 	  case '7':		/* force seven-bit input */
151668481Seric 		SevenBitInput = atobool(val);
151752106Seric 		break;
151852106Seric 
151969480Seric #if MIME8TO7
152068481Seric 	  case '8':		/* handling of 8-bit input */
152168481Seric 		switch (*val)
152268481Seric 		{
152368481Seric 		  case 'm':		/* convert 8-bit, convert MIME */
152468481Seric 			MimeMode = MM_CVTMIME|MM_MIME8BIT;
152568481Seric 			break;
152668481Seric 
152768481Seric 		  case 'p':		/* pass 8 bit, convert MIME */
152868856Seric 			MimeMode = MM_CVTMIME|MM_PASS8BIT;
152968481Seric 			break;
153068481Seric 
153168481Seric 		  case 's':		/* strict adherence */
153268481Seric 			MimeMode = MM_CVTMIME;
153368481Seric 			break;
153468481Seric 
153568856Seric #if 0
153668856Seric 		  case 'r':		/* reject 8-bit, don't convert MIME */
153768856Seric 			MimeMode = 0;
153868856Seric 			break;
153968856Seric 
154068856Seric 		  case 'j':		/* "just send 8" */
154168856Seric 			MimeMode = MM_PASS8BIT;
154268856Seric 			break;
154368856Seric 
154468481Seric 		  case 'a':		/* encode 8 bit if available */
154568481Seric 			MimeMode = MM_MIME8BIT|MM_PASS8BIT|MM_CVTMIME;
154668481Seric 			break;
154768481Seric 
154868481Seric 		  case 'c':		/* convert 8 bit to MIME, never 7 bit */
154968481Seric 			MimeMode = MM_MIME8BIT;
155068481Seric 			break;
155168856Seric #endif
155268481Seric 
155368481Seric 		  default:
155468481Seric 			syserr("Unknown 8-bit mode %c", *val);
155568481Seric 			exit(EX_USAGE);
155668481Seric 		}
155768481Seric 		break;
155869480Seric #endif
155968481Seric 
15608256Seric 	  case 'A':		/* set default alias file */
15619381Seric 		if (val[0] == '\0')
156259672Seric 			setalias("aliases");
15639381Seric 		else
156459672Seric 			setalias(val);
15658256Seric 		break;
15668256Seric 
156717474Seric 	  case 'a':		/* look N minutes for "@:@" in alias file */
156817474Seric 		if (val[0] == '\0')
156964796Seric 			SafeAlias = 5 * 60;		/* five minutes */
157017474Seric 		else
157164796Seric 			SafeAlias = convtime(val, 'm');
157217474Seric 		break;
157317474Seric 
157416843Seric 	  case 'B':		/* substitution for blank character */
157516843Seric 		SpaceSub = val[0];
157616843Seric 		if (SpaceSub == '\0')
157716843Seric 			SpaceSub = ' ';
157816843Seric 		break;
157916843Seric 
158059283Seric 	  case 'b':		/* min blocks free on queue fs/max msg size */
158159283Seric 		p = strchr(val, '/');
158259283Seric 		if (p != NULL)
158359283Seric 		{
158459283Seric 			*p++ = '\0';
158559283Seric 			MaxMessageSize = atol(p);
158659283Seric 		}
158758082Seric 		MinBlocksFree = atol(val);
158858082Seric 		break;
158958082Seric 
15909284Seric 	  case 'c':		/* don't connect to "expensive" mailers */
15919381Seric 		NoConnect = atobool(val);
15929284Seric 		break;
15939284Seric 
159451305Seric 	  case 'C':		/* checkpoint every N addresses */
159551305Seric 		CheckpointInterval = atoi(val);
159624944Seric 		break;
159724944Seric 
15989284Seric 	  case 'd':		/* delivery mode */
15999284Seric 		switch (*val)
16008269Seric 		{
16019284Seric 		  case '\0':
160258734Seric 			e->e_sendmode = SM_DELIVER;
16038269Seric 			break;
16048269Seric 
160510755Seric 		  case SM_QUEUE:	/* queue only */
160610755Seric #ifndef QUEUE
160710755Seric 			syserr("need QUEUE to set -odqueue");
160856795Seric #endif /* QUEUE */
160910755Seric 			/* fall through..... */
161010755Seric 
16119284Seric 		  case SM_DELIVER:	/* do everything */
16129284Seric 		  case SM_FORK:		/* fork after verification */
161358734Seric 			e->e_sendmode = *val;
16148269Seric 			break;
16158269Seric 
16168269Seric 		  default:
16179284Seric 			syserr("Unknown delivery mode %c", *val);
16188269Seric 			exit(EX_USAGE);
16198269Seric 		}
16208269Seric 		break;
16218269Seric 
16229146Seric 	  case 'D':		/* rebuild alias database as needed */
16239381Seric 		AutoRebuild = atobool(val);
16249146Seric 		break;
16259146Seric 
162655372Seric 	  case 'E':		/* error message header/header file */
162755379Seric 		if (*val != '\0')
162855379Seric 			ErrMsgFile = newstr(val);
162955372Seric 		break;
163055372Seric 
16318269Seric 	  case 'e':		/* set error processing mode */
16328269Seric 		switch (*val)
16338269Seric 		{
16349381Seric 		  case EM_QUIET:	/* be silent about it */
16359381Seric 		  case EM_MAIL:		/* mail back */
16369381Seric 		  case EM_BERKNET:	/* do berknet error processing */
16379381Seric 		  case EM_WRITE:	/* write back (or mail) */
16389381Seric 		  case EM_PRINT:	/* print errors normally (default) */
163958734Seric 			e->e_errormode = *val;
16408269Seric 			break;
16418269Seric 		}
16428269Seric 		break;
16438269Seric 
16449049Seric 	  case 'F':		/* file mode */
164517975Seric 		FileMode = atooct(val) & 0777;
16469049Seric 		break;
16479049Seric 
16488269Seric 	  case 'f':		/* save Unix-style From lines on front */
16499381Seric 		SaveFrom = atobool(val);
16508269Seric 		break;
16518269Seric 
165253735Seric 	  case 'G':		/* match recipients against GECOS field */
165353735Seric 		MatchGecos = atobool(val);
165453735Seric 		break;
165553735Seric 
16568256Seric 	  case 'g':		/* default gid */
165768481Seric   g_opt:
165864133Seric 		if (isascii(*val) && isdigit(*val))
165964133Seric 			DefGid = atoi(val);
166064133Seric 		else
166164133Seric 		{
166264133Seric 			register struct group *gr;
166364133Seric 
166464133Seric 			DefGid = -1;
166564133Seric 			gr = getgrnam(val);
166664133Seric 			if (gr == NULL)
166768481Seric 				syserr("readcf: option %c: unknown group %s",
166868481Seric 					opt, val);
166964133Seric 			else
167064133Seric 				DefGid = gr->gr_gid;
167164133Seric 		}
16728256Seric 		break;
16738256Seric 
16748256Seric 	  case 'H':		/* help file */
16759381Seric 		if (val[0] == '\0')
16768269Seric 			HelpFile = "sendmail.hf";
16779381Seric 		else
16789381Seric 			HelpFile = newstr(val);
16798256Seric 		break;
16808256Seric 
168151305Seric 	  case 'h':		/* maximum hop count */
168251305Seric 		MaxHopCount = atoi(val);
168351305Seric 		break;
168451305Seric 
168535651Seric 	  case 'I':		/* use internet domain name server */
168666334Seric #if NAMED_BIND
168757207Seric 		for (p = val; *p != 0; )
168857207Seric 		{
168957207Seric 			bool clearmode;
169057207Seric 			char *q;
169157207Seric 			struct resolverflags *rfp;
169257207Seric 
169357207Seric 			while (*p == ' ')
169457207Seric 				p++;
169557207Seric 			if (*p == '\0')
169657207Seric 				break;
169757207Seric 			clearmode = FALSE;
169857207Seric 			if (*p == '-')
169957207Seric 				clearmode = TRUE;
170057207Seric 			else if (*p != '+')
170157207Seric 				p--;
170257207Seric 			p++;
170357207Seric 			q = p;
170458050Seric 			while (*p != '\0' && !(isascii(*p) && isspace(*p)))
170557207Seric 				p++;
170657207Seric 			if (*p != '\0')
170757207Seric 				*p++ = '\0';
170868759Seric 			if (strcasecmp(q, "HasWildcardMX") == 0)
170968759Seric 			{
171068759Seric 				NoMXforCanon = !clearmode;
171168759Seric 				continue;
171268759Seric 			}
171357207Seric 			for (rfp = ResolverFlags; rfp->rf_name != NULL; rfp++)
171457207Seric 			{
171557207Seric 				if (strcasecmp(q, rfp->rf_name) == 0)
171657207Seric 					break;
171757207Seric 			}
171864923Seric 			if (rfp->rf_name == NULL)
171964923Seric 				syserr("readcf: I option value %s unrecognized", q);
172064923Seric 			else if (clearmode)
172157207Seric 				_res.options &= ~rfp->rf_bits;
172257207Seric 			else
172357207Seric 				_res.options |= rfp->rf_bits;
172457207Seric 		}
172557207Seric 		if (tTd(8, 2))
172668759Seric 			printf("_res.options = %x, HasWildcardMX = %d\n",
172768759Seric 				_res.options, !NoMXforCanon);
172857207Seric #else
172957207Seric 		usrerr("name server (I option) specified but BIND not compiled in");
173057207Seric #endif
173135651Seric 		break;
173235651Seric 
17338269Seric 	  case 'i':		/* ignore dot lines in message */
17349381Seric 		IgnrDot = atobool(val);
17358269Seric 		break;
17368269Seric 
173759730Seric 	  case 'j':		/* send errors in MIME (RFC 1341) format */
173859730Seric 		SendMIMEErrors = atobool(val);
173959730Seric 		break;
174059730Seric 
174157136Seric 	  case 'J':		/* .forward search path */
174257136Seric 		ForwardPath = newstr(val);
174357136Seric 		break;
174457136Seric 
174554967Seric 	  case 'k':		/* connection cache size */
174654967Seric 		MaxMciCache = atoi(val);
174756215Seric 		if (MaxMciCache < 0)
174856215Seric 			MaxMciCache = 0;
174954967Seric 		break;
175054967Seric 
175154967Seric 	  case 'K':		/* connection cache timeout */
175258796Seric 		MciCacheTimeout = convtime(val, 'm');
175354967Seric 		break;
175454967Seric 
175561104Seric 	  case 'l':		/* use Errors-To: header */
175661104Seric 		UseErrorsTo = atobool(val);
175761104Seric 		break;
175861104Seric 
17598256Seric 	  case 'L':		/* log level */
176064140Seric 		if (safe || LogLevel < atoi(val))
176164140Seric 			LogLevel = atoi(val);
17628256Seric 		break;
17638256Seric 
17648269Seric 	  case 'M':		/* define macro */
176568267Seric 		p = newstr(&val[1]);
176668267Seric 		if (!safe)
176768267Seric 			cleanstrcpy(p, p, MAXNAME);
176868267Seric 		define(val[0], p, CurEnv);
176916878Seric 		sticky = FALSE;
17708269Seric 		break;
17718269Seric 
17728269Seric 	  case 'm':		/* send to me too */
17739381Seric 		MeToo = atobool(val);
17748269Seric 		break;
17758269Seric 
177625820Seric 	  case 'n':		/* validate RHS in newaliases */
177725820Seric 		CheckAliases = atobool(val);
177825820Seric 		break;
177925820Seric 
178061104Seric 	    /* 'N' available -- was "net name" */
178161104Seric 
178258851Seric 	  case 'O':		/* daemon options */
178358851Seric 		setdaemonoptions(val);
178458851Seric 		break;
178558851Seric 
17868269Seric 	  case 'o':		/* assume old style headers */
17879381Seric 		if (atobool(val))
17889341Seric 			CurEnv->e_flags |= EF_OLDSTYLE;
17899341Seric 		else
17909341Seric 			CurEnv->e_flags &= ~EF_OLDSTYLE;
17918269Seric 		break;
17928269Seric 
179358082Seric 	  case 'p':		/* select privacy level */
179458082Seric 		p = val;
179558082Seric 		for (;;)
179658082Seric 		{
179758082Seric 			register struct prival *pv;
179858082Seric 			extern struct prival PrivacyValues[];
179958082Seric 
180058082Seric 			while (isascii(*p) && (isspace(*p) || ispunct(*p)))
180158082Seric 				p++;
180258082Seric 			if (*p == '\0')
180358082Seric 				break;
180458082Seric 			val = p;
180558082Seric 			while (isascii(*p) && isalnum(*p))
180658082Seric 				p++;
180758082Seric 			if (*p != '\0')
180858082Seric 				*p++ = '\0';
180958082Seric 
181058082Seric 			for (pv = PrivacyValues; pv->pv_name != NULL; pv++)
181158082Seric 			{
181258082Seric 				if (strcasecmp(val, pv->pv_name) == 0)
181358082Seric 					break;
181458082Seric 			}
181558886Seric 			if (pv->pv_name == NULL)
181658886Seric 				syserr("readcf: Op line: %s unrecognized", val);
181758082Seric 			PrivacyFlags |= pv->pv_flag;
181858082Seric 		}
181968479Seric 		sticky = FALSE;
182058082Seric 		break;
182158082Seric 
182224944Seric 	  case 'P':		/* postmaster copy address for returned mail */
182324944Seric 		PostMasterCopy = newstr(val);
182424944Seric 		break;
182524944Seric 
182624944Seric 	  case 'q':		/* slope of queue only function */
182724944Seric 		QueueFactor = atoi(val);
182824944Seric 		break;
182924944Seric 
18308256Seric 	  case 'Q':		/* queue directory */
18319381Seric 		if (val[0] == '\0')
18328269Seric 			QueueDir = "mqueue";
18339381Seric 		else
18349381Seric 			QueueDir = newstr(val);
183558789Seric 		if (RealUid != 0 && !safe)
183664718Seric 			Warn_Q_option = TRUE;
18378256Seric 		break;
18388256Seric 
183958148Seric 	  case 'R':		/* don't prune routes */
184058148Seric 		DontPruneRoutes = atobool(val);
184158148Seric 		break;
184258148Seric 
18438256Seric 	  case 'r':		/* read timeout */
184468481Seric 		if (subopt == NULL)
184568481Seric 			inittimeouts(val);
184668481Seric 		else
184768481Seric 			settimeout(subopt, val);
18488256Seric 		break;
18498256Seric 
18508256Seric 	  case 'S':		/* status file */
18519381Seric 		if (val[0] == '\0')
18528269Seric 			StatFile = "sendmail.st";
18539381Seric 		else
18549381Seric 			StatFile = newstr(val);
18558256Seric 		break;
18568256Seric 
18578265Seric 	  case 's':		/* be super safe, even if expensive */
18589381Seric 		SuperSafe = atobool(val);
18598256Seric 		break;
18608256Seric 
18618256Seric 	  case 'T':		/* queue timeout */
186258737Seric 		p = strchr(val, '/');
186358737Seric 		if (p != NULL)
186458737Seric 		{
186558737Seric 			*p++ = '\0';
186668481Seric 			settimeout("queuewarn", p);
186758737Seric 		}
186868481Seric 		settimeout("queuereturn", val);
186954967Seric 		break;
18708256Seric 
18718265Seric 	  case 't':		/* time zone name */
187252106Seric 		TimeZoneSpec = newstr(val);
18738265Seric 		break;
18748265Seric 
187550556Seric 	  case 'U':		/* location of user database */
187651360Seric 		UdbSpec = newstr(val);
187750556Seric 		break;
187850556Seric 
18798256Seric 	  case 'u':		/* set default uid */
188068481Seric 		for (p = val; *p != '\0'; p++)
188168481Seric 		{
188268481Seric 			if (*p == '.' || *p == '/' || *p == ':')
188368481Seric 			{
188468481Seric 				*p++ = '\0';
188568481Seric 				break;
188668481Seric 			}
188768481Seric 		}
188864133Seric 		if (isascii(*val) && isdigit(*val))
188964133Seric 			DefUid = atoi(val);
189064133Seric 		else
189164133Seric 		{
189264133Seric 			register struct passwd *pw;
189364133Seric 
189464133Seric 			DefUid = -1;
189568693Seric 			pw = sm_getpwnam(val);
189664133Seric 			if (pw == NULL)
189764133Seric 				syserr("readcf: option u: unknown user %s", val);
189864133Seric 			else
189968481Seric 			{
190064133Seric 				DefUid = pw->pw_uid;
190168481Seric 				DefGid = pw->pw_gid;
190268481Seric 			}
190364133Seric 		}
190440973Sbostic 		setdefuser();
19058256Seric 
190668481Seric 		/* handle the group if it is there */
190768481Seric 		if (*p == '\0')
190868481Seric 			break;
190968481Seric 		val = p;
191068481Seric 		goto g_opt;
191168481Seric 
191258851Seric 	  case 'V':		/* fallback MX host */
191358851Seric 		FallBackMX = newstr(val);
191458851Seric 		break;
191558851Seric 
19168269Seric 	  case 'v':		/* run in verbose mode */
19179381Seric 		Verbose = atobool(val);
19188256Seric 		break;
19198256Seric 
192063837Seric 	  case 'w':		/* if we are best MX, try host directly */
192163837Seric 		TryNullMXList = atobool(val);
192263837Seric 		break;
192361104Seric 
192461104Seric 	    /* 'W' available -- was wizard password */
192561104Seric 
192614879Seric 	  case 'x':		/* load avg at which to auto-queue msgs */
192714879Seric 		QueueLA = atoi(val);
192814879Seric 		break;
192914879Seric 
193014879Seric 	  case 'X':		/* load avg at which to auto-reject connections */
193114879Seric 		RefuseLA = atoi(val);
193214879Seric 		break;
193314879Seric 
193424981Seric 	  case 'y':		/* work recipient factor */
193524981Seric 		WkRecipFact = atoi(val);
193624981Seric 		break;
193724981Seric 
193824981Seric 	  case 'Y':		/* fork jobs during queue runs */
193924952Seric 		ForkQueueRuns = atobool(val);
194024952Seric 		break;
194124952Seric 
194224981Seric 	  case 'z':		/* work message class factor */
194324981Seric 		WkClassFact = atoi(val);
194424981Seric 		break;
194524981Seric 
194624981Seric 	  case 'Z':		/* work time factor */
194724981Seric 		WkTimeFact = atoi(val);
194824981Seric 		break;
194924981Seric 
195068481Seric 	  case O_QUEUESORTORD:	/* queue sorting order */
195168481Seric 		switch (*val)
195268481Seric 		{
195368481Seric 		  case 'h':	/* Host first */
195468481Seric 		  case 'H':
195568481Seric 			QueueSortOrder = QS_BYHOST;
195668481Seric 			break;
195768481Seric 
195868481Seric 		  case 'p':	/* Priority order */
195968481Seric 		  case 'P':
196068481Seric 			QueueSortOrder = QS_BYPRIORITY;
196168481Seric 			break;
196268481Seric 
196368481Seric 		  default:
196468481Seric 			syserr("Invalid queue sort order \"%s\"", val);
196568481Seric 		}
196668481Seric 		break;
196768481Seric 
196869401Seric 	  case O_HOSTSFILE:	/* pathname of /etc/hosts file */
196969401Seric 		HostsFile = newstr(val);
197069401Seric 		break;
197169401Seric 
197268481Seric 	  case O_MQA:		/* minimum queue age between deliveries */
197368481Seric 		MinQueueAge = convtime(val, 'm');
197468481Seric 		break;
197568481Seric 
197668481Seric 	  case O_MHSA:		/* maximum age of cached host status */
197768481Seric 		MaxHostStatAge = convtime(val, 'm');
197868481Seric 		break;
197968481Seric 
198068481Seric 	  case O_DEFCHARSET:	/* default character set for mimefying */
198168481Seric 		DefaultCharSet = newstr(denlstring(val, TRUE, TRUE));
198268481Seric 		break;
198368481Seric 
198468481Seric 	  case O_SSFILE:	/* service switch file */
198568481Seric 		ServiceSwitchFile = newstr(val);
198668481Seric 		break;
198768481Seric 
198868481Seric 	  case O_DIALDELAY:	/* delay for dial-on-demand operation */
198968481Seric 		DialDelay = convtime(val, 's');
199068481Seric 		break;
199168481Seric 
199268481Seric 	  case O_NORCPTACTION:	/* what to do if no recipient */
199368481Seric 		if (strcasecmp(val, "none") == 0)
199468481Seric 			NoRecipientAction = NRA_NO_ACTION;
199568481Seric 		else if (strcasecmp(val, "add-to") == 0)
199668481Seric 			NoRecipientAction = NRA_ADD_TO;
199768481Seric 		else if (strcasecmp(val, "add-apparently-to") == 0)
199868481Seric 			NoRecipientAction = NRA_ADD_APPARENTLY_TO;
199968481Seric 		else if (strcasecmp(val, "add-bcc") == 0)
200068481Seric 			NoRecipientAction = NRA_ADD_BCC;
200168481Seric 		else if (strcasecmp(val, "add-to-undisclosed") == 0)
200268481Seric 			NoRecipientAction = NRA_ADD_TO_UNDISCLOSED;
200368481Seric 		else
200468481Seric 			syserr("Invalid NoRecipientAction: %s", val);
200568481Seric 
200668490Seric 	  case O_SAFEFILEENV:	/* chroot() environ for writing to files */
200768490Seric 		SafeFileEnv = newstr(val);
200868490Seric 		break;
200968490Seric 
201068569Seric 	  case O_MAXMSGSIZE:	/* maximum message size */
201168569Seric 		MaxMessageSize = atol(p);
201268569Seric 		break;
201368569Seric 
201468756Seric 	  case O_COLONOKINADDR:	/* old style handling of colon addresses */
201568756Seric 		ColonOkInAddr = atobool(p);
201668756Seric 		break;
201768756Seric 
2018*69724Seric 	  case O_MAXQUEUERUN:	/* max # of jobs in a single queue run */
2019*69724Seric 		MaxQueueRun = atol(p);
2020*69724Seric 		break;
2021*69724Seric 
20228256Seric 	  default:
202368481Seric 		if (tTd(37, 1))
202468481Seric 		{
202568481Seric 			if (isascii(opt) && isprint(opt))
202668481Seric 				printf("Warning: option %c unknown\n", opt);
202768481Seric 			else
202868481Seric 				printf("Warning: option 0x%x unknown\n", opt);
202968481Seric 		}
20308256Seric 		break;
20318256Seric 	}
203216878Seric 	if (sticky)
203316878Seric 		setbitn(opt, StickyOpt);
20349188Seric 	return;
20358256Seric }
203610687Seric /*
203768481Seric **  SETCLASS -- set a string into a class
203810687Seric **
203910687Seric **	Parameters:
204068481Seric **		class -- the class to put the string in.
204168481Seric **		str -- the string to enter
204210687Seric **
204310687Seric **	Returns:
204410687Seric **		none.
204510687Seric **
204610687Seric **	Side Effects:
204710687Seric **		puts the word into the symbol table.
204810687Seric */
204910687Seric 
205068481Seric setclass(class, str)
205110687Seric 	int class;
205268481Seric 	char *str;
205310687Seric {
205410687Seric 	register STAB *s;
205510687Seric 
205657943Seric 	if (tTd(37, 8))
205768481Seric 		printf("setclass(%c, %s)\n", class, str);
205868481Seric 	s = stab(str, ST_CLASS, ST_ENTER);
205910687Seric 	setbitn(class, s->s_class);
206010687Seric }
206153654Seric /*
206253654Seric **  MAKEMAPENTRY -- create a map entry
206353654Seric **
206453654Seric **	Parameters:
206553654Seric **		line -- the config file line
206653654Seric **
206753654Seric **	Returns:
206853654Seric **		TRUE if it successfully entered the map entry.
206953654Seric **		FALSE otherwise (usually syntax error).
207053654Seric **
207153654Seric **	Side Effects:
207253654Seric **		Enters the map into the dictionary.
207353654Seric */
207453654Seric 
207553654Seric void
207653654Seric makemapentry(line)
207753654Seric 	char *line;
207853654Seric {
207953654Seric 	register char *p;
208053654Seric 	char *mapname;
208153654Seric 	char *classname;
208264078Seric 	register STAB *s;
208353654Seric 	STAB *class;
208453654Seric 
208558050Seric 	for (p = line; isascii(*p) && isspace(*p); p++)
208653654Seric 		continue;
208758050Seric 	if (!(isascii(*p) && isalnum(*p)))
208853654Seric 	{
208953654Seric 		syserr("readcf: config K line: no map name");
209053654Seric 		return;
209153654Seric 	}
209253654Seric 
209353654Seric 	mapname = p;
209468481Seric 	while ((isascii(*++p) && isalnum(*p)) || *p == '.')
209553654Seric 		continue;
209653654Seric 	if (*p != '\0')
209753654Seric 		*p++ = '\0';
209858050Seric 	while (isascii(*p) && isspace(*p))
209953654Seric 		p++;
210058050Seric 	if (!(isascii(*p) && isalnum(*p)))
210153654Seric 	{
210253654Seric 		syserr("readcf: config K line, map %s: no map class", mapname);
210353654Seric 		return;
210453654Seric 	}
210553654Seric 	classname = p;
210658050Seric 	while (isascii(*++p) && isalnum(*p))
210753654Seric 		continue;
210853654Seric 	if (*p != '\0')
210953654Seric 		*p++ = '\0';
211058050Seric 	while (isascii(*p) && isspace(*p))
211153654Seric 		p++;
211253654Seric 
211353654Seric 	/* look up the class */
211453654Seric 	class = stab(classname, ST_MAPCLASS, ST_FIND);
211553654Seric 	if (class == NULL)
211653654Seric 	{
211753654Seric 		syserr("readcf: map %s: class %s not available", mapname, classname);
211853654Seric 		return;
211953654Seric 	}
212053654Seric 
212153654Seric 	/* enter the map */
212264078Seric 	s = stab(mapname, ST_MAP, ST_ENTER);
212364078Seric 	s->s_map.map_class = &class->s_mapclass;
212464078Seric 	s->s_map.map_mname = newstr(mapname);
212553654Seric 
212664078Seric 	if (class->s_mapclass.map_parse(&s->s_map, p))
212764078Seric 		s->s_map.map_mflags |= MF_VALID;
212864078Seric 
212964078Seric 	if (tTd(37, 5))
213064078Seric 	{
213164078Seric 		printf("map %s, class %s, flags %x, file %s,\n",
213264078Seric 			s->s_map.map_mname, s->s_map.map_class->map_cname,
213364078Seric 			s->s_map.map_mflags,
213464078Seric 			s->s_map.map_file == NULL ? "(null)" : s->s_map.map_file);
213564078Seric 		printf("\tapp %s, domain %s, rebuild %s\n",
213664078Seric 			s->s_map.map_app == NULL ? "(null)" : s->s_map.map_app,
213764078Seric 			s->s_map.map_domain == NULL ? "(null)" : s->s_map.map_domain,
213864078Seric 			s->s_map.map_rebuild == NULL ? "(null)" : s->s_map.map_rebuild);
213964078Seric 	}
214053654Seric }
214158112Seric /*
214268481Seric **  INITTIMEOUTS -- parse and set timeout values
214358112Seric **
214458112Seric **	Parameters:
214558112Seric **		val -- a pointer to the values.  If NULL, do initial
214658112Seric **			settings.
214758112Seric **
214858112Seric **	Returns:
214958112Seric **		none.
215058112Seric **
215158112Seric **	Side Effects:
215258112Seric **		Initializes the TimeOuts structure
215358112Seric */
215458112Seric 
215564255Seric #define SECONDS
215658112Seric #define MINUTES	* 60
215758112Seric #define HOUR	* 3600
215858112Seric 
215968481Seric inittimeouts(val)
216058112Seric 	register char *val;
216158112Seric {
216258112Seric 	register char *p;
216358671Seric 	extern time_t convtime();
216458112Seric 
216558112Seric 	if (val == NULL)
216658112Seric 	{
216758112Seric 		TimeOuts.to_initial = (time_t) 5 MINUTES;
216858112Seric 		TimeOuts.to_helo = (time_t) 5 MINUTES;
216958112Seric 		TimeOuts.to_mail = (time_t) 10 MINUTES;
217058112Seric 		TimeOuts.to_rcpt = (time_t) 1 HOUR;
217158112Seric 		TimeOuts.to_datainit = (time_t) 5 MINUTES;
217258112Seric 		TimeOuts.to_datablock = (time_t) 1 HOUR;
217358112Seric 		TimeOuts.to_datafinal = (time_t) 1 HOUR;
217458112Seric 		TimeOuts.to_rset = (time_t) 5 MINUTES;
217558112Seric 		TimeOuts.to_quit = (time_t) 2 MINUTES;
217658112Seric 		TimeOuts.to_nextcommand = (time_t) 1 HOUR;
217758112Seric 		TimeOuts.to_miscshort = (time_t) 2 MINUTES;
217868481Seric #if IDENTPROTO
217964255Seric 		TimeOuts.to_ident = (time_t) 30 SECONDS;
218068481Seric #else
218168481Seric 		TimeOuts.to_ident = (time_t) 0 SECONDS;
218268481Seric #endif
218368481Seric 		TimeOuts.to_fileopen = (time_t) 60 SECONDS;
218458112Seric 		return;
218558112Seric 	}
218658112Seric 
218758112Seric 	for (;; val = p)
218858112Seric 	{
218958112Seric 		while (isascii(*val) && isspace(*val))
219058112Seric 			val++;
219158112Seric 		if (*val == '\0')
219258112Seric 			break;
219358112Seric 		for (p = val; *p != '\0' && *p != ','; p++)
219458112Seric 			continue;
219558112Seric 		if (*p != '\0')
219658112Seric 			*p++ = '\0';
219758112Seric 
219858112Seric 		if (isascii(*val) && isdigit(*val))
219958112Seric 		{
220058112Seric 			/* old syntax -- set everything */
220158796Seric 			TimeOuts.to_mail = convtime(val, 'm');
220258112Seric 			TimeOuts.to_rcpt = TimeOuts.to_mail;
220358112Seric 			TimeOuts.to_datainit = TimeOuts.to_mail;
220458112Seric 			TimeOuts.to_datablock = TimeOuts.to_mail;
220558112Seric 			TimeOuts.to_datafinal = TimeOuts.to_mail;
220658112Seric 			TimeOuts.to_nextcommand = TimeOuts.to_mail;
220758112Seric 			continue;
220858112Seric 		}
220958112Seric 		else
221058112Seric 		{
221168481Seric 			register char *q = strchr(val, ':');
221258112Seric 
221368481Seric 			if (q == NULL && (q = strchr(val, '=')) == NULL)
221458112Seric 			{
221558112Seric 				/* syntax error */
221658112Seric 				continue;
221758112Seric 			}
221858112Seric 			*q++ = '\0';
221968481Seric 			settimeout(val, q);
222068481Seric 		}
222168481Seric 	}
222268481Seric }
222368481Seric /*
222468481Seric **  SETTIMEOUT -- set an individual timeout
222568481Seric **
222668481Seric **	Parameters:
222768481Seric **		name -- the name of the timeout.
222868481Seric **		val -- the value of the timeout.
222968481Seric **
223068481Seric **	Returns:
223168481Seric **		none.
223268481Seric */
223358112Seric 
223468481Seric settimeout(name, val)
223568481Seric 	char *name;
223668481Seric 	char *val;
223768481Seric {
223868481Seric 	register char *p;
223968481Seric 	time_t to;
224068481Seric 	extern time_t convtime();
224168481Seric 
224268481Seric 	to = convtime(val, 'm');
224368481Seric 	p = strchr(name, '.');
224468481Seric 	if (p != NULL)
224568481Seric 		*p++ = '\0';
224668481Seric 
224768481Seric 	if (strcasecmp(name, "initial") == 0)
224868481Seric 		TimeOuts.to_initial = to;
224968481Seric 	else if (strcasecmp(name, "mail") == 0)
225068481Seric 		TimeOuts.to_mail = to;
225168481Seric 	else if (strcasecmp(name, "rcpt") == 0)
225268481Seric 		TimeOuts.to_rcpt = to;
225368481Seric 	else if (strcasecmp(name, "datainit") == 0)
225468481Seric 		TimeOuts.to_datainit = to;
225568481Seric 	else if (strcasecmp(name, "datablock") == 0)
225668481Seric 		TimeOuts.to_datablock = to;
225768481Seric 	else if (strcasecmp(name, "datafinal") == 0)
225868481Seric 		TimeOuts.to_datafinal = to;
225968481Seric 	else if (strcasecmp(name, "command") == 0)
226068481Seric 		TimeOuts.to_nextcommand = to;
226168481Seric 	else if (strcasecmp(name, "rset") == 0)
226268481Seric 		TimeOuts.to_rset = to;
226368481Seric 	else if (strcasecmp(name, "helo") == 0)
226468481Seric 		TimeOuts.to_helo = to;
226568481Seric 	else if (strcasecmp(name, "quit") == 0)
226668481Seric 		TimeOuts.to_quit = to;
226768481Seric 	else if (strcasecmp(name, "misc") == 0)
226868481Seric 		TimeOuts.to_miscshort = to;
226968481Seric 	else if (strcasecmp(name, "ident") == 0)
227068481Seric 		TimeOuts.to_ident = to;
227168481Seric 	else if (strcasecmp(name, "fileopen") == 0)
227268481Seric 		TimeOuts.to_fileopen = to;
227368481Seric 	else if (strcasecmp(name, "queuewarn") == 0)
227468481Seric 	{
227568481Seric 		to = convtime(val, 'h');
227668481Seric 		if (p == NULL || strcmp(p, "*") == 0)
227768481Seric 		{
227868481Seric 			TimeOuts.to_q_warning[TOC_NORMAL] = to;
227968481Seric 			TimeOuts.to_q_warning[TOC_URGENT] = to;
228068481Seric 			TimeOuts.to_q_warning[TOC_NONURGENT] = to;
228158112Seric 		}
228268481Seric 		else if (strcasecmp(p, "normal") == 0)
228368481Seric 			TimeOuts.to_q_warning[TOC_NORMAL] = to;
228468481Seric 		else if (strcasecmp(p, "urgent") == 0)
228568481Seric 			TimeOuts.to_q_warning[TOC_URGENT] = to;
228668481Seric 		else if (strcasecmp(p, "non-urgent") == 0)
228768481Seric 			TimeOuts.to_q_warning[TOC_NONURGENT] = to;
228868481Seric 		else
228968481Seric 			syserr("settimeout: invalid queuewarn subtimeout %s", p);
229058112Seric 	}
229168481Seric 	else if (strcasecmp(name, "queuereturn") == 0)
229268481Seric 	{
229368481Seric 		to = convtime(val, 'd');
229468481Seric 		if (p == NULL || strcmp(p, "*") == 0)
229568481Seric 		{
229668481Seric 			TimeOuts.to_q_return[TOC_NORMAL] = to;
229768481Seric 			TimeOuts.to_q_return[TOC_URGENT] = to;
229868481Seric 			TimeOuts.to_q_return[TOC_NONURGENT] = to;
229968481Seric 		}
230068481Seric 		else if (strcasecmp(p, "normal") == 0)
230168481Seric 			TimeOuts.to_q_return[TOC_NORMAL] = to;
230268481Seric 		else if (strcasecmp(p, "urgent") == 0)
230368481Seric 			TimeOuts.to_q_return[TOC_URGENT] = to;
230468481Seric 		else if (strcasecmp(p, "non-urgent") == 0)
230568481Seric 			TimeOuts.to_q_return[TOC_NONURGENT] = to;
230668481Seric 		else
230768481Seric 			syserr("settimeout: invalid queuereturn subtimeout %s", p);
230868481Seric 	}
230968481Seric 	else
231068481Seric 		syserr("settimeout: invalid timeout %s", name);
231158112Seric }
2312