xref: /csrg-svn/usr.sbin/sendmail/src/readcf.c (revision 69956)
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*69956Seric static char sccsid[] = "@(#)readcf.c	8.107 (Berkeley) 06/20/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 			}
472*69956Seric 			for (p = &bp[1]; *p != '\0' && *p != '='; 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;
68669935Seric # if 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 */
86969858Seric 			/* extract MTA name type; default to "dns" */
87068481Seric 			m->m_mtatype = newstr(p);
87168481Seric 			p = strchr(m->m_mtatype, '/');
87268481Seric 			if (p != NULL)
87368481Seric 			{
87468481Seric 				*p++ = '\0';
87568481Seric 				if (*p == '\0')
87668481Seric 					p = NULL;
87768481Seric 			}
87869858Seric 			if (*m->m_mtatype == '\0')
87969858Seric 				m->m_mtatype = "dns";
88069858Seric 
88169858Seric 			/* extract address type; default to "rfc822" */
88269858Seric 			m->m_addrtype = p;
88369858Seric 			if (p != NULL)
88468481Seric 				p = strchr(p, '/');
88568481Seric 			if (p != NULL)
88668481Seric 			{
88768481Seric 				*p++ = '\0';
88868481Seric 				if (*p == '\0')
88968481Seric 					p = NULL;
89068481Seric 			}
89169858Seric 			if (m->m_addrtype == NULL || *m->m_addrtype == '\0')
89269858Seric 				m->m_addrtype = "rfc822";
89369858Seric 
89469858Seric 			/* extract diagnostic type; default to "smtp" */
89569858Seric 			m->m_diagtype = p;
89669858Seric 			if (m->m_diagtype == NULL || *m->m_diagtype == '\0')
89769858Seric 				m->m_diagtype = "smtp";
89868481Seric 			break;
89968481Seric 
90068481Seric 		  case 'U':		/* user id */
90168481Seric 			if (isascii(*p) && !isdigit(*p))
90268481Seric 			{
90368481Seric 				char *q = p;
90468481Seric 				struct passwd *pw;
90568481Seric 
90668481Seric 				while (isascii(*p) && isalnum(*p))
90768481Seric 					p++;
90868481Seric 				while (isascii(*p) && isspace(*p))
90968481Seric 					*p++ = '\0';
91068481Seric 				if (*p != '\0')
91168481Seric 					*p++ = '\0';
91268693Seric 				pw = sm_getpwnam(q);
91368481Seric 				if (pw == NULL)
91468481Seric 					syserr("readcf: mailer U= flag: unknown user %s", q);
91568481Seric 				else
91668481Seric 				{
91768481Seric 					m->m_uid = pw->pw_uid;
91868481Seric 					m->m_gid = pw->pw_gid;
91968481Seric 				}
92068481Seric 			}
92168481Seric 			else
92268481Seric 			{
92368481Seric 				auto char *q;
92468481Seric 
92568481Seric 				m->m_uid = strtol(p, &q, 0);
92668481Seric 				p = q;
92768481Seric 			}
92868481Seric 			while (isascii(*p) && isspace(*p))
92968481Seric 				p++;
93068481Seric 			if (*p == '\0')
93168481Seric 				break;
93268481Seric 			if (isascii(*p) && !isdigit(*p))
93368481Seric 			{
93468481Seric 				char *q = p;
93568481Seric 				struct group *gr;
93668481Seric 
93768481Seric 				while (isascii(*p) && isalnum(*p))
93868481Seric 					p++;
93968481Seric 				*p++ = '\0';
94068481Seric 				gr = getgrnam(q);
94168481Seric 				if (gr == NULL)
94268481Seric 					syserr("readcf: mailer U= flag: unknown group %s", q);
94368481Seric 				else
94468481Seric 					m->m_gid = gr->gr_gid;
94568481Seric 			}
94668481Seric 			else
94768481Seric 			{
94868481Seric 				m->m_gid = strtol(p, NULL, 0);
94968481Seric 			}
95068481Seric 			break;
95110327Seric 		}
95210327Seric 
95358333Seric 		p = delimptr;
95410327Seric 	}
95510327Seric 
95658321Seric 	/* do some rationality checking */
95758321Seric 	if (m->m_argv == NULL)
95858321Seric 	{
95958321Seric 		syserr("M%s: A= argument required", m->m_name);
96058321Seric 		return;
96158321Seric 	}
96258321Seric 	if (m->m_mailer == NULL)
96358321Seric 	{
96458321Seric 		syserr("M%s: P= argument required", m->m_name);
96558321Seric 		return;
96658321Seric 	}
96758321Seric 
9684096Seric 	if (NextMailer >= MAXMAILERS)
9694096Seric 	{
9709381Seric 		syserr("too many mailers defined (%d max)", MAXMAILERS);
9714096Seric 		return;
9724096Seric 	}
97357402Seric 
97468481Seric 	/* do some heuristic cleanup for back compatibility */
97568481Seric 	if (bitnset(M_LIMITS, m->m_flags))
97668481Seric 	{
97768481Seric 		if (m->m_linelimit == 0)
97868481Seric 			m->m_linelimit = SMTPLINELIM;
97968481Seric 		if (ConfigLevel < 2)
98068481Seric 			setbitn(M_7BITS, m->m_flags);
98168481Seric 	}
98268481Seric 
98368481Seric 	if (ConfigLevel < 6 &&
98468481Seric 	    (strcmp(m->m_mailer, "[IPC]") == 0 ||
98568481Seric 	     strcmp(m->m_mailer, "[TCP]") == 0))
98668481Seric 	{
98768481Seric 		if (m->m_mtatype == NULL)
98868481Seric 			m->m_mtatype = "dns";
98968481Seric 		if (m->m_addrtype == NULL)
99068481Seric 			m->m_addrtype = "rfc822";
99168481Seric 		if (m->m_diagtype == NULL)
99268481Seric 			m->m_diagtype = "smtp";
99368481Seric 	}
99468481Seric 
99568481Seric 	/* enter the mailer into the symbol table */
99610327Seric 	s = stab(m->m_name, ST_MAILER, ST_ENTER);
99757402Seric 	if (s->s_mailer != NULL)
99857402Seric 	{
99957402Seric 		i = s->s_mailer->m_mno;
100057402Seric 		free(s->s_mailer);
100157402Seric 	}
100257402Seric 	else
100357402Seric 	{
100457402Seric 		i = NextMailer++;
100557402Seric 	}
100657402Seric 	Mailer[i] = s->s_mailer = m;
100757454Seric 	m->m_mno = i;
100810327Seric }
100910327Seric /*
101010327Seric **  MUNCHSTRING -- translate a string into internal form.
101110327Seric **
101210327Seric **	Parameters:
101310327Seric **		p -- the string to munch.
101458333Seric **		delimptr -- if non-NULL, set to the pointer of the
101558333Seric **			field delimiter character.
101610327Seric **
101710327Seric **	Returns:
101810327Seric **		the munched string.
101910327Seric */
10204096Seric 
102110327Seric char *
102258333Seric munchstring(p, delimptr)
102310327Seric 	register char *p;
102458333Seric 	char **delimptr;
102510327Seric {
102610327Seric 	register char *q;
102710327Seric 	bool backslash = FALSE;
102810327Seric 	bool quotemode = FALSE;
102910327Seric 	static char buf[MAXLINE];
10304096Seric 
103110327Seric 	for (q = buf; *p != '\0'; p++)
10324096Seric 	{
103310327Seric 		if (backslash)
103410327Seric 		{
103510327Seric 			/* everything is roughly literal */
103610357Seric 			backslash = FALSE;
103710327Seric 			switch (*p)
103810327Seric 			{
103910327Seric 			  case 'r':		/* carriage return */
104010327Seric 				*q++ = '\r';
104110327Seric 				continue;
104210327Seric 
104310327Seric 			  case 'n':		/* newline */
104410327Seric 				*q++ = '\n';
104510327Seric 				continue;
104610327Seric 
104710327Seric 			  case 'f':		/* form feed */
104810327Seric 				*q++ = '\f';
104910327Seric 				continue;
105010327Seric 
105110327Seric 			  case 'b':		/* backspace */
105210327Seric 				*q++ = '\b';
105310327Seric 				continue;
105410327Seric 			}
105510327Seric 			*q++ = *p;
105610327Seric 		}
105710327Seric 		else
105810327Seric 		{
105910327Seric 			if (*p == '\\')
106010327Seric 				backslash = TRUE;
106110327Seric 			else if (*p == '"')
106210327Seric 				quotemode = !quotemode;
106310327Seric 			else if (quotemode || *p != ',')
106410327Seric 				*q++ = *p;
106510327Seric 			else
106610327Seric 				break;
106710327Seric 		}
10684096Seric 	}
10694096Seric 
107058333Seric 	if (delimptr != NULL)
107158333Seric 		*delimptr = p;
107210327Seric 	*q++ = '\0';
107310327Seric 	return (buf);
107410327Seric }
107510327Seric /*
107610327Seric **  MAKEARGV -- break up a string into words
107710327Seric **
107810327Seric **	Parameters:
107910327Seric **		p -- the string to break up.
108010327Seric **
108110327Seric **	Returns:
108210327Seric **		a char **argv (dynamically allocated)
108310327Seric **
108410327Seric **	Side Effects:
108510327Seric **		munges p.
108610327Seric */
10874096Seric 
108810327Seric char **
108910327Seric makeargv(p)
109010327Seric 	register char *p;
109110327Seric {
109210327Seric 	char *q;
109310327Seric 	int i;
109410327Seric 	char **avp;
109510327Seric 	char *argv[MAXPV + 1];
109610327Seric 
109710327Seric 	/* take apart the words */
109810327Seric 	i = 0;
109910327Seric 	while (*p != '\0' && i < MAXPV)
11004096Seric 	{
110110327Seric 		q = p;
110258050Seric 		while (*p != '\0' && !(isascii(*p) && isspace(*p)))
110310327Seric 			p++;
110458050Seric 		while (isascii(*p) && isspace(*p))
110510327Seric 			*p++ = '\0';
110610327Seric 		argv[i++] = newstr(q);
11074096Seric 	}
110810327Seric 	argv[i++] = NULL;
11094096Seric 
111010327Seric 	/* now make a copy of the argv */
111110327Seric 	avp = (char **) xalloc(sizeof *avp * i);
111216893Seric 	bcopy((char *) argv, (char *) avp, sizeof *avp * i);
111310327Seric 
111410327Seric 	return (avp);
11153308Seric }
11163308Seric /*
11173308Seric **  PRINTRULES -- print rewrite rules (for debugging)
11183308Seric **
11193308Seric **	Parameters:
11203308Seric **		none.
11213308Seric **
11223308Seric **	Returns:
11233308Seric **		none.
11243308Seric **
11253308Seric **	Side Effects:
11263308Seric **		prints rewrite rules.
11273308Seric */
11283308Seric 
112969748Seric void
11303308Seric printrules()
11313308Seric {
11323308Seric 	register struct rewrite *rwp;
11334072Seric 	register int ruleset;
11343308Seric 
11354072Seric 	for (ruleset = 0; ruleset < 10; ruleset++)
11363308Seric 	{
11374072Seric 		if (RewriteRules[ruleset] == NULL)
11384072Seric 			continue;
11398067Seric 		printf("\n----Rule Set %d:", ruleset);
11403308Seric 
11414072Seric 		for (rwp = RewriteRules[ruleset]; rwp != NULL; rwp = rwp->r_next)
11423308Seric 		{
11438067Seric 			printf("\nLHS:");
11448067Seric 			printav(rwp->r_lhs);
11458067Seric 			printf("RHS:");
11468067Seric 			printav(rwp->r_rhs);
11473308Seric 		}
11483308Seric 	}
11493308Seric }
115068481Seric /*
115168481Seric **  PRINTMAILER -- print mailer structure (for debugging)
115268481Seric **
115368481Seric **	Parameters:
115468481Seric **		m -- the mailer to print
115568481Seric **
115668481Seric **	Returns:
115768481Seric **		none.
115868481Seric */
11594319Seric 
116069748Seric void
116168481Seric printmailer(m)
116268481Seric 	register MAILER *m;
116368481Seric {
116468481Seric 	int j;
116568481Seric 
116668481Seric 	printf("mailer %d (%s): P=%s S=%d/%d R=%d/%d M=%ld U=%d:%d F=",
116768481Seric 		m->m_mno, m->m_name,
116868481Seric 		m->m_mailer, m->m_se_rwset, m->m_sh_rwset,
116968481Seric 		m->m_re_rwset, m->m_rh_rwset, m->m_maxsize,
117068481Seric 		m->m_uid, m->m_gid);
117168481Seric 	for (j = '\0'; j <= '\177'; j++)
117268481Seric 		if (bitnset(j, m->m_flags))
117368481Seric 			(void) putchar(j);
117468481Seric 	printf(" L=%d E=", m->m_linelimit);
117568481Seric 	xputs(m->m_eol);
117668481Seric 	if (m->m_defcharset != NULL)
117768481Seric 		printf(" C=%s", m->m_defcharset);
117868481Seric 	printf(" T=%s/%s/%s",
117968481Seric 		m->m_mtatype == NULL ? "<undefined>" : m->m_mtatype,
118068481Seric 		m->m_addrtype == NULL ? "<undefined>" : m->m_addrtype,
118168481Seric 		m->m_diagtype == NULL ? "<undefined>" : m->m_diagtype);
118268481Seric 	if (m->m_argv != NULL)
118368481Seric 	{
118468481Seric 		char **a = m->m_argv;
118568481Seric 
118668481Seric 		printf(" A=");
118768481Seric 		while (*a != NULL)
118868481Seric 		{
118968481Seric 			if (a != m->m_argv)
119068481Seric 				printf(" ");
119168481Seric 			xputs(*a++);
119268481Seric 		}
119368481Seric 	}
119468481Seric 	printf("\n");
119568481Seric }
11964096Seric /*
11978256Seric **  SETOPTION -- set global processing option
11988256Seric **
11998256Seric **	Parameters:
12008256Seric **		opt -- option name.
12018256Seric **		val -- option value (as a text string).
120221755Seric **		safe -- set if this came from a configuration file.
120321755Seric **			Some options (if set from the command line) will
120421755Seric **			reset the user id to avoid security problems.
12058269Seric **		sticky -- if set, don't let other setoptions override
12068269Seric **			this value.
120758734Seric **		e -- the main envelope.
12088256Seric **
12098256Seric **	Returns:
12108256Seric **		none.
12118256Seric **
12128256Seric **	Side Effects:
12138256Seric **		Sets options as implied by the arguments.
12148256Seric */
12158256Seric 
121610687Seric static BITMAP	StickyOpt;		/* set if option is stuck */
121769748Seric extern void	settimeout __P((char *, char *));
12188269Seric 
121957207Seric 
122066334Seric #if NAMED_BIND
122157207Seric 
122257207Seric struct resolverflags
122357207Seric {
122457207Seric 	char	*rf_name;	/* name of the flag */
122557207Seric 	long	rf_bits;	/* bits to set/clear */
122657207Seric } ResolverFlags[] =
122757207Seric {
122857207Seric 	"debug",	RES_DEBUG,
122957207Seric 	"aaonly",	RES_AAONLY,
123057207Seric 	"usevc",	RES_USEVC,
123157207Seric 	"primary",	RES_PRIMARY,
123257207Seric 	"igntc",	RES_IGNTC,
123357207Seric 	"recurse",	RES_RECURSE,
123457207Seric 	"defnames",	RES_DEFNAMES,
123557207Seric 	"stayopen",	RES_STAYOPEN,
123657207Seric 	"dnsrch",	RES_DNSRCH,
123765583Seric 	"true",		0,		/* to avoid error on old syntax */
123857207Seric 	NULL,		0
123957207Seric };
124057207Seric 
124157207Seric #endif
124257207Seric 
124368481Seric struct optioninfo
124468481Seric {
124568481Seric 	char	*o_name;	/* long name of option */
124668481Seric 	u_char	o_code;		/* short name of option */
124768481Seric 	bool	o_safe;		/* safe for random people to use */
124868481Seric } OptionTab[] =
124968481Seric {
125068481Seric 	"SevenBitInput",	'7',		TRUE,
125169480Seric #if MIME8TO7
125268481Seric 	"EightBitMode",		'8',		TRUE,
125369480Seric #endif
125468481Seric 	"AliasFile",		'A',		FALSE,
125568481Seric 	"AliasWait",		'a',		FALSE,
125668481Seric 	"BlankSub",		'B',		FALSE,
125768481Seric 	"MinFreeBlocks",	'b',		TRUE,
125868481Seric 	"CheckpointInterval",	'C',		TRUE,
125968481Seric 	"HoldExpensive",	'c',		FALSE,
126068481Seric 	"AutoRebuildAliases",	'D',		FALSE,
126168481Seric 	"DeliveryMode",		'd',		TRUE,
126268481Seric 	"ErrorHeader",		'E',		FALSE,
126368481Seric 	"ErrorMode",		'e',		TRUE,
126468481Seric 	"TempFileMode",		'F',		FALSE,
126568481Seric 	"SaveFromLine",		'f',		FALSE,
126668481Seric 	"MatchGECOS",		'G',		FALSE,
126768481Seric 	"HelpFile",		'H',		FALSE,
126868481Seric 	"MaxHopCount",		'h',		FALSE,
126968569Seric 	"ResolverOptions",	'I',		FALSE,
127068481Seric 	"IgnoreDots",		'i',		TRUE,
127168481Seric 	"ForwardPath",		'J',		FALSE,
127268481Seric 	"SendMimeErrors",	'j',		TRUE,
127368481Seric 	"ConnectionCacheSize",	'k',		FALSE,
127468481Seric 	"ConnectionCacheTimeout", 'K',		FALSE,
127568481Seric 	"UseErrorsTo",		'l',		FALSE,
127668481Seric 	"LogLevel",		'L',		FALSE,
127768481Seric 	"MeToo",		'm',		TRUE,
127868481Seric 	"CheckAliases",		'n',		FALSE,
127968481Seric 	"OldStyleHeaders",	'o',		TRUE,
128068481Seric 	"DaemonPortOptions",	'O',		FALSE,
128168481Seric 	"PrivacyOptions",	'p',		TRUE,
128268481Seric 	"PostmasterCopy",	'P',		FALSE,
128368481Seric 	"QueueFactor",		'q',		FALSE,
128468481Seric 	"QueueDirectory",	'Q',		FALSE,
128568481Seric 	"DontPruneRoutes",	'R',		FALSE,
128668481Seric 	"Timeout",		'r',		TRUE,
128768481Seric 	"StatusFile",		'S',		FALSE,
128868481Seric 	"SuperSafe",		's',		TRUE,
128968481Seric 	"QueueTimeout",		'T',		FALSE,
129068481Seric 	"TimeZoneSpec",		't',		FALSE,
129168481Seric 	"UserDatabaseSpec",	'U',		FALSE,
129268481Seric 	"DefaultUser",		'u',		FALSE,
129368481Seric 	"FallbackMXhost",	'V',		FALSE,
129468481Seric 	"Verbose",		'v',		TRUE,
129568481Seric 	"TryNullMXList",	'w',		TRUE,
129668481Seric 	"QueueLA",		'x',		FALSE,
129768481Seric 	"RefuseLA",		'X',		FALSE,
129868481Seric 	"RecipientFactor",	'y',		FALSE,
129968569Seric 	"ForkEachJob",		'Y',		FALSE,
130068481Seric 	"ClassFactor",		'z',		FALSE,
130168569Seric 	"RetryFactor",		'Z',		FALSE,
130268481Seric #define O_QUEUESORTORD	0x81
130368481Seric 	"QueueSortOrder",	O_QUEUESORTORD,	TRUE,
130469401Seric #define O_HOSTSFILE	0x82
130569401Seric 	"HostsFile",		O_HOSTSFILE,	FALSE,
130668481Seric #define O_MQA		0x83
130768481Seric 	"MinQueueAge",		O_MQA,		TRUE,
130868481Seric #define O_MHSA		0x84
130968481Seric /*
131068481Seric 	"MaxHostStatAge",	O_MHSA,		TRUE,
131168481Seric */
131268481Seric #define O_DEFCHARSET	0x85
131368481Seric 	"DefaultCharSet",	O_DEFCHARSET,	TRUE,
131468481Seric #define O_SSFILE	0x86
131568481Seric 	"ServiceSwitchFile",	O_SSFILE,	FALSE,
131668481Seric #define O_DIALDELAY	0x87
131768481Seric 	"DialDelay",		O_DIALDELAY,	TRUE,
131868481Seric #define O_NORCPTACTION	0x88
131968481Seric 	"NoRecipientAction",	O_NORCPTACTION,	TRUE,
132068490Seric #define O_SAFEFILEENV	0x89
132168490Seric 	"SafeFileEnvironment",	O_SAFEFILEENV,	FALSE,
132268569Seric #define O_MAXMSGSIZE	0x8a
132368569Seric 	"MaxMessageSize",	O_MAXMSGSIZE,	FALSE,
132468756Seric #define O_COLONOKINADDR	0x8b
132568756Seric 	"ColonOkInAddr",	O_COLONOKINADDR, TRUE,
132669724Seric #define O_MAXQUEUERUN	0x8c
132769724Seric 	"MaxQueueRunSize",	O_MAXQUEUERUN,	TRUE,
132869838Seric #define O_MAXCHILDREN	0x8d
132969838Seric /*
133069838Seric 	"MaxDaemonChildren",	O_MAXCHILDREN,	FALSE,
133169838Seric */
133269852Seric #define O_KEEPCNAMES	0x8e
133369852Seric 	"DontExpandCnames",	O_KEEPCNAMES,	FALSE,
133468481Seric 
133568481Seric 	NULL,			'\0',		FALSE,
133668481Seric };
133768481Seric 
133868481Seric 
133968481Seric 
134069748Seric void
134158734Seric setoption(opt, val, safe, sticky, e)
134269748Seric 	int opt;
13438256Seric 	char *val;
134421755Seric 	bool safe;
13458269Seric 	bool sticky;
134658734Seric 	register ENVELOPE *e;
13478256Seric {
134857207Seric 	register char *p;
134968481Seric 	register struct optioninfo *o;
135068481Seric 	char *subopt;
13518265Seric 	extern bool atobool();
135212633Seric 	extern time_t convtime();
135314879Seric 	extern int QueueLA;
135414879Seric 	extern int RefuseLA;
135564718Seric 	extern bool Warn_Q_option;
13568256Seric 
135768481Seric 	errno = 0;
135868481Seric 	if (opt == ' ')
135968481Seric 	{
136068481Seric 		/* full word options */
136168481Seric 		struct optioninfo *sel;
136268481Seric 
136368481Seric 		p = strchr(val, '=');
136468481Seric 		if (p == NULL)
136568481Seric 			p = &val[strlen(val)];
136668481Seric 		while (*--p == ' ')
136768481Seric 			continue;
136868481Seric 		while (*++p == ' ')
136968481Seric 			*p = '\0';
137068481Seric 		if (p == val)
137168481Seric 		{
137268481Seric 			syserr("readcf: null option name");
137368481Seric 			return;
137468481Seric 		}
137568481Seric 		if (*p == '=')
137668481Seric 			*p++ = '\0';
137768481Seric 		while (*p == ' ')
137868481Seric 			p++;
137968481Seric 		subopt = strchr(val, '.');
138068481Seric 		if (subopt != NULL)
138168481Seric 			*subopt++ = '\0';
138268481Seric 		sel = NULL;
138368481Seric 		for (o = OptionTab; o->o_name != NULL; o++)
138468481Seric 		{
138568481Seric 			if (strncasecmp(o->o_name, val, strlen(val)) != 0)
138668481Seric 				continue;
138768481Seric 			if (strlen(o->o_name) == strlen(val))
138868481Seric 			{
138968481Seric 				/* completely specified -- this must be it */
139068481Seric 				sel = NULL;
139168481Seric 				break;
139268481Seric 			}
139368481Seric 			if (sel != NULL)
139468481Seric 				break;
139568481Seric 			sel = o;
139668481Seric 		}
139768481Seric 		if (sel != NULL && o->o_name == NULL)
139868481Seric 			o = sel;
139968481Seric 		else if (o->o_name == NULL)
140068481Seric 		{
140168481Seric 			syserr("readcf: unknown option name %s", val);
140268481Seric 			return;
140368481Seric 		}
140468481Seric 		else if (sel != NULL)
140568481Seric 		{
140668481Seric 			syserr("readcf: ambiguous option name %s (matches %s and %s)",
140768481Seric 				val, sel->o_name, o->o_name);
140868481Seric 			return;
140968481Seric 		}
141068481Seric 		if (strlen(val) != strlen(o->o_name))
141168481Seric 		{
141268481Seric 			bool oldVerbose = Verbose;
141368481Seric 
141468481Seric 			Verbose = TRUE;
141568481Seric 			message("Option %s used as abbreviation for %s",
141668481Seric 				val, o->o_name);
141768481Seric 			Verbose = oldVerbose;
141868481Seric 		}
141968481Seric 		opt = o->o_code;
142068481Seric 		val = p;
142168481Seric 	}
142268481Seric 	else
142368481Seric 	{
142468481Seric 		for (o = OptionTab; o->o_name != NULL; o++)
142568481Seric 		{
142668481Seric 			if (o->o_code == opt)
142768481Seric 				break;
142868481Seric 		}
142968481Seric 		subopt = NULL;
143068481Seric 	}
143168481Seric 
14328256Seric 	if (tTd(37, 1))
143368481Seric 	{
143468481Seric 		printf(isascii(opt) && isprint(opt) ?
143569917Seric 			    "setoption %s (%c).%s=" :
143669917Seric 			    "setoption %s (0x%x).%s=",
143768481Seric 			o->o_name == NULL ? "<unknown>" : o->o_name,
143868481Seric 			opt,
143969917Seric 			subopt == NULL ? "" : subopt);
144069917Seric 		xputs(val);
144168481Seric 	}
14428256Seric 
14438256Seric 	/*
14448269Seric 	**  See if this option is preset for us.
14458256Seric 	*/
14468256Seric 
144759731Seric 	if (!sticky && bitnset(opt, StickyOpt))
14488269Seric 	{
14499341Seric 		if (tTd(37, 1))
14509341Seric 			printf(" (ignored)\n");
14518269Seric 		return;
14528269Seric 	}
14538269Seric 
145421755Seric 	/*
145521755Seric 	**  Check to see if this option can be specified by this user.
145621755Seric 	*/
145721755Seric 
145863787Seric 	if (!safe && RealUid == 0)
145921755Seric 		safe = TRUE;
146068481Seric 	if (!safe && !o->o_safe)
146121755Seric 	{
146239111Srick 		if (opt != 'M' || (val[0] != 'r' && val[0] != 's'))
146321755Seric 		{
146436582Sbostic 			if (tTd(37, 1))
146536582Sbostic 				printf(" (unsafe)");
146663787Seric 			if (RealUid != geteuid())
146736582Sbostic 			{
146851210Seric 				if (tTd(37, 1))
146951210Seric 					printf("(Resetting uid)");
147063787Seric 				(void) setgid(RealGid);
147163787Seric 				(void) setuid(RealUid);
147236582Sbostic 			}
147321755Seric 		}
147421755Seric 	}
147551210Seric 	if (tTd(37, 1))
147617985Seric 		printf("\n");
14778269Seric 
147868481Seric 	switch (opt & 0xff)
14798256Seric 	{
148059709Seric 	  case '7':		/* force seven-bit input */
148168481Seric 		SevenBitInput = atobool(val);
148252106Seric 		break;
148352106Seric 
148469480Seric #if MIME8TO7
148568481Seric 	  case '8':		/* handling of 8-bit input */
148668481Seric 		switch (*val)
148768481Seric 		{
148868481Seric 		  case 'm':		/* convert 8-bit, convert MIME */
148968481Seric 			MimeMode = MM_CVTMIME|MM_MIME8BIT;
149068481Seric 			break;
149168481Seric 
149268481Seric 		  case 'p':		/* pass 8 bit, convert MIME */
149368856Seric 			MimeMode = MM_CVTMIME|MM_PASS8BIT;
149468481Seric 			break;
149568481Seric 
149668481Seric 		  case 's':		/* strict adherence */
149768481Seric 			MimeMode = MM_CVTMIME;
149868481Seric 			break;
149968481Seric 
150068856Seric #if 0
150168856Seric 		  case 'r':		/* reject 8-bit, don't convert MIME */
150268856Seric 			MimeMode = 0;
150368856Seric 			break;
150468856Seric 
150568856Seric 		  case 'j':		/* "just send 8" */
150668856Seric 			MimeMode = MM_PASS8BIT;
150768856Seric 			break;
150868856Seric 
150968481Seric 		  case 'a':		/* encode 8 bit if available */
151068481Seric 			MimeMode = MM_MIME8BIT|MM_PASS8BIT|MM_CVTMIME;
151168481Seric 			break;
151268481Seric 
151368481Seric 		  case 'c':		/* convert 8 bit to MIME, never 7 bit */
151468481Seric 			MimeMode = MM_MIME8BIT;
151568481Seric 			break;
151668856Seric #endif
151768481Seric 
151868481Seric 		  default:
151968481Seric 			syserr("Unknown 8-bit mode %c", *val);
152068481Seric 			exit(EX_USAGE);
152168481Seric 		}
152268481Seric 		break;
152369480Seric #endif
152468481Seric 
15258256Seric 	  case 'A':		/* set default alias file */
15269381Seric 		if (val[0] == '\0')
152759672Seric 			setalias("aliases");
15289381Seric 		else
152959672Seric 			setalias(val);
15308256Seric 		break;
15318256Seric 
153217474Seric 	  case 'a':		/* look N minutes for "@:@" in alias file */
153317474Seric 		if (val[0] == '\0')
153464796Seric 			SafeAlias = 5 * 60;		/* five minutes */
153517474Seric 		else
153664796Seric 			SafeAlias = convtime(val, 'm');
153717474Seric 		break;
153817474Seric 
153916843Seric 	  case 'B':		/* substitution for blank character */
154016843Seric 		SpaceSub = val[0];
154116843Seric 		if (SpaceSub == '\0')
154216843Seric 			SpaceSub = ' ';
154316843Seric 		break;
154416843Seric 
154559283Seric 	  case 'b':		/* min blocks free on queue fs/max msg size */
154659283Seric 		p = strchr(val, '/');
154759283Seric 		if (p != NULL)
154859283Seric 		{
154959283Seric 			*p++ = '\0';
155059283Seric 			MaxMessageSize = atol(p);
155159283Seric 		}
155258082Seric 		MinBlocksFree = atol(val);
155358082Seric 		break;
155458082Seric 
15559284Seric 	  case 'c':		/* don't connect to "expensive" mailers */
15569381Seric 		NoConnect = atobool(val);
15579284Seric 		break;
15589284Seric 
155951305Seric 	  case 'C':		/* checkpoint every N addresses */
156051305Seric 		CheckpointInterval = atoi(val);
156124944Seric 		break;
156224944Seric 
15639284Seric 	  case 'd':		/* delivery mode */
15649284Seric 		switch (*val)
15658269Seric 		{
15669284Seric 		  case '\0':
156758734Seric 			e->e_sendmode = SM_DELIVER;
15688269Seric 			break;
15698269Seric 
157010755Seric 		  case SM_QUEUE:	/* queue only */
157110755Seric #ifndef QUEUE
157210755Seric 			syserr("need QUEUE to set -odqueue");
157356795Seric #endif /* QUEUE */
157410755Seric 			/* fall through..... */
157510755Seric 
15769284Seric 		  case SM_DELIVER:	/* do everything */
15779284Seric 		  case SM_FORK:		/* fork after verification */
157858734Seric 			e->e_sendmode = *val;
15798269Seric 			break;
15808269Seric 
15818269Seric 		  default:
15829284Seric 			syserr("Unknown delivery mode %c", *val);
15838269Seric 			exit(EX_USAGE);
15848269Seric 		}
15858269Seric 		break;
15868269Seric 
15879146Seric 	  case 'D':		/* rebuild alias database as needed */
15889381Seric 		AutoRebuild = atobool(val);
15899146Seric 		break;
15909146Seric 
159155372Seric 	  case 'E':		/* error message header/header file */
159255379Seric 		if (*val != '\0')
159355379Seric 			ErrMsgFile = newstr(val);
159455372Seric 		break;
159555372Seric 
15968269Seric 	  case 'e':		/* set error processing mode */
15978269Seric 		switch (*val)
15988269Seric 		{
15999381Seric 		  case EM_QUIET:	/* be silent about it */
16009381Seric 		  case EM_MAIL:		/* mail back */
16019381Seric 		  case EM_BERKNET:	/* do berknet error processing */
16029381Seric 		  case EM_WRITE:	/* write back (or mail) */
16039381Seric 		  case EM_PRINT:	/* print errors normally (default) */
160458734Seric 			e->e_errormode = *val;
16058269Seric 			break;
16068269Seric 		}
16078269Seric 		break;
16088269Seric 
16099049Seric 	  case 'F':		/* file mode */
161017975Seric 		FileMode = atooct(val) & 0777;
16119049Seric 		break;
16129049Seric 
16138269Seric 	  case 'f':		/* save Unix-style From lines on front */
16149381Seric 		SaveFrom = atobool(val);
16158269Seric 		break;
16168269Seric 
161753735Seric 	  case 'G':		/* match recipients against GECOS field */
161853735Seric 		MatchGecos = atobool(val);
161953735Seric 		break;
162053735Seric 
16218256Seric 	  case 'g':		/* default gid */
162268481Seric   g_opt:
162364133Seric 		if (isascii(*val) && isdigit(*val))
162464133Seric 			DefGid = atoi(val);
162564133Seric 		else
162664133Seric 		{
162764133Seric 			register struct group *gr;
162864133Seric 
162964133Seric 			DefGid = -1;
163064133Seric 			gr = getgrnam(val);
163164133Seric 			if (gr == NULL)
163268481Seric 				syserr("readcf: option %c: unknown group %s",
163368481Seric 					opt, val);
163464133Seric 			else
163564133Seric 				DefGid = gr->gr_gid;
163664133Seric 		}
16378256Seric 		break;
16388256Seric 
16398256Seric 	  case 'H':		/* help file */
16409381Seric 		if (val[0] == '\0')
16418269Seric 			HelpFile = "sendmail.hf";
16429381Seric 		else
16439381Seric 			HelpFile = newstr(val);
16448256Seric 		break;
16458256Seric 
164651305Seric 	  case 'h':		/* maximum hop count */
164751305Seric 		MaxHopCount = atoi(val);
164851305Seric 		break;
164951305Seric 
165035651Seric 	  case 'I':		/* use internet domain name server */
165166334Seric #if NAMED_BIND
165257207Seric 		for (p = val; *p != 0; )
165357207Seric 		{
165457207Seric 			bool clearmode;
165557207Seric 			char *q;
165657207Seric 			struct resolverflags *rfp;
165757207Seric 
165857207Seric 			while (*p == ' ')
165957207Seric 				p++;
166057207Seric 			if (*p == '\0')
166157207Seric 				break;
166257207Seric 			clearmode = FALSE;
166357207Seric 			if (*p == '-')
166457207Seric 				clearmode = TRUE;
166557207Seric 			else if (*p != '+')
166657207Seric 				p--;
166757207Seric 			p++;
166857207Seric 			q = p;
166958050Seric 			while (*p != '\0' && !(isascii(*p) && isspace(*p)))
167057207Seric 				p++;
167157207Seric 			if (*p != '\0')
167257207Seric 				*p++ = '\0';
167368759Seric 			if (strcasecmp(q, "HasWildcardMX") == 0)
167468759Seric 			{
167569896Seric 				HasWildcardMX = !clearmode;
167668759Seric 				continue;
167768759Seric 			}
167857207Seric 			for (rfp = ResolverFlags; rfp->rf_name != NULL; rfp++)
167957207Seric 			{
168057207Seric 				if (strcasecmp(q, rfp->rf_name) == 0)
168157207Seric 					break;
168257207Seric 			}
168364923Seric 			if (rfp->rf_name == NULL)
168464923Seric 				syserr("readcf: I option value %s unrecognized", q);
168564923Seric 			else if (clearmode)
168657207Seric 				_res.options &= ~rfp->rf_bits;
168757207Seric 			else
168857207Seric 				_res.options |= rfp->rf_bits;
168957207Seric 		}
169057207Seric 		if (tTd(8, 2))
169168759Seric 			printf("_res.options = %x, HasWildcardMX = %d\n",
169269896Seric 				_res.options, HasWildcardMX);
169357207Seric #else
169457207Seric 		usrerr("name server (I option) specified but BIND not compiled in");
169557207Seric #endif
169635651Seric 		break;
169735651Seric 
16988269Seric 	  case 'i':		/* ignore dot lines in message */
16999381Seric 		IgnrDot = atobool(val);
17008269Seric 		break;
17018269Seric 
170259730Seric 	  case 'j':		/* send errors in MIME (RFC 1341) format */
170359730Seric 		SendMIMEErrors = atobool(val);
170459730Seric 		break;
170559730Seric 
170657136Seric 	  case 'J':		/* .forward search path */
170757136Seric 		ForwardPath = newstr(val);
170857136Seric 		break;
170957136Seric 
171054967Seric 	  case 'k':		/* connection cache size */
171154967Seric 		MaxMciCache = atoi(val);
171256215Seric 		if (MaxMciCache < 0)
171356215Seric 			MaxMciCache = 0;
171454967Seric 		break;
171554967Seric 
171654967Seric 	  case 'K':		/* connection cache timeout */
171758796Seric 		MciCacheTimeout = convtime(val, 'm');
171854967Seric 		break;
171954967Seric 
172061104Seric 	  case 'l':		/* use Errors-To: header */
172161104Seric 		UseErrorsTo = atobool(val);
172261104Seric 		break;
172361104Seric 
17248256Seric 	  case 'L':		/* log level */
172564140Seric 		if (safe || LogLevel < atoi(val))
172664140Seric 			LogLevel = atoi(val);
17278256Seric 		break;
17288256Seric 
17298269Seric 	  case 'M':		/* define macro */
173068267Seric 		p = newstr(&val[1]);
173168267Seric 		if (!safe)
173268267Seric 			cleanstrcpy(p, p, MAXNAME);
173368267Seric 		define(val[0], p, CurEnv);
173416878Seric 		sticky = FALSE;
17358269Seric 		break;
17368269Seric 
17378269Seric 	  case 'm':		/* send to me too */
17389381Seric 		MeToo = atobool(val);
17398269Seric 		break;
17408269Seric 
174125820Seric 	  case 'n':		/* validate RHS in newaliases */
174225820Seric 		CheckAliases = atobool(val);
174325820Seric 		break;
174425820Seric 
174561104Seric 	    /* 'N' available -- was "net name" */
174661104Seric 
174758851Seric 	  case 'O':		/* daemon options */
174858851Seric 		setdaemonoptions(val);
174958851Seric 		break;
175058851Seric 
17518269Seric 	  case 'o':		/* assume old style headers */
17529381Seric 		if (atobool(val))
17539341Seric 			CurEnv->e_flags |= EF_OLDSTYLE;
17549341Seric 		else
17559341Seric 			CurEnv->e_flags &= ~EF_OLDSTYLE;
17568269Seric 		break;
17578269Seric 
175858082Seric 	  case 'p':		/* select privacy level */
175958082Seric 		p = val;
176058082Seric 		for (;;)
176158082Seric 		{
176258082Seric 			register struct prival *pv;
176358082Seric 			extern struct prival PrivacyValues[];
176458082Seric 
176558082Seric 			while (isascii(*p) && (isspace(*p) || ispunct(*p)))
176658082Seric 				p++;
176758082Seric 			if (*p == '\0')
176858082Seric 				break;
176958082Seric 			val = p;
177058082Seric 			while (isascii(*p) && isalnum(*p))
177158082Seric 				p++;
177258082Seric 			if (*p != '\0')
177358082Seric 				*p++ = '\0';
177458082Seric 
177558082Seric 			for (pv = PrivacyValues; pv->pv_name != NULL; pv++)
177658082Seric 			{
177758082Seric 				if (strcasecmp(val, pv->pv_name) == 0)
177858082Seric 					break;
177958082Seric 			}
178058886Seric 			if (pv->pv_name == NULL)
178158886Seric 				syserr("readcf: Op line: %s unrecognized", val);
178258082Seric 			PrivacyFlags |= pv->pv_flag;
178358082Seric 		}
178468479Seric 		sticky = FALSE;
178558082Seric 		break;
178658082Seric 
178724944Seric 	  case 'P':		/* postmaster copy address for returned mail */
178824944Seric 		PostMasterCopy = newstr(val);
178924944Seric 		break;
179024944Seric 
179124944Seric 	  case 'q':		/* slope of queue only function */
179224944Seric 		QueueFactor = atoi(val);
179324944Seric 		break;
179424944Seric 
17958256Seric 	  case 'Q':		/* queue directory */
17969381Seric 		if (val[0] == '\0')
17978269Seric 			QueueDir = "mqueue";
17989381Seric 		else
17999381Seric 			QueueDir = newstr(val);
180058789Seric 		if (RealUid != 0 && !safe)
180164718Seric 			Warn_Q_option = TRUE;
18028256Seric 		break;
18038256Seric 
180458148Seric 	  case 'R':		/* don't prune routes */
180558148Seric 		DontPruneRoutes = atobool(val);
180658148Seric 		break;
180758148Seric 
18088256Seric 	  case 'r':		/* read timeout */
180968481Seric 		if (subopt == NULL)
181068481Seric 			inittimeouts(val);
181168481Seric 		else
181268481Seric 			settimeout(subopt, val);
18138256Seric 		break;
18148256Seric 
18158256Seric 	  case 'S':		/* status file */
18169381Seric 		if (val[0] == '\0')
18178269Seric 			StatFile = "sendmail.st";
18189381Seric 		else
18199381Seric 			StatFile = newstr(val);
18208256Seric 		break;
18218256Seric 
18228265Seric 	  case 's':		/* be super safe, even if expensive */
18239381Seric 		SuperSafe = atobool(val);
18248256Seric 		break;
18258256Seric 
18268256Seric 	  case 'T':		/* queue timeout */
182758737Seric 		p = strchr(val, '/');
182858737Seric 		if (p != NULL)
182958737Seric 		{
183058737Seric 			*p++ = '\0';
183168481Seric 			settimeout("queuewarn", p);
183258737Seric 		}
183368481Seric 		settimeout("queuereturn", val);
183454967Seric 		break;
18358256Seric 
18368265Seric 	  case 't':		/* time zone name */
183752106Seric 		TimeZoneSpec = newstr(val);
18388265Seric 		break;
18398265Seric 
184050556Seric 	  case 'U':		/* location of user database */
184151360Seric 		UdbSpec = newstr(val);
184250556Seric 		break;
184350556Seric 
18448256Seric 	  case 'u':		/* set default uid */
184568481Seric 		for (p = val; *p != '\0'; p++)
184668481Seric 		{
184768481Seric 			if (*p == '.' || *p == '/' || *p == ':')
184868481Seric 			{
184968481Seric 				*p++ = '\0';
185068481Seric 				break;
185168481Seric 			}
185268481Seric 		}
185364133Seric 		if (isascii(*val) && isdigit(*val))
185464133Seric 			DefUid = atoi(val);
185564133Seric 		else
185664133Seric 		{
185764133Seric 			register struct passwd *pw;
185864133Seric 
185964133Seric 			DefUid = -1;
186068693Seric 			pw = sm_getpwnam(val);
186164133Seric 			if (pw == NULL)
186264133Seric 				syserr("readcf: option u: unknown user %s", val);
186364133Seric 			else
186468481Seric 			{
186564133Seric 				DefUid = pw->pw_uid;
186668481Seric 				DefGid = pw->pw_gid;
186768481Seric 			}
186864133Seric 		}
186940973Sbostic 		setdefuser();
18708256Seric 
187168481Seric 		/* handle the group if it is there */
187268481Seric 		if (*p == '\0')
187368481Seric 			break;
187468481Seric 		val = p;
187568481Seric 		goto g_opt;
187668481Seric 
187758851Seric 	  case 'V':		/* fallback MX host */
187858851Seric 		FallBackMX = newstr(val);
187958851Seric 		break;
188058851Seric 
18818269Seric 	  case 'v':		/* run in verbose mode */
18829381Seric 		Verbose = atobool(val);
18838256Seric 		break;
18848256Seric 
188563837Seric 	  case 'w':		/* if we are best MX, try host directly */
188663837Seric 		TryNullMXList = atobool(val);
188763837Seric 		break;
188861104Seric 
188961104Seric 	    /* 'W' available -- was wizard password */
189061104Seric 
189114879Seric 	  case 'x':		/* load avg at which to auto-queue msgs */
189214879Seric 		QueueLA = atoi(val);
189314879Seric 		break;
189414879Seric 
189514879Seric 	  case 'X':		/* load avg at which to auto-reject connections */
189614879Seric 		RefuseLA = atoi(val);
189714879Seric 		break;
189814879Seric 
189924981Seric 	  case 'y':		/* work recipient factor */
190024981Seric 		WkRecipFact = atoi(val);
190124981Seric 		break;
190224981Seric 
190324981Seric 	  case 'Y':		/* fork jobs during queue runs */
190424952Seric 		ForkQueueRuns = atobool(val);
190524952Seric 		break;
190624952Seric 
190724981Seric 	  case 'z':		/* work message class factor */
190824981Seric 		WkClassFact = atoi(val);
190924981Seric 		break;
191024981Seric 
191124981Seric 	  case 'Z':		/* work time factor */
191224981Seric 		WkTimeFact = atoi(val);
191324981Seric 		break;
191424981Seric 
191568481Seric 	  case O_QUEUESORTORD:	/* queue sorting order */
191668481Seric 		switch (*val)
191768481Seric 		{
191868481Seric 		  case 'h':	/* Host first */
191968481Seric 		  case 'H':
192068481Seric 			QueueSortOrder = QS_BYHOST;
192168481Seric 			break;
192268481Seric 
192368481Seric 		  case 'p':	/* Priority order */
192468481Seric 		  case 'P':
192568481Seric 			QueueSortOrder = QS_BYPRIORITY;
192668481Seric 			break;
192768481Seric 
192868481Seric 		  default:
192968481Seric 			syserr("Invalid queue sort order \"%s\"", val);
193068481Seric 		}
193168481Seric 		break;
193268481Seric 
193369401Seric 	  case O_HOSTSFILE:	/* pathname of /etc/hosts file */
193469401Seric 		HostsFile = newstr(val);
193569401Seric 		break;
193669401Seric 
193768481Seric 	  case O_MQA:		/* minimum queue age between deliveries */
193868481Seric 		MinQueueAge = convtime(val, 'm');
193968481Seric 		break;
194068481Seric 
194168481Seric 	  case O_MHSA:		/* maximum age of cached host status */
194268481Seric 		MaxHostStatAge = convtime(val, 'm');
194368481Seric 		break;
194468481Seric 
194568481Seric 	  case O_DEFCHARSET:	/* default character set for mimefying */
194668481Seric 		DefaultCharSet = newstr(denlstring(val, TRUE, TRUE));
194768481Seric 		break;
194868481Seric 
194968481Seric 	  case O_SSFILE:	/* service switch file */
195068481Seric 		ServiceSwitchFile = newstr(val);
195168481Seric 		break;
195268481Seric 
195368481Seric 	  case O_DIALDELAY:	/* delay for dial-on-demand operation */
195468481Seric 		DialDelay = convtime(val, 's');
195568481Seric 		break;
195668481Seric 
195768481Seric 	  case O_NORCPTACTION:	/* what to do if no recipient */
195868481Seric 		if (strcasecmp(val, "none") == 0)
195968481Seric 			NoRecipientAction = NRA_NO_ACTION;
196068481Seric 		else if (strcasecmp(val, "add-to") == 0)
196168481Seric 			NoRecipientAction = NRA_ADD_TO;
196268481Seric 		else if (strcasecmp(val, "add-apparently-to") == 0)
196368481Seric 			NoRecipientAction = NRA_ADD_APPARENTLY_TO;
196468481Seric 		else if (strcasecmp(val, "add-bcc") == 0)
196568481Seric 			NoRecipientAction = NRA_ADD_BCC;
196668481Seric 		else if (strcasecmp(val, "add-to-undisclosed") == 0)
196768481Seric 			NoRecipientAction = NRA_ADD_TO_UNDISCLOSED;
196868481Seric 		else
196968481Seric 			syserr("Invalid NoRecipientAction: %s", val);
197068481Seric 
197168490Seric 	  case O_SAFEFILEENV:	/* chroot() environ for writing to files */
197268490Seric 		SafeFileEnv = newstr(val);
197368490Seric 		break;
197468490Seric 
197568569Seric 	  case O_MAXMSGSIZE:	/* maximum message size */
197669748Seric 		MaxMessageSize = atol(val);
197768569Seric 		break;
197868569Seric 
197968756Seric 	  case O_COLONOKINADDR:	/* old style handling of colon addresses */
198069748Seric 		ColonOkInAddr = atobool(val);
198168756Seric 		break;
198268756Seric 
198369724Seric 	  case O_MAXQUEUERUN:	/* max # of jobs in a single queue run */
198469748Seric 		MaxQueueRun = atol(val);
198569724Seric 		break;
198669724Seric 
198769838Seric 	  case O_MAXCHILDREN:	/* max # of children of daemon */
198869838Seric 		MaxChildren = atoi(val);
198969852Seric 		break;
199069838Seric 
199169852Seric 	  case O_KEEPCNAMES:	/* don't expand CNAME records */
199269852Seric 		DontExpandCnames = atobool(val);
199369852Seric 		break;
199469852Seric 
19958256Seric 	  default:
199668481Seric 		if (tTd(37, 1))
199768481Seric 		{
199868481Seric 			if (isascii(opt) && isprint(opt))
199968481Seric 				printf("Warning: option %c unknown\n", opt);
200068481Seric 			else
200168481Seric 				printf("Warning: option 0x%x unknown\n", opt);
200268481Seric 		}
20038256Seric 		break;
20048256Seric 	}
200516878Seric 	if (sticky)
200616878Seric 		setbitn(opt, StickyOpt);
20078256Seric }
200810687Seric /*
200968481Seric **  SETCLASS -- set a string into a class
201010687Seric **
201110687Seric **	Parameters:
201268481Seric **		class -- the class to put the string in.
201368481Seric **		str -- the string to enter
201410687Seric **
201510687Seric **	Returns:
201610687Seric **		none.
201710687Seric **
201810687Seric **	Side Effects:
201910687Seric **		puts the word into the symbol table.
202010687Seric */
202110687Seric 
202269748Seric void
202368481Seric setclass(class, str)
202410687Seric 	int class;
202568481Seric 	char *str;
202610687Seric {
202710687Seric 	register STAB *s;
202810687Seric 
202957943Seric 	if (tTd(37, 8))
203068481Seric 		printf("setclass(%c, %s)\n", class, str);
203168481Seric 	s = stab(str, ST_CLASS, ST_ENTER);
203210687Seric 	setbitn(class, s->s_class);
203310687Seric }
203453654Seric /*
203553654Seric **  MAKEMAPENTRY -- create a map entry
203653654Seric **
203753654Seric **	Parameters:
203853654Seric **		line -- the config file line
203953654Seric **
204053654Seric **	Returns:
204169774Seric **		A pointer to the map that has been created.
204269774Seric **		NULL if there was a syntax error.
204353654Seric **
204453654Seric **	Side Effects:
204553654Seric **		Enters the map into the dictionary.
204653654Seric */
204753654Seric 
204869774Seric MAP *
204953654Seric makemapentry(line)
205053654Seric 	char *line;
205153654Seric {
205253654Seric 	register char *p;
205353654Seric 	char *mapname;
205453654Seric 	char *classname;
205564078Seric 	register STAB *s;
205653654Seric 	STAB *class;
205753654Seric 
205858050Seric 	for (p = line; isascii(*p) && isspace(*p); p++)
205953654Seric 		continue;
206058050Seric 	if (!(isascii(*p) && isalnum(*p)))
206153654Seric 	{
206253654Seric 		syserr("readcf: config K line: no map name");
206369774Seric 		return NULL;
206453654Seric 	}
206553654Seric 
206653654Seric 	mapname = p;
206768481Seric 	while ((isascii(*++p) && isalnum(*p)) || *p == '.')
206853654Seric 		continue;
206953654Seric 	if (*p != '\0')
207053654Seric 		*p++ = '\0';
207158050Seric 	while (isascii(*p) && isspace(*p))
207253654Seric 		p++;
207358050Seric 	if (!(isascii(*p) && isalnum(*p)))
207453654Seric 	{
207553654Seric 		syserr("readcf: config K line, map %s: no map class", mapname);
207669774Seric 		return NULL;
207753654Seric 	}
207853654Seric 	classname = p;
207958050Seric 	while (isascii(*++p) && isalnum(*p))
208053654Seric 		continue;
208153654Seric 	if (*p != '\0')
208253654Seric 		*p++ = '\0';
208358050Seric 	while (isascii(*p) && isspace(*p))
208453654Seric 		p++;
208553654Seric 
208653654Seric 	/* look up the class */
208753654Seric 	class = stab(classname, ST_MAPCLASS, ST_FIND);
208853654Seric 	if (class == NULL)
208953654Seric 	{
209053654Seric 		syserr("readcf: map %s: class %s not available", mapname, classname);
209169774Seric 		return NULL;
209253654Seric 	}
209353654Seric 
209453654Seric 	/* enter the map */
209564078Seric 	s = stab(mapname, ST_MAP, ST_ENTER);
209664078Seric 	s->s_map.map_class = &class->s_mapclass;
209764078Seric 	s->s_map.map_mname = newstr(mapname);
209853654Seric 
209964078Seric 	if (class->s_mapclass.map_parse(&s->s_map, p))
210064078Seric 		s->s_map.map_mflags |= MF_VALID;
210164078Seric 
210264078Seric 	if (tTd(37, 5))
210364078Seric 	{
210464078Seric 		printf("map %s, class %s, flags %x, file %s,\n",
210564078Seric 			s->s_map.map_mname, s->s_map.map_class->map_cname,
210664078Seric 			s->s_map.map_mflags,
210764078Seric 			s->s_map.map_file == NULL ? "(null)" : s->s_map.map_file);
210864078Seric 		printf("\tapp %s, domain %s, rebuild %s\n",
210964078Seric 			s->s_map.map_app == NULL ? "(null)" : s->s_map.map_app,
211064078Seric 			s->s_map.map_domain == NULL ? "(null)" : s->s_map.map_domain,
211164078Seric 			s->s_map.map_rebuild == NULL ? "(null)" : s->s_map.map_rebuild);
211264078Seric 	}
211369774Seric 
211469774Seric 	return &s->s_map;
211553654Seric }
211658112Seric /*
211769783Seric **  STRTORWSET -- convert string to rewriting set number
211869783Seric **
211969783Seric **	Parameters:
212069783Seric **		p -- the pointer to the string to decode.
212169783Seric **		endp -- if set, store the trailing delimiter here.
212269783Seric **		stabmode -- ST_ENTER to create this entry, ST_FIND if
212369783Seric **			it must already exist.
212469783Seric **
212569783Seric **	Returns:
212669783Seric **		The appropriate ruleset number.
212769783Seric **		-1 if it is not valid (error already printed)
212869783Seric */
212969783Seric 
213069783Seric int
213169783Seric strtorwset(p, endp, stabmode)
213269783Seric 	char *p;
213369783Seric 	char **endp;
213469783Seric 	int stabmode;
213569783Seric {
213669783Seric 	int ruleset;
213769783Seric 	static int nextruleset = MAXRWSETS;
213869783Seric 
213969783Seric 	while (isascii(*p) && isspace(*p))
214069783Seric 		p++;
214169783Seric 	if (!isascii(*p))
214269783Seric 	{
214369783Seric 		syserr("invalid ruleset name: \"%.20s\"", p);
214469783Seric 		return -1;
214569783Seric 	}
214669783Seric 	if (isdigit(*p))
214769783Seric 	{
214869783Seric 		ruleset = strtol(p, endp, 10);
214969783Seric 		if (ruleset >= MAXRWSETS / 2 || ruleset < 0)
215069783Seric 		{
215169783Seric 			syserr("bad ruleset %d (%d max)",
215269783Seric 				ruleset, MAXRWSETS / 2);
215369783Seric 			ruleset = -1;
215469783Seric 		}
215569783Seric 	}
215669783Seric 	else
215769783Seric 	{
215869783Seric 		STAB *s;
215969783Seric 		char delim;
216069783Seric 		char *q;
216169783Seric 
216269783Seric 		q = p;
216369783Seric 		while (*p != '\0' && isascii(*p) &&
216469783Seric 		       (isalnum(*p) || strchr("-_$", *p) != NULL))
216569783Seric 			p++;
216669783Seric 		while (isascii(*p) && isspace(*p))
216769783Seric 			*p++ = '\0';
216869783Seric 		delim = *p;
216969783Seric 		if (delim != '\0')
217069783Seric 			*p = '\0';
217169783Seric 		s = stab(q, ST_RULESET, stabmode);
217269783Seric 		if (delim != '\0')
217369783Seric 			*p = delim;
217469783Seric 
217569783Seric 		if (s == NULL)
217669783Seric 		{
217769783Seric 			syserr("unknown ruleset %s", q);
217869783Seric 			return -1;
217969783Seric 		}
218069783Seric 
218169783Seric 		if (stabmode == ST_ENTER && delim == '=')
218269783Seric 		{
218369783Seric 			ruleset = strtol(p, endp, 10);
218469783Seric 			if (ruleset >= MAXRWSETS / 2 || ruleset < 0)
218569783Seric 			{
218669783Seric 				syserr("bad ruleset %s = %d (%d max)",
218769783Seric 					q, ruleset, MAXRWSETS / 2);
218869783Seric 				ruleset = -1;
218969783Seric 			}
219069783Seric 		}
219169783Seric 		else
219269783Seric 		{
219369783Seric 			if (endp != NULL)
219469783Seric 				*endp = p;
219569783Seric 			if (s->s_ruleset > 0)
219669783Seric 				ruleset = s->s_ruleset;
219769783Seric 			else if ((ruleset = --nextruleset) < MAXRWSETS / 2)
219869783Seric 			{
219969783Seric 				syserr("%s: too many named rulesets (%d max)",
220069783Seric 					q, MAXRWSETS / 2);
220169783Seric 				ruleset = -1;
220269783Seric 			}
220369783Seric 		}
220469783Seric 		if (s->s_ruleset > 0 && ruleset >= 0 && ruleset != s->s_ruleset)
220569783Seric 		{
220669783Seric 			syserr("%s: ruleset changed value (old %d, new %d)",
220769783Seric 				q, ruleset, s->s_ruleset);
220869783Seric 			ruleset = s->s_ruleset;
220969783Seric 		}
221069783Seric 		else if (ruleset > 0)
221169783Seric 		{
221269783Seric 			s->s_ruleset = ruleset;
221369783Seric 		}
221469783Seric 	}
221569783Seric 	return ruleset;
221669783Seric }
221769783Seric /*
221868481Seric **  INITTIMEOUTS -- parse and set timeout values
221958112Seric **
222058112Seric **	Parameters:
222158112Seric **		val -- a pointer to the values.  If NULL, do initial
222258112Seric **			settings.
222358112Seric **
222458112Seric **	Returns:
222558112Seric **		none.
222658112Seric **
222758112Seric **	Side Effects:
222858112Seric **		Initializes the TimeOuts structure
222958112Seric */
223058112Seric 
223164255Seric #define SECONDS
223258112Seric #define MINUTES	* 60
223358112Seric #define HOUR	* 3600
223458112Seric 
223569748Seric void
223668481Seric inittimeouts(val)
223758112Seric 	register char *val;
223858112Seric {
223958112Seric 	register char *p;
224058671Seric 	extern time_t convtime();
224158112Seric 
224258112Seric 	if (val == NULL)
224358112Seric 	{
224458112Seric 		TimeOuts.to_initial = (time_t) 5 MINUTES;
224558112Seric 		TimeOuts.to_helo = (time_t) 5 MINUTES;
224658112Seric 		TimeOuts.to_mail = (time_t) 10 MINUTES;
224758112Seric 		TimeOuts.to_rcpt = (time_t) 1 HOUR;
224858112Seric 		TimeOuts.to_datainit = (time_t) 5 MINUTES;
224958112Seric 		TimeOuts.to_datablock = (time_t) 1 HOUR;
225058112Seric 		TimeOuts.to_datafinal = (time_t) 1 HOUR;
225158112Seric 		TimeOuts.to_rset = (time_t) 5 MINUTES;
225258112Seric 		TimeOuts.to_quit = (time_t) 2 MINUTES;
225358112Seric 		TimeOuts.to_nextcommand = (time_t) 1 HOUR;
225458112Seric 		TimeOuts.to_miscshort = (time_t) 2 MINUTES;
225568481Seric #if IDENTPROTO
225664255Seric 		TimeOuts.to_ident = (time_t) 30 SECONDS;
225768481Seric #else
225868481Seric 		TimeOuts.to_ident = (time_t) 0 SECONDS;
225968481Seric #endif
226068481Seric 		TimeOuts.to_fileopen = (time_t) 60 SECONDS;
226158112Seric 		return;
226258112Seric 	}
226358112Seric 
226458112Seric 	for (;; val = p)
226558112Seric 	{
226658112Seric 		while (isascii(*val) && isspace(*val))
226758112Seric 			val++;
226858112Seric 		if (*val == '\0')
226958112Seric 			break;
227058112Seric 		for (p = val; *p != '\0' && *p != ','; p++)
227158112Seric 			continue;
227258112Seric 		if (*p != '\0')
227358112Seric 			*p++ = '\0';
227458112Seric 
227558112Seric 		if (isascii(*val) && isdigit(*val))
227658112Seric 		{
227758112Seric 			/* old syntax -- set everything */
227858796Seric 			TimeOuts.to_mail = convtime(val, 'm');
227958112Seric 			TimeOuts.to_rcpt = TimeOuts.to_mail;
228058112Seric 			TimeOuts.to_datainit = TimeOuts.to_mail;
228158112Seric 			TimeOuts.to_datablock = TimeOuts.to_mail;
228258112Seric 			TimeOuts.to_datafinal = TimeOuts.to_mail;
228358112Seric 			TimeOuts.to_nextcommand = TimeOuts.to_mail;
228458112Seric 			continue;
228558112Seric 		}
228658112Seric 		else
228758112Seric 		{
228868481Seric 			register char *q = strchr(val, ':');
228958112Seric 
229068481Seric 			if (q == NULL && (q = strchr(val, '=')) == NULL)
229158112Seric 			{
229258112Seric 				/* syntax error */
229358112Seric 				continue;
229458112Seric 			}
229558112Seric 			*q++ = '\0';
229668481Seric 			settimeout(val, q);
229768481Seric 		}
229868481Seric 	}
229968481Seric }
230068481Seric /*
230168481Seric **  SETTIMEOUT -- set an individual timeout
230268481Seric **
230368481Seric **	Parameters:
230468481Seric **		name -- the name of the timeout.
230568481Seric **		val -- the value of the timeout.
230668481Seric **
230768481Seric **	Returns:
230868481Seric **		none.
230968481Seric */
231058112Seric 
231169748Seric void
231268481Seric settimeout(name, val)
231368481Seric 	char *name;
231468481Seric 	char *val;
231568481Seric {
231668481Seric 	register char *p;
231768481Seric 	time_t to;
231868481Seric 	extern time_t convtime();
231968481Seric 
232068481Seric 	to = convtime(val, 'm');
232168481Seric 	p = strchr(name, '.');
232268481Seric 	if (p != NULL)
232368481Seric 		*p++ = '\0';
232468481Seric 
232568481Seric 	if (strcasecmp(name, "initial") == 0)
232668481Seric 		TimeOuts.to_initial = to;
232768481Seric 	else if (strcasecmp(name, "mail") == 0)
232868481Seric 		TimeOuts.to_mail = to;
232968481Seric 	else if (strcasecmp(name, "rcpt") == 0)
233068481Seric 		TimeOuts.to_rcpt = to;
233168481Seric 	else if (strcasecmp(name, "datainit") == 0)
233268481Seric 		TimeOuts.to_datainit = to;
233368481Seric 	else if (strcasecmp(name, "datablock") == 0)
233468481Seric 		TimeOuts.to_datablock = to;
233568481Seric 	else if (strcasecmp(name, "datafinal") == 0)
233668481Seric 		TimeOuts.to_datafinal = to;
233768481Seric 	else if (strcasecmp(name, "command") == 0)
233868481Seric 		TimeOuts.to_nextcommand = to;
233968481Seric 	else if (strcasecmp(name, "rset") == 0)
234068481Seric 		TimeOuts.to_rset = to;
234168481Seric 	else if (strcasecmp(name, "helo") == 0)
234268481Seric 		TimeOuts.to_helo = to;
234368481Seric 	else if (strcasecmp(name, "quit") == 0)
234468481Seric 		TimeOuts.to_quit = to;
234568481Seric 	else if (strcasecmp(name, "misc") == 0)
234668481Seric 		TimeOuts.to_miscshort = to;
234768481Seric 	else if (strcasecmp(name, "ident") == 0)
234868481Seric 		TimeOuts.to_ident = to;
234968481Seric 	else if (strcasecmp(name, "fileopen") == 0)
235068481Seric 		TimeOuts.to_fileopen = to;
235168481Seric 	else if (strcasecmp(name, "queuewarn") == 0)
235268481Seric 	{
235368481Seric 		to = convtime(val, 'h');
235468481Seric 		if (p == NULL || strcmp(p, "*") == 0)
235568481Seric 		{
235668481Seric 			TimeOuts.to_q_warning[TOC_NORMAL] = to;
235768481Seric 			TimeOuts.to_q_warning[TOC_URGENT] = to;
235868481Seric 			TimeOuts.to_q_warning[TOC_NONURGENT] = to;
235958112Seric 		}
236068481Seric 		else if (strcasecmp(p, "normal") == 0)
236168481Seric 			TimeOuts.to_q_warning[TOC_NORMAL] = to;
236268481Seric 		else if (strcasecmp(p, "urgent") == 0)
236368481Seric 			TimeOuts.to_q_warning[TOC_URGENT] = to;
236468481Seric 		else if (strcasecmp(p, "non-urgent") == 0)
236568481Seric 			TimeOuts.to_q_warning[TOC_NONURGENT] = to;
236668481Seric 		else
236768481Seric 			syserr("settimeout: invalid queuewarn subtimeout %s", p);
236858112Seric 	}
236968481Seric 	else if (strcasecmp(name, "queuereturn") == 0)
237068481Seric 	{
237168481Seric 		to = convtime(val, 'd');
237268481Seric 		if (p == NULL || strcmp(p, "*") == 0)
237368481Seric 		{
237468481Seric 			TimeOuts.to_q_return[TOC_NORMAL] = to;
237568481Seric 			TimeOuts.to_q_return[TOC_URGENT] = to;
237668481Seric 			TimeOuts.to_q_return[TOC_NONURGENT] = to;
237768481Seric 		}
237868481Seric 		else if (strcasecmp(p, "normal") == 0)
237968481Seric 			TimeOuts.to_q_return[TOC_NORMAL] = to;
238068481Seric 		else if (strcasecmp(p, "urgent") == 0)
238168481Seric 			TimeOuts.to_q_return[TOC_URGENT] = to;
238268481Seric 		else if (strcasecmp(p, "non-urgent") == 0)
238368481Seric 			TimeOuts.to_q_return[TOC_NONURGENT] = to;
238468481Seric 		else
238568481Seric 			syserr("settimeout: invalid queuereturn subtimeout %s", p);
238668481Seric 	}
238768481Seric 	else
238868481Seric 		syserr("settimeout: invalid timeout %s", name);
238958112Seric }
2390