xref: /csrg-svn/usr.sbin/sendmail/src/readcf.c (revision 69852)
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*69852Seric static char sccsid[] = "@(#)readcf.c	8.101 (Berkeley) 06/10/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);
37369789Seric 			rwp = RewriteRules[ruleset];
37469789Seric 			if (rwp != NULL)
37569789Seric 			{
37669789Seric 				while (rwp->r_next != NULL)
37769789Seric 					rwp = rwp->r_next;
37869789Seric 				fprintf(stderr, "WARNING: Ruleset %s redefined\n",
37969789Seric 					&bp[1]);
38069789Seric 			}
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,
132469838Seric #define O_MAXCHILDREN	0x8d
132569838Seric /*
132669838Seric 	"MaxDaemonChildren",	O_MAXCHILDREN,	FALSE,
132769838Seric */
1328*69852Seric #define O_KEEPCNAMES	0x8e
1329*69852Seric 	"DontExpandCnames",	O_KEEPCNAMES,	FALSE,
133068481Seric 
133168481Seric 	NULL,			'\0',		FALSE,
133268481Seric };
133368481Seric 
133468481Seric 
133568481Seric 
133669748Seric void
133758734Seric setoption(opt, val, safe, sticky, e)
133869748Seric 	int opt;
13398256Seric 	char *val;
134021755Seric 	bool safe;
13418269Seric 	bool sticky;
134258734Seric 	register ENVELOPE *e;
13438256Seric {
134457207Seric 	register char *p;
134568481Seric 	register struct optioninfo *o;
134668481Seric 	char *subopt;
13478265Seric 	extern bool atobool();
134812633Seric 	extern time_t convtime();
134914879Seric 	extern int QueueLA;
135014879Seric 	extern int RefuseLA;
135164718Seric 	extern bool Warn_Q_option;
13528256Seric 
135368481Seric 	errno = 0;
135468481Seric 	if (opt == ' ')
135568481Seric 	{
135668481Seric 		/* full word options */
135768481Seric 		struct optioninfo *sel;
135868481Seric 
135968481Seric 		p = strchr(val, '=');
136068481Seric 		if (p == NULL)
136168481Seric 			p = &val[strlen(val)];
136268481Seric 		while (*--p == ' ')
136368481Seric 			continue;
136468481Seric 		while (*++p == ' ')
136568481Seric 			*p = '\0';
136668481Seric 		if (p == val)
136768481Seric 		{
136868481Seric 			syserr("readcf: null option name");
136968481Seric 			return;
137068481Seric 		}
137168481Seric 		if (*p == '=')
137268481Seric 			*p++ = '\0';
137368481Seric 		while (*p == ' ')
137468481Seric 			p++;
137568481Seric 		subopt = strchr(val, '.');
137668481Seric 		if (subopt != NULL)
137768481Seric 			*subopt++ = '\0';
137868481Seric 		sel = NULL;
137968481Seric 		for (o = OptionTab; o->o_name != NULL; o++)
138068481Seric 		{
138168481Seric 			if (strncasecmp(o->o_name, val, strlen(val)) != 0)
138268481Seric 				continue;
138368481Seric 			if (strlen(o->o_name) == strlen(val))
138468481Seric 			{
138568481Seric 				/* completely specified -- this must be it */
138668481Seric 				sel = NULL;
138768481Seric 				break;
138868481Seric 			}
138968481Seric 			if (sel != NULL)
139068481Seric 				break;
139168481Seric 			sel = o;
139268481Seric 		}
139368481Seric 		if (sel != NULL && o->o_name == NULL)
139468481Seric 			o = sel;
139568481Seric 		else if (o->o_name == NULL)
139668481Seric 		{
139768481Seric 			syserr("readcf: unknown option name %s", val);
139868481Seric 			return;
139968481Seric 		}
140068481Seric 		else if (sel != NULL)
140168481Seric 		{
140268481Seric 			syserr("readcf: ambiguous option name %s (matches %s and %s)",
140368481Seric 				val, sel->o_name, o->o_name);
140468481Seric 			return;
140568481Seric 		}
140668481Seric 		if (strlen(val) != strlen(o->o_name))
140768481Seric 		{
140868481Seric 			bool oldVerbose = Verbose;
140968481Seric 
141068481Seric 			Verbose = TRUE;
141168481Seric 			message("Option %s used as abbreviation for %s",
141268481Seric 				val, o->o_name);
141368481Seric 			Verbose = oldVerbose;
141468481Seric 		}
141568481Seric 		opt = o->o_code;
141668481Seric 		val = p;
141768481Seric 	}
141868481Seric 	else
141968481Seric 	{
142068481Seric 		for (o = OptionTab; o->o_name != NULL; o++)
142168481Seric 		{
142268481Seric 			if (o->o_code == opt)
142368481Seric 				break;
142468481Seric 		}
142568481Seric 		subopt = NULL;
142668481Seric 	}
142768481Seric 
14288256Seric 	if (tTd(37, 1))
142968481Seric 	{
143068481Seric 		printf(isascii(opt) && isprint(opt) ?
143168481Seric 			    "setoption %s (%c).%s=%s" :
143268481Seric 			    "setoption %s (0x%x).%s=%s",
143368481Seric 			o->o_name == NULL ? "<unknown>" : o->o_name,
143468481Seric 			opt,
143568481Seric 			subopt == NULL ? "" : subopt,
143668481Seric 			val);
143768481Seric 	}
14388256Seric 
14398256Seric 	/*
14408269Seric 	**  See if this option is preset for us.
14418256Seric 	*/
14428256Seric 
144359731Seric 	if (!sticky && bitnset(opt, StickyOpt))
14448269Seric 	{
14459341Seric 		if (tTd(37, 1))
14469341Seric 			printf(" (ignored)\n");
14478269Seric 		return;
14488269Seric 	}
14498269Seric 
145021755Seric 	/*
145121755Seric 	**  Check to see if this option can be specified by this user.
145221755Seric 	*/
145321755Seric 
145463787Seric 	if (!safe && RealUid == 0)
145521755Seric 		safe = TRUE;
145668481Seric 	if (!safe && !o->o_safe)
145721755Seric 	{
145839111Srick 		if (opt != 'M' || (val[0] != 'r' && val[0] != 's'))
145921755Seric 		{
146036582Sbostic 			if (tTd(37, 1))
146136582Sbostic 				printf(" (unsafe)");
146263787Seric 			if (RealUid != geteuid())
146336582Sbostic 			{
146451210Seric 				if (tTd(37, 1))
146551210Seric 					printf("(Resetting uid)");
146663787Seric 				(void) setgid(RealGid);
146763787Seric 				(void) setuid(RealUid);
146836582Sbostic 			}
146921755Seric 		}
147021755Seric 	}
147151210Seric 	if (tTd(37, 1))
147217985Seric 		printf("\n");
14738269Seric 
147468481Seric 	switch (opt & 0xff)
14758256Seric 	{
147659709Seric 	  case '7':		/* force seven-bit input */
147768481Seric 		SevenBitInput = atobool(val);
147852106Seric 		break;
147952106Seric 
148069480Seric #if MIME8TO7
148168481Seric 	  case '8':		/* handling of 8-bit input */
148268481Seric 		switch (*val)
148368481Seric 		{
148468481Seric 		  case 'm':		/* convert 8-bit, convert MIME */
148568481Seric 			MimeMode = MM_CVTMIME|MM_MIME8BIT;
148668481Seric 			break;
148768481Seric 
148868481Seric 		  case 'p':		/* pass 8 bit, convert MIME */
148968856Seric 			MimeMode = MM_CVTMIME|MM_PASS8BIT;
149068481Seric 			break;
149168481Seric 
149268481Seric 		  case 's':		/* strict adherence */
149368481Seric 			MimeMode = MM_CVTMIME;
149468481Seric 			break;
149568481Seric 
149668856Seric #if 0
149768856Seric 		  case 'r':		/* reject 8-bit, don't convert MIME */
149868856Seric 			MimeMode = 0;
149968856Seric 			break;
150068856Seric 
150168856Seric 		  case 'j':		/* "just send 8" */
150268856Seric 			MimeMode = MM_PASS8BIT;
150368856Seric 			break;
150468856Seric 
150568481Seric 		  case 'a':		/* encode 8 bit if available */
150668481Seric 			MimeMode = MM_MIME8BIT|MM_PASS8BIT|MM_CVTMIME;
150768481Seric 			break;
150868481Seric 
150968481Seric 		  case 'c':		/* convert 8 bit to MIME, never 7 bit */
151068481Seric 			MimeMode = MM_MIME8BIT;
151168481Seric 			break;
151268856Seric #endif
151368481Seric 
151468481Seric 		  default:
151568481Seric 			syserr("Unknown 8-bit mode %c", *val);
151668481Seric 			exit(EX_USAGE);
151768481Seric 		}
151868481Seric 		break;
151969480Seric #endif
152068481Seric 
15218256Seric 	  case 'A':		/* set default alias file */
15229381Seric 		if (val[0] == '\0')
152359672Seric 			setalias("aliases");
15249381Seric 		else
152559672Seric 			setalias(val);
15268256Seric 		break;
15278256Seric 
152817474Seric 	  case 'a':		/* look N minutes for "@:@" in alias file */
152917474Seric 		if (val[0] == '\0')
153064796Seric 			SafeAlias = 5 * 60;		/* five minutes */
153117474Seric 		else
153264796Seric 			SafeAlias = convtime(val, 'm');
153317474Seric 		break;
153417474Seric 
153516843Seric 	  case 'B':		/* substitution for blank character */
153616843Seric 		SpaceSub = val[0];
153716843Seric 		if (SpaceSub == '\0')
153816843Seric 			SpaceSub = ' ';
153916843Seric 		break;
154016843Seric 
154159283Seric 	  case 'b':		/* min blocks free on queue fs/max msg size */
154259283Seric 		p = strchr(val, '/');
154359283Seric 		if (p != NULL)
154459283Seric 		{
154559283Seric 			*p++ = '\0';
154659283Seric 			MaxMessageSize = atol(p);
154759283Seric 		}
154858082Seric 		MinBlocksFree = atol(val);
154958082Seric 		break;
155058082Seric 
15519284Seric 	  case 'c':		/* don't connect to "expensive" mailers */
15529381Seric 		NoConnect = atobool(val);
15539284Seric 		break;
15549284Seric 
155551305Seric 	  case 'C':		/* checkpoint every N addresses */
155651305Seric 		CheckpointInterval = atoi(val);
155724944Seric 		break;
155824944Seric 
15599284Seric 	  case 'd':		/* delivery mode */
15609284Seric 		switch (*val)
15618269Seric 		{
15629284Seric 		  case '\0':
156358734Seric 			e->e_sendmode = SM_DELIVER;
15648269Seric 			break;
15658269Seric 
156610755Seric 		  case SM_QUEUE:	/* queue only */
156710755Seric #ifndef QUEUE
156810755Seric 			syserr("need QUEUE to set -odqueue");
156956795Seric #endif /* QUEUE */
157010755Seric 			/* fall through..... */
157110755Seric 
15729284Seric 		  case SM_DELIVER:	/* do everything */
15739284Seric 		  case SM_FORK:		/* fork after verification */
157458734Seric 			e->e_sendmode = *val;
15758269Seric 			break;
15768269Seric 
15778269Seric 		  default:
15789284Seric 			syserr("Unknown delivery mode %c", *val);
15798269Seric 			exit(EX_USAGE);
15808269Seric 		}
15818269Seric 		break;
15828269Seric 
15839146Seric 	  case 'D':		/* rebuild alias database as needed */
15849381Seric 		AutoRebuild = atobool(val);
15859146Seric 		break;
15869146Seric 
158755372Seric 	  case 'E':		/* error message header/header file */
158855379Seric 		if (*val != '\0')
158955379Seric 			ErrMsgFile = newstr(val);
159055372Seric 		break;
159155372Seric 
15928269Seric 	  case 'e':		/* set error processing mode */
15938269Seric 		switch (*val)
15948269Seric 		{
15959381Seric 		  case EM_QUIET:	/* be silent about it */
15969381Seric 		  case EM_MAIL:		/* mail back */
15979381Seric 		  case EM_BERKNET:	/* do berknet error processing */
15989381Seric 		  case EM_WRITE:	/* write back (or mail) */
15999381Seric 		  case EM_PRINT:	/* print errors normally (default) */
160058734Seric 			e->e_errormode = *val;
16018269Seric 			break;
16028269Seric 		}
16038269Seric 		break;
16048269Seric 
16059049Seric 	  case 'F':		/* file mode */
160617975Seric 		FileMode = atooct(val) & 0777;
16079049Seric 		break;
16089049Seric 
16098269Seric 	  case 'f':		/* save Unix-style From lines on front */
16109381Seric 		SaveFrom = atobool(val);
16118269Seric 		break;
16128269Seric 
161353735Seric 	  case 'G':		/* match recipients against GECOS field */
161453735Seric 		MatchGecos = atobool(val);
161553735Seric 		break;
161653735Seric 
16178256Seric 	  case 'g':		/* default gid */
161868481Seric   g_opt:
161964133Seric 		if (isascii(*val) && isdigit(*val))
162064133Seric 			DefGid = atoi(val);
162164133Seric 		else
162264133Seric 		{
162364133Seric 			register struct group *gr;
162464133Seric 
162564133Seric 			DefGid = -1;
162664133Seric 			gr = getgrnam(val);
162764133Seric 			if (gr == NULL)
162868481Seric 				syserr("readcf: option %c: unknown group %s",
162968481Seric 					opt, val);
163064133Seric 			else
163164133Seric 				DefGid = gr->gr_gid;
163264133Seric 		}
16338256Seric 		break;
16348256Seric 
16358256Seric 	  case 'H':		/* help file */
16369381Seric 		if (val[0] == '\0')
16378269Seric 			HelpFile = "sendmail.hf";
16389381Seric 		else
16399381Seric 			HelpFile = newstr(val);
16408256Seric 		break;
16418256Seric 
164251305Seric 	  case 'h':		/* maximum hop count */
164351305Seric 		MaxHopCount = atoi(val);
164451305Seric 		break;
164551305Seric 
164635651Seric 	  case 'I':		/* use internet domain name server */
164766334Seric #if NAMED_BIND
164857207Seric 		for (p = val; *p != 0; )
164957207Seric 		{
165057207Seric 			bool clearmode;
165157207Seric 			char *q;
165257207Seric 			struct resolverflags *rfp;
165357207Seric 
165457207Seric 			while (*p == ' ')
165557207Seric 				p++;
165657207Seric 			if (*p == '\0')
165757207Seric 				break;
165857207Seric 			clearmode = FALSE;
165957207Seric 			if (*p == '-')
166057207Seric 				clearmode = TRUE;
166157207Seric 			else if (*p != '+')
166257207Seric 				p--;
166357207Seric 			p++;
166457207Seric 			q = p;
166558050Seric 			while (*p != '\0' && !(isascii(*p) && isspace(*p)))
166657207Seric 				p++;
166757207Seric 			if (*p != '\0')
166857207Seric 				*p++ = '\0';
166968759Seric 			if (strcasecmp(q, "HasWildcardMX") == 0)
167068759Seric 			{
167168759Seric 				NoMXforCanon = !clearmode;
167268759Seric 				continue;
167368759Seric 			}
167457207Seric 			for (rfp = ResolverFlags; rfp->rf_name != NULL; rfp++)
167557207Seric 			{
167657207Seric 				if (strcasecmp(q, rfp->rf_name) == 0)
167757207Seric 					break;
167857207Seric 			}
167964923Seric 			if (rfp->rf_name == NULL)
168064923Seric 				syserr("readcf: I option value %s unrecognized", q);
168164923Seric 			else if (clearmode)
168257207Seric 				_res.options &= ~rfp->rf_bits;
168357207Seric 			else
168457207Seric 				_res.options |= rfp->rf_bits;
168557207Seric 		}
168657207Seric 		if (tTd(8, 2))
168768759Seric 			printf("_res.options = %x, HasWildcardMX = %d\n",
168868759Seric 				_res.options, !NoMXforCanon);
168957207Seric #else
169057207Seric 		usrerr("name server (I option) specified but BIND not compiled in");
169157207Seric #endif
169235651Seric 		break;
169335651Seric 
16948269Seric 	  case 'i':		/* ignore dot lines in message */
16959381Seric 		IgnrDot = atobool(val);
16968269Seric 		break;
16978269Seric 
169859730Seric 	  case 'j':		/* send errors in MIME (RFC 1341) format */
169959730Seric 		SendMIMEErrors = atobool(val);
170059730Seric 		break;
170159730Seric 
170257136Seric 	  case 'J':		/* .forward search path */
170357136Seric 		ForwardPath = newstr(val);
170457136Seric 		break;
170557136Seric 
170654967Seric 	  case 'k':		/* connection cache size */
170754967Seric 		MaxMciCache = atoi(val);
170856215Seric 		if (MaxMciCache < 0)
170956215Seric 			MaxMciCache = 0;
171054967Seric 		break;
171154967Seric 
171254967Seric 	  case 'K':		/* connection cache timeout */
171358796Seric 		MciCacheTimeout = convtime(val, 'm');
171454967Seric 		break;
171554967Seric 
171661104Seric 	  case 'l':		/* use Errors-To: header */
171761104Seric 		UseErrorsTo = atobool(val);
171861104Seric 		break;
171961104Seric 
17208256Seric 	  case 'L':		/* log level */
172164140Seric 		if (safe || LogLevel < atoi(val))
172264140Seric 			LogLevel = atoi(val);
17238256Seric 		break;
17248256Seric 
17258269Seric 	  case 'M':		/* define macro */
172668267Seric 		p = newstr(&val[1]);
172768267Seric 		if (!safe)
172868267Seric 			cleanstrcpy(p, p, MAXNAME);
172968267Seric 		define(val[0], p, CurEnv);
173016878Seric 		sticky = FALSE;
17318269Seric 		break;
17328269Seric 
17338269Seric 	  case 'm':		/* send to me too */
17349381Seric 		MeToo = atobool(val);
17358269Seric 		break;
17368269Seric 
173725820Seric 	  case 'n':		/* validate RHS in newaliases */
173825820Seric 		CheckAliases = atobool(val);
173925820Seric 		break;
174025820Seric 
174161104Seric 	    /* 'N' available -- was "net name" */
174261104Seric 
174358851Seric 	  case 'O':		/* daemon options */
174458851Seric 		setdaemonoptions(val);
174558851Seric 		break;
174658851Seric 
17478269Seric 	  case 'o':		/* assume old style headers */
17489381Seric 		if (atobool(val))
17499341Seric 			CurEnv->e_flags |= EF_OLDSTYLE;
17509341Seric 		else
17519341Seric 			CurEnv->e_flags &= ~EF_OLDSTYLE;
17528269Seric 		break;
17538269Seric 
175458082Seric 	  case 'p':		/* select privacy level */
175558082Seric 		p = val;
175658082Seric 		for (;;)
175758082Seric 		{
175858082Seric 			register struct prival *pv;
175958082Seric 			extern struct prival PrivacyValues[];
176058082Seric 
176158082Seric 			while (isascii(*p) && (isspace(*p) || ispunct(*p)))
176258082Seric 				p++;
176358082Seric 			if (*p == '\0')
176458082Seric 				break;
176558082Seric 			val = p;
176658082Seric 			while (isascii(*p) && isalnum(*p))
176758082Seric 				p++;
176858082Seric 			if (*p != '\0')
176958082Seric 				*p++ = '\0';
177058082Seric 
177158082Seric 			for (pv = PrivacyValues; pv->pv_name != NULL; pv++)
177258082Seric 			{
177358082Seric 				if (strcasecmp(val, pv->pv_name) == 0)
177458082Seric 					break;
177558082Seric 			}
177658886Seric 			if (pv->pv_name == NULL)
177758886Seric 				syserr("readcf: Op line: %s unrecognized", val);
177858082Seric 			PrivacyFlags |= pv->pv_flag;
177958082Seric 		}
178068479Seric 		sticky = FALSE;
178158082Seric 		break;
178258082Seric 
178324944Seric 	  case 'P':		/* postmaster copy address for returned mail */
178424944Seric 		PostMasterCopy = newstr(val);
178524944Seric 		break;
178624944Seric 
178724944Seric 	  case 'q':		/* slope of queue only function */
178824944Seric 		QueueFactor = atoi(val);
178924944Seric 		break;
179024944Seric 
17918256Seric 	  case 'Q':		/* queue directory */
17929381Seric 		if (val[0] == '\0')
17938269Seric 			QueueDir = "mqueue";
17949381Seric 		else
17959381Seric 			QueueDir = newstr(val);
179658789Seric 		if (RealUid != 0 && !safe)
179764718Seric 			Warn_Q_option = TRUE;
17988256Seric 		break;
17998256Seric 
180058148Seric 	  case 'R':		/* don't prune routes */
180158148Seric 		DontPruneRoutes = atobool(val);
180258148Seric 		break;
180358148Seric 
18048256Seric 	  case 'r':		/* read timeout */
180568481Seric 		if (subopt == NULL)
180668481Seric 			inittimeouts(val);
180768481Seric 		else
180868481Seric 			settimeout(subopt, val);
18098256Seric 		break;
18108256Seric 
18118256Seric 	  case 'S':		/* status file */
18129381Seric 		if (val[0] == '\0')
18138269Seric 			StatFile = "sendmail.st";
18149381Seric 		else
18159381Seric 			StatFile = newstr(val);
18168256Seric 		break;
18178256Seric 
18188265Seric 	  case 's':		/* be super safe, even if expensive */
18199381Seric 		SuperSafe = atobool(val);
18208256Seric 		break;
18218256Seric 
18228256Seric 	  case 'T':		/* queue timeout */
182358737Seric 		p = strchr(val, '/');
182458737Seric 		if (p != NULL)
182558737Seric 		{
182658737Seric 			*p++ = '\0';
182768481Seric 			settimeout("queuewarn", p);
182858737Seric 		}
182968481Seric 		settimeout("queuereturn", val);
183054967Seric 		break;
18318256Seric 
18328265Seric 	  case 't':		/* time zone name */
183352106Seric 		TimeZoneSpec = newstr(val);
18348265Seric 		break;
18358265Seric 
183650556Seric 	  case 'U':		/* location of user database */
183751360Seric 		UdbSpec = newstr(val);
183850556Seric 		break;
183950556Seric 
18408256Seric 	  case 'u':		/* set default uid */
184168481Seric 		for (p = val; *p != '\0'; p++)
184268481Seric 		{
184368481Seric 			if (*p == '.' || *p == '/' || *p == ':')
184468481Seric 			{
184568481Seric 				*p++ = '\0';
184668481Seric 				break;
184768481Seric 			}
184868481Seric 		}
184964133Seric 		if (isascii(*val) && isdigit(*val))
185064133Seric 			DefUid = atoi(val);
185164133Seric 		else
185264133Seric 		{
185364133Seric 			register struct passwd *pw;
185464133Seric 
185564133Seric 			DefUid = -1;
185668693Seric 			pw = sm_getpwnam(val);
185764133Seric 			if (pw == NULL)
185864133Seric 				syserr("readcf: option u: unknown user %s", val);
185964133Seric 			else
186068481Seric 			{
186164133Seric 				DefUid = pw->pw_uid;
186268481Seric 				DefGid = pw->pw_gid;
186368481Seric 			}
186464133Seric 		}
186540973Sbostic 		setdefuser();
18668256Seric 
186768481Seric 		/* handle the group if it is there */
186868481Seric 		if (*p == '\0')
186968481Seric 			break;
187068481Seric 		val = p;
187168481Seric 		goto g_opt;
187268481Seric 
187358851Seric 	  case 'V':		/* fallback MX host */
187458851Seric 		FallBackMX = newstr(val);
187558851Seric 		break;
187658851Seric 
18778269Seric 	  case 'v':		/* run in verbose mode */
18789381Seric 		Verbose = atobool(val);
18798256Seric 		break;
18808256Seric 
188163837Seric 	  case 'w':		/* if we are best MX, try host directly */
188263837Seric 		TryNullMXList = atobool(val);
188363837Seric 		break;
188461104Seric 
188561104Seric 	    /* 'W' available -- was wizard password */
188661104Seric 
188714879Seric 	  case 'x':		/* load avg at which to auto-queue msgs */
188814879Seric 		QueueLA = atoi(val);
188914879Seric 		break;
189014879Seric 
189114879Seric 	  case 'X':		/* load avg at which to auto-reject connections */
189214879Seric 		RefuseLA = atoi(val);
189314879Seric 		break;
189414879Seric 
189524981Seric 	  case 'y':		/* work recipient factor */
189624981Seric 		WkRecipFact = atoi(val);
189724981Seric 		break;
189824981Seric 
189924981Seric 	  case 'Y':		/* fork jobs during queue runs */
190024952Seric 		ForkQueueRuns = atobool(val);
190124952Seric 		break;
190224952Seric 
190324981Seric 	  case 'z':		/* work message class factor */
190424981Seric 		WkClassFact = atoi(val);
190524981Seric 		break;
190624981Seric 
190724981Seric 	  case 'Z':		/* work time factor */
190824981Seric 		WkTimeFact = atoi(val);
190924981Seric 		break;
191024981Seric 
191168481Seric 	  case O_QUEUESORTORD:	/* queue sorting order */
191268481Seric 		switch (*val)
191368481Seric 		{
191468481Seric 		  case 'h':	/* Host first */
191568481Seric 		  case 'H':
191668481Seric 			QueueSortOrder = QS_BYHOST;
191768481Seric 			break;
191868481Seric 
191968481Seric 		  case 'p':	/* Priority order */
192068481Seric 		  case 'P':
192168481Seric 			QueueSortOrder = QS_BYPRIORITY;
192268481Seric 			break;
192368481Seric 
192468481Seric 		  default:
192568481Seric 			syserr("Invalid queue sort order \"%s\"", val);
192668481Seric 		}
192768481Seric 		break;
192868481Seric 
192969401Seric 	  case O_HOSTSFILE:	/* pathname of /etc/hosts file */
193069401Seric 		HostsFile = newstr(val);
193169401Seric 		break;
193269401Seric 
193368481Seric 	  case O_MQA:		/* minimum queue age between deliveries */
193468481Seric 		MinQueueAge = convtime(val, 'm');
193568481Seric 		break;
193668481Seric 
193768481Seric 	  case O_MHSA:		/* maximum age of cached host status */
193868481Seric 		MaxHostStatAge = convtime(val, 'm');
193968481Seric 		break;
194068481Seric 
194168481Seric 	  case O_DEFCHARSET:	/* default character set for mimefying */
194268481Seric 		DefaultCharSet = newstr(denlstring(val, TRUE, TRUE));
194368481Seric 		break;
194468481Seric 
194568481Seric 	  case O_SSFILE:	/* service switch file */
194668481Seric 		ServiceSwitchFile = newstr(val);
194768481Seric 		break;
194868481Seric 
194968481Seric 	  case O_DIALDELAY:	/* delay for dial-on-demand operation */
195068481Seric 		DialDelay = convtime(val, 's');
195168481Seric 		break;
195268481Seric 
195368481Seric 	  case O_NORCPTACTION:	/* what to do if no recipient */
195468481Seric 		if (strcasecmp(val, "none") == 0)
195568481Seric 			NoRecipientAction = NRA_NO_ACTION;
195668481Seric 		else if (strcasecmp(val, "add-to") == 0)
195768481Seric 			NoRecipientAction = NRA_ADD_TO;
195868481Seric 		else if (strcasecmp(val, "add-apparently-to") == 0)
195968481Seric 			NoRecipientAction = NRA_ADD_APPARENTLY_TO;
196068481Seric 		else if (strcasecmp(val, "add-bcc") == 0)
196168481Seric 			NoRecipientAction = NRA_ADD_BCC;
196268481Seric 		else if (strcasecmp(val, "add-to-undisclosed") == 0)
196368481Seric 			NoRecipientAction = NRA_ADD_TO_UNDISCLOSED;
196468481Seric 		else
196568481Seric 			syserr("Invalid NoRecipientAction: %s", val);
196668481Seric 
196768490Seric 	  case O_SAFEFILEENV:	/* chroot() environ for writing to files */
196868490Seric 		SafeFileEnv = newstr(val);
196968490Seric 		break;
197068490Seric 
197168569Seric 	  case O_MAXMSGSIZE:	/* maximum message size */
197269748Seric 		MaxMessageSize = atol(val);
197368569Seric 		break;
197468569Seric 
197568756Seric 	  case O_COLONOKINADDR:	/* old style handling of colon addresses */
197669748Seric 		ColonOkInAddr = atobool(val);
197768756Seric 		break;
197868756Seric 
197969724Seric 	  case O_MAXQUEUERUN:	/* max # of jobs in a single queue run */
198069748Seric 		MaxQueueRun = atol(val);
198169724Seric 		break;
198269724Seric 
198369838Seric 	  case O_MAXCHILDREN:	/* max # of children of daemon */
198469838Seric 		MaxChildren = atoi(val);
1985*69852Seric 		break;
198669838Seric 
1987*69852Seric 	  case O_KEEPCNAMES:	/* don't expand CNAME records */
1988*69852Seric 		DontExpandCnames = atobool(val);
1989*69852Seric 		break;
1990*69852Seric 
19918256Seric 	  default:
199268481Seric 		if (tTd(37, 1))
199368481Seric 		{
199468481Seric 			if (isascii(opt) && isprint(opt))
199568481Seric 				printf("Warning: option %c unknown\n", opt);
199668481Seric 			else
199768481Seric 				printf("Warning: option 0x%x unknown\n", opt);
199868481Seric 		}
19998256Seric 		break;
20008256Seric 	}
200116878Seric 	if (sticky)
200216878Seric 		setbitn(opt, StickyOpt);
20038256Seric }
200410687Seric /*
200568481Seric **  SETCLASS -- set a string into a class
200610687Seric **
200710687Seric **	Parameters:
200868481Seric **		class -- the class to put the string in.
200968481Seric **		str -- the string to enter
201010687Seric **
201110687Seric **	Returns:
201210687Seric **		none.
201310687Seric **
201410687Seric **	Side Effects:
201510687Seric **		puts the word into the symbol table.
201610687Seric */
201710687Seric 
201869748Seric void
201968481Seric setclass(class, str)
202010687Seric 	int class;
202168481Seric 	char *str;
202210687Seric {
202310687Seric 	register STAB *s;
202410687Seric 
202557943Seric 	if (tTd(37, 8))
202668481Seric 		printf("setclass(%c, %s)\n", class, str);
202768481Seric 	s = stab(str, ST_CLASS, ST_ENTER);
202810687Seric 	setbitn(class, s->s_class);
202910687Seric }
203053654Seric /*
203153654Seric **  MAKEMAPENTRY -- create a map entry
203253654Seric **
203353654Seric **	Parameters:
203453654Seric **		line -- the config file line
203553654Seric **
203653654Seric **	Returns:
203769774Seric **		A pointer to the map that has been created.
203869774Seric **		NULL if there was a syntax error.
203953654Seric **
204053654Seric **	Side Effects:
204153654Seric **		Enters the map into the dictionary.
204253654Seric */
204353654Seric 
204469774Seric MAP *
204553654Seric makemapentry(line)
204653654Seric 	char *line;
204753654Seric {
204853654Seric 	register char *p;
204953654Seric 	char *mapname;
205053654Seric 	char *classname;
205164078Seric 	register STAB *s;
205253654Seric 	STAB *class;
205353654Seric 
205458050Seric 	for (p = line; isascii(*p) && isspace(*p); p++)
205553654Seric 		continue;
205658050Seric 	if (!(isascii(*p) && isalnum(*p)))
205753654Seric 	{
205853654Seric 		syserr("readcf: config K line: no map name");
205969774Seric 		return NULL;
206053654Seric 	}
206153654Seric 
206253654Seric 	mapname = p;
206368481Seric 	while ((isascii(*++p) && isalnum(*p)) || *p == '.')
206453654Seric 		continue;
206553654Seric 	if (*p != '\0')
206653654Seric 		*p++ = '\0';
206758050Seric 	while (isascii(*p) && isspace(*p))
206853654Seric 		p++;
206958050Seric 	if (!(isascii(*p) && isalnum(*p)))
207053654Seric 	{
207153654Seric 		syserr("readcf: config K line, map %s: no map class", mapname);
207269774Seric 		return NULL;
207353654Seric 	}
207453654Seric 	classname = p;
207558050Seric 	while (isascii(*++p) && isalnum(*p))
207653654Seric 		continue;
207753654Seric 	if (*p != '\0')
207853654Seric 		*p++ = '\0';
207958050Seric 	while (isascii(*p) && isspace(*p))
208053654Seric 		p++;
208153654Seric 
208253654Seric 	/* look up the class */
208353654Seric 	class = stab(classname, ST_MAPCLASS, ST_FIND);
208453654Seric 	if (class == NULL)
208553654Seric 	{
208653654Seric 		syserr("readcf: map %s: class %s not available", mapname, classname);
208769774Seric 		return NULL;
208853654Seric 	}
208953654Seric 
209053654Seric 	/* enter the map */
209164078Seric 	s = stab(mapname, ST_MAP, ST_ENTER);
209264078Seric 	s->s_map.map_class = &class->s_mapclass;
209364078Seric 	s->s_map.map_mname = newstr(mapname);
209453654Seric 
209564078Seric 	if (class->s_mapclass.map_parse(&s->s_map, p))
209664078Seric 		s->s_map.map_mflags |= MF_VALID;
209764078Seric 
209864078Seric 	if (tTd(37, 5))
209964078Seric 	{
210064078Seric 		printf("map %s, class %s, flags %x, file %s,\n",
210164078Seric 			s->s_map.map_mname, s->s_map.map_class->map_cname,
210264078Seric 			s->s_map.map_mflags,
210364078Seric 			s->s_map.map_file == NULL ? "(null)" : s->s_map.map_file);
210464078Seric 		printf("\tapp %s, domain %s, rebuild %s\n",
210564078Seric 			s->s_map.map_app == NULL ? "(null)" : s->s_map.map_app,
210664078Seric 			s->s_map.map_domain == NULL ? "(null)" : s->s_map.map_domain,
210764078Seric 			s->s_map.map_rebuild == NULL ? "(null)" : s->s_map.map_rebuild);
210864078Seric 	}
210969774Seric 
211069774Seric 	return &s->s_map;
211153654Seric }
211258112Seric /*
211369783Seric **  STRTORWSET -- convert string to rewriting set number
211469783Seric **
211569783Seric **	Parameters:
211669783Seric **		p -- the pointer to the string to decode.
211769783Seric **		endp -- if set, store the trailing delimiter here.
211869783Seric **		stabmode -- ST_ENTER to create this entry, ST_FIND if
211969783Seric **			it must already exist.
212069783Seric **
212169783Seric **	Returns:
212269783Seric **		The appropriate ruleset number.
212369783Seric **		-1 if it is not valid (error already printed)
212469783Seric */
212569783Seric 
212669783Seric int
212769783Seric strtorwset(p, endp, stabmode)
212869783Seric 	char *p;
212969783Seric 	char **endp;
213069783Seric 	int stabmode;
213169783Seric {
213269783Seric 	int ruleset;
213369783Seric 	static int nextruleset = MAXRWSETS;
213469783Seric 
213569783Seric 	while (isascii(*p) && isspace(*p))
213669783Seric 		p++;
213769783Seric 	if (!isascii(*p))
213869783Seric 	{
213969783Seric 		syserr("invalid ruleset name: \"%.20s\"", p);
214069783Seric 		return -1;
214169783Seric 	}
214269783Seric 	if (isdigit(*p))
214369783Seric 	{
214469783Seric 		ruleset = strtol(p, endp, 10);
214569783Seric 		if (ruleset >= MAXRWSETS / 2 || ruleset < 0)
214669783Seric 		{
214769783Seric 			syserr("bad ruleset %d (%d max)",
214869783Seric 				ruleset, MAXRWSETS / 2);
214969783Seric 			ruleset = -1;
215069783Seric 		}
215169783Seric 	}
215269783Seric 	else
215369783Seric 	{
215469783Seric 		STAB *s;
215569783Seric 		char delim;
215669783Seric 		char *q;
215769783Seric 
215869783Seric 		q = p;
215969783Seric 		while (*p != '\0' && isascii(*p) &&
216069783Seric 		       (isalnum(*p) || strchr("-_$", *p) != NULL))
216169783Seric 			p++;
216269783Seric 		while (isascii(*p) && isspace(*p))
216369783Seric 			*p++ = '\0';
216469783Seric 		delim = *p;
216569783Seric 		if (delim != '\0')
216669783Seric 			*p = '\0';
216769783Seric 		s = stab(q, ST_RULESET, stabmode);
216869783Seric 		if (delim != '\0')
216969783Seric 			*p = delim;
217069783Seric 
217169783Seric 		if (s == NULL)
217269783Seric 		{
217369783Seric 			syserr("unknown ruleset %s", q);
217469783Seric 			return -1;
217569783Seric 		}
217669783Seric 
217769783Seric 		if (stabmode == ST_ENTER && delim == '=')
217869783Seric 		{
217969783Seric 			ruleset = strtol(p, endp, 10);
218069783Seric 			if (ruleset >= MAXRWSETS / 2 || ruleset < 0)
218169783Seric 			{
218269783Seric 				syserr("bad ruleset %s = %d (%d max)",
218369783Seric 					q, ruleset, MAXRWSETS / 2);
218469783Seric 				ruleset = -1;
218569783Seric 			}
218669783Seric 		}
218769783Seric 		else
218869783Seric 		{
218969783Seric 			if (endp != NULL)
219069783Seric 				*endp = p;
219169783Seric 			if (s->s_ruleset > 0)
219269783Seric 				ruleset = s->s_ruleset;
219369783Seric 			else if ((ruleset = --nextruleset) < MAXRWSETS / 2)
219469783Seric 			{
219569783Seric 				syserr("%s: too many named rulesets (%d max)",
219669783Seric 					q, MAXRWSETS / 2);
219769783Seric 				ruleset = -1;
219869783Seric 			}
219969783Seric 		}
220069783Seric 		if (s->s_ruleset > 0 && ruleset >= 0 && ruleset != s->s_ruleset)
220169783Seric 		{
220269783Seric 			syserr("%s: ruleset changed value (old %d, new %d)",
220369783Seric 				q, ruleset, s->s_ruleset);
220469783Seric 			ruleset = s->s_ruleset;
220569783Seric 		}
220669783Seric 		else if (ruleset > 0)
220769783Seric 		{
220869783Seric 			s->s_ruleset = ruleset;
220969783Seric 		}
221069783Seric 	}
221169783Seric 	return ruleset;
221269783Seric }
221369783Seric /*
221468481Seric **  INITTIMEOUTS -- parse and set timeout values
221558112Seric **
221658112Seric **	Parameters:
221758112Seric **		val -- a pointer to the values.  If NULL, do initial
221858112Seric **			settings.
221958112Seric **
222058112Seric **	Returns:
222158112Seric **		none.
222258112Seric **
222358112Seric **	Side Effects:
222458112Seric **		Initializes the TimeOuts structure
222558112Seric */
222658112Seric 
222764255Seric #define SECONDS
222858112Seric #define MINUTES	* 60
222958112Seric #define HOUR	* 3600
223058112Seric 
223169748Seric void
223268481Seric inittimeouts(val)
223358112Seric 	register char *val;
223458112Seric {
223558112Seric 	register char *p;
223658671Seric 	extern time_t convtime();
223758112Seric 
223858112Seric 	if (val == NULL)
223958112Seric 	{
224058112Seric 		TimeOuts.to_initial = (time_t) 5 MINUTES;
224158112Seric 		TimeOuts.to_helo = (time_t) 5 MINUTES;
224258112Seric 		TimeOuts.to_mail = (time_t) 10 MINUTES;
224358112Seric 		TimeOuts.to_rcpt = (time_t) 1 HOUR;
224458112Seric 		TimeOuts.to_datainit = (time_t) 5 MINUTES;
224558112Seric 		TimeOuts.to_datablock = (time_t) 1 HOUR;
224658112Seric 		TimeOuts.to_datafinal = (time_t) 1 HOUR;
224758112Seric 		TimeOuts.to_rset = (time_t) 5 MINUTES;
224858112Seric 		TimeOuts.to_quit = (time_t) 2 MINUTES;
224958112Seric 		TimeOuts.to_nextcommand = (time_t) 1 HOUR;
225058112Seric 		TimeOuts.to_miscshort = (time_t) 2 MINUTES;
225168481Seric #if IDENTPROTO
225264255Seric 		TimeOuts.to_ident = (time_t) 30 SECONDS;
225368481Seric #else
225468481Seric 		TimeOuts.to_ident = (time_t) 0 SECONDS;
225568481Seric #endif
225668481Seric 		TimeOuts.to_fileopen = (time_t) 60 SECONDS;
225758112Seric 		return;
225858112Seric 	}
225958112Seric 
226058112Seric 	for (;; val = p)
226158112Seric 	{
226258112Seric 		while (isascii(*val) && isspace(*val))
226358112Seric 			val++;
226458112Seric 		if (*val == '\0')
226558112Seric 			break;
226658112Seric 		for (p = val; *p != '\0' && *p != ','; p++)
226758112Seric 			continue;
226858112Seric 		if (*p != '\0')
226958112Seric 			*p++ = '\0';
227058112Seric 
227158112Seric 		if (isascii(*val) && isdigit(*val))
227258112Seric 		{
227358112Seric 			/* old syntax -- set everything */
227458796Seric 			TimeOuts.to_mail = convtime(val, 'm');
227558112Seric 			TimeOuts.to_rcpt = TimeOuts.to_mail;
227658112Seric 			TimeOuts.to_datainit = TimeOuts.to_mail;
227758112Seric 			TimeOuts.to_datablock = TimeOuts.to_mail;
227858112Seric 			TimeOuts.to_datafinal = TimeOuts.to_mail;
227958112Seric 			TimeOuts.to_nextcommand = TimeOuts.to_mail;
228058112Seric 			continue;
228158112Seric 		}
228258112Seric 		else
228358112Seric 		{
228468481Seric 			register char *q = strchr(val, ':');
228558112Seric 
228668481Seric 			if (q == NULL && (q = strchr(val, '=')) == NULL)
228758112Seric 			{
228858112Seric 				/* syntax error */
228958112Seric 				continue;
229058112Seric 			}
229158112Seric 			*q++ = '\0';
229268481Seric 			settimeout(val, q);
229368481Seric 		}
229468481Seric 	}
229568481Seric }
229668481Seric /*
229768481Seric **  SETTIMEOUT -- set an individual timeout
229868481Seric **
229968481Seric **	Parameters:
230068481Seric **		name -- the name of the timeout.
230168481Seric **		val -- the value of the timeout.
230268481Seric **
230368481Seric **	Returns:
230468481Seric **		none.
230568481Seric */
230658112Seric 
230769748Seric void
230868481Seric settimeout(name, val)
230968481Seric 	char *name;
231068481Seric 	char *val;
231168481Seric {
231268481Seric 	register char *p;
231368481Seric 	time_t to;
231468481Seric 	extern time_t convtime();
231568481Seric 
231668481Seric 	to = convtime(val, 'm');
231768481Seric 	p = strchr(name, '.');
231868481Seric 	if (p != NULL)
231968481Seric 		*p++ = '\0';
232068481Seric 
232168481Seric 	if (strcasecmp(name, "initial") == 0)
232268481Seric 		TimeOuts.to_initial = to;
232368481Seric 	else if (strcasecmp(name, "mail") == 0)
232468481Seric 		TimeOuts.to_mail = to;
232568481Seric 	else if (strcasecmp(name, "rcpt") == 0)
232668481Seric 		TimeOuts.to_rcpt = to;
232768481Seric 	else if (strcasecmp(name, "datainit") == 0)
232868481Seric 		TimeOuts.to_datainit = to;
232968481Seric 	else if (strcasecmp(name, "datablock") == 0)
233068481Seric 		TimeOuts.to_datablock = to;
233168481Seric 	else if (strcasecmp(name, "datafinal") == 0)
233268481Seric 		TimeOuts.to_datafinal = to;
233368481Seric 	else if (strcasecmp(name, "command") == 0)
233468481Seric 		TimeOuts.to_nextcommand = to;
233568481Seric 	else if (strcasecmp(name, "rset") == 0)
233668481Seric 		TimeOuts.to_rset = to;
233768481Seric 	else if (strcasecmp(name, "helo") == 0)
233868481Seric 		TimeOuts.to_helo = to;
233968481Seric 	else if (strcasecmp(name, "quit") == 0)
234068481Seric 		TimeOuts.to_quit = to;
234168481Seric 	else if (strcasecmp(name, "misc") == 0)
234268481Seric 		TimeOuts.to_miscshort = to;
234368481Seric 	else if (strcasecmp(name, "ident") == 0)
234468481Seric 		TimeOuts.to_ident = to;
234568481Seric 	else if (strcasecmp(name, "fileopen") == 0)
234668481Seric 		TimeOuts.to_fileopen = to;
234768481Seric 	else if (strcasecmp(name, "queuewarn") == 0)
234868481Seric 	{
234968481Seric 		to = convtime(val, 'h');
235068481Seric 		if (p == NULL || strcmp(p, "*") == 0)
235168481Seric 		{
235268481Seric 			TimeOuts.to_q_warning[TOC_NORMAL] = to;
235368481Seric 			TimeOuts.to_q_warning[TOC_URGENT] = to;
235468481Seric 			TimeOuts.to_q_warning[TOC_NONURGENT] = to;
235558112Seric 		}
235668481Seric 		else if (strcasecmp(p, "normal") == 0)
235768481Seric 			TimeOuts.to_q_warning[TOC_NORMAL] = to;
235868481Seric 		else if (strcasecmp(p, "urgent") == 0)
235968481Seric 			TimeOuts.to_q_warning[TOC_URGENT] = to;
236068481Seric 		else if (strcasecmp(p, "non-urgent") == 0)
236168481Seric 			TimeOuts.to_q_warning[TOC_NONURGENT] = to;
236268481Seric 		else
236368481Seric 			syserr("settimeout: invalid queuewarn subtimeout %s", p);
236458112Seric 	}
236568481Seric 	else if (strcasecmp(name, "queuereturn") == 0)
236668481Seric 	{
236768481Seric 		to = convtime(val, 'd');
236868481Seric 		if (p == NULL || strcmp(p, "*") == 0)
236968481Seric 		{
237068481Seric 			TimeOuts.to_q_return[TOC_NORMAL] = to;
237168481Seric 			TimeOuts.to_q_return[TOC_URGENT] = to;
237268481Seric 			TimeOuts.to_q_return[TOC_NONURGENT] = to;
237368481Seric 		}
237468481Seric 		else if (strcasecmp(p, "normal") == 0)
237568481Seric 			TimeOuts.to_q_return[TOC_NORMAL] = to;
237668481Seric 		else if (strcasecmp(p, "urgent") == 0)
237768481Seric 			TimeOuts.to_q_return[TOC_URGENT] = to;
237868481Seric 		else if (strcasecmp(p, "non-urgent") == 0)
237968481Seric 			TimeOuts.to_q_return[TOC_NONURGENT] = to;
238068481Seric 		else
238168481Seric 			syserr("settimeout: invalid queuereturn subtimeout %s", p);
238268481Seric 	}
238368481Seric 	else
238468481Seric 		syserr("settimeout: invalid timeout %s", name);
238558112Seric }
2386