xref: /csrg-svn/usr.sbin/sendmail/src/readcf.c (revision 69975)
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*69975Seric static char sccsid[] = "@(#)readcf.c	8.112 (Berkeley) 06/21/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 
186*69975Seric 			/* special handling for $=, $~, and $& */
187*69975Seric 			if (*p == '=' || *p == '~' || *p == '&')
188*69975Seric 				p++;
189*69975Seric 
19068481Seric 			/* convert macro name to code */
19168481Seric 			*p = macid(p, &ep);
19268481Seric 			if (ep != p)
19368481Seric 				strcpy(p + 1, ep);
19416157Seric 		}
19516157Seric 
19616157Seric 		/* interpret this line */
19764718Seric 		errno = 0;
19857135Seric 		switch (bp[0])
1993308Seric 		{
2003308Seric 		  case '\0':
2013308Seric 		  case '#':		/* comment */
2023308Seric 			break;
2033308Seric 
2043308Seric 		  case 'R':		/* rewriting rule */
20557135Seric 			for (p = &bp[1]; *p != '\0' && *p != '\t'; p++)
2063308Seric 				continue;
2073308Seric 
2083308Seric 			if (*p == '\0')
2095909Seric 			{
21065821Seric 				syserr("invalid rewrite line \"%s\" (tab expected)", bp);
2115909Seric 				break;
2125909Seric 			}
2135909Seric 
2145909Seric 			/* allocate space for the rule header */
2155909Seric 			if (rwp == NULL)
2165909Seric 			{
2175909Seric 				RewriteRules[ruleset] = rwp =
2185909Seric 					(struct rewrite *) xalloc(sizeof *rwp);
2195909Seric 			}
2203308Seric 			else
2213308Seric 			{
2225909Seric 				rwp->r_next = (struct rewrite *) xalloc(sizeof *rwp);
2235909Seric 				rwp = rwp->r_next;
2245909Seric 			}
2255909Seric 			rwp->r_next = NULL;
2263308Seric 
2275909Seric 			/* expand and save the LHS */
2285909Seric 			*p = '\0';
22968529Seric 			expand(&bp[1], exbuf, sizeof exbuf, e);
23065066Seric 			rwp->r_lhs = prescan(exbuf, '\t', pvpbuf,
23168711Seric 					     sizeof pvpbuf, NULL, NULL);
23257589Seric 			nfuzzy = 0;
2335909Seric 			if (rwp->r_lhs != NULL)
23457589Seric 			{
23557589Seric 				register char **ap;
23657589Seric 
2375909Seric 				rwp->r_lhs = copyplist(rwp->r_lhs, TRUE);
23857589Seric 
23957589Seric 				/* count the number of fuzzy matches in LHS */
24057589Seric 				for (ap = rwp->r_lhs; *ap != NULL; ap++)
24157589Seric 				{
24258148Seric 					char *botch;
24358148Seric 
24458148Seric 					botch = NULL;
24558050Seric 					switch (**ap & 0377)
24657589Seric 					{
24757589Seric 					  case MATCHZANY:
24857589Seric 					  case MATCHANY:
24957589Seric 					  case MATCHONE:
25057589Seric 					  case MATCHCLASS:
25157589Seric 					  case MATCHNCLASS:
25257589Seric 						nfuzzy++;
25358148Seric 						break;
25458148Seric 
25558148Seric 					  case MATCHREPL:
25658148Seric 						botch = "$0-$9";
25758148Seric 						break;
25858148Seric 
25958148Seric 					  case CANONNET:
26058148Seric 						botch = "$#";
26158148Seric 						break;
26258148Seric 
26358148Seric 					  case CANONUSER:
26458148Seric 						botch = "$:";
26558148Seric 						break;
26658148Seric 
26758148Seric 					  case CALLSUBR:
26858148Seric 						botch = "$>";
26958148Seric 						break;
27058148Seric 
27158148Seric 					  case CONDIF:
27258148Seric 						botch = "$?";
27358148Seric 						break;
27458148Seric 
27558148Seric 					  case CONDELSE:
27658148Seric 						botch = "$|";
27758148Seric 						break;
27858148Seric 
27958148Seric 					  case CONDFI:
28058148Seric 						botch = "$.";
28158148Seric 						break;
28258148Seric 
28358148Seric 					  case HOSTBEGIN:
28458148Seric 						botch = "$[";
28558148Seric 						break;
28658148Seric 
28758148Seric 					  case HOSTEND:
28858148Seric 						botch = "$]";
28958148Seric 						break;
29058148Seric 
29158148Seric 					  case LOOKUPBEGIN:
29258148Seric 						botch = "$(";
29358148Seric 						break;
29458148Seric 
29558148Seric 					  case LOOKUPEND:
29658148Seric 						botch = "$)";
29758148Seric 						break;
29857589Seric 					}
29958148Seric 					if (botch != NULL)
30058148Seric 						syserr("Inappropriate use of %s on LHS",
30158148Seric 							botch);
30257589Seric 				}
30357589Seric 			}
30456678Seric 			else
30568481Seric 			{
30656678Seric 				syserr("R line: null LHS");
30768481Seric 				rwp->r_lhs = null_list;
30868481Seric 			}
3095909Seric 
3105909Seric 			/* expand and save the RHS */
3115909Seric 			while (*++p == '\t')
3125909Seric 				continue;
3137231Seric 			q = p;
3147231Seric 			while (*p != '\0' && *p != '\t')
3157231Seric 				p++;
3167231Seric 			*p = '\0';
31768529Seric 			expand(q, exbuf, sizeof exbuf, e);
31865066Seric 			rwp->r_rhs = prescan(exbuf, '\t', pvpbuf,
31968711Seric 					     sizeof pvpbuf, NULL, NULL);
3205909Seric 			if (rwp->r_rhs != NULL)
32157589Seric 			{
32257589Seric 				register char **ap;
32357589Seric 
3245909Seric 				rwp->r_rhs = copyplist(rwp->r_rhs, TRUE);
32557589Seric 
32657589Seric 				/* check no out-of-bounds replacements */
32757589Seric 				nfuzzy += '0';
32857589Seric 				for (ap = rwp->r_rhs; *ap != NULL; ap++)
32957589Seric 				{
33058148Seric 					char *botch;
33158148Seric 
33258148Seric 					botch = NULL;
33358148Seric 					switch (**ap & 0377)
33457589Seric 					{
33558148Seric 					  case MATCHREPL:
33658148Seric 						if ((*ap)[1] <= '0' || (*ap)[1] > nfuzzy)
33758148Seric 						{
33858148Seric 							syserr("replacement $%c out of bounds",
33958148Seric 								(*ap)[1]);
34058148Seric 						}
34158148Seric 						break;
34258148Seric 
34358148Seric 					  case MATCHZANY:
34458148Seric 						botch = "$*";
34558148Seric 						break;
34658148Seric 
34758148Seric 					  case MATCHANY:
34858148Seric 						botch = "$+";
34958148Seric 						break;
35058148Seric 
35158148Seric 					  case MATCHONE:
35258148Seric 						botch = "$-";
35358148Seric 						break;
35458148Seric 
35558148Seric 					  case MATCHCLASS:
35658148Seric 						botch = "$=";
35758148Seric 						break;
35858148Seric 
35958148Seric 					  case MATCHNCLASS:
36058148Seric 						botch = "$~";
36158148Seric 						break;
36257589Seric 					}
36358148Seric 					if (botch != NULL)
36458148Seric 						syserr("Inappropriate use of %s on RHS",
36558148Seric 							botch);
36657589Seric 				}
36757589Seric 			}
36856678Seric 			else
36968481Seric 			{
37056678Seric 				syserr("R line: null RHS");
37168481Seric 				rwp->r_rhs = null_list;
37268481Seric 			}
3733308Seric 			break;
3743308Seric 
3754072Seric 		  case 'S':		/* select rewriting set */
37669783Seric 			ruleset = strtorwset(&bp[1], NULL, ST_ENTER);
37769789Seric 			rwp = RewriteRules[ruleset];
37869789Seric 			if (rwp != NULL)
37969789Seric 			{
38069789Seric 				while (rwp->r_next != NULL)
38169789Seric 					rwp = rwp->r_next;
38269789Seric 				fprintf(stderr, "WARNING: Ruleset %s redefined\n",
38369789Seric 					&bp[1]);
38469789Seric 			}
3854072Seric 			break;
3864072Seric 
3873308Seric 		  case 'D':		/* macro definition */
38868481Seric 			mid = macid(&bp[1], &ep);
38968481Seric 			p = munchstring(ep, NULL);
39068481Seric 			define(mid, newstr(p), e);
3913308Seric 			break;
3923308Seric 
3933387Seric 		  case 'H':		/* required header line */
39468717Seric 			(void) chompheader(&bp[1], TRUE, NULL, e);
3953387Seric 			break;
3963387Seric 
3974061Seric 		  case 'C':		/* word class */
39868481Seric 		  case 'T':		/* trusted user (set class `t') */
39968481Seric 			if (bp[0] == 'C')
4004061Seric 			{
40168481Seric 				mid = macid(&bp[1], &ep);
40268529Seric 				expand(ep, exbuf, sizeof exbuf, e);
40368481Seric 				p = exbuf;
40468481Seric 			}
40568481Seric 			else
40668481Seric 			{
40768481Seric 				mid = 't';
40868481Seric 				p = &bp[1];
40968481Seric 			}
41068481Seric 			while (*p != '\0')
41168481Seric 			{
4124061Seric 				register char *wd;
4134061Seric 				char delim;
4144061Seric 
41558050Seric 				while (*p != '\0' && isascii(*p) && isspace(*p))
4164061Seric 					p++;
4174061Seric 				wd = p;
41858050Seric 				while (*p != '\0' && !(isascii(*p) && isspace(*p)))
4194061Seric 					p++;
4204061Seric 				delim = *p;
4214061Seric 				*p = '\0';
4224061Seric 				if (wd[0] != '\0')
42368481Seric 					setclass(mid, wd);
4244061Seric 				*p = delim;
4254061Seric 			}
4264061Seric 			break;
4274061Seric 
42859272Seric 		  case 'F':		/* word class from file */
42968481Seric 			mid = macid(&bp[1], &ep);
43068481Seric 			for (p = ep; isascii(*p) && isspace(*p); )
43164133Seric 				p++;
43264133Seric 			if (p[0] == '-' && p[1] == 'o')
43364133Seric 			{
43464133Seric 				optional = TRUE;
43564133Seric 				while (*p != '\0' && !(isascii(*p) && isspace(*p)))
43664133Seric 					p++;
43764133Seric 				while (isascii(*p) && isspace(*p))
43868481Seric 					p++;
43964133Seric 			}
44064133Seric 			else
44164133Seric 				optional = FALSE;
44264133Seric 			file = p;
44364133Seric 			while (*p != '\0' && !(isascii(*p) && isspace(*p)))
44464133Seric 				p++;
44559272Seric 			if (*p == '\0')
44659272Seric 				p = "%s";
44759272Seric 			else
44859272Seric 			{
44959272Seric 				*p = '\0';
45059272Seric 				while (isascii(*++p) && isspace(*p))
45159272Seric 					continue;
45259272Seric 			}
45364133Seric 			fileclass(bp[1], file, p, safe, optional);
45459272Seric 			break;
45559272Seric 
45659156Seric #ifdef XLA
45759156Seric 		  case 'L':		/* extended load average description */
45859156Seric 			xla_init(&bp[1]);
45959156Seric 			break;
46059156Seric #endif
46159156Seric 
4624096Seric 		  case 'M':		/* define mailer */
46357135Seric 			makemailer(&bp[1]);
4644096Seric 			break;
4654096Seric 
4668252Seric 		  case 'O':		/* set option */
46758734Seric 			setoption(bp[1], &bp[2], safe, FALSE, e);
4688252Seric 			break;
4698252Seric 
4708252Seric 		  case 'P':		/* set precedence */
4718252Seric 			if (NumPriorities >= MAXPRIORITIES)
4728252Seric 			{
4738547Seric 				toomany('P', MAXPRIORITIES);
4748252Seric 				break;
4758252Seric 			}
47669956Seric 			for (p = &bp[1]; *p != '\0' && *p != '='; p++)
4778252Seric 				continue;
4788252Seric 			if (*p == '\0')
4798252Seric 				goto badline;
4808252Seric 			*p = '\0';
48157135Seric 			Priorities[NumPriorities].pri_name = newstr(&bp[1]);
4828252Seric 			Priorities[NumPriorities].pri_val = atoi(++p);
4838252Seric 			NumPriorities++;
4848252Seric 			break;
4858252Seric 
48652645Seric 		  case 'V':		/* configuration syntax version */
48764440Seric 			for (p = &bp[1]; isascii(*p) && isspace(*p); p++)
48864440Seric 				continue;
48964440Seric 			if (!isascii(*p) || !isdigit(*p))
49064440Seric 			{
49164440Seric 				syserr("invalid argument to V line: \"%.20s\"",
49264440Seric 					&bp[1]);
49364440Seric 				break;
49464440Seric 			}
49564718Seric 			ConfigLevel = strtol(p, &ep, 10);
49668805Seric 
49768805Seric 			/*
49868805Seric 			**  Do heuristic tweaking for back compatibility.
49968805Seric 			*/
50068805Seric 
50164279Seric 			if (ConfigLevel >= 5)
50264279Seric 			{
50364279Seric 				/* level 5 configs have short name in $w */
50464279Seric 				p = macvalue('w', e);
50564279Seric 				if (p != NULL && (p = strchr(p, '.')) != NULL)
50664279Seric 					*p = '\0';
50764279Seric 			}
50868805Seric 			if (ConfigLevel >= 6)
50968805Seric 			{
51068805Seric 				ColonOkInAddr = FALSE;
51168805Seric 			}
51268805Seric 
51368805Seric 			/*
51468805Seric 			**  Look for vendor code.
51568805Seric 			*/
51668805Seric 
51764718Seric 			if (*ep++ == '/')
51864718Seric 			{
51964718Seric 				/* extract vendor code */
52064718Seric 				for (p = ep; isascii(*p) && isalpha(*p); )
52164718Seric 					p++;
52264718Seric 				*p = '\0';
52364718Seric 
52464718Seric 				if (!setvendor(ep))
52564718Seric 					syserr("invalid V line vendor code: \"%s\"",
52664718Seric 						ep);
52764718Seric 			}
52852645Seric 			break;
52952645Seric 
53053654Seric 		  case 'K':
53169774Seric 			(void) makemapentry(&bp[1]);
53253654Seric 			break;
53353654Seric 
53469476Seric 		  case 'E':
53569476Seric 			p = strchr(bp, '=');
53669476Seric 			if (p != NULL)
53769476Seric 				*p++ = '\0';
53869476Seric 			setuserenv(&bp[1], p);
53969476Seric 			break;
54069476Seric 
5413308Seric 		  default:
5424061Seric 		  badline:
54357135Seric 			syserr("unknown control line \"%s\"", bp);
5443308Seric 		}
54557135Seric 		if (bp != buf)
54657135Seric 			free(bp);
5473308Seric 	}
54852637Seric 	if (ferror(cf))
54952637Seric 	{
55052647Seric 		syserr("I/O read error", cfname);
55152637Seric 		exit(EX_OSFILE);
55252637Seric 	}
55352637Seric 	fclose(cf);
5549381Seric 	FileName = NULL;
55556836Seric 
55668481Seric 	/* initialize host maps from local service tables */
55768481Seric 	inithostmaps();
55868481Seric 
55968481Seric 	/* determine if we need to do special name-server frotz */
56067905Seric 	{
56168481Seric 		int nmaps;
56268481Seric 		char *maptype[MAXMAPSTACK];
56368481Seric 		short mapreturn[MAXMAPACTIONS];
56468481Seric 
56568481Seric 		nmaps = switch_map_find("hosts", maptype, mapreturn);
56668481Seric 		UseNameServer = FALSE;
56768481Seric 		if (nmaps > 0 && nmaps <= MAXMAPSTACK)
56868481Seric 		{
56968481Seric 			register int mapno;
57068481Seric 
57168481Seric 			for (mapno = 0; mapno < nmaps && !UseNameServer; mapno++)
57268481Seric 			{
57368481Seric 				if (strcmp(maptype[mapno], "dns") == 0)
57468481Seric 					UseNameServer = TRUE;
57568481Seric 			}
57668481Seric 		}
57768481Seric 
57868481Seric #ifdef HESIOD
57968481Seric 		nmaps = switch_map_find("passwd", maptype, mapreturn);
58068481Seric 		UseHesiod = FALSE;
58168481Seric 		if (nmaps > 0 && nmaps <= MAXMAPSTACK)
58268481Seric 		{
58368481Seric 			register int mapno;
58468481Seric 
58568481Seric 			for (mapno = 0; mapno < nmaps && !UseHesiod; mapno++)
58668481Seric 			{
58768481Seric 				if (strcmp(maptype[mapno], "hesiod") == 0)
58868481Seric 					UseHesiod = TRUE;
58968481Seric 			}
59068481Seric 		}
59168204Seric #endif
59267905Seric 	}
5934096Seric }
5944096Seric /*
5958547Seric **  TOOMANY -- signal too many of some option
5968547Seric **
5978547Seric **	Parameters:
5988547Seric **		id -- the id of the error line
5998547Seric **		maxcnt -- the maximum possible values
6008547Seric **
6018547Seric **	Returns:
6028547Seric **		none.
6038547Seric **
6048547Seric **	Side Effects:
6058547Seric **		gives a syserr.
6068547Seric */
6078547Seric 
60869748Seric void
6098547Seric toomany(id, maxcnt)
61069748Seric 	int id;
6118547Seric 	int maxcnt;
6128547Seric {
6139381Seric 	syserr("too many %c lines, %d max", id, maxcnt);
6148547Seric }
6158547Seric /*
6164432Seric **  FILECLASS -- read members of a class from a file
6174432Seric **
6184432Seric **	Parameters:
6194432Seric **		class -- class to define.
6204432Seric **		filename -- name of file to read.
6214432Seric **		fmt -- scanf string to use for match.
62264133Seric **		safe -- if set, this is a safe read.
62364133Seric **		optional -- if set, it is not an error for the file to
62464133Seric **			not exist.
6254432Seric **
6264432Seric **	Returns:
6274432Seric **		none
6284432Seric **
6294432Seric **	Side Effects:
6304432Seric **
6314432Seric **		puts all lines in filename that match a scanf into
6324432Seric **			the named class.
6334432Seric */
6344432Seric 
63569748Seric void
63664133Seric fileclass(class, filename, fmt, safe, optional)
6374432Seric 	int class;
6384432Seric 	char *filename;
6394432Seric 	char *fmt;
64054973Seric 	bool safe;
64164133Seric 	bool optional;
6424432Seric {
64325808Seric 	FILE *f;
64468513Seric 	int sff;
64569453Seric 	int pid;
64669453Seric 	register char *p;
6474432Seric 	char buf[MAXLINE];
6484432Seric 
64966101Seric 	if (tTd(37, 2))
65066101Seric 		printf("fileclass(%s, fmt=%s)\n", filename, fmt);
65166101Seric 
65266031Seric 	if (filename[0] == '|')
65366031Seric 	{
65469453Seric 		auto int fd;
65569453Seric 		int i;
65669453Seric 		char *argv[MAXPV + 1];
65769453Seric 
65869453Seric 		i = 0;
65969453Seric 		for (p = strtok(&filename[1], " \t"); p != NULL; p = strtok(NULL, " \t"))
66069453Seric 		{
66169453Seric 			if (i >= MAXPV)
66269453Seric 				break;
66369453Seric 			argv[i++] = p;
66469453Seric 		}
66569453Seric 		argv[i] = NULL;
66669453Seric 		pid = prog_open(argv, &fd, CurEnv);
66769453Seric 		if (pid < 0)
66869453Seric 			f = NULL;
66969453Seric 		else
67069453Seric 			f = fdopen(fd, "r");
67166031Seric 	}
67269453Seric 	else
67369453Seric 	{
67469453Seric 		pid = -1;
67569453Seric 		sff = SFF_REGONLY;
67669453Seric 		if (safe)
67769453Seric 			sff |= SFF_OPENASROOT;
67869453Seric 		f = safefopen(filename, O_RDONLY, 0, sff);
67969453Seric 	}
68068602Seric 	if (f == NULL)
68154973Seric 	{
68268602Seric 		if (!optional)
68368602Seric 			syserr("fileclass: cannot open %s", filename);
6844432Seric 		return;
6854432Seric 	}
6864432Seric 
6874432Seric 	while (fgets(buf, sizeof buf, f) != NULL)
6884432Seric 	{
68925808Seric 		register char *p;
69069935Seric # if SCANF
6914432Seric 		char wordbuf[MAXNAME+1];
6924432Seric 
6934432Seric 		if (sscanf(buf, fmt, wordbuf) != 1)
6944432Seric 			continue;
69525808Seric 		p = wordbuf;
69656795Seric # else /* SCANF */
69725808Seric 		p = buf;
69856795Seric # endif /* SCANF */
69925808Seric 
70025808Seric 		/*
70125808Seric 		**  Break up the match into words.
70225808Seric 		*/
70325808Seric 
70425808Seric 		while (*p != '\0')
70525808Seric 		{
70625808Seric 			register char *q;
70725808Seric 
70825808Seric 			/* strip leading spaces */
70958050Seric 			while (isascii(*p) && isspace(*p))
71025808Seric 				p++;
71125808Seric 			if (*p == '\0')
71225808Seric 				break;
71325808Seric 
71425808Seric 			/* find the end of the word */
71525808Seric 			q = p;
71658050Seric 			while (*p != '\0' && !(isascii(*p) && isspace(*p)))
71725808Seric 				p++;
71825808Seric 			if (*p != '\0')
71925808Seric 				*p++ = '\0';
72025808Seric 
72125808Seric 			/* enter the word in the symbol table */
72266101Seric 			setclass(class, q);
72325808Seric 		}
7244432Seric 	}
7254432Seric 
72654973Seric 	(void) fclose(f);
72769453Seric 	if (pid > 0)
72869453Seric 		(void) waitfor(pid);
7294432Seric }
7304432Seric /*
7314096Seric **  MAKEMAILER -- define a new mailer.
7324096Seric **
7334096Seric **	Parameters:
73410327Seric **		line -- description of mailer.  This is in labeled
73510327Seric **			fields.  The fields are:
73668481Seric **			   A -- the argv for this mailer
73768481Seric **			   C -- the character set for MIME conversions
73868481Seric **			   D -- the directory to run in
73968481Seric **			   E -- the eol string
74068481Seric **			   F -- the flags associated with the mailer
74168481Seric **			   L -- the maximum line length
74268481Seric **			   M -- the maximum message size
74368816Seric **			   N -- the niceness at which to run
74468479Seric **			   P -- the path to the mailer
74568481Seric **			   R -- the recipient rewriting set
74668479Seric **			   S -- the sender rewriting set
74768481Seric **			   T -- the mailer type (for DSNs)
74868481Seric **			   U -- the uid to run as
74910327Seric **			The first word is the canonical name of the mailer.
7504096Seric **
7514096Seric **	Returns:
7524096Seric **		none.
7534096Seric **
7544096Seric **	Side Effects:
7554096Seric **		enters the mailer into the mailer table.
7564096Seric */
7573308Seric 
75869748Seric void
75921066Seric makemailer(line)
7604096Seric 	char *line;
7614096Seric {
7624096Seric 	register char *p;
7638067Seric 	register struct mailer *m;
7648067Seric 	register STAB *s;
7658067Seric 	int i;
76610327Seric 	char fcode;
76758020Seric 	auto char *endp;
7684096Seric 	extern int NextMailer;
76910327Seric 	extern char **makeargv();
77010327Seric 	extern char *munchstring();
7714096Seric 
77210327Seric 	/* allocate a mailer and set up defaults */
77310327Seric 	m = (struct mailer *) xalloc(sizeof *m);
77410327Seric 	bzero((char *) m, sizeof *m);
77510327Seric 	m->m_eol = "\n";
77668481Seric 	m->m_uid = m->m_gid = 0;
77710327Seric 
77810327Seric 	/* collect the mailer name */
77958050Seric 	for (p = line; *p != '\0' && *p != ',' && !(isascii(*p) && isspace(*p)); p++)
78010327Seric 		continue;
78110327Seric 	if (*p != '\0')
78210327Seric 		*p++ = '\0';
78310327Seric 	m->m_name = newstr(line);
78410327Seric 
78510327Seric 	/* now scan through and assign info from the fields */
78610327Seric 	while (*p != '\0')
78710327Seric 	{
78858333Seric 		auto char *delimptr;
78958333Seric 
79058050Seric 		while (*p != '\0' && (*p == ',' || (isascii(*p) && isspace(*p))))
79110327Seric 			p++;
79210327Seric 
79310327Seric 		/* p now points to field code */
79410327Seric 		fcode = *p;
79510327Seric 		while (*p != '\0' && *p != '=' && *p != ',')
79610327Seric 			p++;
79710327Seric 		if (*p++ != '=')
79810327Seric 		{
79952637Seric 			syserr("mailer %s: `=' expected", m->m_name);
80010327Seric 			return;
80110327Seric 		}
80258050Seric 		while (isascii(*p) && isspace(*p))
80310327Seric 			p++;
80410327Seric 
80510327Seric 		/* p now points to the field body */
80658333Seric 		p = munchstring(p, &delimptr);
80710327Seric 
80810327Seric 		/* install the field into the mailer struct */
80910327Seric 		switch (fcode)
81010327Seric 		{
81110327Seric 		  case 'P':		/* pathname */
81210327Seric 			m->m_mailer = newstr(p);
81310327Seric 			break;
81410327Seric 
81510327Seric 		  case 'F':		/* flags */
81610687Seric 			for (; *p != '\0'; p++)
81758050Seric 				if (!(isascii(*p) && isspace(*p)))
81852637Seric 					setbitn(*p, m->m_flags);
81910327Seric 			break;
82010327Seric 
82110327Seric 		  case 'S':		/* sender rewriting ruleset */
82210327Seric 		  case 'R':		/* recipient rewriting ruleset */
82369783Seric 			i = strtorwset(p, &endp, ST_ENTER);
82469783Seric 			if (i < 0)
82510327Seric 				return;
82610327Seric 			if (fcode == 'S')
82758020Seric 				m->m_sh_rwset = m->m_se_rwset = i;
82810327Seric 			else
82958020Seric 				m->m_rh_rwset = m->m_re_rwset = i;
83058020Seric 
83158020Seric 			p = endp;
83259985Seric 			if (*p++ == '/')
83358020Seric 			{
83469971Seric 				i = strtorwset(p, NULL, ST_ENTER);
83569783Seric 				if (i < 0)
83658020Seric 					return;
83758020Seric 				if (fcode == 'S')
83858020Seric 					m->m_sh_rwset = i;
83958020Seric 				else
84058020Seric 					m->m_rh_rwset = i;
84158020Seric 			}
84210327Seric 			break;
84310327Seric 
84410327Seric 		  case 'E':		/* end of line string */
84510327Seric 			m->m_eol = newstr(p);
84610327Seric 			break;
84710327Seric 
84810327Seric 		  case 'A':		/* argument vector */
84910327Seric 			m->m_argv = makeargv(p);
85010327Seric 			break;
85110701Seric 
85210701Seric 		  case 'M':		/* maximum message size */
85310701Seric 			m->m_maxsize = atol(p);
85410701Seric 			break;
85552106Seric 
85652106Seric 		  case 'L':		/* maximum line length */
85752106Seric 			m->m_linelimit = atoi(p);
85852106Seric 			break;
85958935Seric 
86068816Seric 		  case 'N':		/* run niceness */
86168816Seric 			m->m_nice = atoi(p);
86268816Seric 			break;
86368816Seric 
86458935Seric 		  case 'D':		/* working directory */
86558935Seric 			m->m_execdir = newstr(p);
86658935Seric 			break;
86768481Seric 
86868481Seric 		  case 'C':		/* default charset */
86968481Seric 			m->m_defcharset = newstr(p);
87068481Seric 			break;
87168481Seric 
87269720Seric 		  case 'T':		/* MTA-Name/Address/Diagnostic types */
87369858Seric 			/* extract MTA name type; default to "dns" */
87468481Seric 			m->m_mtatype = newstr(p);
87568481Seric 			p = strchr(m->m_mtatype, '/');
87668481Seric 			if (p != NULL)
87768481Seric 			{
87868481Seric 				*p++ = '\0';
87968481Seric 				if (*p == '\0')
88068481Seric 					p = NULL;
88168481Seric 			}
88269858Seric 			if (*m->m_mtatype == '\0')
88369858Seric 				m->m_mtatype = "dns";
88469858Seric 
88569858Seric 			/* extract address type; default to "rfc822" */
88669858Seric 			m->m_addrtype = p;
88769858Seric 			if (p != NULL)
88868481Seric 				p = strchr(p, '/');
88968481Seric 			if (p != NULL)
89068481Seric 			{
89168481Seric 				*p++ = '\0';
89268481Seric 				if (*p == '\0')
89368481Seric 					p = NULL;
89468481Seric 			}
89569858Seric 			if (m->m_addrtype == NULL || *m->m_addrtype == '\0')
89669858Seric 				m->m_addrtype = "rfc822";
89769858Seric 
89869858Seric 			/* extract diagnostic type; default to "smtp" */
89969858Seric 			m->m_diagtype = p;
90069858Seric 			if (m->m_diagtype == NULL || *m->m_diagtype == '\0')
90169858Seric 				m->m_diagtype = "smtp";
90268481Seric 			break;
90368481Seric 
90468481Seric 		  case 'U':		/* user id */
90568481Seric 			if (isascii(*p) && !isdigit(*p))
90668481Seric 			{
90768481Seric 				char *q = p;
90868481Seric 				struct passwd *pw;
90968481Seric 
91068481Seric 				while (isascii(*p) && isalnum(*p))
91168481Seric 					p++;
91268481Seric 				while (isascii(*p) && isspace(*p))
91368481Seric 					*p++ = '\0';
91468481Seric 				if (*p != '\0')
91568481Seric 					*p++ = '\0';
91668693Seric 				pw = sm_getpwnam(q);
91768481Seric 				if (pw == NULL)
91868481Seric 					syserr("readcf: mailer U= flag: unknown user %s", q);
91968481Seric 				else
92068481Seric 				{
92168481Seric 					m->m_uid = pw->pw_uid;
92268481Seric 					m->m_gid = pw->pw_gid;
92368481Seric 				}
92468481Seric 			}
92568481Seric 			else
92668481Seric 			{
92768481Seric 				auto char *q;
92868481Seric 
92968481Seric 				m->m_uid = strtol(p, &q, 0);
93068481Seric 				p = q;
93168481Seric 			}
93268481Seric 			while (isascii(*p) && isspace(*p))
93368481Seric 				p++;
93468481Seric 			if (*p == '\0')
93568481Seric 				break;
93668481Seric 			if (isascii(*p) && !isdigit(*p))
93768481Seric 			{
93868481Seric 				char *q = p;
93968481Seric 				struct group *gr;
94068481Seric 
94168481Seric 				while (isascii(*p) && isalnum(*p))
94268481Seric 					p++;
94368481Seric 				*p++ = '\0';
94468481Seric 				gr = getgrnam(q);
94568481Seric 				if (gr == NULL)
94668481Seric 					syserr("readcf: mailer U= flag: unknown group %s", q);
94768481Seric 				else
94868481Seric 					m->m_gid = gr->gr_gid;
94968481Seric 			}
95068481Seric 			else
95168481Seric 			{
95268481Seric 				m->m_gid = strtol(p, NULL, 0);
95368481Seric 			}
95468481Seric 			break;
95510327Seric 		}
95610327Seric 
95758333Seric 		p = delimptr;
95810327Seric 	}
95910327Seric 
96058321Seric 	/* do some rationality checking */
96158321Seric 	if (m->m_argv == NULL)
96258321Seric 	{
96358321Seric 		syserr("M%s: A= argument required", m->m_name);
96458321Seric 		return;
96558321Seric 	}
96658321Seric 	if (m->m_mailer == NULL)
96758321Seric 	{
96858321Seric 		syserr("M%s: P= argument required", m->m_name);
96958321Seric 		return;
97058321Seric 	}
97158321Seric 
9724096Seric 	if (NextMailer >= MAXMAILERS)
9734096Seric 	{
9749381Seric 		syserr("too many mailers defined (%d max)", MAXMAILERS);
9754096Seric 		return;
9764096Seric 	}
97757402Seric 
97868481Seric 	/* do some heuristic cleanup for back compatibility */
97968481Seric 	if (bitnset(M_LIMITS, m->m_flags))
98068481Seric 	{
98168481Seric 		if (m->m_linelimit == 0)
98268481Seric 			m->m_linelimit = SMTPLINELIM;
98368481Seric 		if (ConfigLevel < 2)
98468481Seric 			setbitn(M_7BITS, m->m_flags);
98568481Seric 	}
98668481Seric 
98768481Seric 	if (ConfigLevel < 6 &&
98868481Seric 	    (strcmp(m->m_mailer, "[IPC]") == 0 ||
98968481Seric 	     strcmp(m->m_mailer, "[TCP]") == 0))
99068481Seric 	{
99168481Seric 		if (m->m_mtatype == NULL)
99268481Seric 			m->m_mtatype = "dns";
99368481Seric 		if (m->m_addrtype == NULL)
99468481Seric 			m->m_addrtype = "rfc822";
99568481Seric 		if (m->m_diagtype == NULL)
99668481Seric 			m->m_diagtype = "smtp";
99768481Seric 	}
99868481Seric 
99968481Seric 	/* enter the mailer into the symbol table */
100010327Seric 	s = stab(m->m_name, ST_MAILER, ST_ENTER);
100157402Seric 	if (s->s_mailer != NULL)
100257402Seric 	{
100357402Seric 		i = s->s_mailer->m_mno;
100457402Seric 		free(s->s_mailer);
100557402Seric 	}
100657402Seric 	else
100757402Seric 	{
100857402Seric 		i = NextMailer++;
100957402Seric 	}
101057402Seric 	Mailer[i] = s->s_mailer = m;
101157454Seric 	m->m_mno = i;
101210327Seric }
101310327Seric /*
101410327Seric **  MUNCHSTRING -- translate a string into internal form.
101510327Seric **
101610327Seric **	Parameters:
101710327Seric **		p -- the string to munch.
101858333Seric **		delimptr -- if non-NULL, set to the pointer of the
101958333Seric **			field delimiter character.
102010327Seric **
102110327Seric **	Returns:
102210327Seric **		the munched string.
102310327Seric */
10244096Seric 
102510327Seric char *
102658333Seric munchstring(p, delimptr)
102710327Seric 	register char *p;
102858333Seric 	char **delimptr;
102910327Seric {
103010327Seric 	register char *q;
103110327Seric 	bool backslash = FALSE;
103210327Seric 	bool quotemode = FALSE;
103310327Seric 	static char buf[MAXLINE];
10344096Seric 
103510327Seric 	for (q = buf; *p != '\0'; p++)
10364096Seric 	{
103710327Seric 		if (backslash)
103810327Seric 		{
103910327Seric 			/* everything is roughly literal */
104010357Seric 			backslash = FALSE;
104110327Seric 			switch (*p)
104210327Seric 			{
104310327Seric 			  case 'r':		/* carriage return */
104410327Seric 				*q++ = '\r';
104510327Seric 				continue;
104610327Seric 
104710327Seric 			  case 'n':		/* newline */
104810327Seric 				*q++ = '\n';
104910327Seric 				continue;
105010327Seric 
105110327Seric 			  case 'f':		/* form feed */
105210327Seric 				*q++ = '\f';
105310327Seric 				continue;
105410327Seric 
105510327Seric 			  case 'b':		/* backspace */
105610327Seric 				*q++ = '\b';
105710327Seric 				continue;
105810327Seric 			}
105910327Seric 			*q++ = *p;
106010327Seric 		}
106110327Seric 		else
106210327Seric 		{
106310327Seric 			if (*p == '\\')
106410327Seric 				backslash = TRUE;
106510327Seric 			else if (*p == '"')
106610327Seric 				quotemode = !quotemode;
106710327Seric 			else if (quotemode || *p != ',')
106810327Seric 				*q++ = *p;
106910327Seric 			else
107010327Seric 				break;
107110327Seric 		}
10724096Seric 	}
10734096Seric 
107458333Seric 	if (delimptr != NULL)
107558333Seric 		*delimptr = p;
107610327Seric 	*q++ = '\0';
107710327Seric 	return (buf);
107810327Seric }
107910327Seric /*
108010327Seric **  MAKEARGV -- break up a string into words
108110327Seric **
108210327Seric **	Parameters:
108310327Seric **		p -- the string to break up.
108410327Seric **
108510327Seric **	Returns:
108610327Seric **		a char **argv (dynamically allocated)
108710327Seric **
108810327Seric **	Side Effects:
108910327Seric **		munges p.
109010327Seric */
10914096Seric 
109210327Seric char **
109310327Seric makeargv(p)
109410327Seric 	register char *p;
109510327Seric {
109610327Seric 	char *q;
109710327Seric 	int i;
109810327Seric 	char **avp;
109910327Seric 	char *argv[MAXPV + 1];
110010327Seric 
110110327Seric 	/* take apart the words */
110210327Seric 	i = 0;
110310327Seric 	while (*p != '\0' && i < MAXPV)
11044096Seric 	{
110510327Seric 		q = p;
110658050Seric 		while (*p != '\0' && !(isascii(*p) && isspace(*p)))
110710327Seric 			p++;
110858050Seric 		while (isascii(*p) && isspace(*p))
110910327Seric 			*p++ = '\0';
111010327Seric 		argv[i++] = newstr(q);
11114096Seric 	}
111210327Seric 	argv[i++] = NULL;
11134096Seric 
111410327Seric 	/* now make a copy of the argv */
111510327Seric 	avp = (char **) xalloc(sizeof *avp * i);
111616893Seric 	bcopy((char *) argv, (char *) avp, sizeof *avp * i);
111710327Seric 
111810327Seric 	return (avp);
11193308Seric }
11203308Seric /*
11213308Seric **  PRINTRULES -- print rewrite rules (for debugging)
11223308Seric **
11233308Seric **	Parameters:
11243308Seric **		none.
11253308Seric **
11263308Seric **	Returns:
11273308Seric **		none.
11283308Seric **
11293308Seric **	Side Effects:
11303308Seric **		prints rewrite rules.
11313308Seric */
11323308Seric 
113369748Seric void
11343308Seric printrules()
11353308Seric {
11363308Seric 	register struct rewrite *rwp;
11374072Seric 	register int ruleset;
11383308Seric 
11394072Seric 	for (ruleset = 0; ruleset < 10; ruleset++)
11403308Seric 	{
11414072Seric 		if (RewriteRules[ruleset] == NULL)
11424072Seric 			continue;
11438067Seric 		printf("\n----Rule Set %d:", ruleset);
11443308Seric 
11454072Seric 		for (rwp = RewriteRules[ruleset]; rwp != NULL; rwp = rwp->r_next)
11463308Seric 		{
11478067Seric 			printf("\nLHS:");
11488067Seric 			printav(rwp->r_lhs);
11498067Seric 			printf("RHS:");
11508067Seric 			printav(rwp->r_rhs);
11513308Seric 		}
11523308Seric 	}
11533308Seric }
115468481Seric /*
115568481Seric **  PRINTMAILER -- print mailer structure (for debugging)
115668481Seric **
115768481Seric **	Parameters:
115868481Seric **		m -- the mailer to print
115968481Seric **
116068481Seric **	Returns:
116168481Seric **		none.
116268481Seric */
11634319Seric 
116469748Seric void
116568481Seric printmailer(m)
116668481Seric 	register MAILER *m;
116768481Seric {
116868481Seric 	int j;
116968481Seric 
117068481Seric 	printf("mailer %d (%s): P=%s S=%d/%d R=%d/%d M=%ld U=%d:%d F=",
117168481Seric 		m->m_mno, m->m_name,
117268481Seric 		m->m_mailer, m->m_se_rwset, m->m_sh_rwset,
117368481Seric 		m->m_re_rwset, m->m_rh_rwset, m->m_maxsize,
117468481Seric 		m->m_uid, m->m_gid);
117568481Seric 	for (j = '\0'; j <= '\177'; j++)
117668481Seric 		if (bitnset(j, m->m_flags))
117768481Seric 			(void) putchar(j);
117868481Seric 	printf(" L=%d E=", m->m_linelimit);
117968481Seric 	xputs(m->m_eol);
118068481Seric 	if (m->m_defcharset != NULL)
118168481Seric 		printf(" C=%s", m->m_defcharset);
118268481Seric 	printf(" T=%s/%s/%s",
118368481Seric 		m->m_mtatype == NULL ? "<undefined>" : m->m_mtatype,
118468481Seric 		m->m_addrtype == NULL ? "<undefined>" : m->m_addrtype,
118568481Seric 		m->m_diagtype == NULL ? "<undefined>" : m->m_diagtype);
118668481Seric 	if (m->m_argv != NULL)
118768481Seric 	{
118868481Seric 		char **a = m->m_argv;
118968481Seric 
119068481Seric 		printf(" A=");
119168481Seric 		while (*a != NULL)
119268481Seric 		{
119368481Seric 			if (a != m->m_argv)
119468481Seric 				printf(" ");
119568481Seric 			xputs(*a++);
119668481Seric 		}
119768481Seric 	}
119868481Seric 	printf("\n");
119968481Seric }
12004096Seric /*
12018256Seric **  SETOPTION -- set global processing option
12028256Seric **
12038256Seric **	Parameters:
12048256Seric **		opt -- option name.
12058256Seric **		val -- option value (as a text string).
120621755Seric **		safe -- set if this came from a configuration file.
120721755Seric **			Some options (if set from the command line) will
120821755Seric **			reset the user id to avoid security problems.
12098269Seric **		sticky -- if set, don't let other setoptions override
12108269Seric **			this value.
121158734Seric **		e -- the main envelope.
12128256Seric **
12138256Seric **	Returns:
12148256Seric **		none.
12158256Seric **
12168256Seric **	Side Effects:
12178256Seric **		Sets options as implied by the arguments.
12188256Seric */
12198256Seric 
122010687Seric static BITMAP	StickyOpt;		/* set if option is stuck */
122169748Seric extern void	settimeout __P((char *, char *));
12228269Seric 
122357207Seric 
122466334Seric #if NAMED_BIND
122557207Seric 
122657207Seric struct resolverflags
122757207Seric {
122857207Seric 	char	*rf_name;	/* name of the flag */
122957207Seric 	long	rf_bits;	/* bits to set/clear */
123057207Seric } ResolverFlags[] =
123157207Seric {
123257207Seric 	"debug",	RES_DEBUG,
123357207Seric 	"aaonly",	RES_AAONLY,
123457207Seric 	"usevc",	RES_USEVC,
123557207Seric 	"primary",	RES_PRIMARY,
123657207Seric 	"igntc",	RES_IGNTC,
123757207Seric 	"recurse",	RES_RECURSE,
123857207Seric 	"defnames",	RES_DEFNAMES,
123957207Seric 	"stayopen",	RES_STAYOPEN,
124057207Seric 	"dnsrch",	RES_DNSRCH,
124165583Seric 	"true",		0,		/* to avoid error on old syntax */
124257207Seric 	NULL,		0
124357207Seric };
124457207Seric 
124557207Seric #endif
124657207Seric 
124768481Seric struct optioninfo
124868481Seric {
124968481Seric 	char	*o_name;	/* long name of option */
125068481Seric 	u_char	o_code;		/* short name of option */
125168481Seric 	bool	o_safe;		/* safe for random people to use */
125268481Seric } OptionTab[] =
125368481Seric {
125468481Seric 	"SevenBitInput",	'7',		TRUE,
125569480Seric #if MIME8TO7
125668481Seric 	"EightBitMode",		'8',		TRUE,
125769480Seric #endif
125868481Seric 	"AliasFile",		'A',		FALSE,
125968481Seric 	"AliasWait",		'a',		FALSE,
126068481Seric 	"BlankSub",		'B',		FALSE,
126168481Seric 	"MinFreeBlocks",	'b',		TRUE,
126268481Seric 	"CheckpointInterval",	'C',		TRUE,
126368481Seric 	"HoldExpensive",	'c',		FALSE,
126468481Seric 	"AutoRebuildAliases",	'D',		FALSE,
126568481Seric 	"DeliveryMode",		'd',		TRUE,
126668481Seric 	"ErrorHeader",		'E',		FALSE,
126768481Seric 	"ErrorMode",		'e',		TRUE,
126868481Seric 	"TempFileMode",		'F',		FALSE,
126968481Seric 	"SaveFromLine",		'f',		FALSE,
127068481Seric 	"MatchGECOS",		'G',		FALSE,
127168481Seric 	"HelpFile",		'H',		FALSE,
127268481Seric 	"MaxHopCount",		'h',		FALSE,
127368569Seric 	"ResolverOptions",	'I',		FALSE,
127468481Seric 	"IgnoreDots",		'i',		TRUE,
127568481Seric 	"ForwardPath",		'J',		FALSE,
127668481Seric 	"SendMimeErrors",	'j',		TRUE,
127768481Seric 	"ConnectionCacheSize",	'k',		FALSE,
127868481Seric 	"ConnectionCacheTimeout", 'K',		FALSE,
127968481Seric 	"UseErrorsTo",		'l',		FALSE,
128068481Seric 	"LogLevel",		'L',		FALSE,
128168481Seric 	"MeToo",		'm',		TRUE,
128268481Seric 	"CheckAliases",		'n',		FALSE,
128368481Seric 	"OldStyleHeaders",	'o',		TRUE,
128468481Seric 	"DaemonPortOptions",	'O',		FALSE,
128568481Seric 	"PrivacyOptions",	'p',		TRUE,
128668481Seric 	"PostmasterCopy",	'P',		FALSE,
128768481Seric 	"QueueFactor",		'q',		FALSE,
128868481Seric 	"QueueDirectory",	'Q',		FALSE,
128968481Seric 	"DontPruneRoutes",	'R',		FALSE,
129068481Seric 	"Timeout",		'r',		TRUE,
129168481Seric 	"StatusFile",		'S',		FALSE,
129268481Seric 	"SuperSafe",		's',		TRUE,
129368481Seric 	"QueueTimeout",		'T',		FALSE,
129468481Seric 	"TimeZoneSpec",		't',		FALSE,
129568481Seric 	"UserDatabaseSpec",	'U',		FALSE,
129668481Seric 	"DefaultUser",		'u',		FALSE,
129768481Seric 	"FallbackMXhost",	'V',		FALSE,
129868481Seric 	"Verbose",		'v',		TRUE,
129968481Seric 	"TryNullMXList",	'w',		TRUE,
130068481Seric 	"QueueLA",		'x',		FALSE,
130168481Seric 	"RefuseLA",		'X',		FALSE,
130268481Seric 	"RecipientFactor",	'y',		FALSE,
130368569Seric 	"ForkEachJob",		'Y',		FALSE,
130468481Seric 	"ClassFactor",		'z',		FALSE,
130568569Seric 	"RetryFactor",		'Z',		FALSE,
130668481Seric #define O_QUEUESORTORD	0x81
130768481Seric 	"QueueSortOrder",	O_QUEUESORTORD,	TRUE,
130869401Seric #define O_HOSTSFILE	0x82
130969401Seric 	"HostsFile",		O_HOSTSFILE,	FALSE,
131068481Seric #define O_MQA		0x83
131168481Seric 	"MinQueueAge",		O_MQA,		TRUE,
131268481Seric #define O_MHSA		0x84
131368481Seric /*
131468481Seric 	"MaxHostStatAge",	O_MHSA,		TRUE,
131568481Seric */
131668481Seric #define O_DEFCHARSET	0x85
131768481Seric 	"DefaultCharSet",	O_DEFCHARSET,	TRUE,
131868481Seric #define O_SSFILE	0x86
131968481Seric 	"ServiceSwitchFile",	O_SSFILE,	FALSE,
132068481Seric #define O_DIALDELAY	0x87
132168481Seric 	"DialDelay",		O_DIALDELAY,	TRUE,
132268481Seric #define O_NORCPTACTION	0x88
132368481Seric 	"NoRecipientAction",	O_NORCPTACTION,	TRUE,
132468490Seric #define O_SAFEFILEENV	0x89
132568490Seric 	"SafeFileEnvironment",	O_SAFEFILEENV,	FALSE,
132668569Seric #define O_MAXMSGSIZE	0x8a
132768569Seric 	"MaxMessageSize",	O_MAXMSGSIZE,	FALSE,
132868756Seric #define O_COLONOKINADDR	0x8b
132968756Seric 	"ColonOkInAddr",	O_COLONOKINADDR, TRUE,
133069724Seric #define O_MAXQUEUERUN	0x8c
133169724Seric 	"MaxQueueRunSize",	O_MAXQUEUERUN,	TRUE,
133269838Seric #define O_MAXCHILDREN	0x8d
133369838Seric /*
133469838Seric 	"MaxDaemonChildren",	O_MAXCHILDREN,	FALSE,
133569838Seric */
133669852Seric #define O_KEEPCNAMES	0x8e
133769852Seric 	"DontExpandCnames",	O_KEEPCNAMES,	FALSE,
133868481Seric 
133968481Seric 	NULL,			'\0',		FALSE,
134068481Seric };
134168481Seric 
134268481Seric 
134368481Seric 
134469748Seric void
134558734Seric setoption(opt, val, safe, sticky, e)
134669748Seric 	int opt;
13478256Seric 	char *val;
134821755Seric 	bool safe;
13498269Seric 	bool sticky;
135058734Seric 	register ENVELOPE *e;
13518256Seric {
135257207Seric 	register char *p;
135368481Seric 	register struct optioninfo *o;
135468481Seric 	char *subopt;
13558265Seric 	extern bool atobool();
135612633Seric 	extern time_t convtime();
135714879Seric 	extern int QueueLA;
135814879Seric 	extern int RefuseLA;
135964718Seric 	extern bool Warn_Q_option;
13608256Seric 
136168481Seric 	errno = 0;
136268481Seric 	if (opt == ' ')
136368481Seric 	{
136468481Seric 		/* full word options */
136568481Seric 		struct optioninfo *sel;
136668481Seric 
136768481Seric 		p = strchr(val, '=');
136868481Seric 		if (p == NULL)
136968481Seric 			p = &val[strlen(val)];
137068481Seric 		while (*--p == ' ')
137168481Seric 			continue;
137268481Seric 		while (*++p == ' ')
137368481Seric 			*p = '\0';
137468481Seric 		if (p == val)
137568481Seric 		{
137668481Seric 			syserr("readcf: null option name");
137768481Seric 			return;
137868481Seric 		}
137968481Seric 		if (*p == '=')
138068481Seric 			*p++ = '\0';
138168481Seric 		while (*p == ' ')
138268481Seric 			p++;
138368481Seric 		subopt = strchr(val, '.');
138468481Seric 		if (subopt != NULL)
138568481Seric 			*subopt++ = '\0';
138668481Seric 		sel = NULL;
138768481Seric 		for (o = OptionTab; o->o_name != NULL; o++)
138868481Seric 		{
138968481Seric 			if (strncasecmp(o->o_name, val, strlen(val)) != 0)
139068481Seric 				continue;
139168481Seric 			if (strlen(o->o_name) == strlen(val))
139268481Seric 			{
139368481Seric 				/* completely specified -- this must be it */
139468481Seric 				sel = NULL;
139568481Seric 				break;
139668481Seric 			}
139768481Seric 			if (sel != NULL)
139868481Seric 				break;
139968481Seric 			sel = o;
140068481Seric 		}
140168481Seric 		if (sel != NULL && o->o_name == NULL)
140268481Seric 			o = sel;
140368481Seric 		else if (o->o_name == NULL)
140468481Seric 		{
140568481Seric 			syserr("readcf: unknown option name %s", val);
140668481Seric 			return;
140768481Seric 		}
140868481Seric 		else if (sel != NULL)
140968481Seric 		{
141068481Seric 			syserr("readcf: ambiguous option name %s (matches %s and %s)",
141168481Seric 				val, sel->o_name, o->o_name);
141268481Seric 			return;
141368481Seric 		}
141468481Seric 		if (strlen(val) != strlen(o->o_name))
141568481Seric 		{
141668481Seric 			bool oldVerbose = Verbose;
141768481Seric 
141868481Seric 			Verbose = TRUE;
141968481Seric 			message("Option %s used as abbreviation for %s",
142068481Seric 				val, o->o_name);
142168481Seric 			Verbose = oldVerbose;
142268481Seric 		}
142368481Seric 		opt = o->o_code;
142468481Seric 		val = p;
142568481Seric 	}
142668481Seric 	else
142768481Seric 	{
142868481Seric 		for (o = OptionTab; o->o_name != NULL; o++)
142968481Seric 		{
143068481Seric 			if (o->o_code == opt)
143168481Seric 				break;
143268481Seric 		}
143368481Seric 		subopt = NULL;
143468481Seric 	}
143568481Seric 
14368256Seric 	if (tTd(37, 1))
143768481Seric 	{
143868481Seric 		printf(isascii(opt) && isprint(opt) ?
143969917Seric 			    "setoption %s (%c).%s=" :
144069917Seric 			    "setoption %s (0x%x).%s=",
144168481Seric 			o->o_name == NULL ? "<unknown>" : o->o_name,
144268481Seric 			opt,
144369917Seric 			subopt == NULL ? "" : subopt);
144469917Seric 		xputs(val);
144568481Seric 	}
14468256Seric 
14478256Seric 	/*
14488269Seric 	**  See if this option is preset for us.
14498256Seric 	*/
14508256Seric 
145159731Seric 	if (!sticky && bitnset(opt, StickyOpt))
14528269Seric 	{
14539341Seric 		if (tTd(37, 1))
14549341Seric 			printf(" (ignored)\n");
14558269Seric 		return;
14568269Seric 	}
14578269Seric 
145821755Seric 	/*
145921755Seric 	**  Check to see if this option can be specified by this user.
146021755Seric 	*/
146121755Seric 
146263787Seric 	if (!safe && RealUid == 0)
146321755Seric 		safe = TRUE;
146468481Seric 	if (!safe && !o->o_safe)
146521755Seric 	{
146639111Srick 		if (opt != 'M' || (val[0] != 'r' && val[0] != 's'))
146721755Seric 		{
146836582Sbostic 			if (tTd(37, 1))
146936582Sbostic 				printf(" (unsafe)");
147063787Seric 			if (RealUid != geteuid())
147136582Sbostic 			{
147251210Seric 				if (tTd(37, 1))
147351210Seric 					printf("(Resetting uid)");
147469963Seric 				endpwent();
147563787Seric 				(void) setgid(RealGid);
147663787Seric 				(void) setuid(RealUid);
147736582Sbostic 			}
147821755Seric 		}
147921755Seric 	}
148051210Seric 	if (tTd(37, 1))
148117985Seric 		printf("\n");
14828269Seric 
148368481Seric 	switch (opt & 0xff)
14848256Seric 	{
148559709Seric 	  case '7':		/* force seven-bit input */
148668481Seric 		SevenBitInput = atobool(val);
148752106Seric 		break;
148852106Seric 
148969480Seric #if MIME8TO7
149068481Seric 	  case '8':		/* handling of 8-bit input */
149168481Seric 		switch (*val)
149268481Seric 		{
149368481Seric 		  case 'm':		/* convert 8-bit, convert MIME */
149468481Seric 			MimeMode = MM_CVTMIME|MM_MIME8BIT;
149568481Seric 			break;
149668481Seric 
149768481Seric 		  case 'p':		/* pass 8 bit, convert MIME */
149868856Seric 			MimeMode = MM_CVTMIME|MM_PASS8BIT;
149968481Seric 			break;
150068481Seric 
150168481Seric 		  case 's':		/* strict adherence */
150268481Seric 			MimeMode = MM_CVTMIME;
150368481Seric 			break;
150468481Seric 
150568856Seric #if 0
150668856Seric 		  case 'r':		/* reject 8-bit, don't convert MIME */
150768856Seric 			MimeMode = 0;
150868856Seric 			break;
150968856Seric 
151068856Seric 		  case 'j':		/* "just send 8" */
151168856Seric 			MimeMode = MM_PASS8BIT;
151268856Seric 			break;
151368856Seric 
151468481Seric 		  case 'a':		/* encode 8 bit if available */
151568481Seric 			MimeMode = MM_MIME8BIT|MM_PASS8BIT|MM_CVTMIME;
151668481Seric 			break;
151768481Seric 
151868481Seric 		  case 'c':		/* convert 8 bit to MIME, never 7 bit */
151968481Seric 			MimeMode = MM_MIME8BIT;
152068481Seric 			break;
152168856Seric #endif
152268481Seric 
152368481Seric 		  default:
152468481Seric 			syserr("Unknown 8-bit mode %c", *val);
152568481Seric 			exit(EX_USAGE);
152668481Seric 		}
152768481Seric 		break;
152869480Seric #endif
152968481Seric 
15308256Seric 	  case 'A':		/* set default alias file */
15319381Seric 		if (val[0] == '\0')
153259672Seric 			setalias("aliases");
15339381Seric 		else
153459672Seric 			setalias(val);
15358256Seric 		break;
15368256Seric 
153717474Seric 	  case 'a':		/* look N minutes for "@:@" in alias file */
153817474Seric 		if (val[0] == '\0')
153964796Seric 			SafeAlias = 5 * 60;		/* five minutes */
154017474Seric 		else
154164796Seric 			SafeAlias = convtime(val, 'm');
154217474Seric 		break;
154317474Seric 
154416843Seric 	  case 'B':		/* substitution for blank character */
154516843Seric 		SpaceSub = val[0];
154616843Seric 		if (SpaceSub == '\0')
154716843Seric 			SpaceSub = ' ';
154816843Seric 		break;
154916843Seric 
155059283Seric 	  case 'b':		/* min blocks free on queue fs/max msg size */
155159283Seric 		p = strchr(val, '/');
155259283Seric 		if (p != NULL)
155359283Seric 		{
155459283Seric 			*p++ = '\0';
155559283Seric 			MaxMessageSize = atol(p);
155659283Seric 		}
155758082Seric 		MinBlocksFree = atol(val);
155858082Seric 		break;
155958082Seric 
15609284Seric 	  case 'c':		/* don't connect to "expensive" mailers */
15619381Seric 		NoConnect = atobool(val);
15629284Seric 		break;
15639284Seric 
156451305Seric 	  case 'C':		/* checkpoint every N addresses */
156551305Seric 		CheckpointInterval = atoi(val);
156624944Seric 		break;
156724944Seric 
15689284Seric 	  case 'd':		/* delivery mode */
15699284Seric 		switch (*val)
15708269Seric 		{
15719284Seric 		  case '\0':
157258734Seric 			e->e_sendmode = SM_DELIVER;
15738269Seric 			break;
15748269Seric 
157510755Seric 		  case SM_QUEUE:	/* queue only */
157610755Seric #ifndef QUEUE
157710755Seric 			syserr("need QUEUE to set -odqueue");
157856795Seric #endif /* QUEUE */
157910755Seric 			/* fall through..... */
158010755Seric 
15819284Seric 		  case SM_DELIVER:	/* do everything */
15829284Seric 		  case SM_FORK:		/* fork after verification */
158358734Seric 			e->e_sendmode = *val;
15848269Seric 			break;
15858269Seric 
15868269Seric 		  default:
15879284Seric 			syserr("Unknown delivery mode %c", *val);
15888269Seric 			exit(EX_USAGE);
15898269Seric 		}
15908269Seric 		break;
15918269Seric 
15929146Seric 	  case 'D':		/* rebuild alias database as needed */
15939381Seric 		AutoRebuild = atobool(val);
15949146Seric 		break;
15959146Seric 
159655372Seric 	  case 'E':		/* error message header/header file */
159755379Seric 		if (*val != '\0')
159855379Seric 			ErrMsgFile = newstr(val);
159955372Seric 		break;
160055372Seric 
16018269Seric 	  case 'e':		/* set error processing mode */
16028269Seric 		switch (*val)
16038269Seric 		{
16049381Seric 		  case EM_QUIET:	/* be silent about it */
16059381Seric 		  case EM_MAIL:		/* mail back */
16069381Seric 		  case EM_BERKNET:	/* do berknet error processing */
16079381Seric 		  case EM_WRITE:	/* write back (or mail) */
16089381Seric 		  case EM_PRINT:	/* print errors normally (default) */
160958734Seric 			e->e_errormode = *val;
16108269Seric 			break;
16118269Seric 		}
16128269Seric 		break;
16138269Seric 
16149049Seric 	  case 'F':		/* file mode */
161517975Seric 		FileMode = atooct(val) & 0777;
16169049Seric 		break;
16179049Seric 
16188269Seric 	  case 'f':		/* save Unix-style From lines on front */
16199381Seric 		SaveFrom = atobool(val);
16208269Seric 		break;
16218269Seric 
162253735Seric 	  case 'G':		/* match recipients against GECOS field */
162353735Seric 		MatchGecos = atobool(val);
162453735Seric 		break;
162553735Seric 
16268256Seric 	  case 'g':		/* default gid */
162768481Seric   g_opt:
162864133Seric 		if (isascii(*val) && isdigit(*val))
162964133Seric 			DefGid = atoi(val);
163064133Seric 		else
163164133Seric 		{
163264133Seric 			register struct group *gr;
163364133Seric 
163464133Seric 			DefGid = -1;
163564133Seric 			gr = getgrnam(val);
163664133Seric 			if (gr == NULL)
163768481Seric 				syserr("readcf: option %c: unknown group %s",
163868481Seric 					opt, val);
163964133Seric 			else
164064133Seric 				DefGid = gr->gr_gid;
164164133Seric 		}
16428256Seric 		break;
16438256Seric 
16448256Seric 	  case 'H':		/* help file */
16459381Seric 		if (val[0] == '\0')
16468269Seric 			HelpFile = "sendmail.hf";
16479381Seric 		else
16489381Seric 			HelpFile = newstr(val);
16498256Seric 		break;
16508256Seric 
165151305Seric 	  case 'h':		/* maximum hop count */
165251305Seric 		MaxHopCount = atoi(val);
165351305Seric 		break;
165451305Seric 
165535651Seric 	  case 'I':		/* use internet domain name server */
165666334Seric #if NAMED_BIND
165757207Seric 		for (p = val; *p != 0; )
165857207Seric 		{
165957207Seric 			bool clearmode;
166057207Seric 			char *q;
166157207Seric 			struct resolverflags *rfp;
166257207Seric 
166357207Seric 			while (*p == ' ')
166457207Seric 				p++;
166557207Seric 			if (*p == '\0')
166657207Seric 				break;
166757207Seric 			clearmode = FALSE;
166857207Seric 			if (*p == '-')
166957207Seric 				clearmode = TRUE;
167057207Seric 			else if (*p != '+')
167157207Seric 				p--;
167257207Seric 			p++;
167357207Seric 			q = p;
167458050Seric 			while (*p != '\0' && !(isascii(*p) && isspace(*p)))
167557207Seric 				p++;
167657207Seric 			if (*p != '\0')
167757207Seric 				*p++ = '\0';
167868759Seric 			if (strcasecmp(q, "HasWildcardMX") == 0)
167968759Seric 			{
168069896Seric 				HasWildcardMX = !clearmode;
168168759Seric 				continue;
168268759Seric 			}
168357207Seric 			for (rfp = ResolverFlags; rfp->rf_name != NULL; rfp++)
168457207Seric 			{
168557207Seric 				if (strcasecmp(q, rfp->rf_name) == 0)
168657207Seric 					break;
168757207Seric 			}
168864923Seric 			if (rfp->rf_name == NULL)
168964923Seric 				syserr("readcf: I option value %s unrecognized", q);
169064923Seric 			else if (clearmode)
169157207Seric 				_res.options &= ~rfp->rf_bits;
169257207Seric 			else
169357207Seric 				_res.options |= rfp->rf_bits;
169457207Seric 		}
169557207Seric 		if (tTd(8, 2))
169668759Seric 			printf("_res.options = %x, HasWildcardMX = %d\n",
169769896Seric 				_res.options, HasWildcardMX);
169857207Seric #else
169957207Seric 		usrerr("name server (I option) specified but BIND not compiled in");
170057207Seric #endif
170135651Seric 		break;
170235651Seric 
17038269Seric 	  case 'i':		/* ignore dot lines in message */
17049381Seric 		IgnrDot = atobool(val);
17058269Seric 		break;
17068269Seric 
170759730Seric 	  case 'j':		/* send errors in MIME (RFC 1341) format */
170859730Seric 		SendMIMEErrors = atobool(val);
170959730Seric 		break;
171059730Seric 
171157136Seric 	  case 'J':		/* .forward search path */
171257136Seric 		ForwardPath = newstr(val);
171357136Seric 		break;
171457136Seric 
171554967Seric 	  case 'k':		/* connection cache size */
171654967Seric 		MaxMciCache = atoi(val);
171756215Seric 		if (MaxMciCache < 0)
171856215Seric 			MaxMciCache = 0;
171954967Seric 		break;
172054967Seric 
172154967Seric 	  case 'K':		/* connection cache timeout */
172258796Seric 		MciCacheTimeout = convtime(val, 'm');
172354967Seric 		break;
172454967Seric 
172561104Seric 	  case 'l':		/* use Errors-To: header */
172661104Seric 		UseErrorsTo = atobool(val);
172761104Seric 		break;
172861104Seric 
17298256Seric 	  case 'L':		/* log level */
173064140Seric 		if (safe || LogLevel < atoi(val))
173164140Seric 			LogLevel = atoi(val);
17328256Seric 		break;
17338256Seric 
17348269Seric 	  case 'M':		/* define macro */
173568267Seric 		p = newstr(&val[1]);
173668267Seric 		if (!safe)
173768267Seric 			cleanstrcpy(p, p, MAXNAME);
173868267Seric 		define(val[0], p, CurEnv);
173916878Seric 		sticky = FALSE;
17408269Seric 		break;
17418269Seric 
17428269Seric 	  case 'm':		/* send to me too */
17439381Seric 		MeToo = atobool(val);
17448269Seric 		break;
17458269Seric 
174625820Seric 	  case 'n':		/* validate RHS in newaliases */
174725820Seric 		CheckAliases = atobool(val);
174825820Seric 		break;
174925820Seric 
175061104Seric 	    /* 'N' available -- was "net name" */
175161104Seric 
175258851Seric 	  case 'O':		/* daemon options */
175358851Seric 		setdaemonoptions(val);
175458851Seric 		break;
175558851Seric 
17568269Seric 	  case 'o':		/* assume old style headers */
17579381Seric 		if (atobool(val))
17589341Seric 			CurEnv->e_flags |= EF_OLDSTYLE;
17599341Seric 		else
17609341Seric 			CurEnv->e_flags &= ~EF_OLDSTYLE;
17618269Seric 		break;
17628269Seric 
176358082Seric 	  case 'p':		/* select privacy level */
176458082Seric 		p = val;
176558082Seric 		for (;;)
176658082Seric 		{
176758082Seric 			register struct prival *pv;
176858082Seric 			extern struct prival PrivacyValues[];
176958082Seric 
177058082Seric 			while (isascii(*p) && (isspace(*p) || ispunct(*p)))
177158082Seric 				p++;
177258082Seric 			if (*p == '\0')
177358082Seric 				break;
177458082Seric 			val = p;
177558082Seric 			while (isascii(*p) && isalnum(*p))
177658082Seric 				p++;
177758082Seric 			if (*p != '\0')
177858082Seric 				*p++ = '\0';
177958082Seric 
178058082Seric 			for (pv = PrivacyValues; pv->pv_name != NULL; pv++)
178158082Seric 			{
178258082Seric 				if (strcasecmp(val, pv->pv_name) == 0)
178358082Seric 					break;
178458082Seric 			}
178558886Seric 			if (pv->pv_name == NULL)
178658886Seric 				syserr("readcf: Op line: %s unrecognized", val);
178758082Seric 			PrivacyFlags |= pv->pv_flag;
178858082Seric 		}
178968479Seric 		sticky = FALSE;
179058082Seric 		break;
179158082Seric 
179224944Seric 	  case 'P':		/* postmaster copy address for returned mail */
179324944Seric 		PostMasterCopy = newstr(val);
179424944Seric 		break;
179524944Seric 
179624944Seric 	  case 'q':		/* slope of queue only function */
179724944Seric 		QueueFactor = atoi(val);
179824944Seric 		break;
179924944Seric 
18008256Seric 	  case 'Q':		/* queue directory */
18019381Seric 		if (val[0] == '\0')
18028269Seric 			QueueDir = "mqueue";
18039381Seric 		else
18049381Seric 			QueueDir = newstr(val);
180558789Seric 		if (RealUid != 0 && !safe)
180664718Seric 			Warn_Q_option = TRUE;
18078256Seric 		break;
18088256Seric 
180958148Seric 	  case 'R':		/* don't prune routes */
181058148Seric 		DontPruneRoutes = atobool(val);
181158148Seric 		break;
181258148Seric 
18138256Seric 	  case 'r':		/* read timeout */
181468481Seric 		if (subopt == NULL)
181568481Seric 			inittimeouts(val);
181668481Seric 		else
181768481Seric 			settimeout(subopt, val);
18188256Seric 		break;
18198256Seric 
18208256Seric 	  case 'S':		/* status file */
18219381Seric 		if (val[0] == '\0')
18228269Seric 			StatFile = "sendmail.st";
18239381Seric 		else
18249381Seric 			StatFile = newstr(val);
18258256Seric 		break;
18268256Seric 
18278265Seric 	  case 's':		/* be super safe, even if expensive */
18289381Seric 		SuperSafe = atobool(val);
18298256Seric 		break;
18308256Seric 
18318256Seric 	  case 'T':		/* queue timeout */
183258737Seric 		p = strchr(val, '/');
183358737Seric 		if (p != NULL)
183458737Seric 		{
183558737Seric 			*p++ = '\0';
183668481Seric 			settimeout("queuewarn", p);
183758737Seric 		}
183868481Seric 		settimeout("queuereturn", val);
183954967Seric 		break;
18408256Seric 
18418265Seric 	  case 't':		/* time zone name */
184252106Seric 		TimeZoneSpec = newstr(val);
18438265Seric 		break;
18448265Seric 
184550556Seric 	  case 'U':		/* location of user database */
184651360Seric 		UdbSpec = newstr(val);
184750556Seric 		break;
184850556Seric 
18498256Seric 	  case 'u':		/* set default uid */
185068481Seric 		for (p = val; *p != '\0'; p++)
185168481Seric 		{
185268481Seric 			if (*p == '.' || *p == '/' || *p == ':')
185368481Seric 			{
185468481Seric 				*p++ = '\0';
185568481Seric 				break;
185668481Seric 			}
185768481Seric 		}
185864133Seric 		if (isascii(*val) && isdigit(*val))
185964133Seric 			DefUid = atoi(val);
186064133Seric 		else
186164133Seric 		{
186264133Seric 			register struct passwd *pw;
186364133Seric 
186464133Seric 			DefUid = -1;
186568693Seric 			pw = sm_getpwnam(val);
186664133Seric 			if (pw == NULL)
186764133Seric 				syserr("readcf: option u: unknown user %s", val);
186864133Seric 			else
186968481Seric 			{
187064133Seric 				DefUid = pw->pw_uid;
187168481Seric 				DefGid = pw->pw_gid;
187268481Seric 			}
187364133Seric 		}
187440973Sbostic 		setdefuser();
18758256Seric 
187668481Seric 		/* handle the group if it is there */
187768481Seric 		if (*p == '\0')
187868481Seric 			break;
187968481Seric 		val = p;
188068481Seric 		goto g_opt;
188168481Seric 
188258851Seric 	  case 'V':		/* fallback MX host */
188358851Seric 		FallBackMX = newstr(val);
188458851Seric 		break;
188558851Seric 
18868269Seric 	  case 'v':		/* run in verbose mode */
18879381Seric 		Verbose = atobool(val);
18888256Seric 		break;
18898256Seric 
189063837Seric 	  case 'w':		/* if we are best MX, try host directly */
189163837Seric 		TryNullMXList = atobool(val);
189263837Seric 		break;
189361104Seric 
189461104Seric 	    /* 'W' available -- was wizard password */
189561104Seric 
189614879Seric 	  case 'x':		/* load avg at which to auto-queue msgs */
189714879Seric 		QueueLA = atoi(val);
189814879Seric 		break;
189914879Seric 
190014879Seric 	  case 'X':		/* load avg at which to auto-reject connections */
190114879Seric 		RefuseLA = atoi(val);
190214879Seric 		break;
190314879Seric 
190424981Seric 	  case 'y':		/* work recipient factor */
190524981Seric 		WkRecipFact = atoi(val);
190624981Seric 		break;
190724981Seric 
190824981Seric 	  case 'Y':		/* fork jobs during queue runs */
190924952Seric 		ForkQueueRuns = atobool(val);
191024952Seric 		break;
191124952Seric 
191224981Seric 	  case 'z':		/* work message class factor */
191324981Seric 		WkClassFact = atoi(val);
191424981Seric 		break;
191524981Seric 
191624981Seric 	  case 'Z':		/* work time factor */
191724981Seric 		WkTimeFact = atoi(val);
191824981Seric 		break;
191924981Seric 
192068481Seric 	  case O_QUEUESORTORD:	/* queue sorting order */
192168481Seric 		switch (*val)
192268481Seric 		{
192368481Seric 		  case 'h':	/* Host first */
192468481Seric 		  case 'H':
192568481Seric 			QueueSortOrder = QS_BYHOST;
192668481Seric 			break;
192768481Seric 
192868481Seric 		  case 'p':	/* Priority order */
192968481Seric 		  case 'P':
193068481Seric 			QueueSortOrder = QS_BYPRIORITY;
193168481Seric 			break;
193268481Seric 
193368481Seric 		  default:
193468481Seric 			syserr("Invalid queue sort order \"%s\"", val);
193568481Seric 		}
193668481Seric 		break;
193768481Seric 
193869401Seric 	  case O_HOSTSFILE:	/* pathname of /etc/hosts file */
193969401Seric 		HostsFile = newstr(val);
194069401Seric 		break;
194169401Seric 
194268481Seric 	  case O_MQA:		/* minimum queue age between deliveries */
194368481Seric 		MinQueueAge = convtime(val, 'm');
194468481Seric 		break;
194568481Seric 
194668481Seric 	  case O_MHSA:		/* maximum age of cached host status */
194768481Seric 		MaxHostStatAge = convtime(val, 'm');
194868481Seric 		break;
194968481Seric 
195068481Seric 	  case O_DEFCHARSET:	/* default character set for mimefying */
195168481Seric 		DefaultCharSet = newstr(denlstring(val, TRUE, TRUE));
195268481Seric 		break;
195368481Seric 
195468481Seric 	  case O_SSFILE:	/* service switch file */
195568481Seric 		ServiceSwitchFile = newstr(val);
195668481Seric 		break;
195768481Seric 
195868481Seric 	  case O_DIALDELAY:	/* delay for dial-on-demand operation */
195968481Seric 		DialDelay = convtime(val, 's');
196068481Seric 		break;
196168481Seric 
196268481Seric 	  case O_NORCPTACTION:	/* what to do if no recipient */
196368481Seric 		if (strcasecmp(val, "none") == 0)
196468481Seric 			NoRecipientAction = NRA_NO_ACTION;
196568481Seric 		else if (strcasecmp(val, "add-to") == 0)
196668481Seric 			NoRecipientAction = NRA_ADD_TO;
196768481Seric 		else if (strcasecmp(val, "add-apparently-to") == 0)
196868481Seric 			NoRecipientAction = NRA_ADD_APPARENTLY_TO;
196968481Seric 		else if (strcasecmp(val, "add-bcc") == 0)
197068481Seric 			NoRecipientAction = NRA_ADD_BCC;
197168481Seric 		else if (strcasecmp(val, "add-to-undisclosed") == 0)
197268481Seric 			NoRecipientAction = NRA_ADD_TO_UNDISCLOSED;
197368481Seric 		else
197468481Seric 			syserr("Invalid NoRecipientAction: %s", val);
197568481Seric 
197668490Seric 	  case O_SAFEFILEENV:	/* chroot() environ for writing to files */
197768490Seric 		SafeFileEnv = newstr(val);
197868490Seric 		break;
197968490Seric 
198068569Seric 	  case O_MAXMSGSIZE:	/* maximum message size */
198169748Seric 		MaxMessageSize = atol(val);
198268569Seric 		break;
198368569Seric 
198468756Seric 	  case O_COLONOKINADDR:	/* old style handling of colon addresses */
198569748Seric 		ColonOkInAddr = atobool(val);
198668756Seric 		break;
198768756Seric 
198869724Seric 	  case O_MAXQUEUERUN:	/* max # of jobs in a single queue run */
198969748Seric 		MaxQueueRun = atol(val);
199069724Seric 		break;
199169724Seric 
199269838Seric 	  case O_MAXCHILDREN:	/* max # of children of daemon */
199369838Seric 		MaxChildren = atoi(val);
199469852Seric 		break;
199569838Seric 
199669852Seric 	  case O_KEEPCNAMES:	/* don't expand CNAME records */
199769852Seric 		DontExpandCnames = atobool(val);
199869852Seric 		break;
199969852Seric 
20008256Seric 	  default:
200168481Seric 		if (tTd(37, 1))
200268481Seric 		{
200368481Seric 			if (isascii(opt) && isprint(opt))
200468481Seric 				printf("Warning: option %c unknown\n", opt);
200568481Seric 			else
200668481Seric 				printf("Warning: option 0x%x unknown\n", opt);
200768481Seric 		}
20088256Seric 		break;
20098256Seric 	}
201016878Seric 	if (sticky)
201116878Seric 		setbitn(opt, StickyOpt);
20128256Seric }
201310687Seric /*
201468481Seric **  SETCLASS -- set a string into a class
201510687Seric **
201610687Seric **	Parameters:
201768481Seric **		class -- the class to put the string in.
201868481Seric **		str -- the string to enter
201910687Seric **
202010687Seric **	Returns:
202110687Seric **		none.
202210687Seric **
202310687Seric **	Side Effects:
202410687Seric **		puts the word into the symbol table.
202510687Seric */
202610687Seric 
202769748Seric void
202868481Seric setclass(class, str)
202910687Seric 	int class;
203068481Seric 	char *str;
203110687Seric {
203210687Seric 	register STAB *s;
203310687Seric 
203457943Seric 	if (tTd(37, 8))
203568481Seric 		printf("setclass(%c, %s)\n", class, str);
203668481Seric 	s = stab(str, ST_CLASS, ST_ENTER);
203710687Seric 	setbitn(class, s->s_class);
203810687Seric }
203953654Seric /*
204053654Seric **  MAKEMAPENTRY -- create a map entry
204153654Seric **
204253654Seric **	Parameters:
204353654Seric **		line -- the config file line
204453654Seric **
204553654Seric **	Returns:
204669774Seric **		A pointer to the map that has been created.
204769774Seric **		NULL if there was a syntax error.
204853654Seric **
204953654Seric **	Side Effects:
205053654Seric **		Enters the map into the dictionary.
205153654Seric */
205253654Seric 
205369774Seric MAP *
205453654Seric makemapentry(line)
205553654Seric 	char *line;
205653654Seric {
205753654Seric 	register char *p;
205853654Seric 	char *mapname;
205953654Seric 	char *classname;
206064078Seric 	register STAB *s;
206153654Seric 	STAB *class;
206253654Seric 
206358050Seric 	for (p = line; isascii(*p) && isspace(*p); p++)
206453654Seric 		continue;
206558050Seric 	if (!(isascii(*p) && isalnum(*p)))
206653654Seric 	{
206753654Seric 		syserr("readcf: config K line: no map name");
206869774Seric 		return NULL;
206953654Seric 	}
207053654Seric 
207153654Seric 	mapname = p;
207268481Seric 	while ((isascii(*++p) && isalnum(*p)) || *p == '.')
207353654Seric 		continue;
207453654Seric 	if (*p != '\0')
207553654Seric 		*p++ = '\0';
207658050Seric 	while (isascii(*p) && isspace(*p))
207753654Seric 		p++;
207858050Seric 	if (!(isascii(*p) && isalnum(*p)))
207953654Seric 	{
208053654Seric 		syserr("readcf: config K line, map %s: no map class", mapname);
208169774Seric 		return NULL;
208253654Seric 	}
208353654Seric 	classname = p;
208458050Seric 	while (isascii(*++p) && isalnum(*p))
208553654Seric 		continue;
208653654Seric 	if (*p != '\0')
208753654Seric 		*p++ = '\0';
208858050Seric 	while (isascii(*p) && isspace(*p))
208953654Seric 		p++;
209053654Seric 
209153654Seric 	/* look up the class */
209253654Seric 	class = stab(classname, ST_MAPCLASS, ST_FIND);
209353654Seric 	if (class == NULL)
209453654Seric 	{
209553654Seric 		syserr("readcf: map %s: class %s not available", mapname, classname);
209669774Seric 		return NULL;
209753654Seric 	}
209853654Seric 
209953654Seric 	/* enter the map */
210064078Seric 	s = stab(mapname, ST_MAP, ST_ENTER);
210164078Seric 	s->s_map.map_class = &class->s_mapclass;
210264078Seric 	s->s_map.map_mname = newstr(mapname);
210353654Seric 
210464078Seric 	if (class->s_mapclass.map_parse(&s->s_map, p))
210564078Seric 		s->s_map.map_mflags |= MF_VALID;
210664078Seric 
210764078Seric 	if (tTd(37, 5))
210864078Seric 	{
210964078Seric 		printf("map %s, class %s, flags %x, file %s,\n",
211064078Seric 			s->s_map.map_mname, s->s_map.map_class->map_cname,
211164078Seric 			s->s_map.map_mflags,
211264078Seric 			s->s_map.map_file == NULL ? "(null)" : s->s_map.map_file);
211364078Seric 		printf("\tapp %s, domain %s, rebuild %s\n",
211464078Seric 			s->s_map.map_app == NULL ? "(null)" : s->s_map.map_app,
211564078Seric 			s->s_map.map_domain == NULL ? "(null)" : s->s_map.map_domain,
211664078Seric 			s->s_map.map_rebuild == NULL ? "(null)" : s->s_map.map_rebuild);
211764078Seric 	}
211869774Seric 
211969774Seric 	return &s->s_map;
212053654Seric }
212158112Seric /*
212269783Seric **  STRTORWSET -- convert string to rewriting set number
212369783Seric **
212469783Seric **	Parameters:
212569783Seric **		p -- the pointer to the string to decode.
212669783Seric **		endp -- if set, store the trailing delimiter here.
212769783Seric **		stabmode -- ST_ENTER to create this entry, ST_FIND if
212869783Seric **			it must already exist.
212969783Seric **
213069783Seric **	Returns:
213169783Seric **		The appropriate ruleset number.
213269783Seric **		-1 if it is not valid (error already printed)
213369783Seric */
213469783Seric 
213569783Seric int
213669783Seric strtorwset(p, endp, stabmode)
213769783Seric 	char *p;
213869783Seric 	char **endp;
213969783Seric 	int stabmode;
214069783Seric {
214169783Seric 	int ruleset;
214269783Seric 	static int nextruleset = MAXRWSETS;
214369783Seric 
214469783Seric 	while (isascii(*p) && isspace(*p))
214569783Seric 		p++;
214669783Seric 	if (!isascii(*p))
214769783Seric 	{
214869783Seric 		syserr("invalid ruleset name: \"%.20s\"", p);
214969783Seric 		return -1;
215069783Seric 	}
215169783Seric 	if (isdigit(*p))
215269783Seric 	{
215369783Seric 		ruleset = strtol(p, endp, 10);
215469783Seric 		if (ruleset >= MAXRWSETS / 2 || ruleset < 0)
215569783Seric 		{
215669783Seric 			syserr("bad ruleset %d (%d max)",
215769783Seric 				ruleset, MAXRWSETS / 2);
215869783Seric 			ruleset = -1;
215969783Seric 		}
216069783Seric 	}
216169783Seric 	else
216269783Seric 	{
216369783Seric 		STAB *s;
216469783Seric 		char delim;
216569783Seric 		char *q;
216669783Seric 
216769783Seric 		q = p;
216869783Seric 		while (*p != '\0' && isascii(*p) &&
216969783Seric 		       (isalnum(*p) || strchr("-_$", *p) != NULL))
217069783Seric 			p++;
217169970Seric 		if (q == p || !isalpha(*q))
217269970Seric 		{
217369970Seric 			/* no valid characters */
217469970Seric 			syserr("invalid ruleset name: \"%.20s\"", q);
217569970Seric 			return -1;
217669970Seric 		}
217769783Seric 		while (isascii(*p) && isspace(*p))
217869783Seric 			*p++ = '\0';
217969783Seric 		delim = *p;
218069783Seric 		if (delim != '\0')
218169783Seric 			*p = '\0';
218269783Seric 		s = stab(q, ST_RULESET, stabmode);
218369783Seric 		if (delim != '\0')
218469783Seric 			*p = delim;
218569783Seric 
218669783Seric 		if (s == NULL)
218769783Seric 		{
218869783Seric 			syserr("unknown ruleset %s", q);
218969783Seric 			return -1;
219069783Seric 		}
219169783Seric 
219269783Seric 		if (stabmode == ST_ENTER && delim == '=')
219369783Seric 		{
219469962Seric 			ruleset = strtol(++p, endp, 10);
219569783Seric 			if (ruleset >= MAXRWSETS / 2 || ruleset < 0)
219669783Seric 			{
219769783Seric 				syserr("bad ruleset %s = %d (%d max)",
219869783Seric 					q, ruleset, MAXRWSETS / 2);
219969783Seric 				ruleset = -1;
220069783Seric 			}
220169783Seric 		}
220269783Seric 		else
220369783Seric 		{
220469783Seric 			if (endp != NULL)
220569783Seric 				*endp = p;
220669783Seric 			if (s->s_ruleset > 0)
220769783Seric 				ruleset = s->s_ruleset;
220869783Seric 			else if ((ruleset = --nextruleset) < MAXRWSETS / 2)
220969783Seric 			{
221069783Seric 				syserr("%s: too many named rulesets (%d max)",
221169783Seric 					q, MAXRWSETS / 2);
221269783Seric 				ruleset = -1;
221369783Seric 			}
221469783Seric 		}
221569783Seric 		if (s->s_ruleset > 0 && ruleset >= 0 && ruleset != s->s_ruleset)
221669783Seric 		{
221769783Seric 			syserr("%s: ruleset changed value (old %d, new %d)",
221869783Seric 				q, ruleset, s->s_ruleset);
221969783Seric 			ruleset = s->s_ruleset;
222069783Seric 		}
222169783Seric 		else if (ruleset > 0)
222269783Seric 		{
222369783Seric 			s->s_ruleset = ruleset;
222469783Seric 		}
222569783Seric 	}
222669783Seric 	return ruleset;
222769783Seric }
222869783Seric /*
222968481Seric **  INITTIMEOUTS -- parse and set timeout values
223058112Seric **
223158112Seric **	Parameters:
223258112Seric **		val -- a pointer to the values.  If NULL, do initial
223358112Seric **			settings.
223458112Seric **
223558112Seric **	Returns:
223658112Seric **		none.
223758112Seric **
223858112Seric **	Side Effects:
223958112Seric **		Initializes the TimeOuts structure
224058112Seric */
224158112Seric 
224264255Seric #define SECONDS
224358112Seric #define MINUTES	* 60
224458112Seric #define HOUR	* 3600
224558112Seric 
224669748Seric void
224768481Seric inittimeouts(val)
224858112Seric 	register char *val;
224958112Seric {
225058112Seric 	register char *p;
225158671Seric 	extern time_t convtime();
225258112Seric 
225358112Seric 	if (val == NULL)
225458112Seric 	{
225558112Seric 		TimeOuts.to_initial = (time_t) 5 MINUTES;
225658112Seric 		TimeOuts.to_helo = (time_t) 5 MINUTES;
225758112Seric 		TimeOuts.to_mail = (time_t) 10 MINUTES;
225858112Seric 		TimeOuts.to_rcpt = (time_t) 1 HOUR;
225958112Seric 		TimeOuts.to_datainit = (time_t) 5 MINUTES;
226058112Seric 		TimeOuts.to_datablock = (time_t) 1 HOUR;
226158112Seric 		TimeOuts.to_datafinal = (time_t) 1 HOUR;
226258112Seric 		TimeOuts.to_rset = (time_t) 5 MINUTES;
226358112Seric 		TimeOuts.to_quit = (time_t) 2 MINUTES;
226458112Seric 		TimeOuts.to_nextcommand = (time_t) 1 HOUR;
226558112Seric 		TimeOuts.to_miscshort = (time_t) 2 MINUTES;
226668481Seric #if IDENTPROTO
226764255Seric 		TimeOuts.to_ident = (time_t) 30 SECONDS;
226868481Seric #else
226968481Seric 		TimeOuts.to_ident = (time_t) 0 SECONDS;
227068481Seric #endif
227168481Seric 		TimeOuts.to_fileopen = (time_t) 60 SECONDS;
227258112Seric 		return;
227358112Seric 	}
227458112Seric 
227558112Seric 	for (;; val = p)
227658112Seric 	{
227758112Seric 		while (isascii(*val) && isspace(*val))
227858112Seric 			val++;
227958112Seric 		if (*val == '\0')
228058112Seric 			break;
228158112Seric 		for (p = val; *p != '\0' && *p != ','; p++)
228258112Seric 			continue;
228358112Seric 		if (*p != '\0')
228458112Seric 			*p++ = '\0';
228558112Seric 
228658112Seric 		if (isascii(*val) && isdigit(*val))
228758112Seric 		{
228858112Seric 			/* old syntax -- set everything */
228958796Seric 			TimeOuts.to_mail = convtime(val, 'm');
229058112Seric 			TimeOuts.to_rcpt = TimeOuts.to_mail;
229158112Seric 			TimeOuts.to_datainit = TimeOuts.to_mail;
229258112Seric 			TimeOuts.to_datablock = TimeOuts.to_mail;
229358112Seric 			TimeOuts.to_datafinal = TimeOuts.to_mail;
229458112Seric 			TimeOuts.to_nextcommand = TimeOuts.to_mail;
229558112Seric 			continue;
229658112Seric 		}
229758112Seric 		else
229858112Seric 		{
229968481Seric 			register char *q = strchr(val, ':');
230058112Seric 
230168481Seric 			if (q == NULL && (q = strchr(val, '=')) == NULL)
230258112Seric 			{
230358112Seric 				/* syntax error */
230458112Seric 				continue;
230558112Seric 			}
230658112Seric 			*q++ = '\0';
230768481Seric 			settimeout(val, q);
230868481Seric 		}
230968481Seric 	}
231068481Seric }
231168481Seric /*
231268481Seric **  SETTIMEOUT -- set an individual timeout
231368481Seric **
231468481Seric **	Parameters:
231568481Seric **		name -- the name of the timeout.
231668481Seric **		val -- the value of the timeout.
231768481Seric **
231868481Seric **	Returns:
231968481Seric **		none.
232068481Seric */
232158112Seric 
232269748Seric void
232368481Seric settimeout(name, val)
232468481Seric 	char *name;
232568481Seric 	char *val;
232668481Seric {
232768481Seric 	register char *p;
232868481Seric 	time_t to;
232968481Seric 	extern time_t convtime();
233068481Seric 
233168481Seric 	to = convtime(val, 'm');
233268481Seric 	p = strchr(name, '.');
233368481Seric 	if (p != NULL)
233468481Seric 		*p++ = '\0';
233568481Seric 
233668481Seric 	if (strcasecmp(name, "initial") == 0)
233768481Seric 		TimeOuts.to_initial = to;
233868481Seric 	else if (strcasecmp(name, "mail") == 0)
233968481Seric 		TimeOuts.to_mail = to;
234068481Seric 	else if (strcasecmp(name, "rcpt") == 0)
234168481Seric 		TimeOuts.to_rcpt = to;
234268481Seric 	else if (strcasecmp(name, "datainit") == 0)
234368481Seric 		TimeOuts.to_datainit = to;
234468481Seric 	else if (strcasecmp(name, "datablock") == 0)
234568481Seric 		TimeOuts.to_datablock = to;
234668481Seric 	else if (strcasecmp(name, "datafinal") == 0)
234768481Seric 		TimeOuts.to_datafinal = to;
234868481Seric 	else if (strcasecmp(name, "command") == 0)
234968481Seric 		TimeOuts.to_nextcommand = to;
235068481Seric 	else if (strcasecmp(name, "rset") == 0)
235168481Seric 		TimeOuts.to_rset = to;
235268481Seric 	else if (strcasecmp(name, "helo") == 0)
235368481Seric 		TimeOuts.to_helo = to;
235468481Seric 	else if (strcasecmp(name, "quit") == 0)
235568481Seric 		TimeOuts.to_quit = to;
235668481Seric 	else if (strcasecmp(name, "misc") == 0)
235768481Seric 		TimeOuts.to_miscshort = to;
235868481Seric 	else if (strcasecmp(name, "ident") == 0)
235968481Seric 		TimeOuts.to_ident = to;
236068481Seric 	else if (strcasecmp(name, "fileopen") == 0)
236168481Seric 		TimeOuts.to_fileopen = to;
236268481Seric 	else if (strcasecmp(name, "queuewarn") == 0)
236368481Seric 	{
236468481Seric 		to = convtime(val, 'h');
236568481Seric 		if (p == NULL || strcmp(p, "*") == 0)
236668481Seric 		{
236768481Seric 			TimeOuts.to_q_warning[TOC_NORMAL] = to;
236868481Seric 			TimeOuts.to_q_warning[TOC_URGENT] = to;
236968481Seric 			TimeOuts.to_q_warning[TOC_NONURGENT] = to;
237058112Seric 		}
237168481Seric 		else if (strcasecmp(p, "normal") == 0)
237268481Seric 			TimeOuts.to_q_warning[TOC_NORMAL] = to;
237368481Seric 		else if (strcasecmp(p, "urgent") == 0)
237468481Seric 			TimeOuts.to_q_warning[TOC_URGENT] = to;
237568481Seric 		else if (strcasecmp(p, "non-urgent") == 0)
237668481Seric 			TimeOuts.to_q_warning[TOC_NONURGENT] = to;
237768481Seric 		else
237868481Seric 			syserr("settimeout: invalid queuewarn subtimeout %s", p);
237958112Seric 	}
238068481Seric 	else if (strcasecmp(name, "queuereturn") == 0)
238168481Seric 	{
238268481Seric 		to = convtime(val, 'd');
238368481Seric 		if (p == NULL || strcmp(p, "*") == 0)
238468481Seric 		{
238568481Seric 			TimeOuts.to_q_return[TOC_NORMAL] = to;
238668481Seric 			TimeOuts.to_q_return[TOC_URGENT] = to;
238768481Seric 			TimeOuts.to_q_return[TOC_NONURGENT] = to;
238868481Seric 		}
238968481Seric 		else if (strcasecmp(p, "normal") == 0)
239068481Seric 			TimeOuts.to_q_return[TOC_NORMAL] = to;
239168481Seric 		else if (strcasecmp(p, "urgent") == 0)
239268481Seric 			TimeOuts.to_q_return[TOC_URGENT] = to;
239368481Seric 		else if (strcasecmp(p, "non-urgent") == 0)
239468481Seric 			TimeOuts.to_q_return[TOC_NONURGENT] = to;
239568481Seric 		else
239668481Seric 			syserr("settimeout: invalid queuereturn subtimeout %s", p);
239768481Seric 	}
239868481Seric 	else
239968481Seric 		syserr("settimeout: invalid timeout %s", name);
240058112Seric }
2401