xref: /csrg-svn/usr.sbin/sendmail/src/readcf.c (revision 69783)
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*69783Seric static char sccsid[] = "@(#)readcf.c	8.98 (Berkeley) 05/30/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 */
372*69783Seric 			ruleset = strtorwset(&bp[1], NULL, ST_ENTER);
3734072Seric 			rwp = NULL;
3744072Seric 			break;
3754072Seric 
3763308Seric 		  case 'D':		/* macro definition */
37768481Seric 			mid = macid(&bp[1], &ep);
37868481Seric 			p = munchstring(ep, NULL);
37968481Seric 			define(mid, newstr(p), e);
3803308Seric 			break;
3813308Seric 
3823387Seric 		  case 'H':		/* required header line */
38368717Seric 			(void) chompheader(&bp[1], TRUE, NULL, e);
3843387Seric 			break;
3853387Seric 
3864061Seric 		  case 'C':		/* word class */
38768481Seric 		  case 'T':		/* trusted user (set class `t') */
38868481Seric 			if (bp[0] == 'C')
3894061Seric 			{
39068481Seric 				mid = macid(&bp[1], &ep);
39168529Seric 				expand(ep, exbuf, sizeof exbuf, e);
39268481Seric 				p = exbuf;
39368481Seric 			}
39468481Seric 			else
39568481Seric 			{
39668481Seric 				mid = 't';
39768481Seric 				p = &bp[1];
39868481Seric 			}
39968481Seric 			while (*p != '\0')
40068481Seric 			{
4014061Seric 				register char *wd;
4024061Seric 				char delim;
4034061Seric 
40458050Seric 				while (*p != '\0' && isascii(*p) && isspace(*p))
4054061Seric 					p++;
4064061Seric 				wd = p;
40758050Seric 				while (*p != '\0' && !(isascii(*p) && isspace(*p)))
4084061Seric 					p++;
4094061Seric 				delim = *p;
4104061Seric 				*p = '\0';
4114061Seric 				if (wd[0] != '\0')
41268481Seric 					setclass(mid, wd);
4134061Seric 				*p = delim;
4144061Seric 			}
4154061Seric 			break;
4164061Seric 
41759272Seric 		  case 'F':		/* word class from file */
41868481Seric 			mid = macid(&bp[1], &ep);
41968481Seric 			for (p = ep; isascii(*p) && isspace(*p); )
42064133Seric 				p++;
42164133Seric 			if (p[0] == '-' && p[1] == 'o')
42264133Seric 			{
42364133Seric 				optional = TRUE;
42464133Seric 				while (*p != '\0' && !(isascii(*p) && isspace(*p)))
42564133Seric 					p++;
42664133Seric 				while (isascii(*p) && isspace(*p))
42768481Seric 					p++;
42864133Seric 			}
42964133Seric 			else
43064133Seric 				optional = FALSE;
43164133Seric 			file = p;
43264133Seric 			while (*p != '\0' && !(isascii(*p) && isspace(*p)))
43364133Seric 				p++;
43459272Seric 			if (*p == '\0')
43559272Seric 				p = "%s";
43659272Seric 			else
43759272Seric 			{
43859272Seric 				*p = '\0';
43959272Seric 				while (isascii(*++p) && isspace(*p))
44059272Seric 					continue;
44159272Seric 			}
44264133Seric 			fileclass(bp[1], file, p, safe, optional);
44359272Seric 			break;
44459272Seric 
44559156Seric #ifdef XLA
44659156Seric 		  case 'L':		/* extended load average description */
44759156Seric 			xla_init(&bp[1]);
44859156Seric 			break;
44959156Seric #endif
45059156Seric 
4514096Seric 		  case 'M':		/* define mailer */
45257135Seric 			makemailer(&bp[1]);
4534096Seric 			break;
4544096Seric 
4558252Seric 		  case 'O':		/* set option */
45658734Seric 			setoption(bp[1], &bp[2], safe, FALSE, e);
4578252Seric 			break;
4588252Seric 
4598252Seric 		  case 'P':		/* set precedence */
4608252Seric 			if (NumPriorities >= MAXPRIORITIES)
4618252Seric 			{
4628547Seric 				toomany('P', MAXPRIORITIES);
4638252Seric 				break;
4648252Seric 			}
46557135Seric 			for (p = &bp[1]; *p != '\0' && *p != '=' && *p != '\t'; p++)
4668252Seric 				continue;
4678252Seric 			if (*p == '\0')
4688252Seric 				goto badline;
4698252Seric 			*p = '\0';
47057135Seric 			Priorities[NumPriorities].pri_name = newstr(&bp[1]);
4718252Seric 			Priorities[NumPriorities].pri_val = atoi(++p);
4728252Seric 			NumPriorities++;
4738252Seric 			break;
4748252Seric 
47552645Seric 		  case 'V':		/* configuration syntax version */
47664440Seric 			for (p = &bp[1]; isascii(*p) && isspace(*p); p++)
47764440Seric 				continue;
47864440Seric 			if (!isascii(*p) || !isdigit(*p))
47964440Seric 			{
48064440Seric 				syserr("invalid argument to V line: \"%.20s\"",
48164440Seric 					&bp[1]);
48264440Seric 				break;
48364440Seric 			}
48464718Seric 			ConfigLevel = strtol(p, &ep, 10);
48568805Seric 
48668805Seric 			/*
48768805Seric 			**  Do heuristic tweaking for back compatibility.
48868805Seric 			*/
48968805Seric 
49064279Seric 			if (ConfigLevel >= 5)
49164279Seric 			{
49264279Seric 				/* level 5 configs have short name in $w */
49364279Seric 				p = macvalue('w', e);
49464279Seric 				if (p != NULL && (p = strchr(p, '.')) != NULL)
49564279Seric 					*p = '\0';
49664279Seric 			}
49768805Seric 			if (ConfigLevel >= 6)
49868805Seric 			{
49968805Seric 				ColonOkInAddr = FALSE;
50068805Seric 			}
50168805Seric 
50268805Seric 			/*
50368805Seric 			**  Look for vendor code.
50468805Seric 			*/
50568805Seric 
50664718Seric 			if (*ep++ == '/')
50764718Seric 			{
50864718Seric 				/* extract vendor code */
50964718Seric 				for (p = ep; isascii(*p) && isalpha(*p); )
51064718Seric 					p++;
51164718Seric 				*p = '\0';
51264718Seric 
51364718Seric 				if (!setvendor(ep))
51464718Seric 					syserr("invalid V line vendor code: \"%s\"",
51564718Seric 						ep);
51664718Seric 			}
51752645Seric 			break;
51852645Seric 
51953654Seric 		  case 'K':
52069774Seric 			(void) makemapentry(&bp[1]);
52153654Seric 			break;
52253654Seric 
52369476Seric 		  case 'E':
52469476Seric 			p = strchr(bp, '=');
52569476Seric 			if (p != NULL)
52669476Seric 				*p++ = '\0';
52769476Seric 			setuserenv(&bp[1], p);
52869476Seric 			break;
52969476Seric 
5303308Seric 		  default:
5314061Seric 		  badline:
53257135Seric 			syserr("unknown control line \"%s\"", bp);
5333308Seric 		}
53457135Seric 		if (bp != buf)
53557135Seric 			free(bp);
5363308Seric 	}
53752637Seric 	if (ferror(cf))
53852637Seric 	{
53952647Seric 		syserr("I/O read error", cfname);
54052637Seric 		exit(EX_OSFILE);
54152637Seric 	}
54252637Seric 	fclose(cf);
5439381Seric 	FileName = NULL;
54456836Seric 
54568481Seric 	/* initialize host maps from local service tables */
54668481Seric 	inithostmaps();
54768481Seric 
54868481Seric 	/* determine if we need to do special name-server frotz */
54967905Seric 	{
55068481Seric 		int nmaps;
55168481Seric 		char *maptype[MAXMAPSTACK];
55268481Seric 		short mapreturn[MAXMAPACTIONS];
55368481Seric 
55468481Seric 		nmaps = switch_map_find("hosts", maptype, mapreturn);
55568481Seric 		UseNameServer = FALSE;
55668481Seric 		if (nmaps > 0 && nmaps <= MAXMAPSTACK)
55768481Seric 		{
55868481Seric 			register int mapno;
55968481Seric 
56068481Seric 			for (mapno = 0; mapno < nmaps && !UseNameServer; mapno++)
56168481Seric 			{
56268481Seric 				if (strcmp(maptype[mapno], "dns") == 0)
56368481Seric 					UseNameServer = TRUE;
56468481Seric 			}
56568481Seric 		}
56668481Seric 
56768481Seric #ifdef HESIOD
56868481Seric 		nmaps = switch_map_find("passwd", maptype, mapreturn);
56968481Seric 		UseHesiod = FALSE;
57068481Seric 		if (nmaps > 0 && nmaps <= MAXMAPSTACK)
57168481Seric 		{
57268481Seric 			register int mapno;
57368481Seric 
57468481Seric 			for (mapno = 0; mapno < nmaps && !UseHesiod; mapno++)
57568481Seric 			{
57668481Seric 				if (strcmp(maptype[mapno], "hesiod") == 0)
57768481Seric 					UseHesiod = TRUE;
57868481Seric 			}
57968481Seric 		}
58068204Seric #endif
58167905Seric 	}
5824096Seric }
5834096Seric /*
5848547Seric **  TOOMANY -- signal too many of some option
5858547Seric **
5868547Seric **	Parameters:
5878547Seric **		id -- the id of the error line
5888547Seric **		maxcnt -- the maximum possible values
5898547Seric **
5908547Seric **	Returns:
5918547Seric **		none.
5928547Seric **
5938547Seric **	Side Effects:
5948547Seric **		gives a syserr.
5958547Seric */
5968547Seric 
59769748Seric void
5988547Seric toomany(id, maxcnt)
59969748Seric 	int id;
6008547Seric 	int maxcnt;
6018547Seric {
6029381Seric 	syserr("too many %c lines, %d max", id, maxcnt);
6038547Seric }
6048547Seric /*
6054432Seric **  FILECLASS -- read members of a class from a file
6064432Seric **
6074432Seric **	Parameters:
6084432Seric **		class -- class to define.
6094432Seric **		filename -- name of file to read.
6104432Seric **		fmt -- scanf string to use for match.
61164133Seric **		safe -- if set, this is a safe read.
61264133Seric **		optional -- if set, it is not an error for the file to
61364133Seric **			not exist.
6144432Seric **
6154432Seric **	Returns:
6164432Seric **		none
6174432Seric **
6184432Seric **	Side Effects:
6194432Seric **
6204432Seric **		puts all lines in filename that match a scanf into
6214432Seric **			the named class.
6224432Seric */
6234432Seric 
62469748Seric void
62564133Seric fileclass(class, filename, fmt, safe, optional)
6264432Seric 	int class;
6274432Seric 	char *filename;
6284432Seric 	char *fmt;
62954973Seric 	bool safe;
63064133Seric 	bool optional;
6314432Seric {
63225808Seric 	FILE *f;
63368513Seric 	int sff;
63469453Seric 	int pid;
63569453Seric 	register char *p;
6364432Seric 	char buf[MAXLINE];
6374432Seric 
63866101Seric 	if (tTd(37, 2))
63966101Seric 		printf("fileclass(%s, fmt=%s)\n", filename, fmt);
64066101Seric 
64166031Seric 	if (filename[0] == '|')
64266031Seric 	{
64369453Seric 		auto int fd;
64469453Seric 		int i;
64569453Seric 		char *argv[MAXPV + 1];
64669453Seric 
64769453Seric 		i = 0;
64869453Seric 		for (p = strtok(&filename[1], " \t"); p != NULL; p = strtok(NULL, " \t"))
64969453Seric 		{
65069453Seric 			if (i >= MAXPV)
65169453Seric 				break;
65269453Seric 			argv[i++] = p;
65369453Seric 		}
65469453Seric 		argv[i] = NULL;
65569453Seric 		pid = prog_open(argv, &fd, CurEnv);
65669453Seric 		if (pid < 0)
65769453Seric 			f = NULL;
65869453Seric 		else
65969453Seric 			f = fdopen(fd, "r");
66066031Seric 	}
66169453Seric 	else
66269453Seric 	{
66369453Seric 		pid = -1;
66469453Seric 		sff = SFF_REGONLY;
66569453Seric 		if (safe)
66669453Seric 			sff |= SFF_OPENASROOT;
66769453Seric 		f = safefopen(filename, O_RDONLY, 0, sff);
66869453Seric 	}
66968602Seric 	if (f == NULL)
67054973Seric 	{
67168602Seric 		if (!optional)
67268602Seric 			syserr("fileclass: cannot open %s", filename);
6734432Seric 		return;
6744432Seric 	}
6754432Seric 
6764432Seric 	while (fgets(buf, sizeof buf, f) != NULL)
6774432Seric 	{
67825808Seric 		register char *p;
67925808Seric # ifdef SCANF
6804432Seric 		char wordbuf[MAXNAME+1];
6814432Seric 
6824432Seric 		if (sscanf(buf, fmt, wordbuf) != 1)
6834432Seric 			continue;
68425808Seric 		p = wordbuf;
68556795Seric # else /* SCANF */
68625808Seric 		p = buf;
68756795Seric # endif /* SCANF */
68825808Seric 
68925808Seric 		/*
69025808Seric 		**  Break up the match into words.
69125808Seric 		*/
69225808Seric 
69325808Seric 		while (*p != '\0')
69425808Seric 		{
69525808Seric 			register char *q;
69625808Seric 
69725808Seric 			/* strip leading spaces */
69858050Seric 			while (isascii(*p) && isspace(*p))
69925808Seric 				p++;
70025808Seric 			if (*p == '\0')
70125808Seric 				break;
70225808Seric 
70325808Seric 			/* find the end of the word */
70425808Seric 			q = p;
70558050Seric 			while (*p != '\0' && !(isascii(*p) && isspace(*p)))
70625808Seric 				p++;
70725808Seric 			if (*p != '\0')
70825808Seric 				*p++ = '\0';
70925808Seric 
71025808Seric 			/* enter the word in the symbol table */
71166101Seric 			setclass(class, q);
71225808Seric 		}
7134432Seric 	}
7144432Seric 
71554973Seric 	(void) fclose(f);
71669453Seric 	if (pid > 0)
71769453Seric 		(void) waitfor(pid);
7184432Seric }
7194432Seric /*
7204096Seric **  MAKEMAILER -- define a new mailer.
7214096Seric **
7224096Seric **	Parameters:
72310327Seric **		line -- description of mailer.  This is in labeled
72410327Seric **			fields.  The fields are:
72568481Seric **			   A -- the argv for this mailer
72668481Seric **			   C -- the character set for MIME conversions
72768481Seric **			   D -- the directory to run in
72868481Seric **			   E -- the eol string
72968481Seric **			   F -- the flags associated with the mailer
73068481Seric **			   L -- the maximum line length
73168481Seric **			   M -- the maximum message size
73268816Seric **			   N -- the niceness at which to run
73368479Seric **			   P -- the path to the mailer
73468481Seric **			   R -- the recipient rewriting set
73568479Seric **			   S -- the sender rewriting set
73668481Seric **			   T -- the mailer type (for DSNs)
73768481Seric **			   U -- the uid to run as
73810327Seric **			The first word is the canonical name of the mailer.
7394096Seric **
7404096Seric **	Returns:
7414096Seric **		none.
7424096Seric **
7434096Seric **	Side Effects:
7444096Seric **		enters the mailer into the mailer table.
7454096Seric */
7463308Seric 
74769748Seric void
74821066Seric makemailer(line)
7494096Seric 	char *line;
7504096Seric {
7514096Seric 	register char *p;
7528067Seric 	register struct mailer *m;
7538067Seric 	register STAB *s;
7548067Seric 	int i;
75510327Seric 	char fcode;
75658020Seric 	auto char *endp;
7574096Seric 	extern int NextMailer;
75810327Seric 	extern char **makeargv();
75910327Seric 	extern char *munchstring();
7604096Seric 
76110327Seric 	/* allocate a mailer and set up defaults */
76210327Seric 	m = (struct mailer *) xalloc(sizeof *m);
76310327Seric 	bzero((char *) m, sizeof *m);
76410327Seric 	m->m_eol = "\n";
76568481Seric 	m->m_uid = m->m_gid = 0;
76610327Seric 
76710327Seric 	/* collect the mailer name */
76858050Seric 	for (p = line; *p != '\0' && *p != ',' && !(isascii(*p) && isspace(*p)); p++)
76910327Seric 		continue;
77010327Seric 	if (*p != '\0')
77110327Seric 		*p++ = '\0';
77210327Seric 	m->m_name = newstr(line);
77310327Seric 
77410327Seric 	/* now scan through and assign info from the fields */
77510327Seric 	while (*p != '\0')
77610327Seric 	{
77758333Seric 		auto char *delimptr;
77858333Seric 
77958050Seric 		while (*p != '\0' && (*p == ',' || (isascii(*p) && isspace(*p))))
78010327Seric 			p++;
78110327Seric 
78210327Seric 		/* p now points to field code */
78310327Seric 		fcode = *p;
78410327Seric 		while (*p != '\0' && *p != '=' && *p != ',')
78510327Seric 			p++;
78610327Seric 		if (*p++ != '=')
78710327Seric 		{
78852637Seric 			syserr("mailer %s: `=' expected", m->m_name);
78910327Seric 			return;
79010327Seric 		}
79158050Seric 		while (isascii(*p) && isspace(*p))
79210327Seric 			p++;
79310327Seric 
79410327Seric 		/* p now points to the field body */
79558333Seric 		p = munchstring(p, &delimptr);
79610327Seric 
79710327Seric 		/* install the field into the mailer struct */
79810327Seric 		switch (fcode)
79910327Seric 		{
80010327Seric 		  case 'P':		/* pathname */
80110327Seric 			m->m_mailer = newstr(p);
80210327Seric 			break;
80310327Seric 
80410327Seric 		  case 'F':		/* flags */
80510687Seric 			for (; *p != '\0'; p++)
80658050Seric 				if (!(isascii(*p) && isspace(*p)))
80752637Seric 					setbitn(*p, m->m_flags);
80810327Seric 			break;
80910327Seric 
81010327Seric 		  case 'S':		/* sender rewriting ruleset */
81110327Seric 		  case 'R':		/* recipient rewriting ruleset */
812*69783Seric 			i = strtorwset(p, &endp, ST_ENTER);
813*69783Seric 			if (i < 0)
81410327Seric 				return;
81510327Seric 			if (fcode == 'S')
81658020Seric 				m->m_sh_rwset = m->m_se_rwset = i;
81710327Seric 			else
81858020Seric 				m->m_rh_rwset = m->m_re_rwset = i;
81958020Seric 
82058020Seric 			p = endp;
82159985Seric 			if (*p++ == '/')
82258020Seric 			{
823*69783Seric 				i = strtorwset(p, NULL);
824*69783Seric 				if (i < 0)
82558020Seric 					return;
82658020Seric 				if (fcode == 'S')
82758020Seric 					m->m_sh_rwset = i;
82858020Seric 				else
82958020Seric 					m->m_rh_rwset = i;
83058020Seric 			}
83110327Seric 			break;
83210327Seric 
83310327Seric 		  case 'E':		/* end of line string */
83410327Seric 			m->m_eol = newstr(p);
83510327Seric 			break;
83610327Seric 
83710327Seric 		  case 'A':		/* argument vector */
83810327Seric 			m->m_argv = makeargv(p);
83910327Seric 			break;
84010701Seric 
84110701Seric 		  case 'M':		/* maximum message size */
84210701Seric 			m->m_maxsize = atol(p);
84310701Seric 			break;
84452106Seric 
84552106Seric 		  case 'L':		/* maximum line length */
84652106Seric 			m->m_linelimit = atoi(p);
84752106Seric 			break;
84858935Seric 
84968816Seric 		  case 'N':		/* run niceness */
85068816Seric 			m->m_nice = atoi(p);
85168816Seric 			break;
85268816Seric 
85358935Seric 		  case 'D':		/* working directory */
85458935Seric 			m->m_execdir = newstr(p);
85558935Seric 			break;
85668481Seric 
85768481Seric 		  case 'C':		/* default charset */
85868481Seric 			m->m_defcharset = newstr(p);
85968481Seric 			break;
86068481Seric 
86169720Seric 		  case 'T':		/* MTA-Name/Address/Diagnostic types */
86268481Seric 			m->m_mtatype = newstr(p);
86368481Seric 			p = strchr(m->m_mtatype, '/');
86468481Seric 			if (p != NULL)
86568481Seric 			{
86668481Seric 				*p++ = '\0';
86768481Seric 				if (*p == '\0')
86868481Seric 					p = NULL;
86968481Seric 			}
87068481Seric 			if (p == NULL)
87168481Seric 				m->m_addrtype = m->m_mtatype;
87268481Seric 			else
87368481Seric 			{
87468481Seric 				m->m_addrtype = p;
87568481Seric 				p = strchr(p, '/');
87668481Seric 			}
87768481Seric 			if (p != NULL)
87868481Seric 			{
87968481Seric 				*p++ = '\0';
88068481Seric 				if (*p == '\0')
88168481Seric 					p = NULL;
88268481Seric 			}
88368481Seric 			if (p == NULL)
88468481Seric 				m->m_diagtype = m->m_mtatype;
88568481Seric 			else
88668481Seric 				m->m_diagtype = p;
88768481Seric 			break;
88868481Seric 
88968481Seric 		  case 'U':		/* user id */
89068481Seric 			if (isascii(*p) && !isdigit(*p))
89168481Seric 			{
89268481Seric 				char *q = p;
89368481Seric 				struct passwd *pw;
89468481Seric 
89568481Seric 				while (isascii(*p) && isalnum(*p))
89668481Seric 					p++;
89768481Seric 				while (isascii(*p) && isspace(*p))
89868481Seric 					*p++ = '\0';
89968481Seric 				if (*p != '\0')
90068481Seric 					*p++ = '\0';
90168693Seric 				pw = sm_getpwnam(q);
90268481Seric 				if (pw == NULL)
90368481Seric 					syserr("readcf: mailer U= flag: unknown user %s", q);
90468481Seric 				else
90568481Seric 				{
90668481Seric 					m->m_uid = pw->pw_uid;
90768481Seric 					m->m_gid = pw->pw_gid;
90868481Seric 				}
90968481Seric 			}
91068481Seric 			else
91168481Seric 			{
91268481Seric 				auto char *q;
91368481Seric 
91468481Seric 				m->m_uid = strtol(p, &q, 0);
91568481Seric 				p = q;
91668481Seric 			}
91768481Seric 			while (isascii(*p) && isspace(*p))
91868481Seric 				p++;
91968481Seric 			if (*p == '\0')
92068481Seric 				break;
92168481Seric 			if (isascii(*p) && !isdigit(*p))
92268481Seric 			{
92368481Seric 				char *q = p;
92468481Seric 				struct group *gr;
92568481Seric 
92668481Seric 				while (isascii(*p) && isalnum(*p))
92768481Seric 					p++;
92868481Seric 				*p++ = '\0';
92968481Seric 				gr = getgrnam(q);
93068481Seric 				if (gr == NULL)
93168481Seric 					syserr("readcf: mailer U= flag: unknown group %s", q);
93268481Seric 				else
93368481Seric 					m->m_gid = gr->gr_gid;
93468481Seric 			}
93568481Seric 			else
93668481Seric 			{
93768481Seric 				m->m_gid = strtol(p, NULL, 0);
93868481Seric 			}
93968481Seric 			break;
94010327Seric 		}
94110327Seric 
94258333Seric 		p = delimptr;
94310327Seric 	}
94410327Seric 
94558321Seric 	/* do some rationality checking */
94658321Seric 	if (m->m_argv == NULL)
94758321Seric 	{
94858321Seric 		syserr("M%s: A= argument required", m->m_name);
94958321Seric 		return;
95058321Seric 	}
95158321Seric 	if (m->m_mailer == NULL)
95258321Seric 	{
95358321Seric 		syserr("M%s: P= argument required", m->m_name);
95458321Seric 		return;
95558321Seric 	}
95658321Seric 
9574096Seric 	if (NextMailer >= MAXMAILERS)
9584096Seric 	{
9599381Seric 		syserr("too many mailers defined (%d max)", MAXMAILERS);
9604096Seric 		return;
9614096Seric 	}
96257402Seric 
96368481Seric 	/* do some heuristic cleanup for back compatibility */
96468481Seric 	if (bitnset(M_LIMITS, m->m_flags))
96568481Seric 	{
96668481Seric 		if (m->m_linelimit == 0)
96768481Seric 			m->m_linelimit = SMTPLINELIM;
96868481Seric 		if (ConfigLevel < 2)
96968481Seric 			setbitn(M_7BITS, m->m_flags);
97068481Seric 	}
97168481Seric 
97268481Seric 	if (ConfigLevel < 6 &&
97368481Seric 	    (strcmp(m->m_mailer, "[IPC]") == 0 ||
97468481Seric 	     strcmp(m->m_mailer, "[TCP]") == 0))
97568481Seric 	{
97668481Seric 		if (m->m_mtatype == NULL)
97768481Seric 			m->m_mtatype = "dns";
97868481Seric 		if (m->m_addrtype == NULL)
97968481Seric 			m->m_addrtype = "rfc822";
98068481Seric 		if (m->m_diagtype == NULL)
98168481Seric 			m->m_diagtype = "smtp";
98268481Seric 	}
98368481Seric 
98468481Seric 	/* enter the mailer into the symbol table */
98510327Seric 	s = stab(m->m_name, ST_MAILER, ST_ENTER);
98657402Seric 	if (s->s_mailer != NULL)
98757402Seric 	{
98857402Seric 		i = s->s_mailer->m_mno;
98957402Seric 		free(s->s_mailer);
99057402Seric 	}
99157402Seric 	else
99257402Seric 	{
99357402Seric 		i = NextMailer++;
99457402Seric 	}
99557402Seric 	Mailer[i] = s->s_mailer = m;
99657454Seric 	m->m_mno = i;
99710327Seric }
99810327Seric /*
99910327Seric **  MUNCHSTRING -- translate a string into internal form.
100010327Seric **
100110327Seric **	Parameters:
100210327Seric **		p -- the string to munch.
100358333Seric **		delimptr -- if non-NULL, set to the pointer of the
100458333Seric **			field delimiter character.
100510327Seric **
100610327Seric **	Returns:
100710327Seric **		the munched string.
100810327Seric */
10094096Seric 
101010327Seric char *
101158333Seric munchstring(p, delimptr)
101210327Seric 	register char *p;
101358333Seric 	char **delimptr;
101410327Seric {
101510327Seric 	register char *q;
101610327Seric 	bool backslash = FALSE;
101710327Seric 	bool quotemode = FALSE;
101810327Seric 	static char buf[MAXLINE];
10194096Seric 
102010327Seric 	for (q = buf; *p != '\0'; p++)
10214096Seric 	{
102210327Seric 		if (backslash)
102310327Seric 		{
102410327Seric 			/* everything is roughly literal */
102510357Seric 			backslash = FALSE;
102610327Seric 			switch (*p)
102710327Seric 			{
102810327Seric 			  case 'r':		/* carriage return */
102910327Seric 				*q++ = '\r';
103010327Seric 				continue;
103110327Seric 
103210327Seric 			  case 'n':		/* newline */
103310327Seric 				*q++ = '\n';
103410327Seric 				continue;
103510327Seric 
103610327Seric 			  case 'f':		/* form feed */
103710327Seric 				*q++ = '\f';
103810327Seric 				continue;
103910327Seric 
104010327Seric 			  case 'b':		/* backspace */
104110327Seric 				*q++ = '\b';
104210327Seric 				continue;
104310327Seric 			}
104410327Seric 			*q++ = *p;
104510327Seric 		}
104610327Seric 		else
104710327Seric 		{
104810327Seric 			if (*p == '\\')
104910327Seric 				backslash = TRUE;
105010327Seric 			else if (*p == '"')
105110327Seric 				quotemode = !quotemode;
105210327Seric 			else if (quotemode || *p != ',')
105310327Seric 				*q++ = *p;
105410327Seric 			else
105510327Seric 				break;
105610327Seric 		}
10574096Seric 	}
10584096Seric 
105958333Seric 	if (delimptr != NULL)
106058333Seric 		*delimptr = p;
106110327Seric 	*q++ = '\0';
106210327Seric 	return (buf);
106310327Seric }
106410327Seric /*
106510327Seric **  MAKEARGV -- break up a string into words
106610327Seric **
106710327Seric **	Parameters:
106810327Seric **		p -- the string to break up.
106910327Seric **
107010327Seric **	Returns:
107110327Seric **		a char **argv (dynamically allocated)
107210327Seric **
107310327Seric **	Side Effects:
107410327Seric **		munges p.
107510327Seric */
10764096Seric 
107710327Seric char **
107810327Seric makeargv(p)
107910327Seric 	register char *p;
108010327Seric {
108110327Seric 	char *q;
108210327Seric 	int i;
108310327Seric 	char **avp;
108410327Seric 	char *argv[MAXPV + 1];
108510327Seric 
108610327Seric 	/* take apart the words */
108710327Seric 	i = 0;
108810327Seric 	while (*p != '\0' && i < MAXPV)
10894096Seric 	{
109010327Seric 		q = p;
109158050Seric 		while (*p != '\0' && !(isascii(*p) && isspace(*p)))
109210327Seric 			p++;
109358050Seric 		while (isascii(*p) && isspace(*p))
109410327Seric 			*p++ = '\0';
109510327Seric 		argv[i++] = newstr(q);
10964096Seric 	}
109710327Seric 	argv[i++] = NULL;
10984096Seric 
109910327Seric 	/* now make a copy of the argv */
110010327Seric 	avp = (char **) xalloc(sizeof *avp * i);
110116893Seric 	bcopy((char *) argv, (char *) avp, sizeof *avp * i);
110210327Seric 
110310327Seric 	return (avp);
11043308Seric }
11053308Seric /*
11063308Seric **  PRINTRULES -- print rewrite rules (for debugging)
11073308Seric **
11083308Seric **	Parameters:
11093308Seric **		none.
11103308Seric **
11113308Seric **	Returns:
11123308Seric **		none.
11133308Seric **
11143308Seric **	Side Effects:
11153308Seric **		prints rewrite rules.
11163308Seric */
11173308Seric 
111869748Seric void
11193308Seric printrules()
11203308Seric {
11213308Seric 	register struct rewrite *rwp;
11224072Seric 	register int ruleset;
11233308Seric 
11244072Seric 	for (ruleset = 0; ruleset < 10; ruleset++)
11253308Seric 	{
11264072Seric 		if (RewriteRules[ruleset] == NULL)
11274072Seric 			continue;
11288067Seric 		printf("\n----Rule Set %d:", ruleset);
11293308Seric 
11304072Seric 		for (rwp = RewriteRules[ruleset]; rwp != NULL; rwp = rwp->r_next)
11313308Seric 		{
11328067Seric 			printf("\nLHS:");
11338067Seric 			printav(rwp->r_lhs);
11348067Seric 			printf("RHS:");
11358067Seric 			printav(rwp->r_rhs);
11363308Seric 		}
11373308Seric 	}
11383308Seric }
113968481Seric /*
114068481Seric **  PRINTMAILER -- print mailer structure (for debugging)
114168481Seric **
114268481Seric **	Parameters:
114368481Seric **		m -- the mailer to print
114468481Seric **
114568481Seric **	Returns:
114668481Seric **		none.
114768481Seric */
11484319Seric 
114969748Seric void
115068481Seric printmailer(m)
115168481Seric 	register MAILER *m;
115268481Seric {
115368481Seric 	int j;
115468481Seric 
115568481Seric 	printf("mailer %d (%s): P=%s S=%d/%d R=%d/%d M=%ld U=%d:%d F=",
115668481Seric 		m->m_mno, m->m_name,
115768481Seric 		m->m_mailer, m->m_se_rwset, m->m_sh_rwset,
115868481Seric 		m->m_re_rwset, m->m_rh_rwset, m->m_maxsize,
115968481Seric 		m->m_uid, m->m_gid);
116068481Seric 	for (j = '\0'; j <= '\177'; j++)
116168481Seric 		if (bitnset(j, m->m_flags))
116268481Seric 			(void) putchar(j);
116368481Seric 	printf(" L=%d E=", m->m_linelimit);
116468481Seric 	xputs(m->m_eol);
116568481Seric 	if (m->m_defcharset != NULL)
116668481Seric 		printf(" C=%s", m->m_defcharset);
116768481Seric 	printf(" T=%s/%s/%s",
116868481Seric 		m->m_mtatype == NULL ? "<undefined>" : m->m_mtatype,
116968481Seric 		m->m_addrtype == NULL ? "<undefined>" : m->m_addrtype,
117068481Seric 		m->m_diagtype == NULL ? "<undefined>" : m->m_diagtype);
117168481Seric 	if (m->m_argv != NULL)
117268481Seric 	{
117368481Seric 		char **a = m->m_argv;
117468481Seric 
117568481Seric 		printf(" A=");
117668481Seric 		while (*a != NULL)
117768481Seric 		{
117868481Seric 			if (a != m->m_argv)
117968481Seric 				printf(" ");
118068481Seric 			xputs(*a++);
118168481Seric 		}
118268481Seric 	}
118368481Seric 	printf("\n");
118468481Seric }
11854096Seric /*
11868256Seric **  SETOPTION -- set global processing option
11878256Seric **
11888256Seric **	Parameters:
11898256Seric **		opt -- option name.
11908256Seric **		val -- option value (as a text string).
119121755Seric **		safe -- set if this came from a configuration file.
119221755Seric **			Some options (if set from the command line) will
119321755Seric **			reset the user id to avoid security problems.
11948269Seric **		sticky -- if set, don't let other setoptions override
11958269Seric **			this value.
119658734Seric **		e -- the main envelope.
11978256Seric **
11988256Seric **	Returns:
11998256Seric **		none.
12008256Seric **
12018256Seric **	Side Effects:
12028256Seric **		Sets options as implied by the arguments.
12038256Seric */
12048256Seric 
120510687Seric static BITMAP	StickyOpt;		/* set if option is stuck */
120669748Seric extern void	settimeout __P((char *, char *));
12078269Seric 
120857207Seric 
120966334Seric #if NAMED_BIND
121057207Seric 
121157207Seric struct resolverflags
121257207Seric {
121357207Seric 	char	*rf_name;	/* name of the flag */
121457207Seric 	long	rf_bits;	/* bits to set/clear */
121557207Seric } ResolverFlags[] =
121657207Seric {
121757207Seric 	"debug",	RES_DEBUG,
121857207Seric 	"aaonly",	RES_AAONLY,
121957207Seric 	"usevc",	RES_USEVC,
122057207Seric 	"primary",	RES_PRIMARY,
122157207Seric 	"igntc",	RES_IGNTC,
122257207Seric 	"recurse",	RES_RECURSE,
122357207Seric 	"defnames",	RES_DEFNAMES,
122457207Seric 	"stayopen",	RES_STAYOPEN,
122557207Seric 	"dnsrch",	RES_DNSRCH,
122665583Seric 	"true",		0,		/* to avoid error on old syntax */
122757207Seric 	NULL,		0
122857207Seric };
122957207Seric 
123057207Seric #endif
123157207Seric 
123268481Seric struct optioninfo
123368481Seric {
123468481Seric 	char	*o_name;	/* long name of option */
123568481Seric 	u_char	o_code;		/* short name of option */
123668481Seric 	bool	o_safe;		/* safe for random people to use */
123768481Seric } OptionTab[] =
123868481Seric {
123968481Seric 	"SevenBitInput",	'7',		TRUE,
124069480Seric #if MIME8TO7
124168481Seric 	"EightBitMode",		'8',		TRUE,
124269480Seric #endif
124368481Seric 	"AliasFile",		'A',		FALSE,
124468481Seric 	"AliasWait",		'a',		FALSE,
124568481Seric 	"BlankSub",		'B',		FALSE,
124668481Seric 	"MinFreeBlocks",	'b',		TRUE,
124768481Seric 	"CheckpointInterval",	'C',		TRUE,
124868481Seric 	"HoldExpensive",	'c',		FALSE,
124968481Seric 	"AutoRebuildAliases",	'D',		FALSE,
125068481Seric 	"DeliveryMode",		'd',		TRUE,
125168481Seric 	"ErrorHeader",		'E',		FALSE,
125268481Seric 	"ErrorMode",		'e',		TRUE,
125368481Seric 	"TempFileMode",		'F',		FALSE,
125468481Seric 	"SaveFromLine",		'f',		FALSE,
125568481Seric 	"MatchGECOS",		'G',		FALSE,
125668481Seric 	"HelpFile",		'H',		FALSE,
125768481Seric 	"MaxHopCount",		'h',		FALSE,
125868569Seric 	"ResolverOptions",	'I',		FALSE,
125968481Seric 	"IgnoreDots",		'i',		TRUE,
126068481Seric 	"ForwardPath",		'J',		FALSE,
126168481Seric 	"SendMimeErrors",	'j',		TRUE,
126268481Seric 	"ConnectionCacheSize",	'k',		FALSE,
126368481Seric 	"ConnectionCacheTimeout", 'K',		FALSE,
126468481Seric 	"UseErrorsTo",		'l',		FALSE,
126568481Seric 	"LogLevel",		'L',		FALSE,
126668481Seric 	"MeToo",		'm',		TRUE,
126768481Seric 	"CheckAliases",		'n',		FALSE,
126868481Seric 	"OldStyleHeaders",	'o',		TRUE,
126968481Seric 	"DaemonPortOptions",	'O',		FALSE,
127068481Seric 	"PrivacyOptions",	'p',		TRUE,
127168481Seric 	"PostmasterCopy",	'P',		FALSE,
127268481Seric 	"QueueFactor",		'q',		FALSE,
127368481Seric 	"QueueDirectory",	'Q',		FALSE,
127468481Seric 	"DontPruneRoutes",	'R',		FALSE,
127568481Seric 	"Timeout",		'r',		TRUE,
127668481Seric 	"StatusFile",		'S',		FALSE,
127768481Seric 	"SuperSafe",		's',		TRUE,
127868481Seric 	"QueueTimeout",		'T',		FALSE,
127968481Seric 	"TimeZoneSpec",		't',		FALSE,
128068481Seric 	"UserDatabaseSpec",	'U',		FALSE,
128168481Seric 	"DefaultUser",		'u',		FALSE,
128268481Seric 	"FallbackMXhost",	'V',		FALSE,
128368481Seric 	"Verbose",		'v',		TRUE,
128468481Seric 	"TryNullMXList",	'w',		TRUE,
128568481Seric 	"QueueLA",		'x',		FALSE,
128668481Seric 	"RefuseLA",		'X',		FALSE,
128768481Seric 	"RecipientFactor",	'y',		FALSE,
128868569Seric 	"ForkEachJob",		'Y',		FALSE,
128968481Seric 	"ClassFactor",		'z',		FALSE,
129068569Seric 	"RetryFactor",		'Z',		FALSE,
129168481Seric #define O_QUEUESORTORD	0x81
129268481Seric 	"QueueSortOrder",	O_QUEUESORTORD,	TRUE,
129369401Seric #define O_HOSTSFILE	0x82
129469401Seric 	"HostsFile",		O_HOSTSFILE,	FALSE,
129568481Seric #define O_MQA		0x83
129668481Seric 	"MinQueueAge",		O_MQA,		TRUE,
129768481Seric #define O_MHSA		0x84
129868481Seric /*
129968481Seric 	"MaxHostStatAge",	O_MHSA,		TRUE,
130068481Seric */
130168481Seric #define O_DEFCHARSET	0x85
130268481Seric 	"DefaultCharSet",	O_DEFCHARSET,	TRUE,
130368481Seric #define O_SSFILE	0x86
130468481Seric 	"ServiceSwitchFile",	O_SSFILE,	FALSE,
130568481Seric #define O_DIALDELAY	0x87
130668481Seric 	"DialDelay",		O_DIALDELAY,	TRUE,
130768481Seric #define O_NORCPTACTION	0x88
130868481Seric 	"NoRecipientAction",	O_NORCPTACTION,	TRUE,
130968490Seric #define O_SAFEFILEENV	0x89
131068490Seric 	"SafeFileEnvironment",	O_SAFEFILEENV,	FALSE,
131168569Seric #define O_MAXMSGSIZE	0x8a
131268569Seric 	"MaxMessageSize",	O_MAXMSGSIZE,	FALSE,
131368756Seric #define O_COLONOKINADDR	0x8b
131468756Seric 	"ColonOkInAddr",	O_COLONOKINADDR, TRUE,
131569724Seric #define O_MAXQUEUERUN	0x8c
131669724Seric 	"MaxQueueRunSize",	O_MAXQUEUERUN,	TRUE,
131768481Seric 
131868481Seric 	NULL,			'\0',		FALSE,
131968481Seric };
132068481Seric 
132168481Seric 
132268481Seric 
132369748Seric void
132458734Seric setoption(opt, val, safe, sticky, e)
132569748Seric 	int opt;
13268256Seric 	char *val;
132721755Seric 	bool safe;
13288269Seric 	bool sticky;
132958734Seric 	register ENVELOPE *e;
13308256Seric {
133157207Seric 	register char *p;
133268481Seric 	register struct optioninfo *o;
133368481Seric 	char *subopt;
13348265Seric 	extern bool atobool();
133512633Seric 	extern time_t convtime();
133614879Seric 	extern int QueueLA;
133714879Seric 	extern int RefuseLA;
133864718Seric 	extern bool Warn_Q_option;
13398256Seric 
134068481Seric 	errno = 0;
134168481Seric 	if (opt == ' ')
134268481Seric 	{
134368481Seric 		/* full word options */
134468481Seric 		struct optioninfo *sel;
134568481Seric 
134668481Seric 		p = strchr(val, '=');
134768481Seric 		if (p == NULL)
134868481Seric 			p = &val[strlen(val)];
134968481Seric 		while (*--p == ' ')
135068481Seric 			continue;
135168481Seric 		while (*++p == ' ')
135268481Seric 			*p = '\0';
135368481Seric 		if (p == val)
135468481Seric 		{
135568481Seric 			syserr("readcf: null option name");
135668481Seric 			return;
135768481Seric 		}
135868481Seric 		if (*p == '=')
135968481Seric 			*p++ = '\0';
136068481Seric 		while (*p == ' ')
136168481Seric 			p++;
136268481Seric 		subopt = strchr(val, '.');
136368481Seric 		if (subopt != NULL)
136468481Seric 			*subopt++ = '\0';
136568481Seric 		sel = NULL;
136668481Seric 		for (o = OptionTab; o->o_name != NULL; o++)
136768481Seric 		{
136868481Seric 			if (strncasecmp(o->o_name, val, strlen(val)) != 0)
136968481Seric 				continue;
137068481Seric 			if (strlen(o->o_name) == strlen(val))
137168481Seric 			{
137268481Seric 				/* completely specified -- this must be it */
137368481Seric 				sel = NULL;
137468481Seric 				break;
137568481Seric 			}
137668481Seric 			if (sel != NULL)
137768481Seric 				break;
137868481Seric 			sel = o;
137968481Seric 		}
138068481Seric 		if (sel != NULL && o->o_name == NULL)
138168481Seric 			o = sel;
138268481Seric 		else if (o->o_name == NULL)
138368481Seric 		{
138468481Seric 			syserr("readcf: unknown option name %s", val);
138568481Seric 			return;
138668481Seric 		}
138768481Seric 		else if (sel != NULL)
138868481Seric 		{
138968481Seric 			syserr("readcf: ambiguous option name %s (matches %s and %s)",
139068481Seric 				val, sel->o_name, o->o_name);
139168481Seric 			return;
139268481Seric 		}
139368481Seric 		if (strlen(val) != strlen(o->o_name))
139468481Seric 		{
139568481Seric 			bool oldVerbose = Verbose;
139668481Seric 
139768481Seric 			Verbose = TRUE;
139868481Seric 			message("Option %s used as abbreviation for %s",
139968481Seric 				val, o->o_name);
140068481Seric 			Verbose = oldVerbose;
140168481Seric 		}
140268481Seric 		opt = o->o_code;
140368481Seric 		val = p;
140468481Seric 	}
140568481Seric 	else
140668481Seric 	{
140768481Seric 		for (o = OptionTab; o->o_name != NULL; o++)
140868481Seric 		{
140968481Seric 			if (o->o_code == opt)
141068481Seric 				break;
141168481Seric 		}
141268481Seric 		subopt = NULL;
141368481Seric 	}
141468481Seric 
14158256Seric 	if (tTd(37, 1))
141668481Seric 	{
141768481Seric 		printf(isascii(opt) && isprint(opt) ?
141868481Seric 			    "setoption %s (%c).%s=%s" :
141968481Seric 			    "setoption %s (0x%x).%s=%s",
142068481Seric 			o->o_name == NULL ? "<unknown>" : o->o_name,
142168481Seric 			opt,
142268481Seric 			subopt == NULL ? "" : subopt,
142368481Seric 			val);
142468481Seric 	}
14258256Seric 
14268256Seric 	/*
14278269Seric 	**  See if this option is preset for us.
14288256Seric 	*/
14298256Seric 
143059731Seric 	if (!sticky && bitnset(opt, StickyOpt))
14318269Seric 	{
14329341Seric 		if (tTd(37, 1))
14339341Seric 			printf(" (ignored)\n");
14348269Seric 		return;
14358269Seric 	}
14368269Seric 
143721755Seric 	/*
143821755Seric 	**  Check to see if this option can be specified by this user.
143921755Seric 	*/
144021755Seric 
144163787Seric 	if (!safe && RealUid == 0)
144221755Seric 		safe = TRUE;
144368481Seric 	if (!safe && !o->o_safe)
144421755Seric 	{
144539111Srick 		if (opt != 'M' || (val[0] != 'r' && val[0] != 's'))
144621755Seric 		{
144736582Sbostic 			if (tTd(37, 1))
144836582Sbostic 				printf(" (unsafe)");
144963787Seric 			if (RealUid != geteuid())
145036582Sbostic 			{
145151210Seric 				if (tTd(37, 1))
145251210Seric 					printf("(Resetting uid)");
145363787Seric 				(void) setgid(RealGid);
145463787Seric 				(void) setuid(RealUid);
145536582Sbostic 			}
145621755Seric 		}
145721755Seric 	}
145851210Seric 	if (tTd(37, 1))
145917985Seric 		printf("\n");
14608269Seric 
146168481Seric 	switch (opt & 0xff)
14628256Seric 	{
146359709Seric 	  case '7':		/* force seven-bit input */
146468481Seric 		SevenBitInput = atobool(val);
146552106Seric 		break;
146652106Seric 
146769480Seric #if MIME8TO7
146868481Seric 	  case '8':		/* handling of 8-bit input */
146968481Seric 		switch (*val)
147068481Seric 		{
147168481Seric 		  case 'm':		/* convert 8-bit, convert MIME */
147268481Seric 			MimeMode = MM_CVTMIME|MM_MIME8BIT;
147368481Seric 			break;
147468481Seric 
147568481Seric 		  case 'p':		/* pass 8 bit, convert MIME */
147668856Seric 			MimeMode = MM_CVTMIME|MM_PASS8BIT;
147768481Seric 			break;
147868481Seric 
147968481Seric 		  case 's':		/* strict adherence */
148068481Seric 			MimeMode = MM_CVTMIME;
148168481Seric 			break;
148268481Seric 
148368856Seric #if 0
148468856Seric 		  case 'r':		/* reject 8-bit, don't convert MIME */
148568856Seric 			MimeMode = 0;
148668856Seric 			break;
148768856Seric 
148868856Seric 		  case 'j':		/* "just send 8" */
148968856Seric 			MimeMode = MM_PASS8BIT;
149068856Seric 			break;
149168856Seric 
149268481Seric 		  case 'a':		/* encode 8 bit if available */
149368481Seric 			MimeMode = MM_MIME8BIT|MM_PASS8BIT|MM_CVTMIME;
149468481Seric 			break;
149568481Seric 
149668481Seric 		  case 'c':		/* convert 8 bit to MIME, never 7 bit */
149768481Seric 			MimeMode = MM_MIME8BIT;
149868481Seric 			break;
149968856Seric #endif
150068481Seric 
150168481Seric 		  default:
150268481Seric 			syserr("Unknown 8-bit mode %c", *val);
150368481Seric 			exit(EX_USAGE);
150468481Seric 		}
150568481Seric 		break;
150669480Seric #endif
150768481Seric 
15088256Seric 	  case 'A':		/* set default alias file */
15099381Seric 		if (val[0] == '\0')
151059672Seric 			setalias("aliases");
15119381Seric 		else
151259672Seric 			setalias(val);
15138256Seric 		break;
15148256Seric 
151517474Seric 	  case 'a':		/* look N minutes for "@:@" in alias file */
151617474Seric 		if (val[0] == '\0')
151764796Seric 			SafeAlias = 5 * 60;		/* five minutes */
151817474Seric 		else
151964796Seric 			SafeAlias = convtime(val, 'm');
152017474Seric 		break;
152117474Seric 
152216843Seric 	  case 'B':		/* substitution for blank character */
152316843Seric 		SpaceSub = val[0];
152416843Seric 		if (SpaceSub == '\0')
152516843Seric 			SpaceSub = ' ';
152616843Seric 		break;
152716843Seric 
152859283Seric 	  case 'b':		/* min blocks free on queue fs/max msg size */
152959283Seric 		p = strchr(val, '/');
153059283Seric 		if (p != NULL)
153159283Seric 		{
153259283Seric 			*p++ = '\0';
153359283Seric 			MaxMessageSize = atol(p);
153459283Seric 		}
153558082Seric 		MinBlocksFree = atol(val);
153658082Seric 		break;
153758082Seric 
15389284Seric 	  case 'c':		/* don't connect to "expensive" mailers */
15399381Seric 		NoConnect = atobool(val);
15409284Seric 		break;
15419284Seric 
154251305Seric 	  case 'C':		/* checkpoint every N addresses */
154351305Seric 		CheckpointInterval = atoi(val);
154424944Seric 		break;
154524944Seric 
15469284Seric 	  case 'd':		/* delivery mode */
15479284Seric 		switch (*val)
15488269Seric 		{
15499284Seric 		  case '\0':
155058734Seric 			e->e_sendmode = SM_DELIVER;
15518269Seric 			break;
15528269Seric 
155310755Seric 		  case SM_QUEUE:	/* queue only */
155410755Seric #ifndef QUEUE
155510755Seric 			syserr("need QUEUE to set -odqueue");
155656795Seric #endif /* QUEUE */
155710755Seric 			/* fall through..... */
155810755Seric 
15599284Seric 		  case SM_DELIVER:	/* do everything */
15609284Seric 		  case SM_FORK:		/* fork after verification */
156158734Seric 			e->e_sendmode = *val;
15628269Seric 			break;
15638269Seric 
15648269Seric 		  default:
15659284Seric 			syserr("Unknown delivery mode %c", *val);
15668269Seric 			exit(EX_USAGE);
15678269Seric 		}
15688269Seric 		break;
15698269Seric 
15709146Seric 	  case 'D':		/* rebuild alias database as needed */
15719381Seric 		AutoRebuild = atobool(val);
15729146Seric 		break;
15739146Seric 
157455372Seric 	  case 'E':		/* error message header/header file */
157555379Seric 		if (*val != '\0')
157655379Seric 			ErrMsgFile = newstr(val);
157755372Seric 		break;
157855372Seric 
15798269Seric 	  case 'e':		/* set error processing mode */
15808269Seric 		switch (*val)
15818269Seric 		{
15829381Seric 		  case EM_QUIET:	/* be silent about it */
15839381Seric 		  case EM_MAIL:		/* mail back */
15849381Seric 		  case EM_BERKNET:	/* do berknet error processing */
15859381Seric 		  case EM_WRITE:	/* write back (or mail) */
15869381Seric 		  case EM_PRINT:	/* print errors normally (default) */
158758734Seric 			e->e_errormode = *val;
15888269Seric 			break;
15898269Seric 		}
15908269Seric 		break;
15918269Seric 
15929049Seric 	  case 'F':		/* file mode */
159317975Seric 		FileMode = atooct(val) & 0777;
15949049Seric 		break;
15959049Seric 
15968269Seric 	  case 'f':		/* save Unix-style From lines on front */
15979381Seric 		SaveFrom = atobool(val);
15988269Seric 		break;
15998269Seric 
160053735Seric 	  case 'G':		/* match recipients against GECOS field */
160153735Seric 		MatchGecos = atobool(val);
160253735Seric 		break;
160353735Seric 
16048256Seric 	  case 'g':		/* default gid */
160568481Seric   g_opt:
160664133Seric 		if (isascii(*val) && isdigit(*val))
160764133Seric 			DefGid = atoi(val);
160864133Seric 		else
160964133Seric 		{
161064133Seric 			register struct group *gr;
161164133Seric 
161264133Seric 			DefGid = -1;
161364133Seric 			gr = getgrnam(val);
161464133Seric 			if (gr == NULL)
161568481Seric 				syserr("readcf: option %c: unknown group %s",
161668481Seric 					opt, val);
161764133Seric 			else
161864133Seric 				DefGid = gr->gr_gid;
161964133Seric 		}
16208256Seric 		break;
16218256Seric 
16228256Seric 	  case 'H':		/* help file */
16239381Seric 		if (val[0] == '\0')
16248269Seric 			HelpFile = "sendmail.hf";
16259381Seric 		else
16269381Seric 			HelpFile = newstr(val);
16278256Seric 		break;
16288256Seric 
162951305Seric 	  case 'h':		/* maximum hop count */
163051305Seric 		MaxHopCount = atoi(val);
163151305Seric 		break;
163251305Seric 
163335651Seric 	  case 'I':		/* use internet domain name server */
163466334Seric #if NAMED_BIND
163557207Seric 		for (p = val; *p != 0; )
163657207Seric 		{
163757207Seric 			bool clearmode;
163857207Seric 			char *q;
163957207Seric 			struct resolverflags *rfp;
164057207Seric 
164157207Seric 			while (*p == ' ')
164257207Seric 				p++;
164357207Seric 			if (*p == '\0')
164457207Seric 				break;
164557207Seric 			clearmode = FALSE;
164657207Seric 			if (*p == '-')
164757207Seric 				clearmode = TRUE;
164857207Seric 			else if (*p != '+')
164957207Seric 				p--;
165057207Seric 			p++;
165157207Seric 			q = p;
165258050Seric 			while (*p != '\0' && !(isascii(*p) && isspace(*p)))
165357207Seric 				p++;
165457207Seric 			if (*p != '\0')
165557207Seric 				*p++ = '\0';
165668759Seric 			if (strcasecmp(q, "HasWildcardMX") == 0)
165768759Seric 			{
165868759Seric 				NoMXforCanon = !clearmode;
165968759Seric 				continue;
166068759Seric 			}
166157207Seric 			for (rfp = ResolverFlags; rfp->rf_name != NULL; rfp++)
166257207Seric 			{
166357207Seric 				if (strcasecmp(q, rfp->rf_name) == 0)
166457207Seric 					break;
166557207Seric 			}
166664923Seric 			if (rfp->rf_name == NULL)
166764923Seric 				syserr("readcf: I option value %s unrecognized", q);
166864923Seric 			else if (clearmode)
166957207Seric 				_res.options &= ~rfp->rf_bits;
167057207Seric 			else
167157207Seric 				_res.options |= rfp->rf_bits;
167257207Seric 		}
167357207Seric 		if (tTd(8, 2))
167468759Seric 			printf("_res.options = %x, HasWildcardMX = %d\n",
167568759Seric 				_res.options, !NoMXforCanon);
167657207Seric #else
167757207Seric 		usrerr("name server (I option) specified but BIND not compiled in");
167857207Seric #endif
167935651Seric 		break;
168035651Seric 
16818269Seric 	  case 'i':		/* ignore dot lines in message */
16829381Seric 		IgnrDot = atobool(val);
16838269Seric 		break;
16848269Seric 
168559730Seric 	  case 'j':		/* send errors in MIME (RFC 1341) format */
168659730Seric 		SendMIMEErrors = atobool(val);
168759730Seric 		break;
168859730Seric 
168957136Seric 	  case 'J':		/* .forward search path */
169057136Seric 		ForwardPath = newstr(val);
169157136Seric 		break;
169257136Seric 
169354967Seric 	  case 'k':		/* connection cache size */
169454967Seric 		MaxMciCache = atoi(val);
169556215Seric 		if (MaxMciCache < 0)
169656215Seric 			MaxMciCache = 0;
169754967Seric 		break;
169854967Seric 
169954967Seric 	  case 'K':		/* connection cache timeout */
170058796Seric 		MciCacheTimeout = convtime(val, 'm');
170154967Seric 		break;
170254967Seric 
170361104Seric 	  case 'l':		/* use Errors-To: header */
170461104Seric 		UseErrorsTo = atobool(val);
170561104Seric 		break;
170661104Seric 
17078256Seric 	  case 'L':		/* log level */
170864140Seric 		if (safe || LogLevel < atoi(val))
170964140Seric 			LogLevel = atoi(val);
17108256Seric 		break;
17118256Seric 
17128269Seric 	  case 'M':		/* define macro */
171368267Seric 		p = newstr(&val[1]);
171468267Seric 		if (!safe)
171568267Seric 			cleanstrcpy(p, p, MAXNAME);
171668267Seric 		define(val[0], p, CurEnv);
171716878Seric 		sticky = FALSE;
17188269Seric 		break;
17198269Seric 
17208269Seric 	  case 'm':		/* send to me too */
17219381Seric 		MeToo = atobool(val);
17228269Seric 		break;
17238269Seric 
172425820Seric 	  case 'n':		/* validate RHS in newaliases */
172525820Seric 		CheckAliases = atobool(val);
172625820Seric 		break;
172725820Seric 
172861104Seric 	    /* 'N' available -- was "net name" */
172961104Seric 
173058851Seric 	  case 'O':		/* daemon options */
173158851Seric 		setdaemonoptions(val);
173258851Seric 		break;
173358851Seric 
17348269Seric 	  case 'o':		/* assume old style headers */
17359381Seric 		if (atobool(val))
17369341Seric 			CurEnv->e_flags |= EF_OLDSTYLE;
17379341Seric 		else
17389341Seric 			CurEnv->e_flags &= ~EF_OLDSTYLE;
17398269Seric 		break;
17408269Seric 
174158082Seric 	  case 'p':		/* select privacy level */
174258082Seric 		p = val;
174358082Seric 		for (;;)
174458082Seric 		{
174558082Seric 			register struct prival *pv;
174658082Seric 			extern struct prival PrivacyValues[];
174758082Seric 
174858082Seric 			while (isascii(*p) && (isspace(*p) || ispunct(*p)))
174958082Seric 				p++;
175058082Seric 			if (*p == '\0')
175158082Seric 				break;
175258082Seric 			val = p;
175358082Seric 			while (isascii(*p) && isalnum(*p))
175458082Seric 				p++;
175558082Seric 			if (*p != '\0')
175658082Seric 				*p++ = '\0';
175758082Seric 
175858082Seric 			for (pv = PrivacyValues; pv->pv_name != NULL; pv++)
175958082Seric 			{
176058082Seric 				if (strcasecmp(val, pv->pv_name) == 0)
176158082Seric 					break;
176258082Seric 			}
176358886Seric 			if (pv->pv_name == NULL)
176458886Seric 				syserr("readcf: Op line: %s unrecognized", val);
176558082Seric 			PrivacyFlags |= pv->pv_flag;
176658082Seric 		}
176768479Seric 		sticky = FALSE;
176858082Seric 		break;
176958082Seric 
177024944Seric 	  case 'P':		/* postmaster copy address for returned mail */
177124944Seric 		PostMasterCopy = newstr(val);
177224944Seric 		break;
177324944Seric 
177424944Seric 	  case 'q':		/* slope of queue only function */
177524944Seric 		QueueFactor = atoi(val);
177624944Seric 		break;
177724944Seric 
17788256Seric 	  case 'Q':		/* queue directory */
17799381Seric 		if (val[0] == '\0')
17808269Seric 			QueueDir = "mqueue";
17819381Seric 		else
17829381Seric 			QueueDir = newstr(val);
178358789Seric 		if (RealUid != 0 && !safe)
178464718Seric 			Warn_Q_option = TRUE;
17858256Seric 		break;
17868256Seric 
178758148Seric 	  case 'R':		/* don't prune routes */
178858148Seric 		DontPruneRoutes = atobool(val);
178958148Seric 		break;
179058148Seric 
17918256Seric 	  case 'r':		/* read timeout */
179268481Seric 		if (subopt == NULL)
179368481Seric 			inittimeouts(val);
179468481Seric 		else
179568481Seric 			settimeout(subopt, val);
17968256Seric 		break;
17978256Seric 
17988256Seric 	  case 'S':		/* status file */
17999381Seric 		if (val[0] == '\0')
18008269Seric 			StatFile = "sendmail.st";
18019381Seric 		else
18029381Seric 			StatFile = newstr(val);
18038256Seric 		break;
18048256Seric 
18058265Seric 	  case 's':		/* be super safe, even if expensive */
18069381Seric 		SuperSafe = atobool(val);
18078256Seric 		break;
18088256Seric 
18098256Seric 	  case 'T':		/* queue timeout */
181058737Seric 		p = strchr(val, '/');
181158737Seric 		if (p != NULL)
181258737Seric 		{
181358737Seric 			*p++ = '\0';
181468481Seric 			settimeout("queuewarn", p);
181558737Seric 		}
181668481Seric 		settimeout("queuereturn", val);
181754967Seric 		break;
18188256Seric 
18198265Seric 	  case 't':		/* time zone name */
182052106Seric 		TimeZoneSpec = newstr(val);
18218265Seric 		break;
18228265Seric 
182350556Seric 	  case 'U':		/* location of user database */
182451360Seric 		UdbSpec = newstr(val);
182550556Seric 		break;
182650556Seric 
18278256Seric 	  case 'u':		/* set default uid */
182868481Seric 		for (p = val; *p != '\0'; p++)
182968481Seric 		{
183068481Seric 			if (*p == '.' || *p == '/' || *p == ':')
183168481Seric 			{
183268481Seric 				*p++ = '\0';
183368481Seric 				break;
183468481Seric 			}
183568481Seric 		}
183664133Seric 		if (isascii(*val) && isdigit(*val))
183764133Seric 			DefUid = atoi(val);
183864133Seric 		else
183964133Seric 		{
184064133Seric 			register struct passwd *pw;
184164133Seric 
184264133Seric 			DefUid = -1;
184368693Seric 			pw = sm_getpwnam(val);
184464133Seric 			if (pw == NULL)
184564133Seric 				syserr("readcf: option u: unknown user %s", val);
184664133Seric 			else
184768481Seric 			{
184864133Seric 				DefUid = pw->pw_uid;
184968481Seric 				DefGid = pw->pw_gid;
185068481Seric 			}
185164133Seric 		}
185240973Sbostic 		setdefuser();
18538256Seric 
185468481Seric 		/* handle the group if it is there */
185568481Seric 		if (*p == '\0')
185668481Seric 			break;
185768481Seric 		val = p;
185868481Seric 		goto g_opt;
185968481Seric 
186058851Seric 	  case 'V':		/* fallback MX host */
186158851Seric 		FallBackMX = newstr(val);
186258851Seric 		break;
186358851Seric 
18648269Seric 	  case 'v':		/* run in verbose mode */
18659381Seric 		Verbose = atobool(val);
18668256Seric 		break;
18678256Seric 
186863837Seric 	  case 'w':		/* if we are best MX, try host directly */
186963837Seric 		TryNullMXList = atobool(val);
187063837Seric 		break;
187161104Seric 
187261104Seric 	    /* 'W' available -- was wizard password */
187361104Seric 
187414879Seric 	  case 'x':		/* load avg at which to auto-queue msgs */
187514879Seric 		QueueLA = atoi(val);
187614879Seric 		break;
187714879Seric 
187814879Seric 	  case 'X':		/* load avg at which to auto-reject connections */
187914879Seric 		RefuseLA = atoi(val);
188014879Seric 		break;
188114879Seric 
188224981Seric 	  case 'y':		/* work recipient factor */
188324981Seric 		WkRecipFact = atoi(val);
188424981Seric 		break;
188524981Seric 
188624981Seric 	  case 'Y':		/* fork jobs during queue runs */
188724952Seric 		ForkQueueRuns = atobool(val);
188824952Seric 		break;
188924952Seric 
189024981Seric 	  case 'z':		/* work message class factor */
189124981Seric 		WkClassFact = atoi(val);
189224981Seric 		break;
189324981Seric 
189424981Seric 	  case 'Z':		/* work time factor */
189524981Seric 		WkTimeFact = atoi(val);
189624981Seric 		break;
189724981Seric 
189868481Seric 	  case O_QUEUESORTORD:	/* queue sorting order */
189968481Seric 		switch (*val)
190068481Seric 		{
190168481Seric 		  case 'h':	/* Host first */
190268481Seric 		  case 'H':
190368481Seric 			QueueSortOrder = QS_BYHOST;
190468481Seric 			break;
190568481Seric 
190668481Seric 		  case 'p':	/* Priority order */
190768481Seric 		  case 'P':
190868481Seric 			QueueSortOrder = QS_BYPRIORITY;
190968481Seric 			break;
191068481Seric 
191168481Seric 		  default:
191268481Seric 			syserr("Invalid queue sort order \"%s\"", val);
191368481Seric 		}
191468481Seric 		break;
191568481Seric 
191669401Seric 	  case O_HOSTSFILE:	/* pathname of /etc/hosts file */
191769401Seric 		HostsFile = newstr(val);
191869401Seric 		break;
191969401Seric 
192068481Seric 	  case O_MQA:		/* minimum queue age between deliveries */
192168481Seric 		MinQueueAge = convtime(val, 'm');
192268481Seric 		break;
192368481Seric 
192468481Seric 	  case O_MHSA:		/* maximum age of cached host status */
192568481Seric 		MaxHostStatAge = convtime(val, 'm');
192668481Seric 		break;
192768481Seric 
192868481Seric 	  case O_DEFCHARSET:	/* default character set for mimefying */
192968481Seric 		DefaultCharSet = newstr(denlstring(val, TRUE, TRUE));
193068481Seric 		break;
193168481Seric 
193268481Seric 	  case O_SSFILE:	/* service switch file */
193368481Seric 		ServiceSwitchFile = newstr(val);
193468481Seric 		break;
193568481Seric 
193668481Seric 	  case O_DIALDELAY:	/* delay for dial-on-demand operation */
193768481Seric 		DialDelay = convtime(val, 's');
193868481Seric 		break;
193968481Seric 
194068481Seric 	  case O_NORCPTACTION:	/* what to do if no recipient */
194168481Seric 		if (strcasecmp(val, "none") == 0)
194268481Seric 			NoRecipientAction = NRA_NO_ACTION;
194368481Seric 		else if (strcasecmp(val, "add-to") == 0)
194468481Seric 			NoRecipientAction = NRA_ADD_TO;
194568481Seric 		else if (strcasecmp(val, "add-apparently-to") == 0)
194668481Seric 			NoRecipientAction = NRA_ADD_APPARENTLY_TO;
194768481Seric 		else if (strcasecmp(val, "add-bcc") == 0)
194868481Seric 			NoRecipientAction = NRA_ADD_BCC;
194968481Seric 		else if (strcasecmp(val, "add-to-undisclosed") == 0)
195068481Seric 			NoRecipientAction = NRA_ADD_TO_UNDISCLOSED;
195168481Seric 		else
195268481Seric 			syserr("Invalid NoRecipientAction: %s", val);
195368481Seric 
195468490Seric 	  case O_SAFEFILEENV:	/* chroot() environ for writing to files */
195568490Seric 		SafeFileEnv = newstr(val);
195668490Seric 		break;
195768490Seric 
195868569Seric 	  case O_MAXMSGSIZE:	/* maximum message size */
195969748Seric 		MaxMessageSize = atol(val);
196068569Seric 		break;
196168569Seric 
196268756Seric 	  case O_COLONOKINADDR:	/* old style handling of colon addresses */
196369748Seric 		ColonOkInAddr = atobool(val);
196468756Seric 		break;
196568756Seric 
196669724Seric 	  case O_MAXQUEUERUN:	/* max # of jobs in a single queue run */
196769748Seric 		MaxQueueRun = atol(val);
196869724Seric 		break;
196969724Seric 
19708256Seric 	  default:
197168481Seric 		if (tTd(37, 1))
197268481Seric 		{
197368481Seric 			if (isascii(opt) && isprint(opt))
197468481Seric 				printf("Warning: option %c unknown\n", opt);
197568481Seric 			else
197668481Seric 				printf("Warning: option 0x%x unknown\n", opt);
197768481Seric 		}
19788256Seric 		break;
19798256Seric 	}
198016878Seric 	if (sticky)
198116878Seric 		setbitn(opt, StickyOpt);
19828256Seric }
198310687Seric /*
198468481Seric **  SETCLASS -- set a string into a class
198510687Seric **
198610687Seric **	Parameters:
198768481Seric **		class -- the class to put the string in.
198868481Seric **		str -- the string to enter
198910687Seric **
199010687Seric **	Returns:
199110687Seric **		none.
199210687Seric **
199310687Seric **	Side Effects:
199410687Seric **		puts the word into the symbol table.
199510687Seric */
199610687Seric 
199769748Seric void
199868481Seric setclass(class, str)
199910687Seric 	int class;
200068481Seric 	char *str;
200110687Seric {
200210687Seric 	register STAB *s;
200310687Seric 
200457943Seric 	if (tTd(37, 8))
200568481Seric 		printf("setclass(%c, %s)\n", class, str);
200668481Seric 	s = stab(str, ST_CLASS, ST_ENTER);
200710687Seric 	setbitn(class, s->s_class);
200810687Seric }
200953654Seric /*
201053654Seric **  MAKEMAPENTRY -- create a map entry
201153654Seric **
201253654Seric **	Parameters:
201353654Seric **		line -- the config file line
201453654Seric **
201553654Seric **	Returns:
201669774Seric **		A pointer to the map that has been created.
201769774Seric **		NULL if there was a syntax error.
201853654Seric **
201953654Seric **	Side Effects:
202053654Seric **		Enters the map into the dictionary.
202153654Seric */
202253654Seric 
202369774Seric MAP *
202453654Seric makemapentry(line)
202553654Seric 	char *line;
202653654Seric {
202753654Seric 	register char *p;
202853654Seric 	char *mapname;
202953654Seric 	char *classname;
203064078Seric 	register STAB *s;
203153654Seric 	STAB *class;
203253654Seric 
203358050Seric 	for (p = line; isascii(*p) && isspace(*p); p++)
203453654Seric 		continue;
203558050Seric 	if (!(isascii(*p) && isalnum(*p)))
203653654Seric 	{
203753654Seric 		syserr("readcf: config K line: no map name");
203869774Seric 		return NULL;
203953654Seric 	}
204053654Seric 
204153654Seric 	mapname = p;
204268481Seric 	while ((isascii(*++p) && isalnum(*p)) || *p == '.')
204353654Seric 		continue;
204453654Seric 	if (*p != '\0')
204553654Seric 		*p++ = '\0';
204658050Seric 	while (isascii(*p) && isspace(*p))
204753654Seric 		p++;
204858050Seric 	if (!(isascii(*p) && isalnum(*p)))
204953654Seric 	{
205053654Seric 		syserr("readcf: config K line, map %s: no map class", mapname);
205169774Seric 		return NULL;
205253654Seric 	}
205353654Seric 	classname = p;
205458050Seric 	while (isascii(*++p) && isalnum(*p))
205553654Seric 		continue;
205653654Seric 	if (*p != '\0')
205753654Seric 		*p++ = '\0';
205858050Seric 	while (isascii(*p) && isspace(*p))
205953654Seric 		p++;
206053654Seric 
206153654Seric 	/* look up the class */
206253654Seric 	class = stab(classname, ST_MAPCLASS, ST_FIND);
206353654Seric 	if (class == NULL)
206453654Seric 	{
206553654Seric 		syserr("readcf: map %s: class %s not available", mapname, classname);
206669774Seric 		return NULL;
206753654Seric 	}
206853654Seric 
206953654Seric 	/* enter the map */
207064078Seric 	s = stab(mapname, ST_MAP, ST_ENTER);
207164078Seric 	s->s_map.map_class = &class->s_mapclass;
207264078Seric 	s->s_map.map_mname = newstr(mapname);
207353654Seric 
207464078Seric 	if (class->s_mapclass.map_parse(&s->s_map, p))
207564078Seric 		s->s_map.map_mflags |= MF_VALID;
207664078Seric 
207764078Seric 	if (tTd(37, 5))
207864078Seric 	{
207964078Seric 		printf("map %s, class %s, flags %x, file %s,\n",
208064078Seric 			s->s_map.map_mname, s->s_map.map_class->map_cname,
208164078Seric 			s->s_map.map_mflags,
208264078Seric 			s->s_map.map_file == NULL ? "(null)" : s->s_map.map_file);
208364078Seric 		printf("\tapp %s, domain %s, rebuild %s\n",
208464078Seric 			s->s_map.map_app == NULL ? "(null)" : s->s_map.map_app,
208564078Seric 			s->s_map.map_domain == NULL ? "(null)" : s->s_map.map_domain,
208664078Seric 			s->s_map.map_rebuild == NULL ? "(null)" : s->s_map.map_rebuild);
208764078Seric 	}
208869774Seric 
208969774Seric 	return &s->s_map;
209053654Seric }
209158112Seric /*
2092*69783Seric **  STRTORWSET -- convert string to rewriting set number
2093*69783Seric **
2094*69783Seric **	Parameters:
2095*69783Seric **		p -- the pointer to the string to decode.
2096*69783Seric **		endp -- if set, store the trailing delimiter here.
2097*69783Seric **		stabmode -- ST_ENTER to create this entry, ST_FIND if
2098*69783Seric **			it must already exist.
2099*69783Seric **
2100*69783Seric **	Returns:
2101*69783Seric **		The appropriate ruleset number.
2102*69783Seric **		-1 if it is not valid (error already printed)
2103*69783Seric */
2104*69783Seric 
2105*69783Seric int
2106*69783Seric strtorwset(p, endp, stabmode)
2107*69783Seric 	char *p;
2108*69783Seric 	char **endp;
2109*69783Seric 	int stabmode;
2110*69783Seric {
2111*69783Seric 	int ruleset;
2112*69783Seric 	static int nextruleset = MAXRWSETS;
2113*69783Seric 
2114*69783Seric 	while (isascii(*p) && isspace(*p))
2115*69783Seric 		p++;
2116*69783Seric 	if (!isascii(*p))
2117*69783Seric 	{
2118*69783Seric 		syserr("invalid ruleset name: \"%.20s\"", p);
2119*69783Seric 		return -1;
2120*69783Seric 	}
2121*69783Seric 	if (isdigit(*p))
2122*69783Seric 	{
2123*69783Seric 		ruleset = strtol(p, endp, 10);
2124*69783Seric 		if (ruleset >= MAXRWSETS / 2 || ruleset < 0)
2125*69783Seric 		{
2126*69783Seric 			syserr("bad ruleset %d (%d max)",
2127*69783Seric 				ruleset, MAXRWSETS / 2);
2128*69783Seric 			ruleset = -1;
2129*69783Seric 		}
2130*69783Seric 	}
2131*69783Seric 	else
2132*69783Seric 	{
2133*69783Seric 		STAB *s;
2134*69783Seric 		char delim;
2135*69783Seric 		char *q;
2136*69783Seric 
2137*69783Seric 		q = p;
2138*69783Seric 		while (*p != '\0' && isascii(*p) &&
2139*69783Seric 		       (isalnum(*p) || strchr("-_$", *p) != NULL))
2140*69783Seric 			p++;
2141*69783Seric 		while (isascii(*p) && isspace(*p))
2142*69783Seric 			*p++ = '\0';
2143*69783Seric 		delim = *p;
2144*69783Seric 		if (delim != '\0')
2145*69783Seric 			*p = '\0';
2146*69783Seric 		s = stab(q, ST_RULESET, stabmode);
2147*69783Seric 		if (delim != '\0')
2148*69783Seric 			*p = delim;
2149*69783Seric 
2150*69783Seric 		if (s == NULL)
2151*69783Seric 		{
2152*69783Seric 			syserr("unknown ruleset %s", q);
2153*69783Seric 			return -1;
2154*69783Seric 		}
2155*69783Seric 
2156*69783Seric 		if (stabmode == ST_ENTER && delim == '=')
2157*69783Seric 		{
2158*69783Seric 			ruleset = strtol(p, endp, 10);
2159*69783Seric 			if (ruleset >= MAXRWSETS / 2 || ruleset < 0)
2160*69783Seric 			{
2161*69783Seric 				syserr("bad ruleset %s = %d (%d max)",
2162*69783Seric 					q, ruleset, MAXRWSETS / 2);
2163*69783Seric 				ruleset = -1;
2164*69783Seric 			}
2165*69783Seric 		}
2166*69783Seric 		else
2167*69783Seric 		{
2168*69783Seric 			if (endp != NULL)
2169*69783Seric 				*endp = p;
2170*69783Seric 			if (s->s_ruleset > 0)
2171*69783Seric 				ruleset = s->s_ruleset;
2172*69783Seric 			else if ((ruleset = --nextruleset) < MAXRWSETS / 2)
2173*69783Seric 			{
2174*69783Seric 				syserr("%s: too many named rulesets (%d max)",
2175*69783Seric 					q, MAXRWSETS / 2);
2176*69783Seric 				ruleset = -1;
2177*69783Seric 			}
2178*69783Seric 		}
2179*69783Seric 		if (s->s_ruleset > 0 && ruleset >= 0 && ruleset != s->s_ruleset)
2180*69783Seric 		{
2181*69783Seric 			syserr("%s: ruleset changed value (old %d, new %d)",
2182*69783Seric 				q, ruleset, s->s_ruleset);
2183*69783Seric 			ruleset = s->s_ruleset;
2184*69783Seric 		}
2185*69783Seric 		else if (ruleset > 0)
2186*69783Seric 		{
2187*69783Seric 			s->s_ruleset = ruleset;
2188*69783Seric 		}
2189*69783Seric 	}
2190*69783Seric 	return ruleset;
2191*69783Seric }
2192*69783Seric /*
219368481Seric **  INITTIMEOUTS -- parse and set timeout values
219458112Seric **
219558112Seric **	Parameters:
219658112Seric **		val -- a pointer to the values.  If NULL, do initial
219758112Seric **			settings.
219858112Seric **
219958112Seric **	Returns:
220058112Seric **		none.
220158112Seric **
220258112Seric **	Side Effects:
220358112Seric **		Initializes the TimeOuts structure
220458112Seric */
220558112Seric 
220664255Seric #define SECONDS
220758112Seric #define MINUTES	* 60
220858112Seric #define HOUR	* 3600
220958112Seric 
221069748Seric void
221168481Seric inittimeouts(val)
221258112Seric 	register char *val;
221358112Seric {
221458112Seric 	register char *p;
221558671Seric 	extern time_t convtime();
221658112Seric 
221758112Seric 	if (val == NULL)
221858112Seric 	{
221958112Seric 		TimeOuts.to_initial = (time_t) 5 MINUTES;
222058112Seric 		TimeOuts.to_helo = (time_t) 5 MINUTES;
222158112Seric 		TimeOuts.to_mail = (time_t) 10 MINUTES;
222258112Seric 		TimeOuts.to_rcpt = (time_t) 1 HOUR;
222358112Seric 		TimeOuts.to_datainit = (time_t) 5 MINUTES;
222458112Seric 		TimeOuts.to_datablock = (time_t) 1 HOUR;
222558112Seric 		TimeOuts.to_datafinal = (time_t) 1 HOUR;
222658112Seric 		TimeOuts.to_rset = (time_t) 5 MINUTES;
222758112Seric 		TimeOuts.to_quit = (time_t) 2 MINUTES;
222858112Seric 		TimeOuts.to_nextcommand = (time_t) 1 HOUR;
222958112Seric 		TimeOuts.to_miscshort = (time_t) 2 MINUTES;
223068481Seric #if IDENTPROTO
223164255Seric 		TimeOuts.to_ident = (time_t) 30 SECONDS;
223268481Seric #else
223368481Seric 		TimeOuts.to_ident = (time_t) 0 SECONDS;
223468481Seric #endif
223568481Seric 		TimeOuts.to_fileopen = (time_t) 60 SECONDS;
223658112Seric 		return;
223758112Seric 	}
223858112Seric 
223958112Seric 	for (;; val = p)
224058112Seric 	{
224158112Seric 		while (isascii(*val) && isspace(*val))
224258112Seric 			val++;
224358112Seric 		if (*val == '\0')
224458112Seric 			break;
224558112Seric 		for (p = val; *p != '\0' && *p != ','; p++)
224658112Seric 			continue;
224758112Seric 		if (*p != '\0')
224858112Seric 			*p++ = '\0';
224958112Seric 
225058112Seric 		if (isascii(*val) && isdigit(*val))
225158112Seric 		{
225258112Seric 			/* old syntax -- set everything */
225358796Seric 			TimeOuts.to_mail = convtime(val, 'm');
225458112Seric 			TimeOuts.to_rcpt = TimeOuts.to_mail;
225558112Seric 			TimeOuts.to_datainit = TimeOuts.to_mail;
225658112Seric 			TimeOuts.to_datablock = TimeOuts.to_mail;
225758112Seric 			TimeOuts.to_datafinal = TimeOuts.to_mail;
225858112Seric 			TimeOuts.to_nextcommand = TimeOuts.to_mail;
225958112Seric 			continue;
226058112Seric 		}
226158112Seric 		else
226258112Seric 		{
226368481Seric 			register char *q = strchr(val, ':');
226458112Seric 
226568481Seric 			if (q == NULL && (q = strchr(val, '=')) == NULL)
226658112Seric 			{
226758112Seric 				/* syntax error */
226858112Seric 				continue;
226958112Seric 			}
227058112Seric 			*q++ = '\0';
227168481Seric 			settimeout(val, q);
227268481Seric 		}
227368481Seric 	}
227468481Seric }
227568481Seric /*
227668481Seric **  SETTIMEOUT -- set an individual timeout
227768481Seric **
227868481Seric **	Parameters:
227968481Seric **		name -- the name of the timeout.
228068481Seric **		val -- the value of the timeout.
228168481Seric **
228268481Seric **	Returns:
228368481Seric **		none.
228468481Seric */
228558112Seric 
228669748Seric void
228768481Seric settimeout(name, val)
228868481Seric 	char *name;
228968481Seric 	char *val;
229068481Seric {
229168481Seric 	register char *p;
229268481Seric 	time_t to;
229368481Seric 	extern time_t convtime();
229468481Seric 
229568481Seric 	to = convtime(val, 'm');
229668481Seric 	p = strchr(name, '.');
229768481Seric 	if (p != NULL)
229868481Seric 		*p++ = '\0';
229968481Seric 
230068481Seric 	if (strcasecmp(name, "initial") == 0)
230168481Seric 		TimeOuts.to_initial = to;
230268481Seric 	else if (strcasecmp(name, "mail") == 0)
230368481Seric 		TimeOuts.to_mail = to;
230468481Seric 	else if (strcasecmp(name, "rcpt") == 0)
230568481Seric 		TimeOuts.to_rcpt = to;
230668481Seric 	else if (strcasecmp(name, "datainit") == 0)
230768481Seric 		TimeOuts.to_datainit = to;
230868481Seric 	else if (strcasecmp(name, "datablock") == 0)
230968481Seric 		TimeOuts.to_datablock = to;
231068481Seric 	else if (strcasecmp(name, "datafinal") == 0)
231168481Seric 		TimeOuts.to_datafinal = to;
231268481Seric 	else if (strcasecmp(name, "command") == 0)
231368481Seric 		TimeOuts.to_nextcommand = to;
231468481Seric 	else if (strcasecmp(name, "rset") == 0)
231568481Seric 		TimeOuts.to_rset = to;
231668481Seric 	else if (strcasecmp(name, "helo") == 0)
231768481Seric 		TimeOuts.to_helo = to;
231868481Seric 	else if (strcasecmp(name, "quit") == 0)
231968481Seric 		TimeOuts.to_quit = to;
232068481Seric 	else if (strcasecmp(name, "misc") == 0)
232168481Seric 		TimeOuts.to_miscshort = to;
232268481Seric 	else if (strcasecmp(name, "ident") == 0)
232368481Seric 		TimeOuts.to_ident = to;
232468481Seric 	else if (strcasecmp(name, "fileopen") == 0)
232568481Seric 		TimeOuts.to_fileopen = to;
232668481Seric 	else if (strcasecmp(name, "queuewarn") == 0)
232768481Seric 	{
232868481Seric 		to = convtime(val, 'h');
232968481Seric 		if (p == NULL || strcmp(p, "*") == 0)
233068481Seric 		{
233168481Seric 			TimeOuts.to_q_warning[TOC_NORMAL] = to;
233268481Seric 			TimeOuts.to_q_warning[TOC_URGENT] = to;
233368481Seric 			TimeOuts.to_q_warning[TOC_NONURGENT] = to;
233458112Seric 		}
233568481Seric 		else if (strcasecmp(p, "normal") == 0)
233668481Seric 			TimeOuts.to_q_warning[TOC_NORMAL] = to;
233768481Seric 		else if (strcasecmp(p, "urgent") == 0)
233868481Seric 			TimeOuts.to_q_warning[TOC_URGENT] = to;
233968481Seric 		else if (strcasecmp(p, "non-urgent") == 0)
234068481Seric 			TimeOuts.to_q_warning[TOC_NONURGENT] = to;
234168481Seric 		else
234268481Seric 			syserr("settimeout: invalid queuewarn subtimeout %s", p);
234358112Seric 	}
234468481Seric 	else if (strcasecmp(name, "queuereturn") == 0)
234568481Seric 	{
234668481Seric 		to = convtime(val, 'd');
234768481Seric 		if (p == NULL || strcmp(p, "*") == 0)
234868481Seric 		{
234968481Seric 			TimeOuts.to_q_return[TOC_NORMAL] = to;
235068481Seric 			TimeOuts.to_q_return[TOC_URGENT] = to;
235168481Seric 			TimeOuts.to_q_return[TOC_NONURGENT] = to;
235268481Seric 		}
235368481Seric 		else if (strcasecmp(p, "normal") == 0)
235468481Seric 			TimeOuts.to_q_return[TOC_NORMAL] = to;
235568481Seric 		else if (strcasecmp(p, "urgent") == 0)
235668481Seric 			TimeOuts.to_q_return[TOC_URGENT] = to;
235768481Seric 		else if (strcasecmp(p, "non-urgent") == 0)
235868481Seric 			TimeOuts.to_q_return[TOC_NONURGENT] = to;
235968481Seric 		else
236068481Seric 			syserr("settimeout: invalid queuereturn subtimeout %s", p);
236168481Seric 	}
236268481Seric 	else
236368481Seric 		syserr("settimeout: invalid timeout %s", name);
236458112Seric }
2365