xref: /csrg-svn/usr.sbin/sendmail/src/readcf.c (revision 69789)
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*69789Seric static char sccsid[] = "@(#)readcf.c	8.99 (Berkeley) 05/31/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 
6669748Seric void
6755012Seric readcf(cfname, safe, e)
683308Seric 	char *cfname;
6954973Seric 	bool safe;
7055012Seric 	register ENVELOPE *e;
713308Seric {
723308Seric 	FILE *cf;
738547Seric 	int ruleset = 0;
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 };
8969748Seric 	extern char *munchstring __P((char *, char **));
9069748Seric 	extern void fileclass __P((int, char *, char *, bool, bool));
9169748Seric 	extern void toomany __P((int, int));
923308Seric 
9352647Seric 	FileName = cfname;
9452647Seric 	LineNumber = 0;
9552647Seric 
963308Seric 	cf = fopen(cfname, "r");
973308Seric 	if (cf == NULL)
983308Seric 	{
9952647Seric 		syserr("cannot open");
1003308Seric 		exit(EX_OSFILE);
1013308Seric 	}
1023308Seric 
10352647Seric 	if (fstat(fileno(cf), &statb) < 0)
10452647Seric 	{
10552647Seric 		syserr("cannot fstat");
10652647Seric 		exit(EX_OSFILE);
10752647Seric 	}
10852647Seric 
10952647Seric 	if (!S_ISREG(statb.st_mode))
11052647Seric 	{
11152647Seric 		syserr("not a plain file");
11252647Seric 		exit(EX_OSFILE);
11352647Seric 	}
11452647Seric 
11552647Seric 	if (OpMode != MD_TEST && bitset(S_IWGRP|S_IWOTH, statb.st_mode))
11652647Seric 	{
11769686Seric 		if (OpMode == MD_DAEMON || OpMode == MD_INITALIAS)
11853037Seric 			fprintf(stderr, "%s: WARNING: dangerous write permissions\n",
11953037Seric 				FileName);
12053037Seric #ifdef LOG
12153037Seric 		if (LogLevel > 0)
12253037Seric 			syslog(LOG_CRIT, "%s: WARNING: dangerous write permissions",
12353037Seric 				FileName);
12453037Seric #endif
12552647Seric 	}
12652647Seric 
12759254Seric #ifdef XLA
12859254Seric 	xla_zero();
12959254Seric #endif
13059254Seric 
13157135Seric 	while ((bp = fgetfolded(buf, sizeof buf, cf)) != NULL)
1323308Seric 	{
13357135Seric 		if (bp[0] == '#')
13457135Seric 		{
13557135Seric 			if (bp != buf)
13657135Seric 				free(bp);
13752637Seric 			continue;
13857135Seric 		}
13952637Seric 
14068481Seric 		/* do macro expansion mappings */
14157135Seric 		for (p = bp; *p != '\0'; p++)
14216157Seric 		{
14357135Seric 			if (*p == '#' && p > bp && ConfigLevel >= 3)
14452647Seric 			{
14552647Seric 				/* this is an on-line comment */
14652647Seric 				register char *e;
14752647Seric 
14858050Seric 				switch (*--p & 0377)
14952647Seric 				{
15058050Seric 				  case MACROEXPAND:
15152647Seric 					/* it's from $# -- let it go through */
15252647Seric 					p++;
15352647Seric 					break;
15452647Seric 
15552647Seric 				  case '\\':
15652647Seric 					/* it's backslash escaped */
15752647Seric 					(void) strcpy(p, p + 1);
15852647Seric 					break;
15952647Seric 
16052647Seric 				  default:
16152647Seric 					/* delete preceeding white space */
16258050Seric 					while (isascii(*p) && isspace(*p) && p > bp)
16352647Seric 						p--;
16456795Seric 					if ((e = strchr(++p, '\n')) != NULL)
16552647Seric 						(void) strcpy(p, e);
16652647Seric 					else
16752647Seric 						p[0] = p[1] = '\0';
16852647Seric 					break;
16952647Seric 				}
17052647Seric 				continue;
17152647Seric 			}
17252647Seric 
17368481Seric 			if (*p != '$' || p[1] == '\0')
17416157Seric 				continue;
17516157Seric 
17616157Seric 			if (p[1] == '$')
17716157Seric 			{
17816157Seric 				/* actual dollar sign.... */
17923111Seric 				(void) strcpy(p, p + 1);
18016157Seric 				continue;
18116157Seric 			}
18216157Seric 
18316157Seric 			/* convert to macro expansion character */
18468481Seric 			*p++ = MACROEXPAND;
18568481Seric 
18668481Seric 			/* convert macro name to code */
18768481Seric 			*p = macid(p, &ep);
18868481Seric 			if (ep != p)
18968481Seric 				strcpy(p + 1, ep);
19016157Seric 		}
19116157Seric 
19216157Seric 		/* interpret this line */
19364718Seric 		errno = 0;
19457135Seric 		switch (bp[0])
1953308Seric 		{
1963308Seric 		  case '\0':
1973308Seric 		  case '#':		/* comment */
1983308Seric 			break;
1993308Seric 
2003308Seric 		  case 'R':		/* rewriting rule */
20157135Seric 			for (p = &bp[1]; *p != '\0' && *p != '\t'; p++)
2023308Seric 				continue;
2033308Seric 
2043308Seric 			if (*p == '\0')
2055909Seric 			{
20665821Seric 				syserr("invalid rewrite line \"%s\" (tab expected)", bp);
2075909Seric 				break;
2085909Seric 			}
2095909Seric 
2105909Seric 			/* allocate space for the rule header */
2115909Seric 			if (rwp == NULL)
2125909Seric 			{
2135909Seric 				RewriteRules[ruleset] = rwp =
2145909Seric 					(struct rewrite *) xalloc(sizeof *rwp);
2155909Seric 			}
2163308Seric 			else
2173308Seric 			{
2185909Seric 				rwp->r_next = (struct rewrite *) xalloc(sizeof *rwp);
2195909Seric 				rwp = rwp->r_next;
2205909Seric 			}
2215909Seric 			rwp->r_next = NULL;
2223308Seric 
2235909Seric 			/* expand and save the LHS */
2245909Seric 			*p = '\0';
22568529Seric 			expand(&bp[1], exbuf, sizeof exbuf, e);
22665066Seric 			rwp->r_lhs = prescan(exbuf, '\t', pvpbuf,
22768711Seric 					     sizeof pvpbuf, NULL, NULL);
22857589Seric 			nfuzzy = 0;
2295909Seric 			if (rwp->r_lhs != NULL)
23057589Seric 			{
23157589Seric 				register char **ap;
23257589Seric 
2335909Seric 				rwp->r_lhs = copyplist(rwp->r_lhs, TRUE);
23457589Seric 
23557589Seric 				/* count the number of fuzzy matches in LHS */
23657589Seric 				for (ap = rwp->r_lhs; *ap != NULL; ap++)
23757589Seric 				{
23858148Seric 					char *botch;
23958148Seric 
24058148Seric 					botch = NULL;
24158050Seric 					switch (**ap & 0377)
24257589Seric 					{
24357589Seric 					  case MATCHZANY:
24457589Seric 					  case MATCHANY:
24557589Seric 					  case MATCHONE:
24657589Seric 					  case MATCHCLASS:
24757589Seric 					  case MATCHNCLASS:
24857589Seric 						nfuzzy++;
24958148Seric 						break;
25058148Seric 
25158148Seric 					  case MATCHREPL:
25258148Seric 						botch = "$0-$9";
25358148Seric 						break;
25458148Seric 
25558148Seric 					  case CANONNET:
25658148Seric 						botch = "$#";
25758148Seric 						break;
25858148Seric 
25958148Seric 					  case CANONUSER:
26058148Seric 						botch = "$:";
26158148Seric 						break;
26258148Seric 
26358148Seric 					  case CALLSUBR:
26458148Seric 						botch = "$>";
26558148Seric 						break;
26658148Seric 
26758148Seric 					  case CONDIF:
26858148Seric 						botch = "$?";
26958148Seric 						break;
27058148Seric 
27158148Seric 					  case CONDELSE:
27258148Seric 						botch = "$|";
27358148Seric 						break;
27458148Seric 
27558148Seric 					  case CONDFI:
27658148Seric 						botch = "$.";
27758148Seric 						break;
27858148Seric 
27958148Seric 					  case HOSTBEGIN:
28058148Seric 						botch = "$[";
28158148Seric 						break;
28258148Seric 
28358148Seric 					  case HOSTEND:
28458148Seric 						botch = "$]";
28558148Seric 						break;
28658148Seric 
28758148Seric 					  case LOOKUPBEGIN:
28858148Seric 						botch = "$(";
28958148Seric 						break;
29058148Seric 
29158148Seric 					  case LOOKUPEND:
29258148Seric 						botch = "$)";
29358148Seric 						break;
29457589Seric 					}
29558148Seric 					if (botch != NULL)
29658148Seric 						syserr("Inappropriate use of %s on LHS",
29758148Seric 							botch);
29857589Seric 				}
29957589Seric 			}
30056678Seric 			else
30168481Seric 			{
30256678Seric 				syserr("R line: null LHS");
30368481Seric 				rwp->r_lhs = null_list;
30468481Seric 			}
3055909Seric 
3065909Seric 			/* expand and save the RHS */
3075909Seric 			while (*++p == '\t')
3085909Seric 				continue;
3097231Seric 			q = p;
3107231Seric 			while (*p != '\0' && *p != '\t')
3117231Seric 				p++;
3127231Seric 			*p = '\0';
31368529Seric 			expand(q, exbuf, sizeof exbuf, e);
31465066Seric 			rwp->r_rhs = prescan(exbuf, '\t', pvpbuf,
31568711Seric 					     sizeof pvpbuf, NULL, NULL);
3165909Seric 			if (rwp->r_rhs != NULL)
31757589Seric 			{
31857589Seric 				register char **ap;
31957589Seric 
3205909Seric 				rwp->r_rhs = copyplist(rwp->r_rhs, TRUE);
32157589Seric 
32257589Seric 				/* check no out-of-bounds replacements */
32357589Seric 				nfuzzy += '0';
32457589Seric 				for (ap = rwp->r_rhs; *ap != NULL; ap++)
32557589Seric 				{
32658148Seric 					char *botch;
32758148Seric 
32858148Seric 					botch = NULL;
32958148Seric 					switch (**ap & 0377)
33057589Seric 					{
33158148Seric 					  case MATCHREPL:
33258148Seric 						if ((*ap)[1] <= '0' || (*ap)[1] > nfuzzy)
33358148Seric 						{
33458148Seric 							syserr("replacement $%c out of bounds",
33558148Seric 								(*ap)[1]);
33658148Seric 						}
33758148Seric 						break;
33858148Seric 
33958148Seric 					  case MATCHZANY:
34058148Seric 						botch = "$*";
34158148Seric 						break;
34258148Seric 
34358148Seric 					  case MATCHANY:
34458148Seric 						botch = "$+";
34558148Seric 						break;
34658148Seric 
34758148Seric 					  case MATCHONE:
34858148Seric 						botch = "$-";
34958148Seric 						break;
35058148Seric 
35158148Seric 					  case MATCHCLASS:
35258148Seric 						botch = "$=";
35358148Seric 						break;
35458148Seric 
35558148Seric 					  case MATCHNCLASS:
35658148Seric 						botch = "$~";
35758148Seric 						break;
35857589Seric 					}
35958148Seric 					if (botch != NULL)
36058148Seric 						syserr("Inappropriate use of %s on RHS",
36158148Seric 							botch);
36257589Seric 				}
36357589Seric 			}
36456678Seric 			else
36568481Seric 			{
36656678Seric 				syserr("R line: null RHS");
36768481Seric 				rwp->r_rhs = null_list;
36868481Seric 			}
3693308Seric 			break;
3703308Seric 
3714072Seric 		  case 'S':		/* select rewriting set */
37269783Seric 			ruleset = strtorwset(&bp[1], NULL, ST_ENTER);
373*69789Seric 			rwp = RewriteRules[ruleset];
374*69789Seric 			if (rwp != NULL)
375*69789Seric 			{
376*69789Seric 				while (rwp->r_next != NULL)
377*69789Seric 					rwp = rwp->r_next;
378*69789Seric 				fprintf(stderr, "WARNING: Ruleset %s redefined\n",
379*69789Seric 					&bp[1]);
380*69789Seric 			}
3814072Seric 			break;
3824072Seric 
3833308Seric 		  case 'D':		/* macro definition */
38468481Seric 			mid = macid(&bp[1], &ep);
38568481Seric 			p = munchstring(ep, NULL);
38668481Seric 			define(mid, newstr(p), e);
3873308Seric 			break;
3883308Seric 
3893387Seric 		  case 'H':		/* required header line */
39068717Seric 			(void) chompheader(&bp[1], TRUE, NULL, e);
3913387Seric 			break;
3923387Seric 
3934061Seric 		  case 'C':		/* word class */
39468481Seric 		  case 'T':		/* trusted user (set class `t') */
39568481Seric 			if (bp[0] == 'C')
3964061Seric 			{
39768481Seric 				mid = macid(&bp[1], &ep);
39868529Seric 				expand(ep, exbuf, sizeof exbuf, e);
39968481Seric 				p = exbuf;
40068481Seric 			}
40168481Seric 			else
40268481Seric 			{
40368481Seric 				mid = 't';
40468481Seric 				p = &bp[1];
40568481Seric 			}
40668481Seric 			while (*p != '\0')
40768481Seric 			{
4084061Seric 				register char *wd;
4094061Seric 				char delim;
4104061Seric 
41158050Seric 				while (*p != '\0' && isascii(*p) && isspace(*p))
4124061Seric 					p++;
4134061Seric 				wd = p;
41458050Seric 				while (*p != '\0' && !(isascii(*p) && isspace(*p)))
4154061Seric 					p++;
4164061Seric 				delim = *p;
4174061Seric 				*p = '\0';
4184061Seric 				if (wd[0] != '\0')
41968481Seric 					setclass(mid, wd);
4204061Seric 				*p = delim;
4214061Seric 			}
4224061Seric 			break;
4234061Seric 
42459272Seric 		  case 'F':		/* word class from file */
42568481Seric 			mid = macid(&bp[1], &ep);
42668481Seric 			for (p = ep; isascii(*p) && isspace(*p); )
42764133Seric 				p++;
42864133Seric 			if (p[0] == '-' && p[1] == 'o')
42964133Seric 			{
43064133Seric 				optional = TRUE;
43164133Seric 				while (*p != '\0' && !(isascii(*p) && isspace(*p)))
43264133Seric 					p++;
43364133Seric 				while (isascii(*p) && isspace(*p))
43468481Seric 					p++;
43564133Seric 			}
43664133Seric 			else
43764133Seric 				optional = FALSE;
43864133Seric 			file = p;
43964133Seric 			while (*p != '\0' && !(isascii(*p) && isspace(*p)))
44064133Seric 				p++;
44159272Seric 			if (*p == '\0')
44259272Seric 				p = "%s";
44359272Seric 			else
44459272Seric 			{
44559272Seric 				*p = '\0';
44659272Seric 				while (isascii(*++p) && isspace(*p))
44759272Seric 					continue;
44859272Seric 			}
44964133Seric 			fileclass(bp[1], file, p, safe, optional);
45059272Seric 			break;
45159272Seric 
45259156Seric #ifdef XLA
45359156Seric 		  case 'L':		/* extended load average description */
45459156Seric 			xla_init(&bp[1]);
45559156Seric 			break;
45659156Seric #endif
45759156Seric 
4584096Seric 		  case 'M':		/* define mailer */
45957135Seric 			makemailer(&bp[1]);
4604096Seric 			break;
4614096Seric 
4628252Seric 		  case 'O':		/* set option */
46358734Seric 			setoption(bp[1], &bp[2], safe, FALSE, e);
4648252Seric 			break;
4658252Seric 
4668252Seric 		  case 'P':		/* set precedence */
4678252Seric 			if (NumPriorities >= MAXPRIORITIES)
4688252Seric 			{
4698547Seric 				toomany('P', MAXPRIORITIES);
4708252Seric 				break;
4718252Seric 			}
47257135Seric 			for (p = &bp[1]; *p != '\0' && *p != '=' && *p != '\t'; p++)
4738252Seric 				continue;
4748252Seric 			if (*p == '\0')
4758252Seric 				goto badline;
4768252Seric 			*p = '\0';
47757135Seric 			Priorities[NumPriorities].pri_name = newstr(&bp[1]);
4788252Seric 			Priorities[NumPriorities].pri_val = atoi(++p);
4798252Seric 			NumPriorities++;
4808252Seric 			break;
4818252Seric 
48252645Seric 		  case 'V':		/* configuration syntax version */
48364440Seric 			for (p = &bp[1]; isascii(*p) && isspace(*p); p++)
48464440Seric 				continue;
48564440Seric 			if (!isascii(*p) || !isdigit(*p))
48664440Seric 			{
48764440Seric 				syserr("invalid argument to V line: \"%.20s\"",
48864440Seric 					&bp[1]);
48964440Seric 				break;
49064440Seric 			}
49164718Seric 			ConfigLevel = strtol(p, &ep, 10);
49268805Seric 
49368805Seric 			/*
49468805Seric 			**  Do heuristic tweaking for back compatibility.
49568805Seric 			*/
49668805Seric 
49764279Seric 			if (ConfigLevel >= 5)
49864279Seric 			{
49964279Seric 				/* level 5 configs have short name in $w */
50064279Seric 				p = macvalue('w', e);
50164279Seric 				if (p != NULL && (p = strchr(p, '.')) != NULL)
50264279Seric 					*p = '\0';
50364279Seric 			}
50468805Seric 			if (ConfigLevel >= 6)
50568805Seric 			{
50668805Seric 				ColonOkInAddr = FALSE;
50768805Seric 			}
50868805Seric 
50968805Seric 			/*
51068805Seric 			**  Look for vendor code.
51168805Seric 			*/
51268805Seric 
51364718Seric 			if (*ep++ == '/')
51464718Seric 			{
51564718Seric 				/* extract vendor code */
51664718Seric 				for (p = ep; isascii(*p) && isalpha(*p); )
51764718Seric 					p++;
51864718Seric 				*p = '\0';
51964718Seric 
52064718Seric 				if (!setvendor(ep))
52164718Seric 					syserr("invalid V line vendor code: \"%s\"",
52264718Seric 						ep);
52364718Seric 			}
52452645Seric 			break;
52552645Seric 
52653654Seric 		  case 'K':
52769774Seric 			(void) makemapentry(&bp[1]);
52853654Seric 			break;
52953654Seric 
53069476Seric 		  case 'E':
53169476Seric 			p = strchr(bp, '=');
53269476Seric 			if (p != NULL)
53369476Seric 				*p++ = '\0';
53469476Seric 			setuserenv(&bp[1], p);
53569476Seric 			break;
53669476Seric 
5373308Seric 		  default:
5384061Seric 		  badline:
53957135Seric 			syserr("unknown control line \"%s\"", bp);
5403308Seric 		}
54157135Seric 		if (bp != buf)
54257135Seric 			free(bp);
5433308Seric 	}
54452637Seric 	if (ferror(cf))
54552637Seric 	{
54652647Seric 		syserr("I/O read error", cfname);
54752637Seric 		exit(EX_OSFILE);
54852637Seric 	}
54952637Seric 	fclose(cf);
5509381Seric 	FileName = NULL;
55156836Seric 
55268481Seric 	/* initialize host maps from local service tables */
55368481Seric 	inithostmaps();
55468481Seric 
55568481Seric 	/* determine if we need to do special name-server frotz */
55667905Seric 	{
55768481Seric 		int nmaps;
55868481Seric 		char *maptype[MAXMAPSTACK];
55968481Seric 		short mapreturn[MAXMAPACTIONS];
56068481Seric 
56168481Seric 		nmaps = switch_map_find("hosts", maptype, mapreturn);
56268481Seric 		UseNameServer = FALSE;
56368481Seric 		if (nmaps > 0 && nmaps <= MAXMAPSTACK)
56468481Seric 		{
56568481Seric 			register int mapno;
56668481Seric 
56768481Seric 			for (mapno = 0; mapno < nmaps && !UseNameServer; mapno++)
56868481Seric 			{
56968481Seric 				if (strcmp(maptype[mapno], "dns") == 0)
57068481Seric 					UseNameServer = TRUE;
57168481Seric 			}
57268481Seric 		}
57368481Seric 
57468481Seric #ifdef HESIOD
57568481Seric 		nmaps = switch_map_find("passwd", maptype, mapreturn);
57668481Seric 		UseHesiod = FALSE;
57768481Seric 		if (nmaps > 0 && nmaps <= MAXMAPSTACK)
57868481Seric 		{
57968481Seric 			register int mapno;
58068481Seric 
58168481Seric 			for (mapno = 0; mapno < nmaps && !UseHesiod; mapno++)
58268481Seric 			{
58368481Seric 				if (strcmp(maptype[mapno], "hesiod") == 0)
58468481Seric 					UseHesiod = TRUE;
58568481Seric 			}
58668481Seric 		}
58768204Seric #endif
58867905Seric 	}
5894096Seric }
5904096Seric /*
5918547Seric **  TOOMANY -- signal too many of some option
5928547Seric **
5938547Seric **	Parameters:
5948547Seric **		id -- the id of the error line
5958547Seric **		maxcnt -- the maximum possible values
5968547Seric **
5978547Seric **	Returns:
5988547Seric **		none.
5998547Seric **
6008547Seric **	Side Effects:
6018547Seric **		gives a syserr.
6028547Seric */
6038547Seric 
60469748Seric void
6058547Seric toomany(id, maxcnt)
60669748Seric 	int id;
6078547Seric 	int maxcnt;
6088547Seric {
6099381Seric 	syserr("too many %c lines, %d max", id, maxcnt);
6108547Seric }
6118547Seric /*
6124432Seric **  FILECLASS -- read members of a class from a file
6134432Seric **
6144432Seric **	Parameters:
6154432Seric **		class -- class to define.
6164432Seric **		filename -- name of file to read.
6174432Seric **		fmt -- scanf string to use for match.
61864133Seric **		safe -- if set, this is a safe read.
61964133Seric **		optional -- if set, it is not an error for the file to
62064133Seric **			not exist.
6214432Seric **
6224432Seric **	Returns:
6234432Seric **		none
6244432Seric **
6254432Seric **	Side Effects:
6264432Seric **
6274432Seric **		puts all lines in filename that match a scanf into
6284432Seric **			the named class.
6294432Seric */
6304432Seric 
63169748Seric void
63264133Seric fileclass(class, filename, fmt, safe, optional)
6334432Seric 	int class;
6344432Seric 	char *filename;
6354432Seric 	char *fmt;
63654973Seric 	bool safe;
63764133Seric 	bool optional;
6384432Seric {
63925808Seric 	FILE *f;
64068513Seric 	int sff;
64169453Seric 	int pid;
64269453Seric 	register char *p;
6434432Seric 	char buf[MAXLINE];
6444432Seric 
64566101Seric 	if (tTd(37, 2))
64666101Seric 		printf("fileclass(%s, fmt=%s)\n", filename, fmt);
64766101Seric 
64866031Seric 	if (filename[0] == '|')
64966031Seric 	{
65069453Seric 		auto int fd;
65169453Seric 		int i;
65269453Seric 		char *argv[MAXPV + 1];
65369453Seric 
65469453Seric 		i = 0;
65569453Seric 		for (p = strtok(&filename[1], " \t"); p != NULL; p = strtok(NULL, " \t"))
65669453Seric 		{
65769453Seric 			if (i >= MAXPV)
65869453Seric 				break;
65969453Seric 			argv[i++] = p;
66069453Seric 		}
66169453Seric 		argv[i] = NULL;
66269453Seric 		pid = prog_open(argv, &fd, CurEnv);
66369453Seric 		if (pid < 0)
66469453Seric 			f = NULL;
66569453Seric 		else
66669453Seric 			f = fdopen(fd, "r");
66766031Seric 	}
66869453Seric 	else
66969453Seric 	{
67069453Seric 		pid = -1;
67169453Seric 		sff = SFF_REGONLY;
67269453Seric 		if (safe)
67369453Seric 			sff |= SFF_OPENASROOT;
67469453Seric 		f = safefopen(filename, O_RDONLY, 0, sff);
67569453Seric 	}
67668602Seric 	if (f == NULL)
67754973Seric 	{
67868602Seric 		if (!optional)
67968602Seric 			syserr("fileclass: cannot open %s", filename);
6804432Seric 		return;
6814432Seric 	}
6824432Seric 
6834432Seric 	while (fgets(buf, sizeof buf, f) != NULL)
6844432Seric 	{
68525808Seric 		register char *p;
68625808Seric # ifdef SCANF
6874432Seric 		char wordbuf[MAXNAME+1];
6884432Seric 
6894432Seric 		if (sscanf(buf, fmt, wordbuf) != 1)
6904432Seric 			continue;
69125808Seric 		p = wordbuf;
69256795Seric # else /* SCANF */
69325808Seric 		p = buf;
69456795Seric # endif /* SCANF */
69525808Seric 
69625808Seric 		/*
69725808Seric 		**  Break up the match into words.
69825808Seric 		*/
69925808Seric 
70025808Seric 		while (*p != '\0')
70125808Seric 		{
70225808Seric 			register char *q;
70325808Seric 
70425808Seric 			/* strip leading spaces */
70558050Seric 			while (isascii(*p) && isspace(*p))
70625808Seric 				p++;
70725808Seric 			if (*p == '\0')
70825808Seric 				break;
70925808Seric 
71025808Seric 			/* find the end of the word */
71125808Seric 			q = p;
71258050Seric 			while (*p != '\0' && !(isascii(*p) && isspace(*p)))
71325808Seric 				p++;
71425808Seric 			if (*p != '\0')
71525808Seric 				*p++ = '\0';
71625808Seric 
71725808Seric 			/* enter the word in the symbol table */
71866101Seric 			setclass(class, q);
71925808Seric 		}
7204432Seric 	}
7214432Seric 
72254973Seric 	(void) fclose(f);
72369453Seric 	if (pid > 0)
72469453Seric 		(void) waitfor(pid);
7254432Seric }
7264432Seric /*
7274096Seric **  MAKEMAILER -- define a new mailer.
7284096Seric **
7294096Seric **	Parameters:
73010327Seric **		line -- description of mailer.  This is in labeled
73110327Seric **			fields.  The fields are:
73268481Seric **			   A -- the argv for this mailer
73368481Seric **			   C -- the character set for MIME conversions
73468481Seric **			   D -- the directory to run in
73568481Seric **			   E -- the eol string
73668481Seric **			   F -- the flags associated with the mailer
73768481Seric **			   L -- the maximum line length
73868481Seric **			   M -- the maximum message size
73968816Seric **			   N -- the niceness at which to run
74068479Seric **			   P -- the path to the mailer
74168481Seric **			   R -- the recipient rewriting set
74268479Seric **			   S -- the sender rewriting set
74368481Seric **			   T -- the mailer type (for DSNs)
74468481Seric **			   U -- the uid to run as
74510327Seric **			The first word is the canonical name of the mailer.
7464096Seric **
7474096Seric **	Returns:
7484096Seric **		none.
7494096Seric **
7504096Seric **	Side Effects:
7514096Seric **		enters the mailer into the mailer table.
7524096Seric */
7533308Seric 
75469748Seric void
75521066Seric makemailer(line)
7564096Seric 	char *line;
7574096Seric {
7584096Seric 	register char *p;
7598067Seric 	register struct mailer *m;
7608067Seric 	register STAB *s;
7618067Seric 	int i;
76210327Seric 	char fcode;
76358020Seric 	auto char *endp;
7644096Seric 	extern int NextMailer;
76510327Seric 	extern char **makeargv();
76610327Seric 	extern char *munchstring();
7674096Seric 
76810327Seric 	/* allocate a mailer and set up defaults */
76910327Seric 	m = (struct mailer *) xalloc(sizeof *m);
77010327Seric 	bzero((char *) m, sizeof *m);
77110327Seric 	m->m_eol = "\n";
77268481Seric 	m->m_uid = m->m_gid = 0;
77310327Seric 
77410327Seric 	/* collect the mailer name */
77558050Seric 	for (p = line; *p != '\0' && *p != ',' && !(isascii(*p) && isspace(*p)); p++)
77610327Seric 		continue;
77710327Seric 	if (*p != '\0')
77810327Seric 		*p++ = '\0';
77910327Seric 	m->m_name = newstr(line);
78010327Seric 
78110327Seric 	/* now scan through and assign info from the fields */
78210327Seric 	while (*p != '\0')
78310327Seric 	{
78458333Seric 		auto char *delimptr;
78558333Seric 
78658050Seric 		while (*p != '\0' && (*p == ',' || (isascii(*p) && isspace(*p))))
78710327Seric 			p++;
78810327Seric 
78910327Seric 		/* p now points to field code */
79010327Seric 		fcode = *p;
79110327Seric 		while (*p != '\0' && *p != '=' && *p != ',')
79210327Seric 			p++;
79310327Seric 		if (*p++ != '=')
79410327Seric 		{
79552637Seric 			syserr("mailer %s: `=' expected", m->m_name);
79610327Seric 			return;
79710327Seric 		}
79858050Seric 		while (isascii(*p) && isspace(*p))
79910327Seric 			p++;
80010327Seric 
80110327Seric 		/* p now points to the field body */
80258333Seric 		p = munchstring(p, &delimptr);
80310327Seric 
80410327Seric 		/* install the field into the mailer struct */
80510327Seric 		switch (fcode)
80610327Seric 		{
80710327Seric 		  case 'P':		/* pathname */
80810327Seric 			m->m_mailer = newstr(p);
80910327Seric 			break;
81010327Seric 
81110327Seric 		  case 'F':		/* flags */
81210687Seric 			for (; *p != '\0'; p++)
81358050Seric 				if (!(isascii(*p) && isspace(*p)))
81452637Seric 					setbitn(*p, m->m_flags);
81510327Seric 			break;
81610327Seric 
81710327Seric 		  case 'S':		/* sender rewriting ruleset */
81810327Seric 		  case 'R':		/* recipient rewriting ruleset */
81969783Seric 			i = strtorwset(p, &endp, ST_ENTER);
82069783Seric 			if (i < 0)
82110327Seric 				return;
82210327Seric 			if (fcode == 'S')
82358020Seric 				m->m_sh_rwset = m->m_se_rwset = i;
82410327Seric 			else
82558020Seric 				m->m_rh_rwset = m->m_re_rwset = i;
82658020Seric 
82758020Seric 			p = endp;
82859985Seric 			if (*p++ == '/')
82958020Seric 			{
83069783Seric 				i = strtorwset(p, NULL);
83169783Seric 				if (i < 0)
83258020Seric 					return;
83358020Seric 				if (fcode == 'S')
83458020Seric 					m->m_sh_rwset = i;
83558020Seric 				else
83658020Seric 					m->m_rh_rwset = i;
83758020Seric 			}
83810327Seric 			break;
83910327Seric 
84010327Seric 		  case 'E':		/* end of line string */
84110327Seric 			m->m_eol = newstr(p);
84210327Seric 			break;
84310327Seric 
84410327Seric 		  case 'A':		/* argument vector */
84510327Seric 			m->m_argv = makeargv(p);
84610327Seric 			break;
84710701Seric 
84810701Seric 		  case 'M':		/* maximum message size */
84910701Seric 			m->m_maxsize = atol(p);
85010701Seric 			break;
85152106Seric 
85252106Seric 		  case 'L':		/* maximum line length */
85352106Seric 			m->m_linelimit = atoi(p);
85452106Seric 			break;
85558935Seric 
85668816Seric 		  case 'N':		/* run niceness */
85768816Seric 			m->m_nice = atoi(p);
85868816Seric 			break;
85968816Seric 
86058935Seric 		  case 'D':		/* working directory */
86158935Seric 			m->m_execdir = newstr(p);
86258935Seric 			break;
86368481Seric 
86468481Seric 		  case 'C':		/* default charset */
86568481Seric 			m->m_defcharset = newstr(p);
86668481Seric 			break;
86768481Seric 
86869720Seric 		  case 'T':		/* MTA-Name/Address/Diagnostic types */
86968481Seric 			m->m_mtatype = newstr(p);
87068481Seric 			p = strchr(m->m_mtatype, '/');
87168481Seric 			if (p != NULL)
87268481Seric 			{
87368481Seric 				*p++ = '\0';
87468481Seric 				if (*p == '\0')
87568481Seric 					p = NULL;
87668481Seric 			}
87768481Seric 			if (p == NULL)
87868481Seric 				m->m_addrtype = m->m_mtatype;
87968481Seric 			else
88068481Seric 			{
88168481Seric 				m->m_addrtype = p;
88268481Seric 				p = strchr(p, '/');
88368481Seric 			}
88468481Seric 			if (p != NULL)
88568481Seric 			{
88668481Seric 				*p++ = '\0';
88768481Seric 				if (*p == '\0')
88868481Seric 					p = NULL;
88968481Seric 			}
89068481Seric 			if (p == NULL)
89168481Seric 				m->m_diagtype = m->m_mtatype;
89268481Seric 			else
89368481Seric 				m->m_diagtype = p;
89468481Seric 			break;
89568481Seric 
89668481Seric 		  case 'U':		/* user id */
89768481Seric 			if (isascii(*p) && !isdigit(*p))
89868481Seric 			{
89968481Seric 				char *q = p;
90068481Seric 				struct passwd *pw;
90168481Seric 
90268481Seric 				while (isascii(*p) && isalnum(*p))
90368481Seric 					p++;
90468481Seric 				while (isascii(*p) && isspace(*p))
90568481Seric 					*p++ = '\0';
90668481Seric 				if (*p != '\0')
90768481Seric 					*p++ = '\0';
90868693Seric 				pw = sm_getpwnam(q);
90968481Seric 				if (pw == NULL)
91068481Seric 					syserr("readcf: mailer U= flag: unknown user %s", q);
91168481Seric 				else
91268481Seric 				{
91368481Seric 					m->m_uid = pw->pw_uid;
91468481Seric 					m->m_gid = pw->pw_gid;
91568481Seric 				}
91668481Seric 			}
91768481Seric 			else
91868481Seric 			{
91968481Seric 				auto char *q;
92068481Seric 
92168481Seric 				m->m_uid = strtol(p, &q, 0);
92268481Seric 				p = q;
92368481Seric 			}
92468481Seric 			while (isascii(*p) && isspace(*p))
92568481Seric 				p++;
92668481Seric 			if (*p == '\0')
92768481Seric 				break;
92868481Seric 			if (isascii(*p) && !isdigit(*p))
92968481Seric 			{
93068481Seric 				char *q = p;
93168481Seric 				struct group *gr;
93268481Seric 
93368481Seric 				while (isascii(*p) && isalnum(*p))
93468481Seric 					p++;
93568481Seric 				*p++ = '\0';
93668481Seric 				gr = getgrnam(q);
93768481Seric 				if (gr == NULL)
93868481Seric 					syserr("readcf: mailer U= flag: unknown group %s", q);
93968481Seric 				else
94068481Seric 					m->m_gid = gr->gr_gid;
94168481Seric 			}
94268481Seric 			else
94368481Seric 			{
94468481Seric 				m->m_gid = strtol(p, NULL, 0);
94568481Seric 			}
94668481Seric 			break;
94710327Seric 		}
94810327Seric 
94958333Seric 		p = delimptr;
95010327Seric 	}
95110327Seric 
95258321Seric 	/* do some rationality checking */
95358321Seric 	if (m->m_argv == NULL)
95458321Seric 	{
95558321Seric 		syserr("M%s: A= argument required", m->m_name);
95658321Seric 		return;
95758321Seric 	}
95858321Seric 	if (m->m_mailer == NULL)
95958321Seric 	{
96058321Seric 		syserr("M%s: P= argument required", m->m_name);
96158321Seric 		return;
96258321Seric 	}
96358321Seric 
9644096Seric 	if (NextMailer >= MAXMAILERS)
9654096Seric 	{
9669381Seric 		syserr("too many mailers defined (%d max)", MAXMAILERS);
9674096Seric 		return;
9684096Seric 	}
96957402Seric 
97068481Seric 	/* do some heuristic cleanup for back compatibility */
97168481Seric 	if (bitnset(M_LIMITS, m->m_flags))
97268481Seric 	{
97368481Seric 		if (m->m_linelimit == 0)
97468481Seric 			m->m_linelimit = SMTPLINELIM;
97568481Seric 		if (ConfigLevel < 2)
97668481Seric 			setbitn(M_7BITS, m->m_flags);
97768481Seric 	}
97868481Seric 
97968481Seric 	if (ConfigLevel < 6 &&
98068481Seric 	    (strcmp(m->m_mailer, "[IPC]") == 0 ||
98168481Seric 	     strcmp(m->m_mailer, "[TCP]") == 0))
98268481Seric 	{
98368481Seric 		if (m->m_mtatype == NULL)
98468481Seric 			m->m_mtatype = "dns";
98568481Seric 		if (m->m_addrtype == NULL)
98668481Seric 			m->m_addrtype = "rfc822";
98768481Seric 		if (m->m_diagtype == NULL)
98868481Seric 			m->m_diagtype = "smtp";
98968481Seric 	}
99068481Seric 
99168481Seric 	/* enter the mailer into the symbol table */
99210327Seric 	s = stab(m->m_name, ST_MAILER, ST_ENTER);
99357402Seric 	if (s->s_mailer != NULL)
99457402Seric 	{
99557402Seric 		i = s->s_mailer->m_mno;
99657402Seric 		free(s->s_mailer);
99757402Seric 	}
99857402Seric 	else
99957402Seric 	{
100057402Seric 		i = NextMailer++;
100157402Seric 	}
100257402Seric 	Mailer[i] = s->s_mailer = m;
100357454Seric 	m->m_mno = i;
100410327Seric }
100510327Seric /*
100610327Seric **  MUNCHSTRING -- translate a string into internal form.
100710327Seric **
100810327Seric **	Parameters:
100910327Seric **		p -- the string to munch.
101058333Seric **		delimptr -- if non-NULL, set to the pointer of the
101158333Seric **			field delimiter character.
101210327Seric **
101310327Seric **	Returns:
101410327Seric **		the munched string.
101510327Seric */
10164096Seric 
101710327Seric char *
101858333Seric munchstring(p, delimptr)
101910327Seric 	register char *p;
102058333Seric 	char **delimptr;
102110327Seric {
102210327Seric 	register char *q;
102310327Seric 	bool backslash = FALSE;
102410327Seric 	bool quotemode = FALSE;
102510327Seric 	static char buf[MAXLINE];
10264096Seric 
102710327Seric 	for (q = buf; *p != '\0'; p++)
10284096Seric 	{
102910327Seric 		if (backslash)
103010327Seric 		{
103110327Seric 			/* everything is roughly literal */
103210357Seric 			backslash = FALSE;
103310327Seric 			switch (*p)
103410327Seric 			{
103510327Seric 			  case 'r':		/* carriage return */
103610327Seric 				*q++ = '\r';
103710327Seric 				continue;
103810327Seric 
103910327Seric 			  case 'n':		/* newline */
104010327Seric 				*q++ = '\n';
104110327Seric 				continue;
104210327Seric 
104310327Seric 			  case 'f':		/* form feed */
104410327Seric 				*q++ = '\f';
104510327Seric 				continue;
104610327Seric 
104710327Seric 			  case 'b':		/* backspace */
104810327Seric 				*q++ = '\b';
104910327Seric 				continue;
105010327Seric 			}
105110327Seric 			*q++ = *p;
105210327Seric 		}
105310327Seric 		else
105410327Seric 		{
105510327Seric 			if (*p == '\\')
105610327Seric 				backslash = TRUE;
105710327Seric 			else if (*p == '"')
105810327Seric 				quotemode = !quotemode;
105910327Seric 			else if (quotemode || *p != ',')
106010327Seric 				*q++ = *p;
106110327Seric 			else
106210327Seric 				break;
106310327Seric 		}
10644096Seric 	}
10654096Seric 
106658333Seric 	if (delimptr != NULL)
106758333Seric 		*delimptr = p;
106810327Seric 	*q++ = '\0';
106910327Seric 	return (buf);
107010327Seric }
107110327Seric /*
107210327Seric **  MAKEARGV -- break up a string into words
107310327Seric **
107410327Seric **	Parameters:
107510327Seric **		p -- the string to break up.
107610327Seric **
107710327Seric **	Returns:
107810327Seric **		a char **argv (dynamically allocated)
107910327Seric **
108010327Seric **	Side Effects:
108110327Seric **		munges p.
108210327Seric */
10834096Seric 
108410327Seric char **
108510327Seric makeargv(p)
108610327Seric 	register char *p;
108710327Seric {
108810327Seric 	char *q;
108910327Seric 	int i;
109010327Seric 	char **avp;
109110327Seric 	char *argv[MAXPV + 1];
109210327Seric 
109310327Seric 	/* take apart the words */
109410327Seric 	i = 0;
109510327Seric 	while (*p != '\0' && i < MAXPV)
10964096Seric 	{
109710327Seric 		q = p;
109858050Seric 		while (*p != '\0' && !(isascii(*p) && isspace(*p)))
109910327Seric 			p++;
110058050Seric 		while (isascii(*p) && isspace(*p))
110110327Seric 			*p++ = '\0';
110210327Seric 		argv[i++] = newstr(q);
11034096Seric 	}
110410327Seric 	argv[i++] = NULL;
11054096Seric 
110610327Seric 	/* now make a copy of the argv */
110710327Seric 	avp = (char **) xalloc(sizeof *avp * i);
110816893Seric 	bcopy((char *) argv, (char *) avp, sizeof *avp * i);
110910327Seric 
111010327Seric 	return (avp);
11113308Seric }
11123308Seric /*
11133308Seric **  PRINTRULES -- print rewrite rules (for debugging)
11143308Seric **
11153308Seric **	Parameters:
11163308Seric **		none.
11173308Seric **
11183308Seric **	Returns:
11193308Seric **		none.
11203308Seric **
11213308Seric **	Side Effects:
11223308Seric **		prints rewrite rules.
11233308Seric */
11243308Seric 
112569748Seric void
11263308Seric printrules()
11273308Seric {
11283308Seric 	register struct rewrite *rwp;
11294072Seric 	register int ruleset;
11303308Seric 
11314072Seric 	for (ruleset = 0; ruleset < 10; ruleset++)
11323308Seric 	{
11334072Seric 		if (RewriteRules[ruleset] == NULL)
11344072Seric 			continue;
11358067Seric 		printf("\n----Rule Set %d:", ruleset);
11363308Seric 
11374072Seric 		for (rwp = RewriteRules[ruleset]; rwp != NULL; rwp = rwp->r_next)
11383308Seric 		{
11398067Seric 			printf("\nLHS:");
11408067Seric 			printav(rwp->r_lhs);
11418067Seric 			printf("RHS:");
11428067Seric 			printav(rwp->r_rhs);
11433308Seric 		}
11443308Seric 	}
11453308Seric }
114668481Seric /*
114768481Seric **  PRINTMAILER -- print mailer structure (for debugging)
114868481Seric **
114968481Seric **	Parameters:
115068481Seric **		m -- the mailer to print
115168481Seric **
115268481Seric **	Returns:
115368481Seric **		none.
115468481Seric */
11554319Seric 
115669748Seric void
115768481Seric printmailer(m)
115868481Seric 	register MAILER *m;
115968481Seric {
116068481Seric 	int j;
116168481Seric 
116268481Seric 	printf("mailer %d (%s): P=%s S=%d/%d R=%d/%d M=%ld U=%d:%d F=",
116368481Seric 		m->m_mno, m->m_name,
116468481Seric 		m->m_mailer, m->m_se_rwset, m->m_sh_rwset,
116568481Seric 		m->m_re_rwset, m->m_rh_rwset, m->m_maxsize,
116668481Seric 		m->m_uid, m->m_gid);
116768481Seric 	for (j = '\0'; j <= '\177'; j++)
116868481Seric 		if (bitnset(j, m->m_flags))
116968481Seric 			(void) putchar(j);
117068481Seric 	printf(" L=%d E=", m->m_linelimit);
117168481Seric 	xputs(m->m_eol);
117268481Seric 	if (m->m_defcharset != NULL)
117368481Seric 		printf(" C=%s", m->m_defcharset);
117468481Seric 	printf(" T=%s/%s/%s",
117568481Seric 		m->m_mtatype == NULL ? "<undefined>" : m->m_mtatype,
117668481Seric 		m->m_addrtype == NULL ? "<undefined>" : m->m_addrtype,
117768481Seric 		m->m_diagtype == NULL ? "<undefined>" : m->m_diagtype);
117868481Seric 	if (m->m_argv != NULL)
117968481Seric 	{
118068481Seric 		char **a = m->m_argv;
118168481Seric 
118268481Seric 		printf(" A=");
118368481Seric 		while (*a != NULL)
118468481Seric 		{
118568481Seric 			if (a != m->m_argv)
118668481Seric 				printf(" ");
118768481Seric 			xputs(*a++);
118868481Seric 		}
118968481Seric 	}
119068481Seric 	printf("\n");
119168481Seric }
11924096Seric /*
11938256Seric **  SETOPTION -- set global processing option
11948256Seric **
11958256Seric **	Parameters:
11968256Seric **		opt -- option name.
11978256Seric **		val -- option value (as a text string).
119821755Seric **		safe -- set if this came from a configuration file.
119921755Seric **			Some options (if set from the command line) will
120021755Seric **			reset the user id to avoid security problems.
12018269Seric **		sticky -- if set, don't let other setoptions override
12028269Seric **			this value.
120358734Seric **		e -- the main envelope.
12048256Seric **
12058256Seric **	Returns:
12068256Seric **		none.
12078256Seric **
12088256Seric **	Side Effects:
12098256Seric **		Sets options as implied by the arguments.
12108256Seric */
12118256Seric 
121210687Seric static BITMAP	StickyOpt;		/* set if option is stuck */
121369748Seric extern void	settimeout __P((char *, char *));
12148269Seric 
121557207Seric 
121666334Seric #if NAMED_BIND
121757207Seric 
121857207Seric struct resolverflags
121957207Seric {
122057207Seric 	char	*rf_name;	/* name of the flag */
122157207Seric 	long	rf_bits;	/* bits to set/clear */
122257207Seric } ResolverFlags[] =
122357207Seric {
122457207Seric 	"debug",	RES_DEBUG,
122557207Seric 	"aaonly",	RES_AAONLY,
122657207Seric 	"usevc",	RES_USEVC,
122757207Seric 	"primary",	RES_PRIMARY,
122857207Seric 	"igntc",	RES_IGNTC,
122957207Seric 	"recurse",	RES_RECURSE,
123057207Seric 	"defnames",	RES_DEFNAMES,
123157207Seric 	"stayopen",	RES_STAYOPEN,
123257207Seric 	"dnsrch",	RES_DNSRCH,
123365583Seric 	"true",		0,		/* to avoid error on old syntax */
123457207Seric 	NULL,		0
123557207Seric };
123657207Seric 
123757207Seric #endif
123857207Seric 
123968481Seric struct optioninfo
124068481Seric {
124168481Seric 	char	*o_name;	/* long name of option */
124268481Seric 	u_char	o_code;		/* short name of option */
124368481Seric 	bool	o_safe;		/* safe for random people to use */
124468481Seric } OptionTab[] =
124568481Seric {
124668481Seric 	"SevenBitInput",	'7',		TRUE,
124769480Seric #if MIME8TO7
124868481Seric 	"EightBitMode",		'8',		TRUE,
124969480Seric #endif
125068481Seric 	"AliasFile",		'A',		FALSE,
125168481Seric 	"AliasWait",		'a',		FALSE,
125268481Seric 	"BlankSub",		'B',		FALSE,
125368481Seric 	"MinFreeBlocks",	'b',		TRUE,
125468481Seric 	"CheckpointInterval",	'C',		TRUE,
125568481Seric 	"HoldExpensive",	'c',		FALSE,
125668481Seric 	"AutoRebuildAliases",	'D',		FALSE,
125768481Seric 	"DeliveryMode",		'd',		TRUE,
125868481Seric 	"ErrorHeader",		'E',		FALSE,
125968481Seric 	"ErrorMode",		'e',		TRUE,
126068481Seric 	"TempFileMode",		'F',		FALSE,
126168481Seric 	"SaveFromLine",		'f',		FALSE,
126268481Seric 	"MatchGECOS",		'G',		FALSE,
126368481Seric 	"HelpFile",		'H',		FALSE,
126468481Seric 	"MaxHopCount",		'h',		FALSE,
126568569Seric 	"ResolverOptions",	'I',		FALSE,
126668481Seric 	"IgnoreDots",		'i',		TRUE,
126768481Seric 	"ForwardPath",		'J',		FALSE,
126868481Seric 	"SendMimeErrors",	'j',		TRUE,
126968481Seric 	"ConnectionCacheSize",	'k',		FALSE,
127068481Seric 	"ConnectionCacheTimeout", 'K',		FALSE,
127168481Seric 	"UseErrorsTo",		'l',		FALSE,
127268481Seric 	"LogLevel",		'L',		FALSE,
127368481Seric 	"MeToo",		'm',		TRUE,
127468481Seric 	"CheckAliases",		'n',		FALSE,
127568481Seric 	"OldStyleHeaders",	'o',		TRUE,
127668481Seric 	"DaemonPortOptions",	'O',		FALSE,
127768481Seric 	"PrivacyOptions",	'p',		TRUE,
127868481Seric 	"PostmasterCopy",	'P',		FALSE,
127968481Seric 	"QueueFactor",		'q',		FALSE,
128068481Seric 	"QueueDirectory",	'Q',		FALSE,
128168481Seric 	"DontPruneRoutes",	'R',		FALSE,
128268481Seric 	"Timeout",		'r',		TRUE,
128368481Seric 	"StatusFile",		'S',		FALSE,
128468481Seric 	"SuperSafe",		's',		TRUE,
128568481Seric 	"QueueTimeout",		'T',		FALSE,
128668481Seric 	"TimeZoneSpec",		't',		FALSE,
128768481Seric 	"UserDatabaseSpec",	'U',		FALSE,
128868481Seric 	"DefaultUser",		'u',		FALSE,
128968481Seric 	"FallbackMXhost",	'V',		FALSE,
129068481Seric 	"Verbose",		'v',		TRUE,
129168481Seric 	"TryNullMXList",	'w',		TRUE,
129268481Seric 	"QueueLA",		'x',		FALSE,
129368481Seric 	"RefuseLA",		'X',		FALSE,
129468481Seric 	"RecipientFactor",	'y',		FALSE,
129568569Seric 	"ForkEachJob",		'Y',		FALSE,
129668481Seric 	"ClassFactor",		'z',		FALSE,
129768569Seric 	"RetryFactor",		'Z',		FALSE,
129868481Seric #define O_QUEUESORTORD	0x81
129968481Seric 	"QueueSortOrder",	O_QUEUESORTORD,	TRUE,
130069401Seric #define O_HOSTSFILE	0x82
130169401Seric 	"HostsFile",		O_HOSTSFILE,	FALSE,
130268481Seric #define O_MQA		0x83
130368481Seric 	"MinQueueAge",		O_MQA,		TRUE,
130468481Seric #define O_MHSA		0x84
130568481Seric /*
130668481Seric 	"MaxHostStatAge",	O_MHSA,		TRUE,
130768481Seric */
130868481Seric #define O_DEFCHARSET	0x85
130968481Seric 	"DefaultCharSet",	O_DEFCHARSET,	TRUE,
131068481Seric #define O_SSFILE	0x86
131168481Seric 	"ServiceSwitchFile",	O_SSFILE,	FALSE,
131268481Seric #define O_DIALDELAY	0x87
131368481Seric 	"DialDelay",		O_DIALDELAY,	TRUE,
131468481Seric #define O_NORCPTACTION	0x88
131568481Seric 	"NoRecipientAction",	O_NORCPTACTION,	TRUE,
131668490Seric #define O_SAFEFILEENV	0x89
131768490Seric 	"SafeFileEnvironment",	O_SAFEFILEENV,	FALSE,
131868569Seric #define O_MAXMSGSIZE	0x8a
131968569Seric 	"MaxMessageSize",	O_MAXMSGSIZE,	FALSE,
132068756Seric #define O_COLONOKINADDR	0x8b
132168756Seric 	"ColonOkInAddr",	O_COLONOKINADDR, TRUE,
132269724Seric #define O_MAXQUEUERUN	0x8c
132369724Seric 	"MaxQueueRunSize",	O_MAXQUEUERUN,	TRUE,
132468481Seric 
132568481Seric 	NULL,			'\0',		FALSE,
132668481Seric };
132768481Seric 
132868481Seric 
132968481Seric 
133069748Seric void
133158734Seric setoption(opt, val, safe, sticky, e)
133269748Seric 	int opt;
13338256Seric 	char *val;
133421755Seric 	bool safe;
13358269Seric 	bool sticky;
133658734Seric 	register ENVELOPE *e;
13378256Seric {
133857207Seric 	register char *p;
133968481Seric 	register struct optioninfo *o;
134068481Seric 	char *subopt;
13418265Seric 	extern bool atobool();
134212633Seric 	extern time_t convtime();
134314879Seric 	extern int QueueLA;
134414879Seric 	extern int RefuseLA;
134564718Seric 	extern bool Warn_Q_option;
13468256Seric 
134768481Seric 	errno = 0;
134868481Seric 	if (opt == ' ')
134968481Seric 	{
135068481Seric 		/* full word options */
135168481Seric 		struct optioninfo *sel;
135268481Seric 
135368481Seric 		p = strchr(val, '=');
135468481Seric 		if (p == NULL)
135568481Seric 			p = &val[strlen(val)];
135668481Seric 		while (*--p == ' ')
135768481Seric 			continue;
135868481Seric 		while (*++p == ' ')
135968481Seric 			*p = '\0';
136068481Seric 		if (p == val)
136168481Seric 		{
136268481Seric 			syserr("readcf: null option name");
136368481Seric 			return;
136468481Seric 		}
136568481Seric 		if (*p == '=')
136668481Seric 			*p++ = '\0';
136768481Seric 		while (*p == ' ')
136868481Seric 			p++;
136968481Seric 		subopt = strchr(val, '.');
137068481Seric 		if (subopt != NULL)
137168481Seric 			*subopt++ = '\0';
137268481Seric 		sel = NULL;
137368481Seric 		for (o = OptionTab; o->o_name != NULL; o++)
137468481Seric 		{
137568481Seric 			if (strncasecmp(o->o_name, val, strlen(val)) != 0)
137668481Seric 				continue;
137768481Seric 			if (strlen(o->o_name) == strlen(val))
137868481Seric 			{
137968481Seric 				/* completely specified -- this must be it */
138068481Seric 				sel = NULL;
138168481Seric 				break;
138268481Seric 			}
138368481Seric 			if (sel != NULL)
138468481Seric 				break;
138568481Seric 			sel = o;
138668481Seric 		}
138768481Seric 		if (sel != NULL && o->o_name == NULL)
138868481Seric 			o = sel;
138968481Seric 		else if (o->o_name == NULL)
139068481Seric 		{
139168481Seric 			syserr("readcf: unknown option name %s", val);
139268481Seric 			return;
139368481Seric 		}
139468481Seric 		else if (sel != NULL)
139568481Seric 		{
139668481Seric 			syserr("readcf: ambiguous option name %s (matches %s and %s)",
139768481Seric 				val, sel->o_name, o->o_name);
139868481Seric 			return;
139968481Seric 		}
140068481Seric 		if (strlen(val) != strlen(o->o_name))
140168481Seric 		{
140268481Seric 			bool oldVerbose = Verbose;
140368481Seric 
140468481Seric 			Verbose = TRUE;
140568481Seric 			message("Option %s used as abbreviation for %s",
140668481Seric 				val, o->o_name);
140768481Seric 			Verbose = oldVerbose;
140868481Seric 		}
140968481Seric 		opt = o->o_code;
141068481Seric 		val = p;
141168481Seric 	}
141268481Seric 	else
141368481Seric 	{
141468481Seric 		for (o = OptionTab; o->o_name != NULL; o++)
141568481Seric 		{
141668481Seric 			if (o->o_code == opt)
141768481Seric 				break;
141868481Seric 		}
141968481Seric 		subopt = NULL;
142068481Seric 	}
142168481Seric 
14228256Seric 	if (tTd(37, 1))
142368481Seric 	{
142468481Seric 		printf(isascii(opt) && isprint(opt) ?
142568481Seric 			    "setoption %s (%c).%s=%s" :
142668481Seric 			    "setoption %s (0x%x).%s=%s",
142768481Seric 			o->o_name == NULL ? "<unknown>" : o->o_name,
142868481Seric 			opt,
142968481Seric 			subopt == NULL ? "" : subopt,
143068481Seric 			val);
143168481Seric 	}
14328256Seric 
14338256Seric 	/*
14348269Seric 	**  See if this option is preset for us.
14358256Seric 	*/
14368256Seric 
143759731Seric 	if (!sticky && bitnset(opt, StickyOpt))
14388269Seric 	{
14399341Seric 		if (tTd(37, 1))
14409341Seric 			printf(" (ignored)\n");
14418269Seric 		return;
14428269Seric 	}
14438269Seric 
144421755Seric 	/*
144521755Seric 	**  Check to see if this option can be specified by this user.
144621755Seric 	*/
144721755Seric 
144863787Seric 	if (!safe && RealUid == 0)
144921755Seric 		safe = TRUE;
145068481Seric 	if (!safe && !o->o_safe)
145121755Seric 	{
145239111Srick 		if (opt != 'M' || (val[0] != 'r' && val[0] != 's'))
145321755Seric 		{
145436582Sbostic 			if (tTd(37, 1))
145536582Sbostic 				printf(" (unsafe)");
145663787Seric 			if (RealUid != geteuid())
145736582Sbostic 			{
145851210Seric 				if (tTd(37, 1))
145951210Seric 					printf("(Resetting uid)");
146063787Seric 				(void) setgid(RealGid);
146163787Seric 				(void) setuid(RealUid);
146236582Sbostic 			}
146321755Seric 		}
146421755Seric 	}
146551210Seric 	if (tTd(37, 1))
146617985Seric 		printf("\n");
14678269Seric 
146868481Seric 	switch (opt & 0xff)
14698256Seric 	{
147059709Seric 	  case '7':		/* force seven-bit input */
147168481Seric 		SevenBitInput = atobool(val);
147252106Seric 		break;
147352106Seric 
147469480Seric #if MIME8TO7
147568481Seric 	  case '8':		/* handling of 8-bit input */
147668481Seric 		switch (*val)
147768481Seric 		{
147868481Seric 		  case 'm':		/* convert 8-bit, convert MIME */
147968481Seric 			MimeMode = MM_CVTMIME|MM_MIME8BIT;
148068481Seric 			break;
148168481Seric 
148268481Seric 		  case 'p':		/* pass 8 bit, convert MIME */
148368856Seric 			MimeMode = MM_CVTMIME|MM_PASS8BIT;
148468481Seric 			break;
148568481Seric 
148668481Seric 		  case 's':		/* strict adherence */
148768481Seric 			MimeMode = MM_CVTMIME;
148868481Seric 			break;
148968481Seric 
149068856Seric #if 0
149168856Seric 		  case 'r':		/* reject 8-bit, don't convert MIME */
149268856Seric 			MimeMode = 0;
149368856Seric 			break;
149468856Seric 
149568856Seric 		  case 'j':		/* "just send 8" */
149668856Seric 			MimeMode = MM_PASS8BIT;
149768856Seric 			break;
149868856Seric 
149968481Seric 		  case 'a':		/* encode 8 bit if available */
150068481Seric 			MimeMode = MM_MIME8BIT|MM_PASS8BIT|MM_CVTMIME;
150168481Seric 			break;
150268481Seric 
150368481Seric 		  case 'c':		/* convert 8 bit to MIME, never 7 bit */
150468481Seric 			MimeMode = MM_MIME8BIT;
150568481Seric 			break;
150668856Seric #endif
150768481Seric 
150868481Seric 		  default:
150968481Seric 			syserr("Unknown 8-bit mode %c", *val);
151068481Seric 			exit(EX_USAGE);
151168481Seric 		}
151268481Seric 		break;
151369480Seric #endif
151468481Seric 
15158256Seric 	  case 'A':		/* set default alias file */
15169381Seric 		if (val[0] == '\0')
151759672Seric 			setalias("aliases");
15189381Seric 		else
151959672Seric 			setalias(val);
15208256Seric 		break;
15218256Seric 
152217474Seric 	  case 'a':		/* look N minutes for "@:@" in alias file */
152317474Seric 		if (val[0] == '\0')
152464796Seric 			SafeAlias = 5 * 60;		/* five minutes */
152517474Seric 		else
152664796Seric 			SafeAlias = convtime(val, 'm');
152717474Seric 		break;
152817474Seric 
152916843Seric 	  case 'B':		/* substitution for blank character */
153016843Seric 		SpaceSub = val[0];
153116843Seric 		if (SpaceSub == '\0')
153216843Seric 			SpaceSub = ' ';
153316843Seric 		break;
153416843Seric 
153559283Seric 	  case 'b':		/* min blocks free on queue fs/max msg size */
153659283Seric 		p = strchr(val, '/');
153759283Seric 		if (p != NULL)
153859283Seric 		{
153959283Seric 			*p++ = '\0';
154059283Seric 			MaxMessageSize = atol(p);
154159283Seric 		}
154258082Seric 		MinBlocksFree = atol(val);
154358082Seric 		break;
154458082Seric 
15459284Seric 	  case 'c':		/* don't connect to "expensive" mailers */
15469381Seric 		NoConnect = atobool(val);
15479284Seric 		break;
15489284Seric 
154951305Seric 	  case 'C':		/* checkpoint every N addresses */
155051305Seric 		CheckpointInterval = atoi(val);
155124944Seric 		break;
155224944Seric 
15539284Seric 	  case 'd':		/* delivery mode */
15549284Seric 		switch (*val)
15558269Seric 		{
15569284Seric 		  case '\0':
155758734Seric 			e->e_sendmode = SM_DELIVER;
15588269Seric 			break;
15598269Seric 
156010755Seric 		  case SM_QUEUE:	/* queue only */
156110755Seric #ifndef QUEUE
156210755Seric 			syserr("need QUEUE to set -odqueue");
156356795Seric #endif /* QUEUE */
156410755Seric 			/* fall through..... */
156510755Seric 
15669284Seric 		  case SM_DELIVER:	/* do everything */
15679284Seric 		  case SM_FORK:		/* fork after verification */
156858734Seric 			e->e_sendmode = *val;
15698269Seric 			break;
15708269Seric 
15718269Seric 		  default:
15729284Seric 			syserr("Unknown delivery mode %c", *val);
15738269Seric 			exit(EX_USAGE);
15748269Seric 		}
15758269Seric 		break;
15768269Seric 
15779146Seric 	  case 'D':		/* rebuild alias database as needed */
15789381Seric 		AutoRebuild = atobool(val);
15799146Seric 		break;
15809146Seric 
158155372Seric 	  case 'E':		/* error message header/header file */
158255379Seric 		if (*val != '\0')
158355379Seric 			ErrMsgFile = newstr(val);
158455372Seric 		break;
158555372Seric 
15868269Seric 	  case 'e':		/* set error processing mode */
15878269Seric 		switch (*val)
15888269Seric 		{
15899381Seric 		  case EM_QUIET:	/* be silent about it */
15909381Seric 		  case EM_MAIL:		/* mail back */
15919381Seric 		  case EM_BERKNET:	/* do berknet error processing */
15929381Seric 		  case EM_WRITE:	/* write back (or mail) */
15939381Seric 		  case EM_PRINT:	/* print errors normally (default) */
159458734Seric 			e->e_errormode = *val;
15958269Seric 			break;
15968269Seric 		}
15978269Seric 		break;
15988269Seric 
15999049Seric 	  case 'F':		/* file mode */
160017975Seric 		FileMode = atooct(val) & 0777;
16019049Seric 		break;
16029049Seric 
16038269Seric 	  case 'f':		/* save Unix-style From lines on front */
16049381Seric 		SaveFrom = atobool(val);
16058269Seric 		break;
16068269Seric 
160753735Seric 	  case 'G':		/* match recipients against GECOS field */
160853735Seric 		MatchGecos = atobool(val);
160953735Seric 		break;
161053735Seric 
16118256Seric 	  case 'g':		/* default gid */
161268481Seric   g_opt:
161364133Seric 		if (isascii(*val) && isdigit(*val))
161464133Seric 			DefGid = atoi(val);
161564133Seric 		else
161664133Seric 		{
161764133Seric 			register struct group *gr;
161864133Seric 
161964133Seric 			DefGid = -1;
162064133Seric 			gr = getgrnam(val);
162164133Seric 			if (gr == NULL)
162268481Seric 				syserr("readcf: option %c: unknown group %s",
162368481Seric 					opt, val);
162464133Seric 			else
162564133Seric 				DefGid = gr->gr_gid;
162664133Seric 		}
16278256Seric 		break;
16288256Seric 
16298256Seric 	  case 'H':		/* help file */
16309381Seric 		if (val[0] == '\0')
16318269Seric 			HelpFile = "sendmail.hf";
16329381Seric 		else
16339381Seric 			HelpFile = newstr(val);
16348256Seric 		break;
16358256Seric 
163651305Seric 	  case 'h':		/* maximum hop count */
163751305Seric 		MaxHopCount = atoi(val);
163851305Seric 		break;
163951305Seric 
164035651Seric 	  case 'I':		/* use internet domain name server */
164166334Seric #if NAMED_BIND
164257207Seric 		for (p = val; *p != 0; )
164357207Seric 		{
164457207Seric 			bool clearmode;
164557207Seric 			char *q;
164657207Seric 			struct resolverflags *rfp;
164757207Seric 
164857207Seric 			while (*p == ' ')
164957207Seric 				p++;
165057207Seric 			if (*p == '\0')
165157207Seric 				break;
165257207Seric 			clearmode = FALSE;
165357207Seric 			if (*p == '-')
165457207Seric 				clearmode = TRUE;
165557207Seric 			else if (*p != '+')
165657207Seric 				p--;
165757207Seric 			p++;
165857207Seric 			q = p;
165958050Seric 			while (*p != '\0' && !(isascii(*p) && isspace(*p)))
166057207Seric 				p++;
166157207Seric 			if (*p != '\0')
166257207Seric 				*p++ = '\0';
166368759Seric 			if (strcasecmp(q, "HasWildcardMX") == 0)
166468759Seric 			{
166568759Seric 				NoMXforCanon = !clearmode;
166668759Seric 				continue;
166768759Seric 			}
166857207Seric 			for (rfp = ResolverFlags; rfp->rf_name != NULL; rfp++)
166957207Seric 			{
167057207Seric 				if (strcasecmp(q, rfp->rf_name) == 0)
167157207Seric 					break;
167257207Seric 			}
167364923Seric 			if (rfp->rf_name == NULL)
167464923Seric 				syserr("readcf: I option value %s unrecognized", q);
167564923Seric 			else if (clearmode)
167657207Seric 				_res.options &= ~rfp->rf_bits;
167757207Seric 			else
167857207Seric 				_res.options |= rfp->rf_bits;
167957207Seric 		}
168057207Seric 		if (tTd(8, 2))
168168759Seric 			printf("_res.options = %x, HasWildcardMX = %d\n",
168268759Seric 				_res.options, !NoMXforCanon);
168357207Seric #else
168457207Seric 		usrerr("name server (I option) specified but BIND not compiled in");
168557207Seric #endif
168635651Seric 		break;
168735651Seric 
16888269Seric 	  case 'i':		/* ignore dot lines in message */
16899381Seric 		IgnrDot = atobool(val);
16908269Seric 		break;
16918269Seric 
169259730Seric 	  case 'j':		/* send errors in MIME (RFC 1341) format */
169359730Seric 		SendMIMEErrors = atobool(val);
169459730Seric 		break;
169559730Seric 
169657136Seric 	  case 'J':		/* .forward search path */
169757136Seric 		ForwardPath = newstr(val);
169857136Seric 		break;
169957136Seric 
170054967Seric 	  case 'k':		/* connection cache size */
170154967Seric 		MaxMciCache = atoi(val);
170256215Seric 		if (MaxMciCache < 0)
170356215Seric 			MaxMciCache = 0;
170454967Seric 		break;
170554967Seric 
170654967Seric 	  case 'K':		/* connection cache timeout */
170758796Seric 		MciCacheTimeout = convtime(val, 'm');
170854967Seric 		break;
170954967Seric 
171061104Seric 	  case 'l':		/* use Errors-To: header */
171161104Seric 		UseErrorsTo = atobool(val);
171261104Seric 		break;
171361104Seric 
17148256Seric 	  case 'L':		/* log level */
171564140Seric 		if (safe || LogLevel < atoi(val))
171664140Seric 			LogLevel = atoi(val);
17178256Seric 		break;
17188256Seric 
17198269Seric 	  case 'M':		/* define macro */
172068267Seric 		p = newstr(&val[1]);
172168267Seric 		if (!safe)
172268267Seric 			cleanstrcpy(p, p, MAXNAME);
172368267Seric 		define(val[0], p, CurEnv);
172416878Seric 		sticky = FALSE;
17258269Seric 		break;
17268269Seric 
17278269Seric 	  case 'm':		/* send to me too */
17289381Seric 		MeToo = atobool(val);
17298269Seric 		break;
17308269Seric 
173125820Seric 	  case 'n':		/* validate RHS in newaliases */
173225820Seric 		CheckAliases = atobool(val);
173325820Seric 		break;
173425820Seric 
173561104Seric 	    /* 'N' available -- was "net name" */
173661104Seric 
173758851Seric 	  case 'O':		/* daemon options */
173858851Seric 		setdaemonoptions(val);
173958851Seric 		break;
174058851Seric 
17418269Seric 	  case 'o':		/* assume old style headers */
17429381Seric 		if (atobool(val))
17439341Seric 			CurEnv->e_flags |= EF_OLDSTYLE;
17449341Seric 		else
17459341Seric 			CurEnv->e_flags &= ~EF_OLDSTYLE;
17468269Seric 		break;
17478269Seric 
174858082Seric 	  case 'p':		/* select privacy level */
174958082Seric 		p = val;
175058082Seric 		for (;;)
175158082Seric 		{
175258082Seric 			register struct prival *pv;
175358082Seric 			extern struct prival PrivacyValues[];
175458082Seric 
175558082Seric 			while (isascii(*p) && (isspace(*p) || ispunct(*p)))
175658082Seric 				p++;
175758082Seric 			if (*p == '\0')
175858082Seric 				break;
175958082Seric 			val = p;
176058082Seric 			while (isascii(*p) && isalnum(*p))
176158082Seric 				p++;
176258082Seric 			if (*p != '\0')
176358082Seric 				*p++ = '\0';
176458082Seric 
176558082Seric 			for (pv = PrivacyValues; pv->pv_name != NULL; pv++)
176658082Seric 			{
176758082Seric 				if (strcasecmp(val, pv->pv_name) == 0)
176858082Seric 					break;
176958082Seric 			}
177058886Seric 			if (pv->pv_name == NULL)
177158886Seric 				syserr("readcf: Op line: %s unrecognized", val);
177258082Seric 			PrivacyFlags |= pv->pv_flag;
177358082Seric 		}
177468479Seric 		sticky = FALSE;
177558082Seric 		break;
177658082Seric 
177724944Seric 	  case 'P':		/* postmaster copy address for returned mail */
177824944Seric 		PostMasterCopy = newstr(val);
177924944Seric 		break;
178024944Seric 
178124944Seric 	  case 'q':		/* slope of queue only function */
178224944Seric 		QueueFactor = atoi(val);
178324944Seric 		break;
178424944Seric 
17858256Seric 	  case 'Q':		/* queue directory */
17869381Seric 		if (val[0] == '\0')
17878269Seric 			QueueDir = "mqueue";
17889381Seric 		else
17899381Seric 			QueueDir = newstr(val);
179058789Seric 		if (RealUid != 0 && !safe)
179164718Seric 			Warn_Q_option = TRUE;
17928256Seric 		break;
17938256Seric 
179458148Seric 	  case 'R':		/* don't prune routes */
179558148Seric 		DontPruneRoutes = atobool(val);
179658148Seric 		break;
179758148Seric 
17988256Seric 	  case 'r':		/* read timeout */
179968481Seric 		if (subopt == NULL)
180068481Seric 			inittimeouts(val);
180168481Seric 		else
180268481Seric 			settimeout(subopt, val);
18038256Seric 		break;
18048256Seric 
18058256Seric 	  case 'S':		/* status file */
18069381Seric 		if (val[0] == '\0')
18078269Seric 			StatFile = "sendmail.st";
18089381Seric 		else
18099381Seric 			StatFile = newstr(val);
18108256Seric 		break;
18118256Seric 
18128265Seric 	  case 's':		/* be super safe, even if expensive */
18139381Seric 		SuperSafe = atobool(val);
18148256Seric 		break;
18158256Seric 
18168256Seric 	  case 'T':		/* queue timeout */
181758737Seric 		p = strchr(val, '/');
181858737Seric 		if (p != NULL)
181958737Seric 		{
182058737Seric 			*p++ = '\0';
182168481Seric 			settimeout("queuewarn", p);
182258737Seric 		}
182368481Seric 		settimeout("queuereturn", val);
182454967Seric 		break;
18258256Seric 
18268265Seric 	  case 't':		/* time zone name */
182752106Seric 		TimeZoneSpec = newstr(val);
18288265Seric 		break;
18298265Seric 
183050556Seric 	  case 'U':		/* location of user database */
183151360Seric 		UdbSpec = newstr(val);
183250556Seric 		break;
183350556Seric 
18348256Seric 	  case 'u':		/* set default uid */
183568481Seric 		for (p = val; *p != '\0'; p++)
183668481Seric 		{
183768481Seric 			if (*p == '.' || *p == '/' || *p == ':')
183868481Seric 			{
183968481Seric 				*p++ = '\0';
184068481Seric 				break;
184168481Seric 			}
184268481Seric 		}
184364133Seric 		if (isascii(*val) && isdigit(*val))
184464133Seric 			DefUid = atoi(val);
184564133Seric 		else
184664133Seric 		{
184764133Seric 			register struct passwd *pw;
184864133Seric 
184964133Seric 			DefUid = -1;
185068693Seric 			pw = sm_getpwnam(val);
185164133Seric 			if (pw == NULL)
185264133Seric 				syserr("readcf: option u: unknown user %s", val);
185364133Seric 			else
185468481Seric 			{
185564133Seric 				DefUid = pw->pw_uid;
185668481Seric 				DefGid = pw->pw_gid;
185768481Seric 			}
185864133Seric 		}
185940973Sbostic 		setdefuser();
18608256Seric 
186168481Seric 		/* handle the group if it is there */
186268481Seric 		if (*p == '\0')
186368481Seric 			break;
186468481Seric 		val = p;
186568481Seric 		goto g_opt;
186668481Seric 
186758851Seric 	  case 'V':		/* fallback MX host */
186858851Seric 		FallBackMX = newstr(val);
186958851Seric 		break;
187058851Seric 
18718269Seric 	  case 'v':		/* run in verbose mode */
18729381Seric 		Verbose = atobool(val);
18738256Seric 		break;
18748256Seric 
187563837Seric 	  case 'w':		/* if we are best MX, try host directly */
187663837Seric 		TryNullMXList = atobool(val);
187763837Seric 		break;
187861104Seric 
187961104Seric 	    /* 'W' available -- was wizard password */
188061104Seric 
188114879Seric 	  case 'x':		/* load avg at which to auto-queue msgs */
188214879Seric 		QueueLA = atoi(val);
188314879Seric 		break;
188414879Seric 
188514879Seric 	  case 'X':		/* load avg at which to auto-reject connections */
188614879Seric 		RefuseLA = atoi(val);
188714879Seric 		break;
188814879Seric 
188924981Seric 	  case 'y':		/* work recipient factor */
189024981Seric 		WkRecipFact = atoi(val);
189124981Seric 		break;
189224981Seric 
189324981Seric 	  case 'Y':		/* fork jobs during queue runs */
189424952Seric 		ForkQueueRuns = atobool(val);
189524952Seric 		break;
189624952Seric 
189724981Seric 	  case 'z':		/* work message class factor */
189824981Seric 		WkClassFact = atoi(val);
189924981Seric 		break;
190024981Seric 
190124981Seric 	  case 'Z':		/* work time factor */
190224981Seric 		WkTimeFact = atoi(val);
190324981Seric 		break;
190424981Seric 
190568481Seric 	  case O_QUEUESORTORD:	/* queue sorting order */
190668481Seric 		switch (*val)
190768481Seric 		{
190868481Seric 		  case 'h':	/* Host first */
190968481Seric 		  case 'H':
191068481Seric 			QueueSortOrder = QS_BYHOST;
191168481Seric 			break;
191268481Seric 
191368481Seric 		  case 'p':	/* Priority order */
191468481Seric 		  case 'P':
191568481Seric 			QueueSortOrder = QS_BYPRIORITY;
191668481Seric 			break;
191768481Seric 
191868481Seric 		  default:
191968481Seric 			syserr("Invalid queue sort order \"%s\"", val);
192068481Seric 		}
192168481Seric 		break;
192268481Seric 
192369401Seric 	  case O_HOSTSFILE:	/* pathname of /etc/hosts file */
192469401Seric 		HostsFile = newstr(val);
192569401Seric 		break;
192669401Seric 
192768481Seric 	  case O_MQA:		/* minimum queue age between deliveries */
192868481Seric 		MinQueueAge = convtime(val, 'm');
192968481Seric 		break;
193068481Seric 
193168481Seric 	  case O_MHSA:		/* maximum age of cached host status */
193268481Seric 		MaxHostStatAge = convtime(val, 'm');
193368481Seric 		break;
193468481Seric 
193568481Seric 	  case O_DEFCHARSET:	/* default character set for mimefying */
193668481Seric 		DefaultCharSet = newstr(denlstring(val, TRUE, TRUE));
193768481Seric 		break;
193868481Seric 
193968481Seric 	  case O_SSFILE:	/* service switch file */
194068481Seric 		ServiceSwitchFile = newstr(val);
194168481Seric 		break;
194268481Seric 
194368481Seric 	  case O_DIALDELAY:	/* delay for dial-on-demand operation */
194468481Seric 		DialDelay = convtime(val, 's');
194568481Seric 		break;
194668481Seric 
194768481Seric 	  case O_NORCPTACTION:	/* what to do if no recipient */
194868481Seric 		if (strcasecmp(val, "none") == 0)
194968481Seric 			NoRecipientAction = NRA_NO_ACTION;
195068481Seric 		else if (strcasecmp(val, "add-to") == 0)
195168481Seric 			NoRecipientAction = NRA_ADD_TO;
195268481Seric 		else if (strcasecmp(val, "add-apparently-to") == 0)
195368481Seric 			NoRecipientAction = NRA_ADD_APPARENTLY_TO;
195468481Seric 		else if (strcasecmp(val, "add-bcc") == 0)
195568481Seric 			NoRecipientAction = NRA_ADD_BCC;
195668481Seric 		else if (strcasecmp(val, "add-to-undisclosed") == 0)
195768481Seric 			NoRecipientAction = NRA_ADD_TO_UNDISCLOSED;
195868481Seric 		else
195968481Seric 			syserr("Invalid NoRecipientAction: %s", val);
196068481Seric 
196168490Seric 	  case O_SAFEFILEENV:	/* chroot() environ for writing to files */
196268490Seric 		SafeFileEnv = newstr(val);
196368490Seric 		break;
196468490Seric 
196568569Seric 	  case O_MAXMSGSIZE:	/* maximum message size */
196669748Seric 		MaxMessageSize = atol(val);
196768569Seric 		break;
196868569Seric 
196968756Seric 	  case O_COLONOKINADDR:	/* old style handling of colon addresses */
197069748Seric 		ColonOkInAddr = atobool(val);
197168756Seric 		break;
197268756Seric 
197369724Seric 	  case O_MAXQUEUERUN:	/* max # of jobs in a single queue run */
197469748Seric 		MaxQueueRun = atol(val);
197569724Seric 		break;
197669724Seric 
19778256Seric 	  default:
197868481Seric 		if (tTd(37, 1))
197968481Seric 		{
198068481Seric 			if (isascii(opt) && isprint(opt))
198168481Seric 				printf("Warning: option %c unknown\n", opt);
198268481Seric 			else
198368481Seric 				printf("Warning: option 0x%x unknown\n", opt);
198468481Seric 		}
19858256Seric 		break;
19868256Seric 	}
198716878Seric 	if (sticky)
198816878Seric 		setbitn(opt, StickyOpt);
19898256Seric }
199010687Seric /*
199168481Seric **  SETCLASS -- set a string into a class
199210687Seric **
199310687Seric **	Parameters:
199468481Seric **		class -- the class to put the string in.
199568481Seric **		str -- the string to enter
199610687Seric **
199710687Seric **	Returns:
199810687Seric **		none.
199910687Seric **
200010687Seric **	Side Effects:
200110687Seric **		puts the word into the symbol table.
200210687Seric */
200310687Seric 
200469748Seric void
200568481Seric setclass(class, str)
200610687Seric 	int class;
200768481Seric 	char *str;
200810687Seric {
200910687Seric 	register STAB *s;
201010687Seric 
201157943Seric 	if (tTd(37, 8))
201268481Seric 		printf("setclass(%c, %s)\n", class, str);
201368481Seric 	s = stab(str, ST_CLASS, ST_ENTER);
201410687Seric 	setbitn(class, s->s_class);
201510687Seric }
201653654Seric /*
201753654Seric **  MAKEMAPENTRY -- create a map entry
201853654Seric **
201953654Seric **	Parameters:
202053654Seric **		line -- the config file line
202153654Seric **
202253654Seric **	Returns:
202369774Seric **		A pointer to the map that has been created.
202469774Seric **		NULL if there was a syntax error.
202553654Seric **
202653654Seric **	Side Effects:
202753654Seric **		Enters the map into the dictionary.
202853654Seric */
202953654Seric 
203069774Seric MAP *
203153654Seric makemapentry(line)
203253654Seric 	char *line;
203353654Seric {
203453654Seric 	register char *p;
203553654Seric 	char *mapname;
203653654Seric 	char *classname;
203764078Seric 	register STAB *s;
203853654Seric 	STAB *class;
203953654Seric 
204058050Seric 	for (p = line; isascii(*p) && isspace(*p); p++)
204153654Seric 		continue;
204258050Seric 	if (!(isascii(*p) && isalnum(*p)))
204353654Seric 	{
204453654Seric 		syserr("readcf: config K line: no map name");
204569774Seric 		return NULL;
204653654Seric 	}
204753654Seric 
204853654Seric 	mapname = p;
204968481Seric 	while ((isascii(*++p) && isalnum(*p)) || *p == '.')
205053654Seric 		continue;
205153654Seric 	if (*p != '\0')
205253654Seric 		*p++ = '\0';
205358050Seric 	while (isascii(*p) && isspace(*p))
205453654Seric 		p++;
205558050Seric 	if (!(isascii(*p) && isalnum(*p)))
205653654Seric 	{
205753654Seric 		syserr("readcf: config K line, map %s: no map class", mapname);
205869774Seric 		return NULL;
205953654Seric 	}
206053654Seric 	classname = p;
206158050Seric 	while (isascii(*++p) && isalnum(*p))
206253654Seric 		continue;
206353654Seric 	if (*p != '\0')
206453654Seric 		*p++ = '\0';
206558050Seric 	while (isascii(*p) && isspace(*p))
206653654Seric 		p++;
206753654Seric 
206853654Seric 	/* look up the class */
206953654Seric 	class = stab(classname, ST_MAPCLASS, ST_FIND);
207053654Seric 	if (class == NULL)
207153654Seric 	{
207253654Seric 		syserr("readcf: map %s: class %s not available", mapname, classname);
207369774Seric 		return NULL;
207453654Seric 	}
207553654Seric 
207653654Seric 	/* enter the map */
207764078Seric 	s = stab(mapname, ST_MAP, ST_ENTER);
207864078Seric 	s->s_map.map_class = &class->s_mapclass;
207964078Seric 	s->s_map.map_mname = newstr(mapname);
208053654Seric 
208164078Seric 	if (class->s_mapclass.map_parse(&s->s_map, p))
208264078Seric 		s->s_map.map_mflags |= MF_VALID;
208364078Seric 
208464078Seric 	if (tTd(37, 5))
208564078Seric 	{
208664078Seric 		printf("map %s, class %s, flags %x, file %s,\n",
208764078Seric 			s->s_map.map_mname, s->s_map.map_class->map_cname,
208864078Seric 			s->s_map.map_mflags,
208964078Seric 			s->s_map.map_file == NULL ? "(null)" : s->s_map.map_file);
209064078Seric 		printf("\tapp %s, domain %s, rebuild %s\n",
209164078Seric 			s->s_map.map_app == NULL ? "(null)" : s->s_map.map_app,
209264078Seric 			s->s_map.map_domain == NULL ? "(null)" : s->s_map.map_domain,
209364078Seric 			s->s_map.map_rebuild == NULL ? "(null)" : s->s_map.map_rebuild);
209464078Seric 	}
209569774Seric 
209669774Seric 	return &s->s_map;
209753654Seric }
209858112Seric /*
209969783Seric **  STRTORWSET -- convert string to rewriting set number
210069783Seric **
210169783Seric **	Parameters:
210269783Seric **		p -- the pointer to the string to decode.
210369783Seric **		endp -- if set, store the trailing delimiter here.
210469783Seric **		stabmode -- ST_ENTER to create this entry, ST_FIND if
210569783Seric **			it must already exist.
210669783Seric **
210769783Seric **	Returns:
210869783Seric **		The appropriate ruleset number.
210969783Seric **		-1 if it is not valid (error already printed)
211069783Seric */
211169783Seric 
211269783Seric int
211369783Seric strtorwset(p, endp, stabmode)
211469783Seric 	char *p;
211569783Seric 	char **endp;
211669783Seric 	int stabmode;
211769783Seric {
211869783Seric 	int ruleset;
211969783Seric 	static int nextruleset = MAXRWSETS;
212069783Seric 
212169783Seric 	while (isascii(*p) && isspace(*p))
212269783Seric 		p++;
212369783Seric 	if (!isascii(*p))
212469783Seric 	{
212569783Seric 		syserr("invalid ruleset name: \"%.20s\"", p);
212669783Seric 		return -1;
212769783Seric 	}
212869783Seric 	if (isdigit(*p))
212969783Seric 	{
213069783Seric 		ruleset = strtol(p, endp, 10);
213169783Seric 		if (ruleset >= MAXRWSETS / 2 || ruleset < 0)
213269783Seric 		{
213369783Seric 			syserr("bad ruleset %d (%d max)",
213469783Seric 				ruleset, MAXRWSETS / 2);
213569783Seric 			ruleset = -1;
213669783Seric 		}
213769783Seric 	}
213869783Seric 	else
213969783Seric 	{
214069783Seric 		STAB *s;
214169783Seric 		char delim;
214269783Seric 		char *q;
214369783Seric 
214469783Seric 		q = p;
214569783Seric 		while (*p != '\0' && isascii(*p) &&
214669783Seric 		       (isalnum(*p) || strchr("-_$", *p) != NULL))
214769783Seric 			p++;
214869783Seric 		while (isascii(*p) && isspace(*p))
214969783Seric 			*p++ = '\0';
215069783Seric 		delim = *p;
215169783Seric 		if (delim != '\0')
215269783Seric 			*p = '\0';
215369783Seric 		s = stab(q, ST_RULESET, stabmode);
215469783Seric 		if (delim != '\0')
215569783Seric 			*p = delim;
215669783Seric 
215769783Seric 		if (s == NULL)
215869783Seric 		{
215969783Seric 			syserr("unknown ruleset %s", q);
216069783Seric 			return -1;
216169783Seric 		}
216269783Seric 
216369783Seric 		if (stabmode == ST_ENTER && delim == '=')
216469783Seric 		{
216569783Seric 			ruleset = strtol(p, endp, 10);
216669783Seric 			if (ruleset >= MAXRWSETS / 2 || ruleset < 0)
216769783Seric 			{
216869783Seric 				syserr("bad ruleset %s = %d (%d max)",
216969783Seric 					q, ruleset, MAXRWSETS / 2);
217069783Seric 				ruleset = -1;
217169783Seric 			}
217269783Seric 		}
217369783Seric 		else
217469783Seric 		{
217569783Seric 			if (endp != NULL)
217669783Seric 				*endp = p;
217769783Seric 			if (s->s_ruleset > 0)
217869783Seric 				ruleset = s->s_ruleset;
217969783Seric 			else if ((ruleset = --nextruleset) < MAXRWSETS / 2)
218069783Seric 			{
218169783Seric 				syserr("%s: too many named rulesets (%d max)",
218269783Seric 					q, MAXRWSETS / 2);
218369783Seric 				ruleset = -1;
218469783Seric 			}
218569783Seric 		}
218669783Seric 		if (s->s_ruleset > 0 && ruleset >= 0 && ruleset != s->s_ruleset)
218769783Seric 		{
218869783Seric 			syserr("%s: ruleset changed value (old %d, new %d)",
218969783Seric 				q, ruleset, s->s_ruleset);
219069783Seric 			ruleset = s->s_ruleset;
219169783Seric 		}
219269783Seric 		else if (ruleset > 0)
219369783Seric 		{
219469783Seric 			s->s_ruleset = ruleset;
219569783Seric 		}
219669783Seric 	}
219769783Seric 	return ruleset;
219869783Seric }
219969783Seric /*
220068481Seric **  INITTIMEOUTS -- parse and set timeout values
220158112Seric **
220258112Seric **	Parameters:
220358112Seric **		val -- a pointer to the values.  If NULL, do initial
220458112Seric **			settings.
220558112Seric **
220658112Seric **	Returns:
220758112Seric **		none.
220858112Seric **
220958112Seric **	Side Effects:
221058112Seric **		Initializes the TimeOuts structure
221158112Seric */
221258112Seric 
221364255Seric #define SECONDS
221458112Seric #define MINUTES	* 60
221558112Seric #define HOUR	* 3600
221658112Seric 
221769748Seric void
221868481Seric inittimeouts(val)
221958112Seric 	register char *val;
222058112Seric {
222158112Seric 	register char *p;
222258671Seric 	extern time_t convtime();
222358112Seric 
222458112Seric 	if (val == NULL)
222558112Seric 	{
222658112Seric 		TimeOuts.to_initial = (time_t) 5 MINUTES;
222758112Seric 		TimeOuts.to_helo = (time_t) 5 MINUTES;
222858112Seric 		TimeOuts.to_mail = (time_t) 10 MINUTES;
222958112Seric 		TimeOuts.to_rcpt = (time_t) 1 HOUR;
223058112Seric 		TimeOuts.to_datainit = (time_t) 5 MINUTES;
223158112Seric 		TimeOuts.to_datablock = (time_t) 1 HOUR;
223258112Seric 		TimeOuts.to_datafinal = (time_t) 1 HOUR;
223358112Seric 		TimeOuts.to_rset = (time_t) 5 MINUTES;
223458112Seric 		TimeOuts.to_quit = (time_t) 2 MINUTES;
223558112Seric 		TimeOuts.to_nextcommand = (time_t) 1 HOUR;
223658112Seric 		TimeOuts.to_miscshort = (time_t) 2 MINUTES;
223768481Seric #if IDENTPROTO
223864255Seric 		TimeOuts.to_ident = (time_t) 30 SECONDS;
223968481Seric #else
224068481Seric 		TimeOuts.to_ident = (time_t) 0 SECONDS;
224168481Seric #endif
224268481Seric 		TimeOuts.to_fileopen = (time_t) 60 SECONDS;
224358112Seric 		return;
224458112Seric 	}
224558112Seric 
224658112Seric 	for (;; val = p)
224758112Seric 	{
224858112Seric 		while (isascii(*val) && isspace(*val))
224958112Seric 			val++;
225058112Seric 		if (*val == '\0')
225158112Seric 			break;
225258112Seric 		for (p = val; *p != '\0' && *p != ','; p++)
225358112Seric 			continue;
225458112Seric 		if (*p != '\0')
225558112Seric 			*p++ = '\0';
225658112Seric 
225758112Seric 		if (isascii(*val) && isdigit(*val))
225858112Seric 		{
225958112Seric 			/* old syntax -- set everything */
226058796Seric 			TimeOuts.to_mail = convtime(val, 'm');
226158112Seric 			TimeOuts.to_rcpt = TimeOuts.to_mail;
226258112Seric 			TimeOuts.to_datainit = TimeOuts.to_mail;
226358112Seric 			TimeOuts.to_datablock = TimeOuts.to_mail;
226458112Seric 			TimeOuts.to_datafinal = TimeOuts.to_mail;
226558112Seric 			TimeOuts.to_nextcommand = TimeOuts.to_mail;
226658112Seric 			continue;
226758112Seric 		}
226858112Seric 		else
226958112Seric 		{
227068481Seric 			register char *q = strchr(val, ':');
227158112Seric 
227268481Seric 			if (q == NULL && (q = strchr(val, '=')) == NULL)
227358112Seric 			{
227458112Seric 				/* syntax error */
227558112Seric 				continue;
227658112Seric 			}
227758112Seric 			*q++ = '\0';
227868481Seric 			settimeout(val, q);
227968481Seric 		}
228068481Seric 	}
228168481Seric }
228268481Seric /*
228368481Seric **  SETTIMEOUT -- set an individual timeout
228468481Seric **
228568481Seric **	Parameters:
228668481Seric **		name -- the name of the timeout.
228768481Seric **		val -- the value of the timeout.
228868481Seric **
228968481Seric **	Returns:
229068481Seric **		none.
229168481Seric */
229258112Seric 
229369748Seric void
229468481Seric settimeout(name, val)
229568481Seric 	char *name;
229668481Seric 	char *val;
229768481Seric {
229868481Seric 	register char *p;
229968481Seric 	time_t to;
230068481Seric 	extern time_t convtime();
230168481Seric 
230268481Seric 	to = convtime(val, 'm');
230368481Seric 	p = strchr(name, '.');
230468481Seric 	if (p != NULL)
230568481Seric 		*p++ = '\0';
230668481Seric 
230768481Seric 	if (strcasecmp(name, "initial") == 0)
230868481Seric 		TimeOuts.to_initial = to;
230968481Seric 	else if (strcasecmp(name, "mail") == 0)
231068481Seric 		TimeOuts.to_mail = to;
231168481Seric 	else if (strcasecmp(name, "rcpt") == 0)
231268481Seric 		TimeOuts.to_rcpt = to;
231368481Seric 	else if (strcasecmp(name, "datainit") == 0)
231468481Seric 		TimeOuts.to_datainit = to;
231568481Seric 	else if (strcasecmp(name, "datablock") == 0)
231668481Seric 		TimeOuts.to_datablock = to;
231768481Seric 	else if (strcasecmp(name, "datafinal") == 0)
231868481Seric 		TimeOuts.to_datafinal = to;
231968481Seric 	else if (strcasecmp(name, "command") == 0)
232068481Seric 		TimeOuts.to_nextcommand = to;
232168481Seric 	else if (strcasecmp(name, "rset") == 0)
232268481Seric 		TimeOuts.to_rset = to;
232368481Seric 	else if (strcasecmp(name, "helo") == 0)
232468481Seric 		TimeOuts.to_helo = to;
232568481Seric 	else if (strcasecmp(name, "quit") == 0)
232668481Seric 		TimeOuts.to_quit = to;
232768481Seric 	else if (strcasecmp(name, "misc") == 0)
232868481Seric 		TimeOuts.to_miscshort = to;
232968481Seric 	else if (strcasecmp(name, "ident") == 0)
233068481Seric 		TimeOuts.to_ident = to;
233168481Seric 	else if (strcasecmp(name, "fileopen") == 0)
233268481Seric 		TimeOuts.to_fileopen = to;
233368481Seric 	else if (strcasecmp(name, "queuewarn") == 0)
233468481Seric 	{
233568481Seric 		to = convtime(val, 'h');
233668481Seric 		if (p == NULL || strcmp(p, "*") == 0)
233768481Seric 		{
233868481Seric 			TimeOuts.to_q_warning[TOC_NORMAL] = to;
233968481Seric 			TimeOuts.to_q_warning[TOC_URGENT] = to;
234068481Seric 			TimeOuts.to_q_warning[TOC_NONURGENT] = to;
234158112Seric 		}
234268481Seric 		else if (strcasecmp(p, "normal") == 0)
234368481Seric 			TimeOuts.to_q_warning[TOC_NORMAL] = to;
234468481Seric 		else if (strcasecmp(p, "urgent") == 0)
234568481Seric 			TimeOuts.to_q_warning[TOC_URGENT] = to;
234668481Seric 		else if (strcasecmp(p, "non-urgent") == 0)
234768481Seric 			TimeOuts.to_q_warning[TOC_NONURGENT] = to;
234868481Seric 		else
234968481Seric 			syserr("settimeout: invalid queuewarn subtimeout %s", p);
235058112Seric 	}
235168481Seric 	else if (strcasecmp(name, "queuereturn") == 0)
235268481Seric 	{
235368481Seric 		to = convtime(val, 'd');
235468481Seric 		if (p == NULL || strcmp(p, "*") == 0)
235568481Seric 		{
235668481Seric 			TimeOuts.to_q_return[TOC_NORMAL] = to;
235768481Seric 			TimeOuts.to_q_return[TOC_URGENT] = to;
235868481Seric 			TimeOuts.to_q_return[TOC_NONURGENT] = to;
235968481Seric 		}
236068481Seric 		else if (strcasecmp(p, "normal") == 0)
236168481Seric 			TimeOuts.to_q_return[TOC_NORMAL] = to;
236268481Seric 		else if (strcasecmp(p, "urgent") == 0)
236368481Seric 			TimeOuts.to_q_return[TOC_URGENT] = to;
236468481Seric 		else if (strcasecmp(p, "non-urgent") == 0)
236568481Seric 			TimeOuts.to_q_return[TOC_NONURGENT] = to;
236668481Seric 		else
236768481Seric 			syserr("settimeout: invalid queuereturn subtimeout %s", p);
236868481Seric 	}
236968481Seric 	else
237068481Seric 		syserr("settimeout: invalid timeout %s", name);
237158112Seric }
2372