xref: /csrg-svn/usr.sbin/sendmail/src/readcf.c (revision 69976)
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*69976Seric static char sccsid[] = "@(#)readcf.c	8.113 (Berkeley) 06/22/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
readcf(cfname,safe,e)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 
18669975Seric 			/* special handling for $=, $~, and $& */
18769975Seric 			if (*p == '=' || *p == '~' || *p == '&')
18869975Seric 				p++;
18969975Seric 
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';
507*69976Seric 				if (tTd(35, 9))
508*69976Seric 					printf("redefine('w' as %s)\n",
509*69976Seric 						macvalue('w', e));
51064279Seric 			}
51168805Seric 			if (ConfigLevel >= 6)
51268805Seric 			{
51368805Seric 				ColonOkInAddr = FALSE;
51468805Seric 			}
51568805Seric 
51668805Seric 			/*
51768805Seric 			**  Look for vendor code.
51868805Seric 			*/
51968805Seric 
52064718Seric 			if (*ep++ == '/')
52164718Seric 			{
52264718Seric 				/* extract vendor code */
52364718Seric 				for (p = ep; isascii(*p) && isalpha(*p); )
52464718Seric 					p++;
52564718Seric 				*p = '\0';
52664718Seric 
52764718Seric 				if (!setvendor(ep))
52864718Seric 					syserr("invalid V line vendor code: \"%s\"",
52964718Seric 						ep);
53064718Seric 			}
53152645Seric 			break;
53252645Seric 
53353654Seric 		  case 'K':
53469774Seric 			(void) makemapentry(&bp[1]);
53553654Seric 			break;
53653654Seric 
53769476Seric 		  case 'E':
53869476Seric 			p = strchr(bp, '=');
53969476Seric 			if (p != NULL)
54069476Seric 				*p++ = '\0';
54169476Seric 			setuserenv(&bp[1], p);
54269476Seric 			break;
54369476Seric 
5443308Seric 		  default:
5454061Seric 		  badline:
54657135Seric 			syserr("unknown control line \"%s\"", bp);
5473308Seric 		}
54857135Seric 		if (bp != buf)
54957135Seric 			free(bp);
5503308Seric 	}
55152637Seric 	if (ferror(cf))
55252637Seric 	{
55352647Seric 		syserr("I/O read error", cfname);
55452637Seric 		exit(EX_OSFILE);
55552637Seric 	}
55652637Seric 	fclose(cf);
5579381Seric 	FileName = NULL;
55856836Seric 
55968481Seric 	/* initialize host maps from local service tables */
56068481Seric 	inithostmaps();
56168481Seric 
56268481Seric 	/* determine if we need to do special name-server frotz */
56367905Seric 	{
56468481Seric 		int nmaps;
56568481Seric 		char *maptype[MAXMAPSTACK];
56668481Seric 		short mapreturn[MAXMAPACTIONS];
56768481Seric 
56868481Seric 		nmaps = switch_map_find("hosts", maptype, mapreturn);
56968481Seric 		UseNameServer = FALSE;
57068481Seric 		if (nmaps > 0 && nmaps <= MAXMAPSTACK)
57168481Seric 		{
57268481Seric 			register int mapno;
57368481Seric 
57468481Seric 			for (mapno = 0; mapno < nmaps && !UseNameServer; mapno++)
57568481Seric 			{
57668481Seric 				if (strcmp(maptype[mapno], "dns") == 0)
57768481Seric 					UseNameServer = TRUE;
57868481Seric 			}
57968481Seric 		}
58068481Seric 
58168481Seric #ifdef HESIOD
58268481Seric 		nmaps = switch_map_find("passwd", maptype, mapreturn);
58368481Seric 		UseHesiod = FALSE;
58468481Seric 		if (nmaps > 0 && nmaps <= MAXMAPSTACK)
58568481Seric 		{
58668481Seric 			register int mapno;
58768481Seric 
58868481Seric 			for (mapno = 0; mapno < nmaps && !UseHesiod; mapno++)
58968481Seric 			{
59068481Seric 				if (strcmp(maptype[mapno], "hesiod") == 0)
59168481Seric 					UseHesiod = TRUE;
59268481Seric 			}
59368481Seric 		}
59468204Seric #endif
59567905Seric 	}
5964096Seric }
5974096Seric /*
5988547Seric **  TOOMANY -- signal too many of some option
5998547Seric **
6008547Seric **	Parameters:
6018547Seric **		id -- the id of the error line
6028547Seric **		maxcnt -- the maximum possible values
6038547Seric **
6048547Seric **	Returns:
6058547Seric **		none.
6068547Seric **
6078547Seric **	Side Effects:
6088547Seric **		gives a syserr.
6098547Seric */
6108547Seric 
61169748Seric void
toomany(id,maxcnt)6128547Seric toomany(id, maxcnt)
61369748Seric 	int id;
6148547Seric 	int maxcnt;
6158547Seric {
6169381Seric 	syserr("too many %c lines, %d max", id, maxcnt);
6178547Seric }
6188547Seric /*
6194432Seric **  FILECLASS -- read members of a class from a file
6204432Seric **
6214432Seric **	Parameters:
6224432Seric **		class -- class to define.
6234432Seric **		filename -- name of file to read.
6244432Seric **		fmt -- scanf string to use for match.
62564133Seric **		safe -- if set, this is a safe read.
62664133Seric **		optional -- if set, it is not an error for the file to
62764133Seric **			not exist.
6284432Seric **
6294432Seric **	Returns:
6304432Seric **		none
6314432Seric **
6324432Seric **	Side Effects:
6334432Seric **
6344432Seric **		puts all lines in filename that match a scanf into
6354432Seric **			the named class.
6364432Seric */
6374432Seric 
63869748Seric void
fileclass(class,filename,fmt,safe,optional)63964133Seric fileclass(class, filename, fmt, safe, optional)
6404432Seric 	int class;
6414432Seric 	char *filename;
6424432Seric 	char *fmt;
64354973Seric 	bool safe;
64464133Seric 	bool optional;
6454432Seric {
64625808Seric 	FILE *f;
64768513Seric 	int sff;
64869453Seric 	int pid;
64969453Seric 	register char *p;
6504432Seric 	char buf[MAXLINE];
6514432Seric 
65266101Seric 	if (tTd(37, 2))
65366101Seric 		printf("fileclass(%s, fmt=%s)\n", filename, fmt);
65466101Seric 
65566031Seric 	if (filename[0] == '|')
65666031Seric 	{
65769453Seric 		auto int fd;
65869453Seric 		int i;
65969453Seric 		char *argv[MAXPV + 1];
66069453Seric 
66169453Seric 		i = 0;
66269453Seric 		for (p = strtok(&filename[1], " \t"); p != NULL; p = strtok(NULL, " \t"))
66369453Seric 		{
66469453Seric 			if (i >= MAXPV)
66569453Seric 				break;
66669453Seric 			argv[i++] = p;
66769453Seric 		}
66869453Seric 		argv[i] = NULL;
66969453Seric 		pid = prog_open(argv, &fd, CurEnv);
67069453Seric 		if (pid < 0)
67169453Seric 			f = NULL;
67269453Seric 		else
67369453Seric 			f = fdopen(fd, "r");
67466031Seric 	}
67569453Seric 	else
67669453Seric 	{
67769453Seric 		pid = -1;
67869453Seric 		sff = SFF_REGONLY;
67969453Seric 		if (safe)
68069453Seric 			sff |= SFF_OPENASROOT;
68169453Seric 		f = safefopen(filename, O_RDONLY, 0, sff);
68269453Seric 	}
68368602Seric 	if (f == NULL)
68454973Seric 	{
68568602Seric 		if (!optional)
68668602Seric 			syserr("fileclass: cannot open %s", filename);
6874432Seric 		return;
6884432Seric 	}
6894432Seric 
6904432Seric 	while (fgets(buf, sizeof buf, f) != NULL)
6914432Seric 	{
69225808Seric 		register char *p;
69369935Seric # if SCANF
6944432Seric 		char wordbuf[MAXNAME+1];
6954432Seric 
6964432Seric 		if (sscanf(buf, fmt, wordbuf) != 1)
6974432Seric 			continue;
69825808Seric 		p = wordbuf;
69956795Seric # else /* SCANF */
70025808Seric 		p = buf;
70156795Seric # endif /* SCANF */
70225808Seric 
70325808Seric 		/*
70425808Seric 		**  Break up the match into words.
70525808Seric 		*/
70625808Seric 
70725808Seric 		while (*p != '\0')
70825808Seric 		{
70925808Seric 			register char *q;
71025808Seric 
71125808Seric 			/* strip leading spaces */
71258050Seric 			while (isascii(*p) && isspace(*p))
71325808Seric 				p++;
71425808Seric 			if (*p == '\0')
71525808Seric 				break;
71625808Seric 
71725808Seric 			/* find the end of the word */
71825808Seric 			q = p;
71958050Seric 			while (*p != '\0' && !(isascii(*p) && isspace(*p)))
72025808Seric 				p++;
72125808Seric 			if (*p != '\0')
72225808Seric 				*p++ = '\0';
72325808Seric 
72425808Seric 			/* enter the word in the symbol table */
72566101Seric 			setclass(class, q);
72625808Seric 		}
7274432Seric 	}
7284432Seric 
72954973Seric 	(void) fclose(f);
73069453Seric 	if (pid > 0)
73169453Seric 		(void) waitfor(pid);
7324432Seric }
7334432Seric /*
7344096Seric **  MAKEMAILER -- define a new mailer.
7354096Seric **
7364096Seric **	Parameters:
73710327Seric **		line -- description of mailer.  This is in labeled
73810327Seric **			fields.  The fields are:
73968481Seric **			   A -- the argv for this mailer
74068481Seric **			   C -- the character set for MIME conversions
74168481Seric **			   D -- the directory to run in
74268481Seric **			   E -- the eol string
74368481Seric **			   F -- the flags associated with the mailer
74468481Seric **			   L -- the maximum line length
74568481Seric **			   M -- the maximum message size
74668816Seric **			   N -- the niceness at which to run
74768479Seric **			   P -- the path to the mailer
74868481Seric **			   R -- the recipient rewriting set
74968479Seric **			   S -- the sender rewriting set
75068481Seric **			   T -- the mailer type (for DSNs)
75168481Seric **			   U -- the uid to run as
75210327Seric **			The first word is the canonical name of the mailer.
7534096Seric **
7544096Seric **	Returns:
7554096Seric **		none.
7564096Seric **
7574096Seric **	Side Effects:
7584096Seric **		enters the mailer into the mailer table.
7594096Seric */
7603308Seric 
76169748Seric void
makemailer(line)76221066Seric makemailer(line)
7634096Seric 	char *line;
7644096Seric {
7654096Seric 	register char *p;
7668067Seric 	register struct mailer *m;
7678067Seric 	register STAB *s;
7688067Seric 	int i;
76910327Seric 	char fcode;
77058020Seric 	auto char *endp;
7714096Seric 	extern int NextMailer;
77210327Seric 	extern char **makeargv();
77310327Seric 	extern char *munchstring();
7744096Seric 
77510327Seric 	/* allocate a mailer and set up defaults */
77610327Seric 	m = (struct mailer *) xalloc(sizeof *m);
77710327Seric 	bzero((char *) m, sizeof *m);
77810327Seric 	m->m_eol = "\n";
77968481Seric 	m->m_uid = m->m_gid = 0;
78010327Seric 
78110327Seric 	/* collect the mailer name */
78258050Seric 	for (p = line; *p != '\0' && *p != ',' && !(isascii(*p) && isspace(*p)); p++)
78310327Seric 		continue;
78410327Seric 	if (*p != '\0')
78510327Seric 		*p++ = '\0';
78610327Seric 	m->m_name = newstr(line);
78710327Seric 
78810327Seric 	/* now scan through and assign info from the fields */
78910327Seric 	while (*p != '\0')
79010327Seric 	{
79158333Seric 		auto char *delimptr;
79258333Seric 
79358050Seric 		while (*p != '\0' && (*p == ',' || (isascii(*p) && isspace(*p))))
79410327Seric 			p++;
79510327Seric 
79610327Seric 		/* p now points to field code */
79710327Seric 		fcode = *p;
79810327Seric 		while (*p != '\0' && *p != '=' && *p != ',')
79910327Seric 			p++;
80010327Seric 		if (*p++ != '=')
80110327Seric 		{
80252637Seric 			syserr("mailer %s: `=' expected", m->m_name);
80310327Seric 			return;
80410327Seric 		}
80558050Seric 		while (isascii(*p) && isspace(*p))
80610327Seric 			p++;
80710327Seric 
80810327Seric 		/* p now points to the field body */
80958333Seric 		p = munchstring(p, &delimptr);
81010327Seric 
81110327Seric 		/* install the field into the mailer struct */
81210327Seric 		switch (fcode)
81310327Seric 		{
81410327Seric 		  case 'P':		/* pathname */
81510327Seric 			m->m_mailer = newstr(p);
81610327Seric 			break;
81710327Seric 
81810327Seric 		  case 'F':		/* flags */
81910687Seric 			for (; *p != '\0'; p++)
82058050Seric 				if (!(isascii(*p) && isspace(*p)))
82152637Seric 					setbitn(*p, m->m_flags);
82210327Seric 			break;
82310327Seric 
82410327Seric 		  case 'S':		/* sender rewriting ruleset */
82510327Seric 		  case 'R':		/* recipient rewriting ruleset */
82669783Seric 			i = strtorwset(p, &endp, ST_ENTER);
82769783Seric 			if (i < 0)
82810327Seric 				return;
82910327Seric 			if (fcode == 'S')
83058020Seric 				m->m_sh_rwset = m->m_se_rwset = i;
83110327Seric 			else
83258020Seric 				m->m_rh_rwset = m->m_re_rwset = i;
83358020Seric 
83458020Seric 			p = endp;
83559985Seric 			if (*p++ == '/')
83658020Seric 			{
83769971Seric 				i = strtorwset(p, NULL, ST_ENTER);
83869783Seric 				if (i < 0)
83958020Seric 					return;
84058020Seric 				if (fcode == 'S')
84158020Seric 					m->m_sh_rwset = i;
84258020Seric 				else
84358020Seric 					m->m_rh_rwset = i;
84458020Seric 			}
84510327Seric 			break;
84610327Seric 
84710327Seric 		  case 'E':		/* end of line string */
84810327Seric 			m->m_eol = newstr(p);
84910327Seric 			break;
85010327Seric 
85110327Seric 		  case 'A':		/* argument vector */
85210327Seric 			m->m_argv = makeargv(p);
85310327Seric 			break;
85410701Seric 
85510701Seric 		  case 'M':		/* maximum message size */
85610701Seric 			m->m_maxsize = atol(p);
85710701Seric 			break;
85852106Seric 
85952106Seric 		  case 'L':		/* maximum line length */
86052106Seric 			m->m_linelimit = atoi(p);
86152106Seric 			break;
86258935Seric 
86368816Seric 		  case 'N':		/* run niceness */
86468816Seric 			m->m_nice = atoi(p);
86568816Seric 			break;
86668816Seric 
86758935Seric 		  case 'D':		/* working directory */
86858935Seric 			m->m_execdir = newstr(p);
86958935Seric 			break;
87068481Seric 
87168481Seric 		  case 'C':		/* default charset */
87268481Seric 			m->m_defcharset = newstr(p);
87368481Seric 			break;
87468481Seric 
87569720Seric 		  case 'T':		/* MTA-Name/Address/Diagnostic types */
87669858Seric 			/* extract MTA name type; default to "dns" */
87768481Seric 			m->m_mtatype = newstr(p);
87868481Seric 			p = strchr(m->m_mtatype, '/');
87968481Seric 			if (p != NULL)
88068481Seric 			{
88168481Seric 				*p++ = '\0';
88268481Seric 				if (*p == '\0')
88368481Seric 					p = NULL;
88468481Seric 			}
88569858Seric 			if (*m->m_mtatype == '\0')
88669858Seric 				m->m_mtatype = "dns";
88769858Seric 
88869858Seric 			/* extract address type; default to "rfc822" */
88969858Seric 			m->m_addrtype = p;
89069858Seric 			if (p != NULL)
89168481Seric 				p = strchr(p, '/');
89268481Seric 			if (p != NULL)
89368481Seric 			{
89468481Seric 				*p++ = '\0';
89568481Seric 				if (*p == '\0')
89668481Seric 					p = NULL;
89768481Seric 			}
89869858Seric 			if (m->m_addrtype == NULL || *m->m_addrtype == '\0')
89969858Seric 				m->m_addrtype = "rfc822";
90069858Seric 
90169858Seric 			/* extract diagnostic type; default to "smtp" */
90269858Seric 			m->m_diagtype = p;
90369858Seric 			if (m->m_diagtype == NULL || *m->m_diagtype == '\0')
90469858Seric 				m->m_diagtype = "smtp";
90568481Seric 			break;
90668481Seric 
90768481Seric 		  case 'U':		/* user id */
90868481Seric 			if (isascii(*p) && !isdigit(*p))
90968481Seric 			{
91068481Seric 				char *q = p;
91168481Seric 				struct passwd *pw;
91268481Seric 
91368481Seric 				while (isascii(*p) && isalnum(*p))
91468481Seric 					p++;
91568481Seric 				while (isascii(*p) && isspace(*p))
91668481Seric 					*p++ = '\0';
91768481Seric 				if (*p != '\0')
91868481Seric 					*p++ = '\0';
91968693Seric 				pw = sm_getpwnam(q);
92068481Seric 				if (pw == NULL)
92168481Seric 					syserr("readcf: mailer U= flag: unknown user %s", q);
92268481Seric 				else
92368481Seric 				{
92468481Seric 					m->m_uid = pw->pw_uid;
92568481Seric 					m->m_gid = pw->pw_gid;
92668481Seric 				}
92768481Seric 			}
92868481Seric 			else
92968481Seric 			{
93068481Seric 				auto char *q;
93168481Seric 
93268481Seric 				m->m_uid = strtol(p, &q, 0);
93368481Seric 				p = q;
93468481Seric 			}
93568481Seric 			while (isascii(*p) && isspace(*p))
93668481Seric 				p++;
93768481Seric 			if (*p == '\0')
93868481Seric 				break;
93968481Seric 			if (isascii(*p) && !isdigit(*p))
94068481Seric 			{
94168481Seric 				char *q = p;
94268481Seric 				struct group *gr;
94368481Seric 
94468481Seric 				while (isascii(*p) && isalnum(*p))
94568481Seric 					p++;
94668481Seric 				*p++ = '\0';
94768481Seric 				gr = getgrnam(q);
94868481Seric 				if (gr == NULL)
94968481Seric 					syserr("readcf: mailer U= flag: unknown group %s", q);
95068481Seric 				else
95168481Seric 					m->m_gid = gr->gr_gid;
95268481Seric 			}
95368481Seric 			else
95468481Seric 			{
95568481Seric 				m->m_gid = strtol(p, NULL, 0);
95668481Seric 			}
95768481Seric 			break;
95810327Seric 		}
95910327Seric 
96058333Seric 		p = delimptr;
96110327Seric 	}
96210327Seric 
96358321Seric 	/* do some rationality checking */
96458321Seric 	if (m->m_argv == NULL)
96558321Seric 	{
96658321Seric 		syserr("M%s: A= argument required", m->m_name);
96758321Seric 		return;
96858321Seric 	}
96958321Seric 	if (m->m_mailer == NULL)
97058321Seric 	{
97158321Seric 		syserr("M%s: P= argument required", m->m_name);
97258321Seric 		return;
97358321Seric 	}
97458321Seric 
9754096Seric 	if (NextMailer >= MAXMAILERS)
9764096Seric 	{
9779381Seric 		syserr("too many mailers defined (%d max)", MAXMAILERS);
9784096Seric 		return;
9794096Seric 	}
98057402Seric 
98168481Seric 	/* do some heuristic cleanup for back compatibility */
98268481Seric 	if (bitnset(M_LIMITS, m->m_flags))
98368481Seric 	{
98468481Seric 		if (m->m_linelimit == 0)
98568481Seric 			m->m_linelimit = SMTPLINELIM;
98668481Seric 		if (ConfigLevel < 2)
98768481Seric 			setbitn(M_7BITS, m->m_flags);
98868481Seric 	}
98968481Seric 
99068481Seric 	if (ConfigLevel < 6 &&
99168481Seric 	    (strcmp(m->m_mailer, "[IPC]") == 0 ||
99268481Seric 	     strcmp(m->m_mailer, "[TCP]") == 0))
99368481Seric 	{
99468481Seric 		if (m->m_mtatype == NULL)
99568481Seric 			m->m_mtatype = "dns";
99668481Seric 		if (m->m_addrtype == NULL)
99768481Seric 			m->m_addrtype = "rfc822";
99868481Seric 		if (m->m_diagtype == NULL)
99968481Seric 			m->m_diagtype = "smtp";
100068481Seric 	}
100168481Seric 
100268481Seric 	/* enter the mailer into the symbol table */
100310327Seric 	s = stab(m->m_name, ST_MAILER, ST_ENTER);
100457402Seric 	if (s->s_mailer != NULL)
100557402Seric 	{
100657402Seric 		i = s->s_mailer->m_mno;
100757402Seric 		free(s->s_mailer);
100857402Seric 	}
100957402Seric 	else
101057402Seric 	{
101157402Seric 		i = NextMailer++;
101257402Seric 	}
101357402Seric 	Mailer[i] = s->s_mailer = m;
101457454Seric 	m->m_mno = i;
101510327Seric }
101610327Seric /*
101710327Seric **  MUNCHSTRING -- translate a string into internal form.
101810327Seric **
101910327Seric **	Parameters:
102010327Seric **		p -- the string to munch.
102158333Seric **		delimptr -- if non-NULL, set to the pointer of the
102258333Seric **			field delimiter character.
102310327Seric **
102410327Seric **	Returns:
102510327Seric **		the munched string.
102610327Seric */
10274096Seric 
102810327Seric char *
munchstring(p,delimptr)102958333Seric munchstring(p, delimptr)
103010327Seric 	register char *p;
103158333Seric 	char **delimptr;
103210327Seric {
103310327Seric 	register char *q;
103410327Seric 	bool backslash = FALSE;
103510327Seric 	bool quotemode = FALSE;
103610327Seric 	static char buf[MAXLINE];
10374096Seric 
103810327Seric 	for (q = buf; *p != '\0'; p++)
10394096Seric 	{
104010327Seric 		if (backslash)
104110327Seric 		{
104210327Seric 			/* everything is roughly literal */
104310357Seric 			backslash = FALSE;
104410327Seric 			switch (*p)
104510327Seric 			{
104610327Seric 			  case 'r':		/* carriage return */
104710327Seric 				*q++ = '\r';
104810327Seric 				continue;
104910327Seric 
105010327Seric 			  case 'n':		/* newline */
105110327Seric 				*q++ = '\n';
105210327Seric 				continue;
105310327Seric 
105410327Seric 			  case 'f':		/* form feed */
105510327Seric 				*q++ = '\f';
105610327Seric 				continue;
105710327Seric 
105810327Seric 			  case 'b':		/* backspace */
105910327Seric 				*q++ = '\b';
106010327Seric 				continue;
106110327Seric 			}
106210327Seric 			*q++ = *p;
106310327Seric 		}
106410327Seric 		else
106510327Seric 		{
106610327Seric 			if (*p == '\\')
106710327Seric 				backslash = TRUE;
106810327Seric 			else if (*p == '"')
106910327Seric 				quotemode = !quotemode;
107010327Seric 			else if (quotemode || *p != ',')
107110327Seric 				*q++ = *p;
107210327Seric 			else
107310327Seric 				break;
107410327Seric 		}
10754096Seric 	}
10764096Seric 
107758333Seric 	if (delimptr != NULL)
107858333Seric 		*delimptr = p;
107910327Seric 	*q++ = '\0';
108010327Seric 	return (buf);
108110327Seric }
108210327Seric /*
108310327Seric **  MAKEARGV -- break up a string into words
108410327Seric **
108510327Seric **	Parameters:
108610327Seric **		p -- the string to break up.
108710327Seric **
108810327Seric **	Returns:
108910327Seric **		a char **argv (dynamically allocated)
109010327Seric **
109110327Seric **	Side Effects:
109210327Seric **		munges p.
109310327Seric */
10944096Seric 
109510327Seric char **
makeargv(p)109610327Seric makeargv(p)
109710327Seric 	register char *p;
109810327Seric {
109910327Seric 	char *q;
110010327Seric 	int i;
110110327Seric 	char **avp;
110210327Seric 	char *argv[MAXPV + 1];
110310327Seric 
110410327Seric 	/* take apart the words */
110510327Seric 	i = 0;
110610327Seric 	while (*p != '\0' && i < MAXPV)
11074096Seric 	{
110810327Seric 		q = p;
110958050Seric 		while (*p != '\0' && !(isascii(*p) && isspace(*p)))
111010327Seric 			p++;
111158050Seric 		while (isascii(*p) && isspace(*p))
111210327Seric 			*p++ = '\0';
111310327Seric 		argv[i++] = newstr(q);
11144096Seric 	}
111510327Seric 	argv[i++] = NULL;
11164096Seric 
111710327Seric 	/* now make a copy of the argv */
111810327Seric 	avp = (char **) xalloc(sizeof *avp * i);
111916893Seric 	bcopy((char *) argv, (char *) avp, sizeof *avp * i);
112010327Seric 
112110327Seric 	return (avp);
11223308Seric }
11233308Seric /*
11243308Seric **  PRINTRULES -- print rewrite rules (for debugging)
11253308Seric **
11263308Seric **	Parameters:
11273308Seric **		none.
11283308Seric **
11293308Seric **	Returns:
11303308Seric **		none.
11313308Seric **
11323308Seric **	Side Effects:
11333308Seric **		prints rewrite rules.
11343308Seric */
11353308Seric 
113669748Seric void
printrules()11373308Seric printrules()
11383308Seric {
11393308Seric 	register struct rewrite *rwp;
11404072Seric 	register int ruleset;
11413308Seric 
11424072Seric 	for (ruleset = 0; ruleset < 10; ruleset++)
11433308Seric 	{
11444072Seric 		if (RewriteRules[ruleset] == NULL)
11454072Seric 			continue;
11468067Seric 		printf("\n----Rule Set %d:", ruleset);
11473308Seric 
11484072Seric 		for (rwp = RewriteRules[ruleset]; rwp != NULL; rwp = rwp->r_next)
11493308Seric 		{
11508067Seric 			printf("\nLHS:");
11518067Seric 			printav(rwp->r_lhs);
11528067Seric 			printf("RHS:");
11538067Seric 			printav(rwp->r_rhs);
11543308Seric 		}
11553308Seric 	}
11563308Seric }
115768481Seric /*
115868481Seric **  PRINTMAILER -- print mailer structure (for debugging)
115968481Seric **
116068481Seric **	Parameters:
116168481Seric **		m -- the mailer to print
116268481Seric **
116368481Seric **	Returns:
116468481Seric **		none.
116568481Seric */
11664319Seric 
116769748Seric void
printmailer(m)116868481Seric printmailer(m)
116968481Seric 	register MAILER *m;
117068481Seric {
117168481Seric 	int j;
117268481Seric 
117368481Seric 	printf("mailer %d (%s): P=%s S=%d/%d R=%d/%d M=%ld U=%d:%d F=",
117468481Seric 		m->m_mno, m->m_name,
117568481Seric 		m->m_mailer, m->m_se_rwset, m->m_sh_rwset,
117668481Seric 		m->m_re_rwset, m->m_rh_rwset, m->m_maxsize,
117768481Seric 		m->m_uid, m->m_gid);
117868481Seric 	for (j = '\0'; j <= '\177'; j++)
117968481Seric 		if (bitnset(j, m->m_flags))
118068481Seric 			(void) putchar(j);
118168481Seric 	printf(" L=%d E=", m->m_linelimit);
118268481Seric 	xputs(m->m_eol);
118368481Seric 	if (m->m_defcharset != NULL)
118468481Seric 		printf(" C=%s", m->m_defcharset);
118568481Seric 	printf(" T=%s/%s/%s",
118668481Seric 		m->m_mtatype == NULL ? "<undefined>" : m->m_mtatype,
118768481Seric 		m->m_addrtype == NULL ? "<undefined>" : m->m_addrtype,
118868481Seric 		m->m_diagtype == NULL ? "<undefined>" : m->m_diagtype);
118968481Seric 	if (m->m_argv != NULL)
119068481Seric 	{
119168481Seric 		char **a = m->m_argv;
119268481Seric 
119368481Seric 		printf(" A=");
119468481Seric 		while (*a != NULL)
119568481Seric 		{
119668481Seric 			if (a != m->m_argv)
119768481Seric 				printf(" ");
119868481Seric 			xputs(*a++);
119968481Seric 		}
120068481Seric 	}
120168481Seric 	printf("\n");
120268481Seric }
12034096Seric /*
12048256Seric **  SETOPTION -- set global processing option
12058256Seric **
12068256Seric **	Parameters:
12078256Seric **		opt -- option name.
12088256Seric **		val -- option value (as a text string).
120921755Seric **		safe -- set if this came from a configuration file.
121021755Seric **			Some options (if set from the command line) will
121121755Seric **			reset the user id to avoid security problems.
12128269Seric **		sticky -- if set, don't let other setoptions override
12138269Seric **			this value.
121458734Seric **		e -- the main envelope.
12158256Seric **
12168256Seric **	Returns:
12178256Seric **		none.
12188256Seric **
12198256Seric **	Side Effects:
12208256Seric **		Sets options as implied by the arguments.
12218256Seric */
12228256Seric 
122310687Seric static BITMAP	StickyOpt;		/* set if option is stuck */
122469748Seric extern void	settimeout __P((char *, char *));
12258269Seric 
122657207Seric 
122766334Seric #if NAMED_BIND
122857207Seric 
122957207Seric struct resolverflags
123057207Seric {
123157207Seric 	char	*rf_name;	/* name of the flag */
123257207Seric 	long	rf_bits;	/* bits to set/clear */
123357207Seric } ResolverFlags[] =
123457207Seric {
123557207Seric 	"debug",	RES_DEBUG,
123657207Seric 	"aaonly",	RES_AAONLY,
123757207Seric 	"usevc",	RES_USEVC,
123857207Seric 	"primary",	RES_PRIMARY,
123957207Seric 	"igntc",	RES_IGNTC,
124057207Seric 	"recurse",	RES_RECURSE,
124157207Seric 	"defnames",	RES_DEFNAMES,
124257207Seric 	"stayopen",	RES_STAYOPEN,
124357207Seric 	"dnsrch",	RES_DNSRCH,
124465583Seric 	"true",		0,		/* to avoid error on old syntax */
124557207Seric 	NULL,		0
124657207Seric };
124757207Seric 
124857207Seric #endif
124957207Seric 
125068481Seric struct optioninfo
125168481Seric {
125268481Seric 	char	*o_name;	/* long name of option */
125368481Seric 	u_char	o_code;		/* short name of option */
125468481Seric 	bool	o_safe;		/* safe for random people to use */
125568481Seric } OptionTab[] =
125668481Seric {
125768481Seric 	"SevenBitInput",	'7',		TRUE,
125869480Seric #if MIME8TO7
125968481Seric 	"EightBitMode",		'8',		TRUE,
126069480Seric #endif
126168481Seric 	"AliasFile",		'A',		FALSE,
126268481Seric 	"AliasWait",		'a',		FALSE,
126368481Seric 	"BlankSub",		'B',		FALSE,
126468481Seric 	"MinFreeBlocks",	'b',		TRUE,
126568481Seric 	"CheckpointInterval",	'C',		TRUE,
126668481Seric 	"HoldExpensive",	'c',		FALSE,
126768481Seric 	"AutoRebuildAliases",	'D',		FALSE,
126868481Seric 	"DeliveryMode",		'd',		TRUE,
126968481Seric 	"ErrorHeader",		'E',		FALSE,
127068481Seric 	"ErrorMode",		'e',		TRUE,
127168481Seric 	"TempFileMode",		'F',		FALSE,
127268481Seric 	"SaveFromLine",		'f',		FALSE,
127368481Seric 	"MatchGECOS",		'G',		FALSE,
127468481Seric 	"HelpFile",		'H',		FALSE,
127568481Seric 	"MaxHopCount",		'h',		FALSE,
127668569Seric 	"ResolverOptions",	'I',		FALSE,
127768481Seric 	"IgnoreDots",		'i',		TRUE,
127868481Seric 	"ForwardPath",		'J',		FALSE,
127968481Seric 	"SendMimeErrors",	'j',		TRUE,
128068481Seric 	"ConnectionCacheSize",	'k',		FALSE,
128168481Seric 	"ConnectionCacheTimeout", 'K',		FALSE,
128268481Seric 	"UseErrorsTo",		'l',		FALSE,
128368481Seric 	"LogLevel",		'L',		FALSE,
128468481Seric 	"MeToo",		'm',		TRUE,
128568481Seric 	"CheckAliases",		'n',		FALSE,
128668481Seric 	"OldStyleHeaders",	'o',		TRUE,
128768481Seric 	"DaemonPortOptions",	'O',		FALSE,
128868481Seric 	"PrivacyOptions",	'p',		TRUE,
128968481Seric 	"PostmasterCopy",	'P',		FALSE,
129068481Seric 	"QueueFactor",		'q',		FALSE,
129168481Seric 	"QueueDirectory",	'Q',		FALSE,
129268481Seric 	"DontPruneRoutes",	'R',		FALSE,
129368481Seric 	"Timeout",		'r',		TRUE,
129468481Seric 	"StatusFile",		'S',		FALSE,
129568481Seric 	"SuperSafe",		's',		TRUE,
129668481Seric 	"QueueTimeout",		'T',		FALSE,
129768481Seric 	"TimeZoneSpec",		't',		FALSE,
129868481Seric 	"UserDatabaseSpec",	'U',		FALSE,
129968481Seric 	"DefaultUser",		'u',		FALSE,
130068481Seric 	"FallbackMXhost",	'V',		FALSE,
130168481Seric 	"Verbose",		'v',		TRUE,
130268481Seric 	"TryNullMXList",	'w',		TRUE,
130368481Seric 	"QueueLA",		'x',		FALSE,
130468481Seric 	"RefuseLA",		'X',		FALSE,
130568481Seric 	"RecipientFactor",	'y',		FALSE,
130668569Seric 	"ForkEachJob",		'Y',		FALSE,
130768481Seric 	"ClassFactor",		'z',		FALSE,
130868569Seric 	"RetryFactor",		'Z',		FALSE,
130968481Seric #define O_QUEUESORTORD	0x81
131068481Seric 	"QueueSortOrder",	O_QUEUESORTORD,	TRUE,
131169401Seric #define O_HOSTSFILE	0x82
131269401Seric 	"HostsFile",		O_HOSTSFILE,	FALSE,
131368481Seric #define O_MQA		0x83
131468481Seric 	"MinQueueAge",		O_MQA,		TRUE,
131568481Seric #define O_MHSA		0x84
131668481Seric /*
131768481Seric 	"MaxHostStatAge",	O_MHSA,		TRUE,
131868481Seric */
131968481Seric #define O_DEFCHARSET	0x85
132068481Seric 	"DefaultCharSet",	O_DEFCHARSET,	TRUE,
132168481Seric #define O_SSFILE	0x86
132268481Seric 	"ServiceSwitchFile",	O_SSFILE,	FALSE,
132368481Seric #define O_DIALDELAY	0x87
132468481Seric 	"DialDelay",		O_DIALDELAY,	TRUE,
132568481Seric #define O_NORCPTACTION	0x88
132668481Seric 	"NoRecipientAction",	O_NORCPTACTION,	TRUE,
132768490Seric #define O_SAFEFILEENV	0x89
132868490Seric 	"SafeFileEnvironment",	O_SAFEFILEENV,	FALSE,
132968569Seric #define O_MAXMSGSIZE	0x8a
133068569Seric 	"MaxMessageSize",	O_MAXMSGSIZE,	FALSE,
133168756Seric #define O_COLONOKINADDR	0x8b
133268756Seric 	"ColonOkInAddr",	O_COLONOKINADDR, TRUE,
133369724Seric #define O_MAXQUEUERUN	0x8c
133469724Seric 	"MaxQueueRunSize",	O_MAXQUEUERUN,	TRUE,
133569838Seric #define O_MAXCHILDREN	0x8d
133669838Seric /*
133769838Seric 	"MaxDaemonChildren",	O_MAXCHILDREN,	FALSE,
133869838Seric */
133969852Seric #define O_KEEPCNAMES	0x8e
134069852Seric 	"DontExpandCnames",	O_KEEPCNAMES,	FALSE,
134168481Seric 
134268481Seric 	NULL,			'\0',		FALSE,
134368481Seric };
134468481Seric 
134568481Seric 
134668481Seric 
134769748Seric void
setoption(opt,val,safe,sticky,e)134858734Seric setoption(opt, val, safe, sticky, e)
134969748Seric 	int opt;
13508256Seric 	char *val;
135121755Seric 	bool safe;
13528269Seric 	bool sticky;
135358734Seric 	register ENVELOPE *e;
13548256Seric {
135557207Seric 	register char *p;
135668481Seric 	register struct optioninfo *o;
135768481Seric 	char *subopt;
13588265Seric 	extern bool atobool();
135912633Seric 	extern time_t convtime();
136014879Seric 	extern int QueueLA;
136114879Seric 	extern int RefuseLA;
136264718Seric 	extern bool Warn_Q_option;
13638256Seric 
136468481Seric 	errno = 0;
136568481Seric 	if (opt == ' ')
136668481Seric 	{
136768481Seric 		/* full word options */
136868481Seric 		struct optioninfo *sel;
136968481Seric 
137068481Seric 		p = strchr(val, '=');
137168481Seric 		if (p == NULL)
137268481Seric 			p = &val[strlen(val)];
137368481Seric 		while (*--p == ' ')
137468481Seric 			continue;
137568481Seric 		while (*++p == ' ')
137668481Seric 			*p = '\0';
137768481Seric 		if (p == val)
137868481Seric 		{
137968481Seric 			syserr("readcf: null option name");
138068481Seric 			return;
138168481Seric 		}
138268481Seric 		if (*p == '=')
138368481Seric 			*p++ = '\0';
138468481Seric 		while (*p == ' ')
138568481Seric 			p++;
138668481Seric 		subopt = strchr(val, '.');
138768481Seric 		if (subopt != NULL)
138868481Seric 			*subopt++ = '\0';
138968481Seric 		sel = NULL;
139068481Seric 		for (o = OptionTab; o->o_name != NULL; o++)
139168481Seric 		{
139268481Seric 			if (strncasecmp(o->o_name, val, strlen(val)) != 0)
139368481Seric 				continue;
139468481Seric 			if (strlen(o->o_name) == strlen(val))
139568481Seric 			{
139668481Seric 				/* completely specified -- this must be it */
139768481Seric 				sel = NULL;
139868481Seric 				break;
139968481Seric 			}
140068481Seric 			if (sel != NULL)
140168481Seric 				break;
140268481Seric 			sel = o;
140368481Seric 		}
140468481Seric 		if (sel != NULL && o->o_name == NULL)
140568481Seric 			o = sel;
140668481Seric 		else if (o->o_name == NULL)
140768481Seric 		{
140868481Seric 			syserr("readcf: unknown option name %s", val);
140968481Seric 			return;
141068481Seric 		}
141168481Seric 		else if (sel != NULL)
141268481Seric 		{
141368481Seric 			syserr("readcf: ambiguous option name %s (matches %s and %s)",
141468481Seric 				val, sel->o_name, o->o_name);
141568481Seric 			return;
141668481Seric 		}
141768481Seric 		if (strlen(val) != strlen(o->o_name))
141868481Seric 		{
141968481Seric 			bool oldVerbose = Verbose;
142068481Seric 
142168481Seric 			Verbose = TRUE;
142268481Seric 			message("Option %s used as abbreviation for %s",
142368481Seric 				val, o->o_name);
142468481Seric 			Verbose = oldVerbose;
142568481Seric 		}
142668481Seric 		opt = o->o_code;
142768481Seric 		val = p;
142868481Seric 	}
142968481Seric 	else
143068481Seric 	{
143168481Seric 		for (o = OptionTab; o->o_name != NULL; o++)
143268481Seric 		{
143368481Seric 			if (o->o_code == opt)
143468481Seric 				break;
143568481Seric 		}
143668481Seric 		subopt = NULL;
143768481Seric 	}
143868481Seric 
14398256Seric 	if (tTd(37, 1))
144068481Seric 	{
144168481Seric 		printf(isascii(opt) && isprint(opt) ?
144269917Seric 			    "setoption %s (%c).%s=" :
144369917Seric 			    "setoption %s (0x%x).%s=",
144468481Seric 			o->o_name == NULL ? "<unknown>" : o->o_name,
144568481Seric 			opt,
144669917Seric 			subopt == NULL ? "" : subopt);
144769917Seric 		xputs(val);
144868481Seric 	}
14498256Seric 
14508256Seric 	/*
14518269Seric 	**  See if this option is preset for us.
14528256Seric 	*/
14538256Seric 
145459731Seric 	if (!sticky && bitnset(opt, StickyOpt))
14558269Seric 	{
14569341Seric 		if (tTd(37, 1))
14579341Seric 			printf(" (ignored)\n");
14588269Seric 		return;
14598269Seric 	}
14608269Seric 
146121755Seric 	/*
146221755Seric 	**  Check to see if this option can be specified by this user.
146321755Seric 	*/
146421755Seric 
146563787Seric 	if (!safe && RealUid == 0)
146621755Seric 		safe = TRUE;
146768481Seric 	if (!safe && !o->o_safe)
146821755Seric 	{
146939111Srick 		if (opt != 'M' || (val[0] != 'r' && val[0] != 's'))
147021755Seric 		{
147136582Sbostic 			if (tTd(37, 1))
147236582Sbostic 				printf(" (unsafe)");
147363787Seric 			if (RealUid != geteuid())
147436582Sbostic 			{
147551210Seric 				if (tTd(37, 1))
147651210Seric 					printf("(Resetting uid)");
147769963Seric 				endpwent();
147863787Seric 				(void) setgid(RealGid);
147963787Seric 				(void) setuid(RealUid);
148036582Sbostic 			}
148121755Seric 		}
148221755Seric 	}
148351210Seric 	if (tTd(37, 1))
148417985Seric 		printf("\n");
14858269Seric 
148668481Seric 	switch (opt & 0xff)
14878256Seric 	{
148859709Seric 	  case '7':		/* force seven-bit input */
148968481Seric 		SevenBitInput = atobool(val);
149052106Seric 		break;
149152106Seric 
149269480Seric #if MIME8TO7
149368481Seric 	  case '8':		/* handling of 8-bit input */
149468481Seric 		switch (*val)
149568481Seric 		{
149668481Seric 		  case 'm':		/* convert 8-bit, convert MIME */
149768481Seric 			MimeMode = MM_CVTMIME|MM_MIME8BIT;
149868481Seric 			break;
149968481Seric 
150068481Seric 		  case 'p':		/* pass 8 bit, convert MIME */
150168856Seric 			MimeMode = MM_CVTMIME|MM_PASS8BIT;
150268481Seric 			break;
150368481Seric 
150468481Seric 		  case 's':		/* strict adherence */
150568481Seric 			MimeMode = MM_CVTMIME;
150668481Seric 			break;
150768481Seric 
150868856Seric #if 0
150968856Seric 		  case 'r':		/* reject 8-bit, don't convert MIME */
151068856Seric 			MimeMode = 0;
151168856Seric 			break;
151268856Seric 
151368856Seric 		  case 'j':		/* "just send 8" */
151468856Seric 			MimeMode = MM_PASS8BIT;
151568856Seric 			break;
151668856Seric 
151768481Seric 		  case 'a':		/* encode 8 bit if available */
151868481Seric 			MimeMode = MM_MIME8BIT|MM_PASS8BIT|MM_CVTMIME;
151968481Seric 			break;
152068481Seric 
152168481Seric 		  case 'c':		/* convert 8 bit to MIME, never 7 bit */
152268481Seric 			MimeMode = MM_MIME8BIT;
152368481Seric 			break;
152468856Seric #endif
152568481Seric 
152668481Seric 		  default:
152768481Seric 			syserr("Unknown 8-bit mode %c", *val);
152868481Seric 			exit(EX_USAGE);
152968481Seric 		}
153068481Seric 		break;
153169480Seric #endif
153268481Seric 
15338256Seric 	  case 'A':		/* set default alias file */
15349381Seric 		if (val[0] == '\0')
153559672Seric 			setalias("aliases");
15369381Seric 		else
153759672Seric 			setalias(val);
15388256Seric 		break;
15398256Seric 
154017474Seric 	  case 'a':		/* look N minutes for "@:@" in alias file */
154117474Seric 		if (val[0] == '\0')
154264796Seric 			SafeAlias = 5 * 60;		/* five minutes */
154317474Seric 		else
154464796Seric 			SafeAlias = convtime(val, 'm');
154517474Seric 		break;
154617474Seric 
154716843Seric 	  case 'B':		/* substitution for blank character */
154816843Seric 		SpaceSub = val[0];
154916843Seric 		if (SpaceSub == '\0')
155016843Seric 			SpaceSub = ' ';
155116843Seric 		break;
155216843Seric 
155359283Seric 	  case 'b':		/* min blocks free on queue fs/max msg size */
155459283Seric 		p = strchr(val, '/');
155559283Seric 		if (p != NULL)
155659283Seric 		{
155759283Seric 			*p++ = '\0';
155859283Seric 			MaxMessageSize = atol(p);
155959283Seric 		}
156058082Seric 		MinBlocksFree = atol(val);
156158082Seric 		break;
156258082Seric 
15639284Seric 	  case 'c':		/* don't connect to "expensive" mailers */
15649381Seric 		NoConnect = atobool(val);
15659284Seric 		break;
15669284Seric 
156751305Seric 	  case 'C':		/* checkpoint every N addresses */
156851305Seric 		CheckpointInterval = atoi(val);
156924944Seric 		break;
157024944Seric 
15719284Seric 	  case 'd':		/* delivery mode */
15729284Seric 		switch (*val)
15738269Seric 		{
15749284Seric 		  case '\0':
157558734Seric 			e->e_sendmode = SM_DELIVER;
15768269Seric 			break;
15778269Seric 
157810755Seric 		  case SM_QUEUE:	/* queue only */
157910755Seric #ifndef QUEUE
158010755Seric 			syserr("need QUEUE to set -odqueue");
158156795Seric #endif /* QUEUE */
158210755Seric 			/* fall through..... */
158310755Seric 
15849284Seric 		  case SM_DELIVER:	/* do everything */
15859284Seric 		  case SM_FORK:		/* fork after verification */
158658734Seric 			e->e_sendmode = *val;
15878269Seric 			break;
15888269Seric 
15898269Seric 		  default:
15909284Seric 			syserr("Unknown delivery mode %c", *val);
15918269Seric 			exit(EX_USAGE);
15928269Seric 		}
15938269Seric 		break;
15948269Seric 
15959146Seric 	  case 'D':		/* rebuild alias database as needed */
15969381Seric 		AutoRebuild = atobool(val);
15979146Seric 		break;
15989146Seric 
159955372Seric 	  case 'E':		/* error message header/header file */
160055379Seric 		if (*val != '\0')
160155379Seric 			ErrMsgFile = newstr(val);
160255372Seric 		break;
160355372Seric 
16048269Seric 	  case 'e':		/* set error processing mode */
16058269Seric 		switch (*val)
16068269Seric 		{
16079381Seric 		  case EM_QUIET:	/* be silent about it */
16089381Seric 		  case EM_MAIL:		/* mail back */
16099381Seric 		  case EM_BERKNET:	/* do berknet error processing */
16109381Seric 		  case EM_WRITE:	/* write back (or mail) */
16119381Seric 		  case EM_PRINT:	/* print errors normally (default) */
161258734Seric 			e->e_errormode = *val;
16138269Seric 			break;
16148269Seric 		}
16158269Seric 		break;
16168269Seric 
16179049Seric 	  case 'F':		/* file mode */
161817975Seric 		FileMode = atooct(val) & 0777;
16199049Seric 		break;
16209049Seric 
16218269Seric 	  case 'f':		/* save Unix-style From lines on front */
16229381Seric 		SaveFrom = atobool(val);
16238269Seric 		break;
16248269Seric 
162553735Seric 	  case 'G':		/* match recipients against GECOS field */
162653735Seric 		MatchGecos = atobool(val);
162753735Seric 		break;
162853735Seric 
16298256Seric 	  case 'g':		/* default gid */
163068481Seric   g_opt:
163164133Seric 		if (isascii(*val) && isdigit(*val))
163264133Seric 			DefGid = atoi(val);
163364133Seric 		else
163464133Seric 		{
163564133Seric 			register struct group *gr;
163664133Seric 
163764133Seric 			DefGid = -1;
163864133Seric 			gr = getgrnam(val);
163964133Seric 			if (gr == NULL)
164068481Seric 				syserr("readcf: option %c: unknown group %s",
164168481Seric 					opt, val);
164264133Seric 			else
164364133Seric 				DefGid = gr->gr_gid;
164464133Seric 		}
16458256Seric 		break;
16468256Seric 
16478256Seric 	  case 'H':		/* help file */
16489381Seric 		if (val[0] == '\0')
16498269Seric 			HelpFile = "sendmail.hf";
16509381Seric 		else
16519381Seric 			HelpFile = newstr(val);
16528256Seric 		break;
16538256Seric 
165451305Seric 	  case 'h':		/* maximum hop count */
165551305Seric 		MaxHopCount = atoi(val);
165651305Seric 		break;
165751305Seric 
165835651Seric 	  case 'I':		/* use internet domain name server */
165966334Seric #if NAMED_BIND
166057207Seric 		for (p = val; *p != 0; )
166157207Seric 		{
166257207Seric 			bool clearmode;
166357207Seric 			char *q;
166457207Seric 			struct resolverflags *rfp;
166557207Seric 
166657207Seric 			while (*p == ' ')
166757207Seric 				p++;
166857207Seric 			if (*p == '\0')
166957207Seric 				break;
167057207Seric 			clearmode = FALSE;
167157207Seric 			if (*p == '-')
167257207Seric 				clearmode = TRUE;
167357207Seric 			else if (*p != '+')
167457207Seric 				p--;
167557207Seric 			p++;
167657207Seric 			q = p;
167758050Seric 			while (*p != '\0' && !(isascii(*p) && isspace(*p)))
167857207Seric 				p++;
167957207Seric 			if (*p != '\0')
168057207Seric 				*p++ = '\0';
168168759Seric 			if (strcasecmp(q, "HasWildcardMX") == 0)
168268759Seric 			{
168369896Seric 				HasWildcardMX = !clearmode;
168468759Seric 				continue;
168568759Seric 			}
168657207Seric 			for (rfp = ResolverFlags; rfp->rf_name != NULL; rfp++)
168757207Seric 			{
168857207Seric 				if (strcasecmp(q, rfp->rf_name) == 0)
168957207Seric 					break;
169057207Seric 			}
169164923Seric 			if (rfp->rf_name == NULL)
169264923Seric 				syserr("readcf: I option value %s unrecognized", q);
169364923Seric 			else if (clearmode)
169457207Seric 				_res.options &= ~rfp->rf_bits;
169557207Seric 			else
169657207Seric 				_res.options |= rfp->rf_bits;
169757207Seric 		}
169857207Seric 		if (tTd(8, 2))
169968759Seric 			printf("_res.options = %x, HasWildcardMX = %d\n",
170069896Seric 				_res.options, HasWildcardMX);
170157207Seric #else
170257207Seric 		usrerr("name server (I option) specified but BIND not compiled in");
170357207Seric #endif
170435651Seric 		break;
170535651Seric 
17068269Seric 	  case 'i':		/* ignore dot lines in message */
17079381Seric 		IgnrDot = atobool(val);
17088269Seric 		break;
17098269Seric 
171059730Seric 	  case 'j':		/* send errors in MIME (RFC 1341) format */
171159730Seric 		SendMIMEErrors = atobool(val);
171259730Seric 		break;
171359730Seric 
171457136Seric 	  case 'J':		/* .forward search path */
171557136Seric 		ForwardPath = newstr(val);
171657136Seric 		break;
171757136Seric 
171854967Seric 	  case 'k':		/* connection cache size */
171954967Seric 		MaxMciCache = atoi(val);
172056215Seric 		if (MaxMciCache < 0)
172156215Seric 			MaxMciCache = 0;
172254967Seric 		break;
172354967Seric 
172454967Seric 	  case 'K':		/* connection cache timeout */
172558796Seric 		MciCacheTimeout = convtime(val, 'm');
172654967Seric 		break;
172754967Seric 
172861104Seric 	  case 'l':		/* use Errors-To: header */
172961104Seric 		UseErrorsTo = atobool(val);
173061104Seric 		break;
173161104Seric 
17328256Seric 	  case 'L':		/* log level */
173364140Seric 		if (safe || LogLevel < atoi(val))
173464140Seric 			LogLevel = atoi(val);
17358256Seric 		break;
17368256Seric 
17378269Seric 	  case 'M':		/* define macro */
173868267Seric 		p = newstr(&val[1]);
173968267Seric 		if (!safe)
174068267Seric 			cleanstrcpy(p, p, MAXNAME);
174168267Seric 		define(val[0], p, CurEnv);
174216878Seric 		sticky = FALSE;
17438269Seric 		break;
17448269Seric 
17458269Seric 	  case 'm':		/* send to me too */
17469381Seric 		MeToo = atobool(val);
17478269Seric 		break;
17488269Seric 
174925820Seric 	  case 'n':		/* validate RHS in newaliases */
175025820Seric 		CheckAliases = atobool(val);
175125820Seric 		break;
175225820Seric 
175361104Seric 	    /* 'N' available -- was "net name" */
175461104Seric 
175558851Seric 	  case 'O':		/* daemon options */
175658851Seric 		setdaemonoptions(val);
175758851Seric 		break;
175858851Seric 
17598269Seric 	  case 'o':		/* assume old style headers */
17609381Seric 		if (atobool(val))
17619341Seric 			CurEnv->e_flags |= EF_OLDSTYLE;
17629341Seric 		else
17639341Seric 			CurEnv->e_flags &= ~EF_OLDSTYLE;
17648269Seric 		break;
17658269Seric 
176658082Seric 	  case 'p':		/* select privacy level */
176758082Seric 		p = val;
176858082Seric 		for (;;)
176958082Seric 		{
177058082Seric 			register struct prival *pv;
177158082Seric 			extern struct prival PrivacyValues[];
177258082Seric 
177358082Seric 			while (isascii(*p) && (isspace(*p) || ispunct(*p)))
177458082Seric 				p++;
177558082Seric 			if (*p == '\0')
177658082Seric 				break;
177758082Seric 			val = p;
177858082Seric 			while (isascii(*p) && isalnum(*p))
177958082Seric 				p++;
178058082Seric 			if (*p != '\0')
178158082Seric 				*p++ = '\0';
178258082Seric 
178358082Seric 			for (pv = PrivacyValues; pv->pv_name != NULL; pv++)
178458082Seric 			{
178558082Seric 				if (strcasecmp(val, pv->pv_name) == 0)
178658082Seric 					break;
178758082Seric 			}
178858886Seric 			if (pv->pv_name == NULL)
178958886Seric 				syserr("readcf: Op line: %s unrecognized", val);
179058082Seric 			PrivacyFlags |= pv->pv_flag;
179158082Seric 		}
179268479Seric 		sticky = FALSE;
179358082Seric 		break;
179458082Seric 
179524944Seric 	  case 'P':		/* postmaster copy address for returned mail */
179624944Seric 		PostMasterCopy = newstr(val);
179724944Seric 		break;
179824944Seric 
179924944Seric 	  case 'q':		/* slope of queue only function */
180024944Seric 		QueueFactor = atoi(val);
180124944Seric 		break;
180224944Seric 
18038256Seric 	  case 'Q':		/* queue directory */
18049381Seric 		if (val[0] == '\0')
18058269Seric 			QueueDir = "mqueue";
18069381Seric 		else
18079381Seric 			QueueDir = newstr(val);
180858789Seric 		if (RealUid != 0 && !safe)
180964718Seric 			Warn_Q_option = TRUE;
18108256Seric 		break;
18118256Seric 
181258148Seric 	  case 'R':		/* don't prune routes */
181358148Seric 		DontPruneRoutes = atobool(val);
181458148Seric 		break;
181558148Seric 
18168256Seric 	  case 'r':		/* read timeout */
181768481Seric 		if (subopt == NULL)
181868481Seric 			inittimeouts(val);
181968481Seric 		else
182068481Seric 			settimeout(subopt, val);
18218256Seric 		break;
18228256Seric 
18238256Seric 	  case 'S':		/* status file */
18249381Seric 		if (val[0] == '\0')
18258269Seric 			StatFile = "sendmail.st";
18269381Seric 		else
18279381Seric 			StatFile = newstr(val);
18288256Seric 		break;
18298256Seric 
18308265Seric 	  case 's':		/* be super safe, even if expensive */
18319381Seric 		SuperSafe = atobool(val);
18328256Seric 		break;
18338256Seric 
18348256Seric 	  case 'T':		/* queue timeout */
183558737Seric 		p = strchr(val, '/');
183658737Seric 		if (p != NULL)
183758737Seric 		{
183858737Seric 			*p++ = '\0';
183968481Seric 			settimeout("queuewarn", p);
184058737Seric 		}
184168481Seric 		settimeout("queuereturn", val);
184254967Seric 		break;
18438256Seric 
18448265Seric 	  case 't':		/* time zone name */
184552106Seric 		TimeZoneSpec = newstr(val);
18468265Seric 		break;
18478265Seric 
184850556Seric 	  case 'U':		/* location of user database */
184951360Seric 		UdbSpec = newstr(val);
185050556Seric 		break;
185150556Seric 
18528256Seric 	  case 'u':		/* set default uid */
185368481Seric 		for (p = val; *p != '\0'; p++)
185468481Seric 		{
185568481Seric 			if (*p == '.' || *p == '/' || *p == ':')
185668481Seric 			{
185768481Seric 				*p++ = '\0';
185868481Seric 				break;
185968481Seric 			}
186068481Seric 		}
186164133Seric 		if (isascii(*val) && isdigit(*val))
186264133Seric 			DefUid = atoi(val);
186364133Seric 		else
186464133Seric 		{
186564133Seric 			register struct passwd *pw;
186664133Seric 
186764133Seric 			DefUid = -1;
186868693Seric 			pw = sm_getpwnam(val);
186964133Seric 			if (pw == NULL)
187064133Seric 				syserr("readcf: option u: unknown user %s", val);
187164133Seric 			else
187268481Seric 			{
187364133Seric 				DefUid = pw->pw_uid;
187468481Seric 				DefGid = pw->pw_gid;
187568481Seric 			}
187664133Seric 		}
187740973Sbostic 		setdefuser();
18788256Seric 
187968481Seric 		/* handle the group if it is there */
188068481Seric 		if (*p == '\0')
188168481Seric 			break;
188268481Seric 		val = p;
188368481Seric 		goto g_opt;
188468481Seric 
188558851Seric 	  case 'V':		/* fallback MX host */
188658851Seric 		FallBackMX = newstr(val);
188758851Seric 		break;
188858851Seric 
18898269Seric 	  case 'v':		/* run in verbose mode */
18909381Seric 		Verbose = atobool(val);
18918256Seric 		break;
18928256Seric 
189363837Seric 	  case 'w':		/* if we are best MX, try host directly */
189463837Seric 		TryNullMXList = atobool(val);
189563837Seric 		break;
189661104Seric 
189761104Seric 	    /* 'W' available -- was wizard password */
189861104Seric 
189914879Seric 	  case 'x':		/* load avg at which to auto-queue msgs */
190014879Seric 		QueueLA = atoi(val);
190114879Seric 		break;
190214879Seric 
190314879Seric 	  case 'X':		/* load avg at which to auto-reject connections */
190414879Seric 		RefuseLA = atoi(val);
190514879Seric 		break;
190614879Seric 
190724981Seric 	  case 'y':		/* work recipient factor */
190824981Seric 		WkRecipFact = atoi(val);
190924981Seric 		break;
191024981Seric 
191124981Seric 	  case 'Y':		/* fork jobs during queue runs */
191224952Seric 		ForkQueueRuns = atobool(val);
191324952Seric 		break;
191424952Seric 
191524981Seric 	  case 'z':		/* work message class factor */
191624981Seric 		WkClassFact = atoi(val);
191724981Seric 		break;
191824981Seric 
191924981Seric 	  case 'Z':		/* work time factor */
192024981Seric 		WkTimeFact = atoi(val);
192124981Seric 		break;
192224981Seric 
192368481Seric 	  case O_QUEUESORTORD:	/* queue sorting order */
192468481Seric 		switch (*val)
192568481Seric 		{
192668481Seric 		  case 'h':	/* Host first */
192768481Seric 		  case 'H':
192868481Seric 			QueueSortOrder = QS_BYHOST;
192968481Seric 			break;
193068481Seric 
193168481Seric 		  case 'p':	/* Priority order */
193268481Seric 		  case 'P':
193368481Seric 			QueueSortOrder = QS_BYPRIORITY;
193468481Seric 			break;
193568481Seric 
193668481Seric 		  default:
193768481Seric 			syserr("Invalid queue sort order \"%s\"", val);
193868481Seric 		}
193968481Seric 		break;
194068481Seric 
194169401Seric 	  case O_HOSTSFILE:	/* pathname of /etc/hosts file */
194269401Seric 		HostsFile = newstr(val);
194369401Seric 		break;
194469401Seric 
194568481Seric 	  case O_MQA:		/* minimum queue age between deliveries */
194668481Seric 		MinQueueAge = convtime(val, 'm');
194768481Seric 		break;
194868481Seric 
194968481Seric 	  case O_MHSA:		/* maximum age of cached host status */
195068481Seric 		MaxHostStatAge = convtime(val, 'm');
195168481Seric 		break;
195268481Seric 
195368481Seric 	  case O_DEFCHARSET:	/* default character set for mimefying */
195468481Seric 		DefaultCharSet = newstr(denlstring(val, TRUE, TRUE));
195568481Seric 		break;
195668481Seric 
195768481Seric 	  case O_SSFILE:	/* service switch file */
195868481Seric 		ServiceSwitchFile = newstr(val);
195968481Seric 		break;
196068481Seric 
196168481Seric 	  case O_DIALDELAY:	/* delay for dial-on-demand operation */
196268481Seric 		DialDelay = convtime(val, 's');
196368481Seric 		break;
196468481Seric 
196568481Seric 	  case O_NORCPTACTION:	/* what to do if no recipient */
196668481Seric 		if (strcasecmp(val, "none") == 0)
196768481Seric 			NoRecipientAction = NRA_NO_ACTION;
196868481Seric 		else if (strcasecmp(val, "add-to") == 0)
196968481Seric 			NoRecipientAction = NRA_ADD_TO;
197068481Seric 		else if (strcasecmp(val, "add-apparently-to") == 0)
197168481Seric 			NoRecipientAction = NRA_ADD_APPARENTLY_TO;
197268481Seric 		else if (strcasecmp(val, "add-bcc") == 0)
197368481Seric 			NoRecipientAction = NRA_ADD_BCC;
197468481Seric 		else if (strcasecmp(val, "add-to-undisclosed") == 0)
197568481Seric 			NoRecipientAction = NRA_ADD_TO_UNDISCLOSED;
197668481Seric 		else
197768481Seric 			syserr("Invalid NoRecipientAction: %s", val);
197868481Seric 
197968490Seric 	  case O_SAFEFILEENV:	/* chroot() environ for writing to files */
198068490Seric 		SafeFileEnv = newstr(val);
198168490Seric 		break;
198268490Seric 
198368569Seric 	  case O_MAXMSGSIZE:	/* maximum message size */
198469748Seric 		MaxMessageSize = atol(val);
198568569Seric 		break;
198668569Seric 
198768756Seric 	  case O_COLONOKINADDR:	/* old style handling of colon addresses */
198869748Seric 		ColonOkInAddr = atobool(val);
198968756Seric 		break;
199068756Seric 
199169724Seric 	  case O_MAXQUEUERUN:	/* max # of jobs in a single queue run */
199269748Seric 		MaxQueueRun = atol(val);
199369724Seric 		break;
199469724Seric 
199569838Seric 	  case O_MAXCHILDREN:	/* max # of children of daemon */
199669838Seric 		MaxChildren = atoi(val);
199769852Seric 		break;
199869838Seric 
199969852Seric 	  case O_KEEPCNAMES:	/* don't expand CNAME records */
200069852Seric 		DontExpandCnames = atobool(val);
200169852Seric 		break;
200269852Seric 
20038256Seric 	  default:
200468481Seric 		if (tTd(37, 1))
200568481Seric 		{
200668481Seric 			if (isascii(opt) && isprint(opt))
200768481Seric 				printf("Warning: option %c unknown\n", opt);
200868481Seric 			else
200968481Seric 				printf("Warning: option 0x%x unknown\n", opt);
201068481Seric 		}
20118256Seric 		break;
20128256Seric 	}
201316878Seric 	if (sticky)
201416878Seric 		setbitn(opt, StickyOpt);
20158256Seric }
201610687Seric /*
201768481Seric **  SETCLASS -- set a string into a class
201810687Seric **
201910687Seric **	Parameters:
202068481Seric **		class -- the class to put the string in.
202168481Seric **		str -- the string to enter
202210687Seric **
202310687Seric **	Returns:
202410687Seric **		none.
202510687Seric **
202610687Seric **	Side Effects:
202710687Seric **		puts the word into the symbol table.
202810687Seric */
202910687Seric 
203069748Seric void
setclass(class,str)203168481Seric setclass(class, str)
203210687Seric 	int class;
203368481Seric 	char *str;
203410687Seric {
203510687Seric 	register STAB *s;
203610687Seric 
203757943Seric 	if (tTd(37, 8))
2038*69976Seric 		printf("setclass(%s, %s)\n", macname(class), str);
203968481Seric 	s = stab(str, ST_CLASS, ST_ENTER);
204010687Seric 	setbitn(class, s->s_class);
204110687Seric }
204253654Seric /*
204353654Seric **  MAKEMAPENTRY -- create a map entry
204453654Seric **
204553654Seric **	Parameters:
204653654Seric **		line -- the config file line
204753654Seric **
204853654Seric **	Returns:
204969774Seric **		A pointer to the map that has been created.
205069774Seric **		NULL if there was a syntax error.
205153654Seric **
205253654Seric **	Side Effects:
205353654Seric **		Enters the map into the dictionary.
205453654Seric */
205553654Seric 
205669774Seric MAP *
makemapentry(line)205753654Seric makemapentry(line)
205853654Seric 	char *line;
205953654Seric {
206053654Seric 	register char *p;
206153654Seric 	char *mapname;
206253654Seric 	char *classname;
206364078Seric 	register STAB *s;
206453654Seric 	STAB *class;
206553654Seric 
206658050Seric 	for (p = line; isascii(*p) && isspace(*p); p++)
206753654Seric 		continue;
206858050Seric 	if (!(isascii(*p) && isalnum(*p)))
206953654Seric 	{
207053654Seric 		syserr("readcf: config K line: no map name");
207169774Seric 		return NULL;
207253654Seric 	}
207353654Seric 
207453654Seric 	mapname = p;
207568481Seric 	while ((isascii(*++p) && isalnum(*p)) || *p == '.')
207653654Seric 		continue;
207753654Seric 	if (*p != '\0')
207853654Seric 		*p++ = '\0';
207958050Seric 	while (isascii(*p) && isspace(*p))
208053654Seric 		p++;
208158050Seric 	if (!(isascii(*p) && isalnum(*p)))
208253654Seric 	{
208353654Seric 		syserr("readcf: config K line, map %s: no map class", mapname);
208469774Seric 		return NULL;
208553654Seric 	}
208653654Seric 	classname = p;
208758050Seric 	while (isascii(*++p) && isalnum(*p))
208853654Seric 		continue;
208953654Seric 	if (*p != '\0')
209053654Seric 		*p++ = '\0';
209158050Seric 	while (isascii(*p) && isspace(*p))
209253654Seric 		p++;
209353654Seric 
209453654Seric 	/* look up the class */
209553654Seric 	class = stab(classname, ST_MAPCLASS, ST_FIND);
209653654Seric 	if (class == NULL)
209753654Seric 	{
209853654Seric 		syserr("readcf: map %s: class %s not available", mapname, classname);
209969774Seric 		return NULL;
210053654Seric 	}
210153654Seric 
210253654Seric 	/* enter the map */
210364078Seric 	s = stab(mapname, ST_MAP, ST_ENTER);
210464078Seric 	s->s_map.map_class = &class->s_mapclass;
210564078Seric 	s->s_map.map_mname = newstr(mapname);
210653654Seric 
210764078Seric 	if (class->s_mapclass.map_parse(&s->s_map, p))
210864078Seric 		s->s_map.map_mflags |= MF_VALID;
210964078Seric 
211064078Seric 	if (tTd(37, 5))
211164078Seric 	{
211264078Seric 		printf("map %s, class %s, flags %x, file %s,\n",
211364078Seric 			s->s_map.map_mname, s->s_map.map_class->map_cname,
211464078Seric 			s->s_map.map_mflags,
211564078Seric 			s->s_map.map_file == NULL ? "(null)" : s->s_map.map_file);
211664078Seric 		printf("\tapp %s, domain %s, rebuild %s\n",
211764078Seric 			s->s_map.map_app == NULL ? "(null)" : s->s_map.map_app,
211864078Seric 			s->s_map.map_domain == NULL ? "(null)" : s->s_map.map_domain,
211964078Seric 			s->s_map.map_rebuild == NULL ? "(null)" : s->s_map.map_rebuild);
212064078Seric 	}
212169774Seric 
212269774Seric 	return &s->s_map;
212353654Seric }
212458112Seric /*
212569783Seric **  STRTORWSET -- convert string to rewriting set number
212669783Seric **
212769783Seric **	Parameters:
212869783Seric **		p -- the pointer to the string to decode.
212969783Seric **		endp -- if set, store the trailing delimiter here.
213069783Seric **		stabmode -- ST_ENTER to create this entry, ST_FIND if
213169783Seric **			it must already exist.
213269783Seric **
213369783Seric **	Returns:
213469783Seric **		The appropriate ruleset number.
213569783Seric **		-1 if it is not valid (error already printed)
213669783Seric */
213769783Seric 
213869783Seric int
strtorwset(p,endp,stabmode)213969783Seric strtorwset(p, endp, stabmode)
214069783Seric 	char *p;
214169783Seric 	char **endp;
214269783Seric 	int stabmode;
214369783Seric {
214469783Seric 	int ruleset;
214569783Seric 	static int nextruleset = MAXRWSETS;
214669783Seric 
214769783Seric 	while (isascii(*p) && isspace(*p))
214869783Seric 		p++;
214969783Seric 	if (!isascii(*p))
215069783Seric 	{
215169783Seric 		syserr("invalid ruleset name: \"%.20s\"", p);
215269783Seric 		return -1;
215369783Seric 	}
215469783Seric 	if (isdigit(*p))
215569783Seric 	{
215669783Seric 		ruleset = strtol(p, endp, 10);
215769783Seric 		if (ruleset >= MAXRWSETS / 2 || ruleset < 0)
215869783Seric 		{
215969783Seric 			syserr("bad ruleset %d (%d max)",
216069783Seric 				ruleset, MAXRWSETS / 2);
216169783Seric 			ruleset = -1;
216269783Seric 		}
216369783Seric 	}
216469783Seric 	else
216569783Seric 	{
216669783Seric 		STAB *s;
216769783Seric 		char delim;
216869783Seric 		char *q;
216969783Seric 
217069783Seric 		q = p;
217169783Seric 		while (*p != '\0' && isascii(*p) &&
217269783Seric 		       (isalnum(*p) || strchr("-_$", *p) != NULL))
217369783Seric 			p++;
217469970Seric 		if (q == p || !isalpha(*q))
217569970Seric 		{
217669970Seric 			/* no valid characters */
217769970Seric 			syserr("invalid ruleset name: \"%.20s\"", q);
217869970Seric 			return -1;
217969970Seric 		}
218069783Seric 		while (isascii(*p) && isspace(*p))
218169783Seric 			*p++ = '\0';
218269783Seric 		delim = *p;
218369783Seric 		if (delim != '\0')
218469783Seric 			*p = '\0';
218569783Seric 		s = stab(q, ST_RULESET, stabmode);
218669783Seric 		if (delim != '\0')
218769783Seric 			*p = delim;
218869783Seric 
218969783Seric 		if (s == NULL)
219069783Seric 		{
219169783Seric 			syserr("unknown ruleset %s", q);
219269783Seric 			return -1;
219369783Seric 		}
219469783Seric 
219569783Seric 		if (stabmode == ST_ENTER && delim == '=')
219669783Seric 		{
219769962Seric 			ruleset = strtol(++p, endp, 10);
219869783Seric 			if (ruleset >= MAXRWSETS / 2 || ruleset < 0)
219969783Seric 			{
220069783Seric 				syserr("bad ruleset %s = %d (%d max)",
220169783Seric 					q, ruleset, MAXRWSETS / 2);
220269783Seric 				ruleset = -1;
220369783Seric 			}
220469783Seric 		}
220569783Seric 		else
220669783Seric 		{
220769783Seric 			if (endp != NULL)
220869783Seric 				*endp = p;
220969783Seric 			if (s->s_ruleset > 0)
221069783Seric 				ruleset = s->s_ruleset;
221169783Seric 			else if ((ruleset = --nextruleset) < MAXRWSETS / 2)
221269783Seric 			{
221369783Seric 				syserr("%s: too many named rulesets (%d max)",
221469783Seric 					q, MAXRWSETS / 2);
221569783Seric 				ruleset = -1;
221669783Seric 			}
221769783Seric 		}
221869783Seric 		if (s->s_ruleset > 0 && ruleset >= 0 && ruleset != s->s_ruleset)
221969783Seric 		{
222069783Seric 			syserr("%s: ruleset changed value (old %d, new %d)",
222169783Seric 				q, ruleset, s->s_ruleset);
222269783Seric 			ruleset = s->s_ruleset;
222369783Seric 		}
222469783Seric 		else if (ruleset > 0)
222569783Seric 		{
222669783Seric 			s->s_ruleset = ruleset;
222769783Seric 		}
222869783Seric 	}
222969783Seric 	return ruleset;
223069783Seric }
223169783Seric /*
223268481Seric **  INITTIMEOUTS -- parse and set timeout values
223358112Seric **
223458112Seric **	Parameters:
223558112Seric **		val -- a pointer to the values.  If NULL, do initial
223658112Seric **			settings.
223758112Seric **
223858112Seric **	Returns:
223958112Seric **		none.
224058112Seric **
224158112Seric **	Side Effects:
224258112Seric **		Initializes the TimeOuts structure
224358112Seric */
224458112Seric 
224564255Seric #define SECONDS
224658112Seric #define MINUTES	* 60
224758112Seric #define HOUR	* 3600
224858112Seric 
224969748Seric void
inittimeouts(val)225068481Seric inittimeouts(val)
225158112Seric 	register char *val;
225258112Seric {
225358112Seric 	register char *p;
225458671Seric 	extern time_t convtime();
225558112Seric 
225658112Seric 	if (val == NULL)
225758112Seric 	{
225858112Seric 		TimeOuts.to_initial = (time_t) 5 MINUTES;
225958112Seric 		TimeOuts.to_helo = (time_t) 5 MINUTES;
226058112Seric 		TimeOuts.to_mail = (time_t) 10 MINUTES;
226158112Seric 		TimeOuts.to_rcpt = (time_t) 1 HOUR;
226258112Seric 		TimeOuts.to_datainit = (time_t) 5 MINUTES;
226358112Seric 		TimeOuts.to_datablock = (time_t) 1 HOUR;
226458112Seric 		TimeOuts.to_datafinal = (time_t) 1 HOUR;
226558112Seric 		TimeOuts.to_rset = (time_t) 5 MINUTES;
226658112Seric 		TimeOuts.to_quit = (time_t) 2 MINUTES;
226758112Seric 		TimeOuts.to_nextcommand = (time_t) 1 HOUR;
226858112Seric 		TimeOuts.to_miscshort = (time_t) 2 MINUTES;
226968481Seric #if IDENTPROTO
227064255Seric 		TimeOuts.to_ident = (time_t) 30 SECONDS;
227168481Seric #else
227268481Seric 		TimeOuts.to_ident = (time_t) 0 SECONDS;
227368481Seric #endif
227468481Seric 		TimeOuts.to_fileopen = (time_t) 60 SECONDS;
227558112Seric 		return;
227658112Seric 	}
227758112Seric 
227858112Seric 	for (;; val = p)
227958112Seric 	{
228058112Seric 		while (isascii(*val) && isspace(*val))
228158112Seric 			val++;
228258112Seric 		if (*val == '\0')
228358112Seric 			break;
228458112Seric 		for (p = val; *p != '\0' && *p != ','; p++)
228558112Seric 			continue;
228658112Seric 		if (*p != '\0')
228758112Seric 			*p++ = '\0';
228858112Seric 
228958112Seric 		if (isascii(*val) && isdigit(*val))
229058112Seric 		{
229158112Seric 			/* old syntax -- set everything */
229258796Seric 			TimeOuts.to_mail = convtime(val, 'm');
229358112Seric 			TimeOuts.to_rcpt = TimeOuts.to_mail;
229458112Seric 			TimeOuts.to_datainit = TimeOuts.to_mail;
229558112Seric 			TimeOuts.to_datablock = TimeOuts.to_mail;
229658112Seric 			TimeOuts.to_datafinal = TimeOuts.to_mail;
229758112Seric 			TimeOuts.to_nextcommand = TimeOuts.to_mail;
229858112Seric 			continue;
229958112Seric 		}
230058112Seric 		else
230158112Seric 		{
230268481Seric 			register char *q = strchr(val, ':');
230358112Seric 
230468481Seric 			if (q == NULL && (q = strchr(val, '=')) == NULL)
230558112Seric 			{
230658112Seric 				/* syntax error */
230758112Seric 				continue;
230858112Seric 			}
230958112Seric 			*q++ = '\0';
231068481Seric 			settimeout(val, q);
231168481Seric 		}
231268481Seric 	}
231368481Seric }
231468481Seric /*
231568481Seric **  SETTIMEOUT -- set an individual timeout
231668481Seric **
231768481Seric **	Parameters:
231868481Seric **		name -- the name of the timeout.
231968481Seric **		val -- the value of the timeout.
232068481Seric **
232168481Seric **	Returns:
232268481Seric **		none.
232368481Seric */
232458112Seric 
232569748Seric void
settimeout(name,val)232668481Seric settimeout(name, val)
232768481Seric 	char *name;
232868481Seric 	char *val;
232968481Seric {
233068481Seric 	register char *p;
233168481Seric 	time_t to;
233268481Seric 	extern time_t convtime();
233368481Seric 
233468481Seric 	to = convtime(val, 'm');
233568481Seric 	p = strchr(name, '.');
233668481Seric 	if (p != NULL)
233768481Seric 		*p++ = '\0';
233868481Seric 
233968481Seric 	if (strcasecmp(name, "initial") == 0)
234068481Seric 		TimeOuts.to_initial = to;
234168481Seric 	else if (strcasecmp(name, "mail") == 0)
234268481Seric 		TimeOuts.to_mail = to;
234368481Seric 	else if (strcasecmp(name, "rcpt") == 0)
234468481Seric 		TimeOuts.to_rcpt = to;
234568481Seric 	else if (strcasecmp(name, "datainit") == 0)
234668481Seric 		TimeOuts.to_datainit = to;
234768481Seric 	else if (strcasecmp(name, "datablock") == 0)
234868481Seric 		TimeOuts.to_datablock = to;
234968481Seric 	else if (strcasecmp(name, "datafinal") == 0)
235068481Seric 		TimeOuts.to_datafinal = to;
235168481Seric 	else if (strcasecmp(name, "command") == 0)
235268481Seric 		TimeOuts.to_nextcommand = to;
235368481Seric 	else if (strcasecmp(name, "rset") == 0)
235468481Seric 		TimeOuts.to_rset = to;
235568481Seric 	else if (strcasecmp(name, "helo") == 0)
235668481Seric 		TimeOuts.to_helo = to;
235768481Seric 	else if (strcasecmp(name, "quit") == 0)
235868481Seric 		TimeOuts.to_quit = to;
235968481Seric 	else if (strcasecmp(name, "misc") == 0)
236068481Seric 		TimeOuts.to_miscshort = to;
236168481Seric 	else if (strcasecmp(name, "ident") == 0)
236268481Seric 		TimeOuts.to_ident = to;
236368481Seric 	else if (strcasecmp(name, "fileopen") == 0)
236468481Seric 		TimeOuts.to_fileopen = to;
236568481Seric 	else if (strcasecmp(name, "queuewarn") == 0)
236668481Seric 	{
236768481Seric 		to = convtime(val, 'h');
236868481Seric 		if (p == NULL || strcmp(p, "*") == 0)
236968481Seric 		{
237068481Seric 			TimeOuts.to_q_warning[TOC_NORMAL] = to;
237168481Seric 			TimeOuts.to_q_warning[TOC_URGENT] = to;
237268481Seric 			TimeOuts.to_q_warning[TOC_NONURGENT] = to;
237358112Seric 		}
237468481Seric 		else if (strcasecmp(p, "normal") == 0)
237568481Seric 			TimeOuts.to_q_warning[TOC_NORMAL] = to;
237668481Seric 		else if (strcasecmp(p, "urgent") == 0)
237768481Seric 			TimeOuts.to_q_warning[TOC_URGENT] = to;
237868481Seric 		else if (strcasecmp(p, "non-urgent") == 0)
237968481Seric 			TimeOuts.to_q_warning[TOC_NONURGENT] = to;
238068481Seric 		else
238168481Seric 			syserr("settimeout: invalid queuewarn subtimeout %s", p);
238258112Seric 	}
238368481Seric 	else if (strcasecmp(name, "queuereturn") == 0)
238468481Seric 	{
238568481Seric 		to = convtime(val, 'd');
238668481Seric 		if (p == NULL || strcmp(p, "*") == 0)
238768481Seric 		{
238868481Seric 			TimeOuts.to_q_return[TOC_NORMAL] = to;
238968481Seric 			TimeOuts.to_q_return[TOC_URGENT] = to;
239068481Seric 			TimeOuts.to_q_return[TOC_NONURGENT] = to;
239168481Seric 		}
239268481Seric 		else if (strcasecmp(p, "normal") == 0)
239368481Seric 			TimeOuts.to_q_return[TOC_NORMAL] = to;
239468481Seric 		else if (strcasecmp(p, "urgent") == 0)
239568481Seric 			TimeOuts.to_q_return[TOC_URGENT] = to;
239668481Seric 		else if (strcasecmp(p, "non-urgent") == 0)
239768481Seric 			TimeOuts.to_q_return[TOC_NONURGENT] = to;
239868481Seric 		else
239968481Seric 			syserr("settimeout: invalid queuereturn subtimeout %s", p);
240068481Seric 	}
240168481Seric 	else
240268481Seric 		syserr("settimeout: invalid timeout %s", name);
240358112Seric }
2404