xref: /csrg-svn/usr.sbin/sendmail/src/readcf.c (revision 67990)
122709Sdist /*
234921Sbostic  * Copyright (c) 1983 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*67990Seric static char sccsid[] = "@(#)readcf.c	8.49 (Berkeley) 11/25/94";
1133731Sbostic #endif /* not lint */
1222709Sdist 
133313Seric # include "sendmail.h"
1464133Seric # include <pwd.h>
1564133Seric # include <grp.h>
1666334Seric #if NAMED_BIND
1757207Seric # include <resolv.h>
1857207Seric #endif
193308Seric 
203308Seric /*
213308Seric **  READCF -- read control file.
223308Seric **
233308Seric **	This routine reads the control file and builds the internal
243308Seric **	form.
253308Seric **
264432Seric **	The file is formatted as a sequence of lines, each taken
274432Seric **	atomically.  The first character of each line describes how
284432Seric **	the line is to be interpreted.  The lines are:
294432Seric **		Dxval		Define macro x to have value val.
304432Seric **		Cxword		Put word into class x.
314432Seric **		Fxfile [fmt]	Read file for lines to put into
324432Seric **				class x.  Use scanf string 'fmt'
334432Seric **				or "%s" if not present.  Fmt should
344432Seric **				only produce one string-valued result.
354432Seric **		Hname: value	Define header with field-name 'name'
364432Seric **				and value as specified; this will be
374432Seric **				macro expanded immediately before
384432Seric **				use.
394432Seric **		Sn		Use rewriting set n.
404432Seric **		Rlhs rhs	Rewrite addresses that match lhs to
414432Seric **				be rhs.
4224944Seric **		Mn arg=val...	Define mailer.  n is the internal name.
4324944Seric **				Args specify mailer parameters.
448252Seric **		Oxvalue		Set option x to value.
458252Seric **		Pname=value	Set precedence name to value.
4664718Seric **		Vversioncode[/vendorcode]
4764718Seric **				Version level/vendor name of
4864718Seric **				configuration syntax.
4953654Seric **		Kmapname mapclass arguments....
5053654Seric **				Define keyed lookup of a given class.
5153654Seric **				Arguments are class dependent.
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 
6655012Seric readcf(cfname, safe, e)
673308Seric 	char *cfname;
6854973Seric 	bool safe;
6955012Seric 	register ENVELOPE *e;
703308Seric {
713308Seric 	FILE *cf;
728547Seric 	int ruleset = 0;
738547Seric 	char *q;
749350Seric 	struct rewrite *rwp = NULL;
7557135Seric 	char *bp;
7664718Seric 	auto char *ep;
7757589Seric 	int nfuzzy;
7864133Seric 	char *file;
7964133Seric 	bool optional;
8067769Seric 	int mid;
813308Seric 	char buf[MAXLINE];
823308Seric 	register char *p;
833308Seric 	extern char **copyplist();
8452647Seric 	struct stat statb;
855909Seric 	char exbuf[MAXLINE];
8665066Seric 	char pvpbuf[MAXLINE + MAXATOM];
8767826Seric 	static char *null_list[1] = { NULL };
8810709Seric 	extern char *munchstring();
8953654Seric 	extern void makemapentry();
903308Seric 
9152647Seric 	FileName = cfname;
9252647Seric 	LineNumber = 0;
9352647Seric 
943308Seric 	cf = fopen(cfname, "r");
953308Seric 	if (cf == NULL)
963308Seric 	{
9752647Seric 		syserr("cannot open");
983308Seric 		exit(EX_OSFILE);
993308Seric 	}
1003308Seric 
10152647Seric 	if (fstat(fileno(cf), &statb) < 0)
10252647Seric 	{
10352647Seric 		syserr("cannot fstat");
10452647Seric 		exit(EX_OSFILE);
10552647Seric 	}
10652647Seric 
10752647Seric 	if (!S_ISREG(statb.st_mode))
10852647Seric 	{
10952647Seric 		syserr("not a plain file");
11052647Seric 		exit(EX_OSFILE);
11152647Seric 	}
11252647Seric 
11352647Seric 	if (OpMode != MD_TEST && bitset(S_IWGRP|S_IWOTH, statb.st_mode))
11452647Seric 	{
11553037Seric 		if (OpMode == MD_DAEMON || OpMode == MD_FREEZE)
11653037Seric 			fprintf(stderr, "%s: WARNING: dangerous write permissions\n",
11753037Seric 				FileName);
11853037Seric #ifdef LOG
11953037Seric 		if (LogLevel > 0)
12053037Seric 			syslog(LOG_CRIT, "%s: WARNING: dangerous write permissions",
12153037Seric 				FileName);
12253037Seric #endif
12352647Seric 	}
12452647Seric 
12559254Seric #ifdef XLA
12659254Seric 	xla_zero();
12759254Seric #endif
12859254Seric 
12957135Seric 	while ((bp = fgetfolded(buf, sizeof buf, cf)) != NULL)
1303308Seric 	{
13157135Seric 		if (bp[0] == '#')
13257135Seric 		{
13357135Seric 			if (bp != buf)
13457135Seric 				free(bp);
13552637Seric 			continue;
13657135Seric 		}
13752637Seric 
13867769Seric 		/* do macro expansion mappings */
13957135Seric 		for (p = bp; *p != '\0'; p++)
14016157Seric 		{
14157135Seric 			if (*p == '#' && p > bp && ConfigLevel >= 3)
14252647Seric 			{
14352647Seric 				/* this is an on-line comment */
14452647Seric 				register char *e;
14552647Seric 
14658050Seric 				switch (*--p & 0377)
14752647Seric 				{
14858050Seric 				  case MACROEXPAND:
14952647Seric 					/* it's from $# -- let it go through */
15052647Seric 					p++;
15152647Seric 					break;
15252647Seric 
15352647Seric 				  case '\\':
15452647Seric 					/* it's backslash escaped */
15552647Seric 					(void) strcpy(p, p + 1);
15652647Seric 					break;
15752647Seric 
15852647Seric 				  default:
15952647Seric 					/* delete preceeding white space */
16058050Seric 					while (isascii(*p) && isspace(*p) && p > bp)
16152647Seric 						p--;
16256795Seric 					if ((e = strchr(++p, '\n')) != NULL)
16352647Seric 						(void) strcpy(p, e);
16452647Seric 					else
16552647Seric 						p[0] = p[1] = '\0';
16652647Seric 					break;
16752647Seric 				}
16852647Seric 				continue;
16952647Seric 			}
17052647Seric 
17167769Seric 			if (*p != '$' || p[1] == '\0')
17216157Seric 				continue;
17316157Seric 
17416157Seric 			if (p[1] == '$')
17516157Seric 			{
17616157Seric 				/* actual dollar sign.... */
17723111Seric 				(void) strcpy(p, p + 1);
17816157Seric 				continue;
17916157Seric 			}
18016157Seric 
18116157Seric 			/* convert to macro expansion character */
18267769Seric 			*p++ = MACROEXPAND;
18367769Seric 
18467769Seric 			/* convert macro name to code */
18567769Seric 			*p = macid(p, &ep);
18667769Seric 			if (ep != p)
18767769Seric 				strcpy(p + 1, ep);
18816157Seric 		}
18916157Seric 
19016157Seric 		/* interpret this line */
19164718Seric 		errno = 0;
19257135Seric 		switch (bp[0])
1933308Seric 		{
1943308Seric 		  case '\0':
1953308Seric 		  case '#':		/* comment */
1963308Seric 			break;
1973308Seric 
1983308Seric 		  case 'R':		/* rewriting rule */
19957135Seric 			for (p = &bp[1]; *p != '\0' && *p != '\t'; p++)
2003308Seric 				continue;
2013308Seric 
2023308Seric 			if (*p == '\0')
2035909Seric 			{
20465821Seric 				syserr("invalid rewrite line \"%s\" (tab expected)", bp);
2055909Seric 				break;
2065909Seric 			}
2075909Seric 
2085909Seric 			/* allocate space for the rule header */
2095909Seric 			if (rwp == NULL)
2105909Seric 			{
2115909Seric 				RewriteRules[ruleset] = rwp =
2125909Seric 					(struct rewrite *) xalloc(sizeof *rwp);
2135909Seric 			}
2143308Seric 			else
2153308Seric 			{
2165909Seric 				rwp->r_next = (struct rewrite *) xalloc(sizeof *rwp);
2175909Seric 				rwp = rwp->r_next;
2185909Seric 			}
2195909Seric 			rwp->r_next = NULL;
2203308Seric 
2215909Seric 			/* expand and save the LHS */
2225909Seric 			*p = '\0';
22357135Seric 			expand(&bp[1], exbuf, &exbuf[sizeof exbuf], e);
22465066Seric 			rwp->r_lhs = prescan(exbuf, '\t', pvpbuf,
22565066Seric 					     sizeof pvpbuf, NULL);
22657589Seric 			nfuzzy = 0;
2275909Seric 			if (rwp->r_lhs != NULL)
22857589Seric 			{
22957589Seric 				register char **ap;
23057589Seric 
2315909Seric 				rwp->r_lhs = copyplist(rwp->r_lhs, TRUE);
23257589Seric 
23357589Seric 				/* count the number of fuzzy matches in LHS */
23457589Seric 				for (ap = rwp->r_lhs; *ap != NULL; ap++)
23557589Seric 				{
23658148Seric 					char *botch;
23758148Seric 
23858148Seric 					botch = NULL;
23958050Seric 					switch (**ap & 0377)
24057589Seric 					{
24157589Seric 					  case MATCHZANY:
24257589Seric 					  case MATCHANY:
24357589Seric 					  case MATCHONE:
24457589Seric 					  case MATCHCLASS:
24557589Seric 					  case MATCHNCLASS:
24657589Seric 						nfuzzy++;
24758148Seric 						break;
24858148Seric 
24958148Seric 					  case MATCHREPL:
25058148Seric 						botch = "$0-$9";
25158148Seric 						break;
25258148Seric 
25358148Seric 					  case CANONNET:
25458148Seric 						botch = "$#";
25558148Seric 						break;
25658148Seric 
25758148Seric 					  case CANONUSER:
25858148Seric 						botch = "$:";
25958148Seric 						break;
26058148Seric 
26158148Seric 					  case CALLSUBR:
26258148Seric 						botch = "$>";
26358148Seric 						break;
26458148Seric 
26558148Seric 					  case CONDIF:
26658148Seric 						botch = "$?";
26758148Seric 						break;
26858148Seric 
26958148Seric 					  case CONDELSE:
27058148Seric 						botch = "$|";
27158148Seric 						break;
27258148Seric 
27358148Seric 					  case CONDFI:
27458148Seric 						botch = "$.";
27558148Seric 						break;
27658148Seric 
27758148Seric 					  case HOSTBEGIN:
27858148Seric 						botch = "$[";
27958148Seric 						break;
28058148Seric 
28158148Seric 					  case HOSTEND:
28258148Seric 						botch = "$]";
28358148Seric 						break;
28458148Seric 
28558148Seric 					  case LOOKUPBEGIN:
28658148Seric 						botch = "$(";
28758148Seric 						break;
28858148Seric 
28958148Seric 					  case LOOKUPEND:
29058148Seric 						botch = "$)";
29158148Seric 						break;
29257589Seric 					}
29358148Seric 					if (botch != NULL)
29458148Seric 						syserr("Inappropriate use of %s on LHS",
29558148Seric 							botch);
29657589Seric 				}
29757589Seric 			}
29856678Seric 			else
29967826Seric 			{
30056678Seric 				syserr("R line: null LHS");
30167826Seric 				rwp->r_lhs = null_list;
30267826Seric 			}
3035909Seric 
3045909Seric 			/* expand and save the RHS */
3055909Seric 			while (*++p == '\t')
3065909Seric 				continue;
3077231Seric 			q = p;
3087231Seric 			while (*p != '\0' && *p != '\t')
3097231Seric 				p++;
3107231Seric 			*p = '\0';
31155012Seric 			expand(q, exbuf, &exbuf[sizeof exbuf], e);
31265066Seric 			rwp->r_rhs = prescan(exbuf, '\t', pvpbuf,
31365066Seric 					     sizeof pvpbuf, NULL);
3145909Seric 			if (rwp->r_rhs != NULL)
31557589Seric 			{
31657589Seric 				register char **ap;
31757589Seric 
3185909Seric 				rwp->r_rhs = copyplist(rwp->r_rhs, TRUE);
31957589Seric 
32057589Seric 				/* check no out-of-bounds replacements */
32157589Seric 				nfuzzy += '0';
32257589Seric 				for (ap = rwp->r_rhs; *ap != NULL; ap++)
32357589Seric 				{
32458148Seric 					char *botch;
32558148Seric 
32658148Seric 					botch = NULL;
32758148Seric 					switch (**ap & 0377)
32857589Seric 					{
32958148Seric 					  case MATCHREPL:
33058148Seric 						if ((*ap)[1] <= '0' || (*ap)[1] > nfuzzy)
33158148Seric 						{
33258148Seric 							syserr("replacement $%c out of bounds",
33358148Seric 								(*ap)[1]);
33458148Seric 						}
33558148Seric 						break;
33658148Seric 
33758148Seric 					  case MATCHZANY:
33858148Seric 						botch = "$*";
33958148Seric 						break;
34058148Seric 
34158148Seric 					  case MATCHANY:
34258148Seric 						botch = "$+";
34358148Seric 						break;
34458148Seric 
34558148Seric 					  case MATCHONE:
34658148Seric 						botch = "$-";
34758148Seric 						break;
34858148Seric 
34958148Seric 					  case MATCHCLASS:
35058148Seric 						botch = "$=";
35158148Seric 						break;
35258148Seric 
35358148Seric 					  case MATCHNCLASS:
35458148Seric 						botch = "$~";
35558148Seric 						break;
35657589Seric 					}
35758148Seric 					if (botch != NULL)
35858148Seric 						syserr("Inappropriate use of %s on RHS",
35958148Seric 							botch);
36057589Seric 				}
36157589Seric 			}
36256678Seric 			else
36367826Seric 			{
36456678Seric 				syserr("R line: null RHS");
36567826Seric 				rwp->r_rhs = null_list;
36667826Seric 			}
3673308Seric 			break;
3683308Seric 
3694072Seric 		  case 'S':		/* select rewriting set */
37064440Seric 			for (p = &bp[1]; isascii(*p) && isspace(*p); p++)
37164440Seric 				continue;
37264440Seric 			if (!isascii(*p) || !isdigit(*p))
37364440Seric 			{
37464440Seric 				syserr("invalid argument to S line: \"%.20s\"",
37564440Seric 					&bp[1]);
37664440Seric 				break;
37764440Seric 			}
37864440Seric 			ruleset = atoi(p);
3798056Seric 			if (ruleset >= MAXRWSETS || ruleset < 0)
3808056Seric 			{
3819381Seric 				syserr("bad ruleset %d (%d max)", ruleset, MAXRWSETS);
3828056Seric 				ruleset = 0;
3838056Seric 			}
3844072Seric 			rwp = NULL;
3854072Seric 			break;
3864072Seric 
3873308Seric 		  case 'D':		/* macro definition */
38867769Seric 			mid = macid(&bp[1], &ep);
38967769Seric 			p = munchstring(ep, NULL);
39067769Seric 			define(mid, newstr(p), e);
3913308Seric 			break;
3923308Seric 
3933387Seric 		  case 'H':		/* required header line */
39457135Seric 			(void) chompheader(&bp[1], TRUE, e);
3953387Seric 			break;
3963387Seric 
3974061Seric 		  case 'C':		/* word class */
3984432Seric 			/* scan the list of words and set class for all */
39967769Seric 			mid = macid(&bp[1], &ep);
40067769Seric 			expand(ep, exbuf, &exbuf[sizeof exbuf], e);
40164121Seric 			for (p = exbuf; *p != '\0'; )
4024061Seric 			{
4034061Seric 				register char *wd;
4044061Seric 				char delim;
4054061Seric 
40658050Seric 				while (*p != '\0' && isascii(*p) && isspace(*p))
4074061Seric 					p++;
4084061Seric 				wd = p;
40958050Seric 				while (*p != '\0' && !(isascii(*p) && isspace(*p)))
4104061Seric 					p++;
4114061Seric 				delim = *p;
4124061Seric 				*p = '\0';
4134061Seric 				if (wd[0] != '\0')
41467769Seric 					setclass(mid, wd);
4154061Seric 				*p = delim;
4164061Seric 			}
4174061Seric 			break;
4184061Seric 
41959272Seric 		  case 'F':		/* word class from file */
42067769Seric 			mid = macid(&bp[1], &ep);
42167769Seric 			for (p = ep; isascii(*p) && isspace(*p); )
42264133Seric 				p++;
42364133Seric 			if (p[0] == '-' && p[1] == 'o')
42464133Seric 			{
42564133Seric 				optional = TRUE;
42664133Seric 				while (*p != '\0' && !(isascii(*p) && isspace(*p)))
42764133Seric 					p++;
42864133Seric 				while (isascii(*p) && isspace(*p))
42967615Seric 					p++;
43064133Seric 			}
43164133Seric 			else
43264133Seric 				optional = FALSE;
43364133Seric 			file = p;
43464133Seric 			while (*p != '\0' && !(isascii(*p) && isspace(*p)))
43564133Seric 				p++;
43659272Seric 			if (*p == '\0')
43759272Seric 				p = "%s";
43859272Seric 			else
43959272Seric 			{
44059272Seric 				*p = '\0';
44159272Seric 				while (isascii(*++p) && isspace(*p))
44259272Seric 					continue;
44359272Seric 			}
44464133Seric 			fileclass(bp[1], file, p, safe, optional);
44559272Seric 			break;
44659272Seric 
44759156Seric #ifdef XLA
44859156Seric 		  case 'L':		/* extended load average description */
44959156Seric 			xla_init(&bp[1]);
45059156Seric 			break;
45159156Seric #endif
45259156Seric 
4534096Seric 		  case 'M':		/* define mailer */
45457135Seric 			makemailer(&bp[1]);
4554096Seric 			break;
4564096Seric 
4578252Seric 		  case 'O':		/* set option */
45858734Seric 			setoption(bp[1], &bp[2], safe, FALSE, e);
4598252Seric 			break;
4608252Seric 
4618252Seric 		  case 'P':		/* set precedence */
4628252Seric 			if (NumPriorities >= MAXPRIORITIES)
4638252Seric 			{
4648547Seric 				toomany('P', MAXPRIORITIES);
4658252Seric 				break;
4668252Seric 			}
46757135Seric 			for (p = &bp[1]; *p != '\0' && *p != '=' && *p != '\t'; p++)
4688252Seric 				continue;
4698252Seric 			if (*p == '\0')
4708252Seric 				goto badline;
4718252Seric 			*p = '\0';
47257135Seric 			Priorities[NumPriorities].pri_name = newstr(&bp[1]);
4738252Seric 			Priorities[NumPriorities].pri_val = atoi(++p);
4748252Seric 			NumPriorities++;
4758252Seric 			break;
4768252Seric 
4778547Seric 		  case 'T':		/* trusted user(s) */
47858161Seric 			/* this option is obsolete, but will be ignored */
4798547Seric 			break;
4808547Seric 
48152645Seric 		  case 'V':		/* configuration syntax version */
48264440Seric 			for (p = &bp[1]; isascii(*p) && isspace(*p); p++)
48364440Seric 				continue;
48464440Seric 			if (!isascii(*p) || !isdigit(*p))
48564440Seric 			{
48664440Seric 				syserr("invalid argument to V line: \"%.20s\"",
48764440Seric 					&bp[1]);
48864440Seric 				break;
48964440Seric 			}
49064718Seric 			ConfigLevel = strtol(p, &ep, 10);
49164279Seric 			if (ConfigLevel >= 5)
49264279Seric 			{
49364279Seric 				/* level 5 configs have short name in $w */
49464279Seric 				p = macvalue('w', e);
49564279Seric 				if (p != NULL && (p = strchr(p, '.')) != NULL)
49664279Seric 					*p = '\0';
49764279Seric 			}
49864718Seric 			if (*ep++ == '/')
49964718Seric 			{
50064718Seric 				/* extract vendor code */
50164718Seric 				for (p = ep; isascii(*p) && isalpha(*p); )
50264718Seric 					p++;
50364718Seric 				*p = '\0';
50464718Seric 
50564718Seric 				if (!setvendor(ep))
50664718Seric 					syserr("invalid V line vendor code: \"%s\"",
50764718Seric 						ep);
50864718Seric 			}
50952645Seric 			break;
51052645Seric 
51153654Seric 		  case 'K':
51257135Seric 			makemapentry(&bp[1]);
51353654Seric 			break;
51453654Seric 
5153308Seric 		  default:
5164061Seric 		  badline:
51757135Seric 			syserr("unknown control line \"%s\"", bp);
5183308Seric 		}
51957135Seric 		if (bp != buf)
52057135Seric 			free(bp);
5213308Seric 	}
52252637Seric 	if (ferror(cf))
52352637Seric 	{
52452647Seric 		syserr("I/O read error", cfname);
52552637Seric 		exit(EX_OSFILE);
52652637Seric 	}
52752637Seric 	fclose(cf);
5289381Seric 	FileName = NULL;
52956836Seric 
53067730Seric 	/* initialize host maps from local service tables */
53167730Seric 	inithostmaps();
53267905Seric 
53367905Seric 	/* determine if we need to do special name-server frotz */
53467905Seric 	{
53567905Seric 		int nmaps;
53667905Seric 		char *maptype[MAXMAPSTACK];
53767905Seric 		short mapreturn[MAXMAPACTIONS];
53867905Seric 
53967905Seric 		nmaps = switch_map_find("hosts", maptype, mapreturn);
54067905Seric 		UseNameServer = FALSE;
54167905Seric 		if (nmaps > 0 && nmaps <= MAXMAPSTACK)
54267905Seric 		{
54367905Seric 			register int mapno;
54467905Seric 
54567905Seric 			for (mapno = 0; mapno < nmaps && !UseNameServer; mapno++)
54667905Seric 			{
54767905Seric 				if (strcmp(maptype[mapno], "dns") == 0)
54867905Seric 					UseNameServer = TRUE;
54967905Seric 			}
55067905Seric 		}
55167905Seric 	}
5524096Seric }
5534096Seric /*
5548547Seric **  TOOMANY -- signal too many of some option
5558547Seric **
5568547Seric **	Parameters:
5578547Seric **		id -- the id of the error line
5588547Seric **		maxcnt -- the maximum possible values
5598547Seric **
5608547Seric **	Returns:
5618547Seric **		none.
5628547Seric **
5638547Seric **	Side Effects:
5648547Seric **		gives a syserr.
5658547Seric */
5668547Seric 
5678547Seric toomany(id, maxcnt)
5688547Seric 	char id;
5698547Seric 	int maxcnt;
5708547Seric {
5719381Seric 	syserr("too many %c lines, %d max", id, maxcnt);
5728547Seric }
5738547Seric /*
5744432Seric **  FILECLASS -- read members of a class from a file
5754432Seric **
5764432Seric **	Parameters:
5774432Seric **		class -- class to define.
5784432Seric **		filename -- name of file to read.
5794432Seric **		fmt -- scanf string to use for match.
58064133Seric **		safe -- if set, this is a safe read.
58164133Seric **		optional -- if set, it is not an error for the file to
58264133Seric **			not exist.
5834432Seric **
5844432Seric **	Returns:
5854432Seric **		none
5864432Seric **
5874432Seric **	Side Effects:
5884432Seric **
5894432Seric **		puts all lines in filename that match a scanf into
5904432Seric **			the named class.
5914432Seric */
5924432Seric 
59364133Seric fileclass(class, filename, fmt, safe, optional)
5944432Seric 	int class;
5954432Seric 	char *filename;
5964432Seric 	char *fmt;
59754973Seric 	bool safe;
59864133Seric 	bool optional;
5994432Seric {
60025808Seric 	FILE *f;
60154973Seric 	struct stat stbuf;
6024432Seric 	char buf[MAXLINE];
6034432Seric 
60466101Seric 	if (tTd(37, 2))
60566101Seric 		printf("fileclass(%s, fmt=%s)\n", filename, fmt);
60666101Seric 
60766031Seric 	if (filename[0] == '|')
60866031Seric 	{
60966031Seric 		syserr("fileclass: pipes (F%c%s) not supported due to security problems",
61066031Seric 			class, filename);
61166031Seric 		return;
61266031Seric 	}
61354973Seric 	if (stat(filename, &stbuf) < 0)
61454973Seric 	{
61566101Seric 		if (tTd(37, 2))
61666101Seric 			printf("  cannot stat (%s)\n", errstring(errno));
61764133Seric 		if (!optional)
61864133Seric 			syserr("fileclass: cannot stat %s", filename);
61954973Seric 		return;
62054973Seric 	}
62154973Seric 	if (!S_ISREG(stbuf.st_mode))
62254973Seric 	{
62354973Seric 		syserr("fileclass: %s not a regular file", filename);
62454973Seric 		return;
62554973Seric 	}
62654973Seric 	if (!safe && access(filename, R_OK) < 0)
62754973Seric 	{
62854973Seric 		syserr("fileclass: access denied on %s", filename);
62954973Seric 		return;
63054973Seric 	}
63154973Seric 	f = fopen(filename, "r");
6324432Seric 	if (f == NULL)
6334432Seric 	{
63454973Seric 		syserr("fileclass: cannot open %s", filename);
6354432Seric 		return;
6364432Seric 	}
6374432Seric 
6384432Seric 	while (fgets(buf, sizeof buf, f) != NULL)
6394432Seric 	{
6404432Seric 		register STAB *s;
64125808Seric 		register char *p;
64225808Seric # ifdef SCANF
6434432Seric 		char wordbuf[MAXNAME+1];
6444432Seric 
6454432Seric 		if (sscanf(buf, fmt, wordbuf) != 1)
6464432Seric 			continue;
64725808Seric 		p = wordbuf;
64856795Seric # else /* SCANF */
64925808Seric 		p = buf;
65056795Seric # endif /* SCANF */
65125808Seric 
65225808Seric 		/*
65325808Seric 		**  Break up the match into words.
65425808Seric 		*/
65525808Seric 
65625808Seric 		while (*p != '\0')
65725808Seric 		{
65825808Seric 			register char *q;
65925808Seric 
66025808Seric 			/* strip leading spaces */
66158050Seric 			while (isascii(*p) && isspace(*p))
66225808Seric 				p++;
66325808Seric 			if (*p == '\0')
66425808Seric 				break;
66525808Seric 
66625808Seric 			/* find the end of the word */
66725808Seric 			q = p;
66858050Seric 			while (*p != '\0' && !(isascii(*p) && isspace(*p)))
66925808Seric 				p++;
67025808Seric 			if (*p != '\0')
67125808Seric 				*p++ = '\0';
67225808Seric 
67325808Seric 			/* enter the word in the symbol table */
67466101Seric 			setclass(class, q);
67525808Seric 		}
6764432Seric 	}
6774432Seric 
67854973Seric 	(void) fclose(f);
6794432Seric }
6804432Seric /*
6814096Seric **  MAKEMAILER -- define a new mailer.
6824096Seric **
6834096Seric **	Parameters:
68410327Seric **		line -- description of mailer.  This is in labeled
68510327Seric **			fields.  The fields are:
68610327Seric **			   P -- the path to the mailer
68710327Seric **			   F -- the flags associated with the mailer
68810327Seric **			   A -- the argv for this mailer
68910327Seric **			   S -- the sender rewriting set
69010327Seric **			   R -- the recipient rewriting set
69110327Seric **			   E -- the eol string
69210327Seric **			The first word is the canonical name of the mailer.
6934096Seric **
6944096Seric **	Returns:
6954096Seric **		none.
6964096Seric **
6974096Seric **	Side Effects:
6984096Seric **		enters the mailer into the mailer table.
6994096Seric */
7003308Seric 
70121066Seric makemailer(line)
7024096Seric 	char *line;
7034096Seric {
7044096Seric 	register char *p;
7058067Seric 	register struct mailer *m;
7068067Seric 	register STAB *s;
7078067Seric 	int i;
70810327Seric 	char fcode;
70958020Seric 	auto char *endp;
7104096Seric 	extern int NextMailer;
71110327Seric 	extern char **makeargv();
71210327Seric 	extern char *munchstring();
71310701Seric 	extern long atol();
7144096Seric 
71510327Seric 	/* allocate a mailer and set up defaults */
71610327Seric 	m = (struct mailer *) xalloc(sizeof *m);
71710327Seric 	bzero((char *) m, sizeof *m);
71810327Seric 	m->m_eol = "\n";
71967604Seric 	m->m_uid = m->m_gid = 0;
72010327Seric 
72110327Seric 	/* collect the mailer name */
72258050Seric 	for (p = line; *p != '\0' && *p != ',' && !(isascii(*p) && isspace(*p)); p++)
72310327Seric 		continue;
72410327Seric 	if (*p != '\0')
72510327Seric 		*p++ = '\0';
72610327Seric 	m->m_name = newstr(line);
72710327Seric 
72810327Seric 	/* now scan through and assign info from the fields */
72910327Seric 	while (*p != '\0')
73010327Seric 	{
73158333Seric 		auto char *delimptr;
73258333Seric 
73358050Seric 		while (*p != '\0' && (*p == ',' || (isascii(*p) && isspace(*p))))
73410327Seric 			p++;
73510327Seric 
73610327Seric 		/* p now points to field code */
73710327Seric 		fcode = *p;
73810327Seric 		while (*p != '\0' && *p != '=' && *p != ',')
73910327Seric 			p++;
74010327Seric 		if (*p++ != '=')
74110327Seric 		{
74252637Seric 			syserr("mailer %s: `=' expected", m->m_name);
74310327Seric 			return;
74410327Seric 		}
74558050Seric 		while (isascii(*p) && isspace(*p))
74610327Seric 			p++;
74710327Seric 
74810327Seric 		/* p now points to the field body */
74958333Seric 		p = munchstring(p, &delimptr);
75010327Seric 
75110327Seric 		/* install the field into the mailer struct */
75210327Seric 		switch (fcode)
75310327Seric 		{
75410327Seric 		  case 'P':		/* pathname */
75510327Seric 			m->m_mailer = newstr(p);
75610327Seric 			break;
75710327Seric 
75810327Seric 		  case 'F':		/* flags */
75910687Seric 			for (; *p != '\0'; p++)
76058050Seric 				if (!(isascii(*p) && isspace(*p)))
76152637Seric 					setbitn(*p, m->m_flags);
76210327Seric 			break;
76310327Seric 
76410327Seric 		  case 'S':		/* sender rewriting ruleset */
76510327Seric 		  case 'R':		/* recipient rewriting ruleset */
76658020Seric 			i = strtol(p, &endp, 10);
76710327Seric 			if (i < 0 || i >= MAXRWSETS)
76810327Seric 			{
76910327Seric 				syserr("invalid rewrite set, %d max", MAXRWSETS);
77010327Seric 				return;
77110327Seric 			}
77210327Seric 			if (fcode == 'S')
77358020Seric 				m->m_sh_rwset = m->m_se_rwset = i;
77410327Seric 			else
77558020Seric 				m->m_rh_rwset = m->m_re_rwset = i;
77658020Seric 
77758020Seric 			p = endp;
77859985Seric 			if (*p++ == '/')
77958020Seric 			{
78058020Seric 				i = strtol(p, NULL, 10);
78158020Seric 				if (i < 0 || i >= MAXRWSETS)
78258020Seric 				{
78358020Seric 					syserr("invalid rewrite set, %d max",
78458020Seric 						MAXRWSETS);
78558020Seric 					return;
78658020Seric 				}
78758020Seric 				if (fcode == 'S')
78858020Seric 					m->m_sh_rwset = i;
78958020Seric 				else
79058020Seric 					m->m_rh_rwset = i;
79158020Seric 			}
79210327Seric 			break;
79310327Seric 
79410327Seric 		  case 'E':		/* end of line string */
79510327Seric 			m->m_eol = newstr(p);
79610327Seric 			break;
79710327Seric 
79810327Seric 		  case 'A':		/* argument vector */
79910327Seric 			m->m_argv = makeargv(p);
80010327Seric 			break;
80110701Seric 
80210701Seric 		  case 'M':		/* maximum message size */
80310701Seric 			m->m_maxsize = atol(p);
80410701Seric 			break;
80552106Seric 
80652106Seric 		  case 'L':		/* maximum line length */
80752106Seric 			m->m_linelimit = atoi(p);
80852106Seric 			break;
80958935Seric 
81058935Seric 		  case 'D':		/* working directory */
81158935Seric 			m->m_execdir = newstr(p);
81258935Seric 			break;
81367604Seric 
81467896Seric 		  case 'C':		/* default charset */
81567896Seric 			m->m_defcharset = newstr(p);
81667896Seric 			break;
81767896Seric 
818*67990Seric 		  case 'T':		/* MTS Type */
819*67990Seric 			m->m_mtstype = newstr(p);
820*67990Seric 			break;
821*67990Seric 
82267604Seric 		  case 'U':		/* user id */
82367604Seric 			if (isascii(*p) && !isdigit(*p))
82467604Seric 			{
82567604Seric 				char *q = p;
82667604Seric 				struct passwd *pw;
82767604Seric 
82867604Seric 				while (isascii(*p) && isalnum(*p))
82967604Seric 					p++;
83067604Seric 				while (isascii(*p) && isspace(*p))
83167604Seric 					*p++ = '\0';
83267604Seric 				if (*p != '\0')
83367604Seric 					*p++ = '\0';
83467604Seric 				pw = getpwnam(q);
83567604Seric 				if (pw == NULL)
83667604Seric 					syserr("readcf: mailer U= flag: unknown user %s", q);
83767604Seric 				else
83867604Seric 				{
83967604Seric 					m->m_uid = pw->pw_uid;
84067604Seric 					m->m_gid = pw->pw_gid;
84167604Seric 				}
84267604Seric 			}
84367604Seric 			else
84467604Seric 			{
84567604Seric 				auto char *q;
84667604Seric 
84767604Seric 				m->m_uid = strtol(p, &q, 0);
84867604Seric 				p = q;
84967604Seric 			}
85067604Seric 			while (isascii(*p) && isspace(*p))
85167604Seric 				p++;
85267604Seric 			if (*p == '\0')
85367604Seric 				break;
85467604Seric 			if (isascii(*p) && !isdigit(*p))
85567604Seric 			{
85667604Seric 				char *q = p;
85767604Seric 				struct group *gr;
85867604Seric 
85967604Seric 				while (isascii(*p) && isalnum(*p))
86067604Seric 					p++;
86167604Seric 				*p++ = '\0';
86267604Seric 				gr = getgrnam(q);
86367604Seric 				if (gr == NULL)
86467604Seric 					syserr("readcf: mailer U= flag: unknown group %s", q);
86567604Seric 				else
86667604Seric 					m->m_gid = gr->gr_gid;
86767604Seric 			}
86867604Seric 			else
86967604Seric 			{
87067604Seric 				m->m_gid = strtol(p, NULL, 0);
87167604Seric 			}
87267604Seric 			break;
87310327Seric 		}
87410327Seric 
87558333Seric 		p = delimptr;
87610327Seric 	}
87710327Seric 
87852106Seric 	/* do some heuristic cleanup for back compatibility */
87952106Seric 	if (bitnset(M_LIMITS, m->m_flags))
88052106Seric 	{
88152106Seric 		if (m->m_linelimit == 0)
88252106Seric 			m->m_linelimit = SMTPLINELIM;
88355418Seric 		if (ConfigLevel < 2)
88452106Seric 			setbitn(M_7BITS, m->m_flags);
88552106Seric 	}
88652106Seric 
88758321Seric 	/* do some rationality checking */
88858321Seric 	if (m->m_argv == NULL)
88958321Seric 	{
89058321Seric 		syserr("M%s: A= argument required", m->m_name);
89158321Seric 		return;
89258321Seric 	}
89358321Seric 	if (m->m_mailer == NULL)
89458321Seric 	{
89558321Seric 		syserr("M%s: P= argument required", m->m_name);
89658321Seric 		return;
89758321Seric 	}
89858321Seric 
8994096Seric 	if (NextMailer >= MAXMAILERS)
9004096Seric 	{
9019381Seric 		syserr("too many mailers defined (%d max)", MAXMAILERS);
9024096Seric 		return;
9034096Seric 	}
90457402Seric 
90510327Seric 	s = stab(m->m_name, ST_MAILER, ST_ENTER);
90657402Seric 	if (s->s_mailer != NULL)
90757402Seric 	{
90857402Seric 		i = s->s_mailer->m_mno;
90957402Seric 		free(s->s_mailer);
91057402Seric 	}
91157402Seric 	else
91257402Seric 	{
91357402Seric 		i = NextMailer++;
91457402Seric 	}
91557402Seric 	Mailer[i] = s->s_mailer = m;
91657454Seric 	m->m_mno = i;
91710327Seric }
91810327Seric /*
91910327Seric **  MUNCHSTRING -- translate a string into internal form.
92010327Seric **
92110327Seric **	Parameters:
92210327Seric **		p -- the string to munch.
92358333Seric **		delimptr -- if non-NULL, set to the pointer of the
92458333Seric **			field delimiter character.
92510327Seric **
92610327Seric **	Returns:
92710327Seric **		the munched string.
92810327Seric */
9294096Seric 
93010327Seric char *
93158333Seric munchstring(p, delimptr)
93210327Seric 	register char *p;
93358333Seric 	char **delimptr;
93410327Seric {
93510327Seric 	register char *q;
93610327Seric 	bool backslash = FALSE;
93710327Seric 	bool quotemode = FALSE;
93810327Seric 	static char buf[MAXLINE];
9394096Seric 
94010327Seric 	for (q = buf; *p != '\0'; p++)
9414096Seric 	{
94210327Seric 		if (backslash)
94310327Seric 		{
94410327Seric 			/* everything is roughly literal */
94510357Seric 			backslash = FALSE;
94610327Seric 			switch (*p)
94710327Seric 			{
94810327Seric 			  case 'r':		/* carriage return */
94910327Seric 				*q++ = '\r';
95010327Seric 				continue;
95110327Seric 
95210327Seric 			  case 'n':		/* newline */
95310327Seric 				*q++ = '\n';
95410327Seric 				continue;
95510327Seric 
95610327Seric 			  case 'f':		/* form feed */
95710327Seric 				*q++ = '\f';
95810327Seric 				continue;
95910327Seric 
96010327Seric 			  case 'b':		/* backspace */
96110327Seric 				*q++ = '\b';
96210327Seric 				continue;
96310327Seric 			}
96410327Seric 			*q++ = *p;
96510327Seric 		}
96610327Seric 		else
96710327Seric 		{
96810327Seric 			if (*p == '\\')
96910327Seric 				backslash = TRUE;
97010327Seric 			else if (*p == '"')
97110327Seric 				quotemode = !quotemode;
97210327Seric 			else if (quotemode || *p != ',')
97310327Seric 				*q++ = *p;
97410327Seric 			else
97510327Seric 				break;
97610327Seric 		}
9774096Seric 	}
9784096Seric 
97958333Seric 	if (delimptr != NULL)
98058333Seric 		*delimptr = p;
98110327Seric 	*q++ = '\0';
98210327Seric 	return (buf);
98310327Seric }
98410327Seric /*
98510327Seric **  MAKEARGV -- break up a string into words
98610327Seric **
98710327Seric **	Parameters:
98810327Seric **		p -- the string to break up.
98910327Seric **
99010327Seric **	Returns:
99110327Seric **		a char **argv (dynamically allocated)
99210327Seric **
99310327Seric **	Side Effects:
99410327Seric **		munges p.
99510327Seric */
9964096Seric 
99710327Seric char **
99810327Seric makeargv(p)
99910327Seric 	register char *p;
100010327Seric {
100110327Seric 	char *q;
100210327Seric 	int i;
100310327Seric 	char **avp;
100410327Seric 	char *argv[MAXPV + 1];
100510327Seric 
100610327Seric 	/* take apart the words */
100710327Seric 	i = 0;
100810327Seric 	while (*p != '\0' && i < MAXPV)
10094096Seric 	{
101010327Seric 		q = p;
101158050Seric 		while (*p != '\0' && !(isascii(*p) && isspace(*p)))
101210327Seric 			p++;
101358050Seric 		while (isascii(*p) && isspace(*p))
101410327Seric 			*p++ = '\0';
101510327Seric 		argv[i++] = newstr(q);
10164096Seric 	}
101710327Seric 	argv[i++] = NULL;
10184096Seric 
101910327Seric 	/* now make a copy of the argv */
102010327Seric 	avp = (char **) xalloc(sizeof *avp * i);
102116893Seric 	bcopy((char *) argv, (char *) avp, sizeof *avp * i);
102210327Seric 
102310327Seric 	return (avp);
10243308Seric }
10253308Seric /*
10263308Seric **  PRINTRULES -- print rewrite rules (for debugging)
10273308Seric **
10283308Seric **	Parameters:
10293308Seric **		none.
10303308Seric **
10313308Seric **	Returns:
10323308Seric **		none.
10333308Seric **
10343308Seric **	Side Effects:
10353308Seric **		prints rewrite rules.
10363308Seric */
10373308Seric 
10383308Seric printrules()
10393308Seric {
10403308Seric 	register struct rewrite *rwp;
10414072Seric 	register int ruleset;
10423308Seric 
10434072Seric 	for (ruleset = 0; ruleset < 10; ruleset++)
10443308Seric 	{
10454072Seric 		if (RewriteRules[ruleset] == NULL)
10464072Seric 			continue;
10478067Seric 		printf("\n----Rule Set %d:", ruleset);
10483308Seric 
10494072Seric 		for (rwp = RewriteRules[ruleset]; rwp != NULL; rwp = rwp->r_next)
10503308Seric 		{
10518067Seric 			printf("\nLHS:");
10528067Seric 			printav(rwp->r_lhs);
10538067Seric 			printf("RHS:");
10548067Seric 			printav(rwp->r_rhs);
10553308Seric 		}
10563308Seric 	}
10573308Seric }
10584319Seric 
10594096Seric /*
10608256Seric **  SETOPTION -- set global processing option
10618256Seric **
10628256Seric **	Parameters:
10638256Seric **		opt -- option name.
10648256Seric **		val -- option value (as a text string).
106521755Seric **		safe -- set if this came from a configuration file.
106621755Seric **			Some options (if set from the command line) will
106721755Seric **			reset the user id to avoid security problems.
10688269Seric **		sticky -- if set, don't let other setoptions override
10698269Seric **			this value.
107058734Seric **		e -- the main envelope.
10718256Seric **
10728256Seric **	Returns:
10738256Seric **		none.
10748256Seric **
10758256Seric **	Side Effects:
10768256Seric **		Sets options as implied by the arguments.
10778256Seric */
10788256Seric 
107910687Seric static BITMAP	StickyOpt;		/* set if option is stuck */
10808269Seric 
108157207Seric 
108266334Seric #if NAMED_BIND
108357207Seric 
108457207Seric struct resolverflags
108557207Seric {
108657207Seric 	char	*rf_name;	/* name of the flag */
108757207Seric 	long	rf_bits;	/* bits to set/clear */
108857207Seric } ResolverFlags[] =
108957207Seric {
109057207Seric 	"debug",	RES_DEBUG,
109157207Seric 	"aaonly",	RES_AAONLY,
109257207Seric 	"usevc",	RES_USEVC,
109357207Seric 	"primary",	RES_PRIMARY,
109457207Seric 	"igntc",	RES_IGNTC,
109557207Seric 	"recurse",	RES_RECURSE,
109657207Seric 	"defnames",	RES_DEFNAMES,
109757207Seric 	"stayopen",	RES_STAYOPEN,
109857207Seric 	"dnsrch",	RES_DNSRCH,
109965583Seric 	"true",		0,		/* to avoid error on old syntax */
110057207Seric 	NULL,		0
110157207Seric };
110257207Seric 
110357207Seric #endif
110457207Seric 
110567614Seric struct optioninfo
110667614Seric {
110767614Seric 	char	*o_name;	/* long name of option */
110867787Seric 	u_char	o_code;		/* short name of option */
110967614Seric 	bool	o_safe;		/* safe for random people to use */
111067614Seric } OptionTab[] =
111167614Seric {
111267707Seric 	"SevenBitInput",	'7',		TRUE,
111367707Seric 	"EightBitMode",		'8',		TRUE,
111467707Seric 	"AliasFile",		'A',		FALSE,
111567707Seric 	"AliasWait",		'a',		FALSE,
111667707Seric 	"BlankSub",		'B',		FALSE,
111767707Seric 	"MinFreeBlocks",	'b',		TRUE,
111867707Seric 	"CheckpointInterval",	'C',		TRUE,
111967707Seric 	"HoldExpensive",	'c',		FALSE,
112067707Seric 	"AutoRebuildAliases",	'D',		FALSE,
112167707Seric 	"DeliveryMode",		'd',		TRUE,
112267707Seric 	"ErrorHeader",		'E',		FALSE,
112367707Seric 	"ErrorMode",		'e',		TRUE,
112467707Seric 	"TempFileMode",		'F',		FALSE,
112567707Seric 	"SaveFromLine",		'f',		FALSE,
112667707Seric 	"MatchGECOS",		'G',		FALSE,
112767707Seric 	"HelpFile",		'H',		FALSE,
112867707Seric 	"MaxHopCount",		'h',		FALSE,
112967707Seric 	"NameServerOptions",	'I',		FALSE,
113067707Seric 	"IgnoreDots",		'i',		TRUE,
113167707Seric 	"ForwardPath",		'J',		FALSE,
113267707Seric 	"SendMimeErrors",	'j',		TRUE,
113367707Seric 	"ConnectionCacheSize",	'k',		FALSE,
113467707Seric 	"ConnectionCacheTimeout", 'K',		FALSE,
113567707Seric 	"UseErrorsTo",		'l',		FALSE,
113667707Seric 	"LogLevel",		'L',		FALSE,
113767707Seric 	"MeToo",		'm',		TRUE,
113867707Seric 	"CheckAliases",		'n',		FALSE,
113967707Seric 	"OldStyleHeaders",	'o',		TRUE,
114067707Seric 	"DaemonPortOptions",	'O',		FALSE,
114167707Seric 	"PrivacyOptions",	'p',		TRUE,
114267707Seric 	"PostmasterCopy",	'P',		FALSE,
114367707Seric 	"QueueFactor",		'q',		FALSE,
114467707Seric 	"QueueDirectory",	'Q',		FALSE,
114567707Seric 	"DontPruneRoutes",	'R',		FALSE,
114667711Seric 	"Timeouts",		'r',		TRUE,
114767707Seric 	"StatusFile",		'S',		FALSE,
114867707Seric 	"SuperSafe",		's',		TRUE,
114967707Seric 	"QueueTimeout",		'T',		FALSE,
115067707Seric 	"TimeZoneSpec",		't',		FALSE,
115167707Seric 	"UserDatabaseSpec",	'U',		FALSE,
115267707Seric 	"DefaultUser",		'u',		FALSE,
115367707Seric 	"FallbackMXhost",	'V',		FALSE,
115467707Seric 	"Verbose",		'v',		TRUE,
115567707Seric 	"TryNullMXList",	'w',		TRUE,
115667707Seric 	"QueueLA",		'x',		FALSE,
115767707Seric 	"RefuseLA",		'X',		FALSE,
115867707Seric 	"RecipientFactor",	'y',		FALSE,
115967707Seric 	"ForkQueueRuns",	'Y',		FALSE,
116067707Seric 	"ClassFactor",		'z',		FALSE,
116167707Seric 	"TimeFactor",		'Z',		FALSE,
116267707Seric #define O_BSP		0x80
116367707Seric 	"BrokenSmtpPeers",	O_BSP,		TRUE,
116467707Seric #define O_SQBH		0x81
116567707Seric 	"SortQueueByHost",	O_SQBH,		TRUE,
116667707Seric #define O_DNICE		0x82
116767707Seric 	"DeliveryNiceness",	O_DNICE,	TRUE,
116867707Seric #define O_MQA		0x83
116967707Seric 	"MinQueueAge",		O_MQA,		TRUE,
117067707Seric #define O_MHSA		0x84
117167707Seric 	"MaxHostStatAge",	O_MHSA,		TRUE,
117267813Seric #define O_DEFCHARSET	0x85
117367813Seric 	"DefaultCharSet",	O_DEFCHARSET,	TRUE,
117467848Seric #define O_SSFILE	0x86
117567848Seric 	"ServiceSwitchFile",	O_SSFILE,	FALSE,
117667707Seric 
117767707Seric 	NULL,			'\0',		FALSE,
117867614Seric };
117967614Seric 
118067614Seric 
118167614Seric 
118258734Seric setoption(opt, val, safe, sticky, e)
118367614Seric 	u_char opt;
11848256Seric 	char *val;
118521755Seric 	bool safe;
11868269Seric 	bool sticky;
118758734Seric 	register ENVELOPE *e;
11888256Seric {
118957207Seric 	register char *p;
119067614Seric 	register struct optioninfo *o;
119167903Seric 	char *subopt;
11928265Seric 	extern bool atobool();
119312633Seric 	extern time_t convtime();
119414879Seric 	extern int QueueLA;
119514879Seric 	extern int RefuseLA;
119664718Seric 	extern bool Warn_Q_option;
11978256Seric 
119867736Seric 	errno = 0;
119967614Seric 	if (opt == ' ')
120067614Seric 	{
120167614Seric 		/* full word options */
120267736Seric 		struct optioninfo *sel;
120367614Seric 
120467614Seric 		p = strchr(val, '=');
120567614Seric 		if (p == NULL)
120667614Seric 			p = &val[strlen(val)];
120767614Seric 		while (*--p == ' ')
120867614Seric 			continue;
120967614Seric 		while (*++p == ' ')
121067614Seric 			*p = '\0';
121167731Seric 		if (p == val)
121267731Seric 		{
121367731Seric 			syserr("readcf: null option name");
121467731Seric 			return;
121567731Seric 		}
121667614Seric 		if (*p == '=')
121767614Seric 			*p++ = '\0';
121867614Seric 		while (*p == ' ')
121967614Seric 			p++;
122067903Seric 		subopt = strchr(val, '.');
122167903Seric 		if (subopt != NULL)
122267903Seric 			*subopt++ = '\0';
122367736Seric 		sel = NULL;
122467614Seric 		for (o = OptionTab; o->o_name != NULL; o++)
122567614Seric 		{
122667736Seric 			if (strncasecmp(o->o_name, val, strlen(val)) != 0)
122767736Seric 				continue;
122867736Seric 			if (strlen(o->o_name) == strlen(val))
122967736Seric 			{
123067736Seric 				/* completely specified -- this must be it */
123167736Seric 				sel = NULL;
123267614Seric 				break;
123367736Seric 			}
123467736Seric 			if (sel != NULL)
123567736Seric 				break;
123667736Seric 			sel = o;
123767614Seric 		}
123867736Seric 		if (sel != NULL && o->o_name == NULL)
123967736Seric 			o = sel;
124067736Seric 		else if (o->o_name == NULL)
124167787Seric 		{
124267614Seric 			syserr("readcf: unknown option name %s", val);
124367787Seric 			return;
124467787Seric 		}
124567736Seric 		else if (sel != NULL)
124667736Seric 		{
124767736Seric 			syserr("readcf: ambiguous option name %s (matches %s and %s)",
124867736Seric 				val, sel->o_name, o->o_name);
124967736Seric 			return;
125067736Seric 		}
125167736Seric 		if (strlen(val) != strlen(o->o_name))
125267736Seric 		{
125367736Seric 			bool oldVerbose = Verbose;
125467736Seric 
125567736Seric 			Verbose = TRUE;
125667736Seric 			message("Option %s used as abbreviation for %s",
125767736Seric 				val, o->o_name);
125867736Seric 			Verbose = oldVerbose;
125967736Seric 		}
126067614Seric 		opt = o->o_code;
126167614Seric 		val = p;
126267614Seric 	}
126367614Seric 	else
126467614Seric 	{
126567614Seric 		for (o = OptionTab; o->o_name != NULL; o++)
126667614Seric 		{
126767614Seric 			if (o->o_code == opt)
126867614Seric 				break;
126967614Seric 		}
127067903Seric 		subopt = NULL;
127167614Seric 	}
127267614Seric 
12738256Seric 	if (tTd(37, 1))
127467731Seric 	{
127567731Seric 		printf(isascii(opt) && isprint(opt) ?
127667903Seric 			    "setoption %s (%c).%s=%s" :
127767903Seric 			    "setoption %s (0x%x).%s=%s",
127867614Seric 			o->o_name == NULL ? "<unknown>" : o->o_name,
127967903Seric 			opt,
128067903Seric 			subopt == NULL ? "" : subopt,
128167903Seric 			val);
128267731Seric 	}
12838256Seric 
12848256Seric 	/*
12858269Seric 	**  See if this option is preset for us.
12868256Seric 	*/
12878256Seric 
128859731Seric 	if (!sticky && bitnset(opt, StickyOpt))
12898269Seric 	{
12909341Seric 		if (tTd(37, 1))
12919341Seric 			printf(" (ignored)\n");
12928269Seric 		return;
12938269Seric 	}
12948269Seric 
129521755Seric 	/*
129621755Seric 	**  Check to see if this option can be specified by this user.
129721755Seric 	*/
129821755Seric 
129963787Seric 	if (!safe && RealUid == 0)
130021755Seric 		safe = TRUE;
130167614Seric 	if (!safe && !o->o_safe)
130221755Seric 	{
130339111Srick 		if (opt != 'M' || (val[0] != 'r' && val[0] != 's'))
130421755Seric 		{
130536582Sbostic 			if (tTd(37, 1))
130636582Sbostic 				printf(" (unsafe)");
130763787Seric 			if (RealUid != geteuid())
130836582Sbostic 			{
130951210Seric 				if (tTd(37, 1))
131051210Seric 					printf("(Resetting uid)");
131163787Seric 				(void) setgid(RealGid);
131263787Seric 				(void) setuid(RealUid);
131336582Sbostic 			}
131421755Seric 		}
131521755Seric 	}
131651210Seric 	if (tTd(37, 1))
131717985Seric 		printf("\n");
13188269Seric 
131967614Seric 	switch (opt & 0xff)
13208256Seric 	{
132159709Seric 	  case '7':		/* force seven-bit input */
132267546Seric 		SevenBitInput = atobool(val);
132352106Seric 		break;
132452106Seric 
132567546Seric 	  case '8':		/* handling of 8-bit input */
132667546Seric 		switch (*val)
132767546Seric 		{
132867547Seric 		  case 'r':		/* reject 8-bit, don't convert MIME */
132967546Seric 			MimeMode = 0;
133067546Seric 			break;
133167546Seric 
133267547Seric 		  case 'm':		/* convert 8-bit, convert MIME */
133367546Seric 			MimeMode = MM_CVTMIME|MM_MIME8BIT;
133467546Seric 			break;
133567546Seric 
133667547Seric 		  case 'j':		/* "just send 8" */
133767546Seric 			MimeMode = MM_PASS8BIT;
133867546Seric 			break;
133967546Seric 
134067546Seric 		  case 'p':		/* pass 8 bit, convert MIME */
134167546Seric 			MimeMode = MM_PASS8BIT|MM_CVTMIME;
134267546Seric 			break;
134367546Seric 
134467546Seric 		  case 's':		/* strict adherence */
134567546Seric 			MimeMode = MM_CVTMIME;
134667546Seric 			break;
134767546Seric 
134867547Seric 		  case 'a':		/* encode 8 bit if available */
134967546Seric 			MimeMode = MM_MIME8BIT|MM_PASS8BIT|MM_CVTMIME;
135067546Seric 			break;
135167546Seric 
135267547Seric 		  case 'c':		/* convert 8 bit to MIME, never 7 bit */
135367547Seric 			MimeMode = MM_MIME8BIT;
135467547Seric 			break;
135567547Seric 
135667546Seric 		  default:
135767546Seric 			syserr("Unknown 8-bit mode %c", *val);
135867546Seric 			exit(EX_USAGE);
135967546Seric 		}
136067546Seric 		break;
136167546Seric 
13628256Seric 	  case 'A':		/* set default alias file */
13639381Seric 		if (val[0] == '\0')
136459672Seric 			setalias("aliases");
13659381Seric 		else
136659672Seric 			setalias(val);
13678256Seric 		break;
13688256Seric 
136917474Seric 	  case 'a':		/* look N minutes for "@:@" in alias file */
137017474Seric 		if (val[0] == '\0')
137164796Seric 			SafeAlias = 5 * 60;		/* five minutes */
137217474Seric 		else
137364796Seric 			SafeAlias = convtime(val, 'm');
137417474Seric 		break;
137517474Seric 
137616843Seric 	  case 'B':		/* substitution for blank character */
137716843Seric 		SpaceSub = val[0];
137816843Seric 		if (SpaceSub == '\0')
137916843Seric 			SpaceSub = ' ';
138016843Seric 		break;
138116843Seric 
138259283Seric 	  case 'b':		/* min blocks free on queue fs/max msg size */
138359283Seric 		p = strchr(val, '/');
138459283Seric 		if (p != NULL)
138559283Seric 		{
138659283Seric 			*p++ = '\0';
138759283Seric 			MaxMessageSize = atol(p);
138859283Seric 		}
138958082Seric 		MinBlocksFree = atol(val);
139058082Seric 		break;
139158082Seric 
13929284Seric 	  case 'c':		/* don't connect to "expensive" mailers */
13939381Seric 		NoConnect = atobool(val);
13949284Seric 		break;
13959284Seric 
139651305Seric 	  case 'C':		/* checkpoint every N addresses */
139751305Seric 		CheckpointInterval = atoi(val);
139824944Seric 		break;
139924944Seric 
14009284Seric 	  case 'd':		/* delivery mode */
14019284Seric 		switch (*val)
14028269Seric 		{
14039284Seric 		  case '\0':
140458734Seric 			e->e_sendmode = SM_DELIVER;
14058269Seric 			break;
14068269Seric 
140710755Seric 		  case SM_QUEUE:	/* queue only */
140810755Seric #ifndef QUEUE
140910755Seric 			syserr("need QUEUE to set -odqueue");
141056795Seric #endif /* QUEUE */
141110755Seric 			/* fall through..... */
141210755Seric 
14139284Seric 		  case SM_DELIVER:	/* do everything */
14149284Seric 		  case SM_FORK:		/* fork after verification */
141558734Seric 			e->e_sendmode = *val;
14168269Seric 			break;
14178269Seric 
14188269Seric 		  default:
14199284Seric 			syserr("Unknown delivery mode %c", *val);
14208269Seric 			exit(EX_USAGE);
14218269Seric 		}
14228269Seric 		break;
14238269Seric 
14249146Seric 	  case 'D':		/* rebuild alias database as needed */
14259381Seric 		AutoRebuild = atobool(val);
14269146Seric 		break;
14279146Seric 
142855372Seric 	  case 'E':		/* error message header/header file */
142955379Seric 		if (*val != '\0')
143055379Seric 			ErrMsgFile = newstr(val);
143155372Seric 		break;
143255372Seric 
14338269Seric 	  case 'e':		/* set error processing mode */
14348269Seric 		switch (*val)
14358269Seric 		{
14369381Seric 		  case EM_QUIET:	/* be silent about it */
14379381Seric 		  case EM_MAIL:		/* mail back */
14389381Seric 		  case EM_BERKNET:	/* do berknet error processing */
14399381Seric 		  case EM_WRITE:	/* write back (or mail) */
14409381Seric 		  case EM_PRINT:	/* print errors normally (default) */
144158734Seric 			e->e_errormode = *val;
14428269Seric 			break;
14438269Seric 		}
14448269Seric 		break;
14458269Seric 
14469049Seric 	  case 'F':		/* file mode */
144717975Seric 		FileMode = atooct(val) & 0777;
14489049Seric 		break;
14499049Seric 
14508269Seric 	  case 'f':		/* save Unix-style From lines on front */
14519381Seric 		SaveFrom = atobool(val);
14528269Seric 		break;
14538269Seric 
145453735Seric 	  case 'G':		/* match recipients against GECOS field */
145553735Seric 		MatchGecos = atobool(val);
145653735Seric 		break;
145753735Seric 
14588256Seric 	  case 'g':		/* default gid */
145967823Seric   g_opt:
146064133Seric 		if (isascii(*val) && isdigit(*val))
146164133Seric 			DefGid = atoi(val);
146264133Seric 		else
146364133Seric 		{
146464133Seric 			register struct group *gr;
146564133Seric 
146664133Seric 			DefGid = -1;
146764133Seric 			gr = getgrnam(val);
146864133Seric 			if (gr == NULL)
146967823Seric 				syserr("readcf: option %c: unknown group %s",
147067823Seric 					opt, val);
147164133Seric 			else
147264133Seric 				DefGid = gr->gr_gid;
147364133Seric 		}
14748256Seric 		break;
14758256Seric 
14768256Seric 	  case 'H':		/* help file */
14779381Seric 		if (val[0] == '\0')
14788269Seric 			HelpFile = "sendmail.hf";
14799381Seric 		else
14809381Seric 			HelpFile = newstr(val);
14818256Seric 		break;
14828256Seric 
148351305Seric 	  case 'h':		/* maximum hop count */
148451305Seric 		MaxHopCount = atoi(val);
148551305Seric 		break;
148651305Seric 
148735651Seric 	  case 'I':		/* use internet domain name server */
148866334Seric #if NAMED_BIND
148957207Seric 		for (p = val; *p != 0; )
149057207Seric 		{
149157207Seric 			bool clearmode;
149257207Seric 			char *q;
149357207Seric 			struct resolverflags *rfp;
149457207Seric 
149557207Seric 			while (*p == ' ')
149657207Seric 				p++;
149757207Seric 			if (*p == '\0')
149857207Seric 				break;
149957207Seric 			clearmode = FALSE;
150057207Seric 			if (*p == '-')
150157207Seric 				clearmode = TRUE;
150257207Seric 			else if (*p != '+')
150357207Seric 				p--;
150457207Seric 			p++;
150557207Seric 			q = p;
150658050Seric 			while (*p != '\0' && !(isascii(*p) && isspace(*p)))
150757207Seric 				p++;
150857207Seric 			if (*p != '\0')
150957207Seric 				*p++ = '\0';
151057207Seric 			for (rfp = ResolverFlags; rfp->rf_name != NULL; rfp++)
151157207Seric 			{
151257207Seric 				if (strcasecmp(q, rfp->rf_name) == 0)
151357207Seric 					break;
151457207Seric 			}
151564923Seric 			if (rfp->rf_name == NULL)
151664923Seric 				syserr("readcf: I option value %s unrecognized", q);
151764923Seric 			else if (clearmode)
151857207Seric 				_res.options &= ~rfp->rf_bits;
151957207Seric 			else
152057207Seric 				_res.options |= rfp->rf_bits;
152157207Seric 		}
152257207Seric 		if (tTd(8, 2))
152357207Seric 			printf("_res.options = %x\n", _res.options);
152457207Seric #else
152557207Seric 		usrerr("name server (I option) specified but BIND not compiled in");
152657207Seric #endif
152735651Seric 		break;
152835651Seric 
15298269Seric 	  case 'i':		/* ignore dot lines in message */
15309381Seric 		IgnrDot = atobool(val);
15318269Seric 		break;
15328269Seric 
153359730Seric 	  case 'j':		/* send errors in MIME (RFC 1341) format */
153459730Seric 		SendMIMEErrors = atobool(val);
153559730Seric 		break;
153659730Seric 
153757136Seric 	  case 'J':		/* .forward search path */
153857136Seric 		ForwardPath = newstr(val);
153957136Seric 		break;
154057136Seric 
154154967Seric 	  case 'k':		/* connection cache size */
154254967Seric 		MaxMciCache = atoi(val);
154356215Seric 		if (MaxMciCache < 0)
154456215Seric 			MaxMciCache = 0;
154554967Seric 		break;
154654967Seric 
154754967Seric 	  case 'K':		/* connection cache timeout */
154858796Seric 		MciCacheTimeout = convtime(val, 'm');
154954967Seric 		break;
155054967Seric 
155161104Seric 	  case 'l':		/* use Errors-To: header */
155261104Seric 		UseErrorsTo = atobool(val);
155361104Seric 		break;
155461104Seric 
15558256Seric 	  case 'L':		/* log level */
155664140Seric 		if (safe || LogLevel < atoi(val))
155764140Seric 			LogLevel = atoi(val);
15588256Seric 		break;
15598256Seric 
15608269Seric 	  case 'M':		/* define macro */
15619381Seric 		define(val[0], newstr(&val[1]), CurEnv);
156216878Seric 		sticky = FALSE;
15638269Seric 		break;
15648269Seric 
15658269Seric 	  case 'm':		/* send to me too */
15669381Seric 		MeToo = atobool(val);
15678269Seric 		break;
15688269Seric 
156925820Seric 	  case 'n':		/* validate RHS in newaliases */
157025820Seric 		CheckAliases = atobool(val);
157125820Seric 		break;
157225820Seric 
157361104Seric 	    /* 'N' available -- was "net name" */
157461104Seric 
157558851Seric 	  case 'O':		/* daemon options */
157658851Seric 		setdaemonoptions(val);
157758851Seric 		break;
157858851Seric 
15798269Seric 	  case 'o':		/* assume old style headers */
15809381Seric 		if (atobool(val))
15819341Seric 			CurEnv->e_flags |= EF_OLDSTYLE;
15829341Seric 		else
15839341Seric 			CurEnv->e_flags &= ~EF_OLDSTYLE;
15848269Seric 		break;
15858269Seric 
158658082Seric 	  case 'p':		/* select privacy level */
158758082Seric 		p = val;
158858082Seric 		for (;;)
158958082Seric 		{
159058082Seric 			register struct prival *pv;
159158082Seric 			extern struct prival PrivacyValues[];
159258082Seric 
159358082Seric 			while (isascii(*p) && (isspace(*p) || ispunct(*p)))
159458082Seric 				p++;
159558082Seric 			if (*p == '\0')
159658082Seric 				break;
159758082Seric 			val = p;
159858082Seric 			while (isascii(*p) && isalnum(*p))
159958082Seric 				p++;
160058082Seric 			if (*p != '\0')
160158082Seric 				*p++ = '\0';
160258082Seric 
160358082Seric 			for (pv = PrivacyValues; pv->pv_name != NULL; pv++)
160458082Seric 			{
160558082Seric 				if (strcasecmp(val, pv->pv_name) == 0)
160658082Seric 					break;
160758082Seric 			}
160858886Seric 			if (pv->pv_name == NULL)
160958886Seric 				syserr("readcf: Op line: %s unrecognized", val);
161058082Seric 			PrivacyFlags |= pv->pv_flag;
161158082Seric 		}
161258082Seric 		break;
161358082Seric 
161424944Seric 	  case 'P':		/* postmaster copy address for returned mail */
161524944Seric 		PostMasterCopy = newstr(val);
161624944Seric 		break;
161724944Seric 
161824944Seric 	  case 'q':		/* slope of queue only function */
161924944Seric 		QueueFactor = atoi(val);
162024944Seric 		break;
162124944Seric 
16228256Seric 	  case 'Q':		/* queue directory */
16239381Seric 		if (val[0] == '\0')
16248269Seric 			QueueDir = "mqueue";
16259381Seric 		else
16269381Seric 			QueueDir = newstr(val);
162758789Seric 		if (RealUid != 0 && !safe)
162864718Seric 			Warn_Q_option = TRUE;
16298256Seric 		break;
16308256Seric 
163158148Seric 	  case 'R':		/* don't prune routes */
163258148Seric 		DontPruneRoutes = atobool(val);
163358148Seric 		break;
163458148Seric 
16358256Seric 	  case 'r':		/* read timeout */
163667903Seric 		if (subopt == NULL)
163767903Seric 			inittimeouts(val);
163867903Seric 		else
163967903Seric 			settimeout(subopt, val);
16408256Seric 		break;
16418256Seric 
16428256Seric 	  case 'S':		/* status file */
16439381Seric 		if (val[0] == '\0')
16448269Seric 			StatFile = "sendmail.st";
16459381Seric 		else
16469381Seric 			StatFile = newstr(val);
16478256Seric 		break;
16488256Seric 
16498265Seric 	  case 's':		/* be super safe, even if expensive */
16509381Seric 		SuperSafe = atobool(val);
16518256Seric 		break;
16528256Seric 
16538256Seric 	  case 'T':		/* queue timeout */
165458737Seric 		p = strchr(val, '/');
165558737Seric 		if (p != NULL)
165658737Seric 		{
165758737Seric 			*p++ = '\0';
165867903Seric 			settimeout("queuewarn", p);
165958737Seric 		}
166067903Seric 		settimeout("queuereturn", val);
166154967Seric 		break;
16628256Seric 
16638265Seric 	  case 't':		/* time zone name */
166452106Seric 		TimeZoneSpec = newstr(val);
16658265Seric 		break;
16668265Seric 
166750556Seric 	  case 'U':		/* location of user database */
166851360Seric 		UdbSpec = newstr(val);
166950556Seric 		break;
167050556Seric 
16718256Seric 	  case 'u':		/* set default uid */
167267823Seric 		for (p = val; *p != '\0'; p++)
167367823Seric 		{
167467823Seric 			if (*p == '.' || *p == '/' || *p == ':')
167567823Seric 			{
167667823Seric 				*p++ = '\0';
167767823Seric 				break;
167867823Seric 			}
167967823Seric 		}
168064133Seric 		if (isascii(*val) && isdigit(*val))
168164133Seric 			DefUid = atoi(val);
168264133Seric 		else
168364133Seric 		{
168464133Seric 			register struct passwd *pw;
168564133Seric 
168664133Seric 			DefUid = -1;
168764133Seric 			pw = getpwnam(val);
168864133Seric 			if (pw == NULL)
168964133Seric 				syserr("readcf: option u: unknown user %s", val);
169064133Seric 			else
169167823Seric 			{
169264133Seric 				DefUid = pw->pw_uid;
169367823Seric 				DefGid = pw->pw_gid;
169467823Seric 			}
169564133Seric 		}
169640973Sbostic 		setdefuser();
16978256Seric 
169867823Seric 		/* handle the group if it is there */
169967823Seric 		if (*p == '\0')
170067823Seric 			break;
170167823Seric 		val = p;
170267823Seric 		goto g_opt;
170367823Seric 
170458851Seric 	  case 'V':		/* fallback MX host */
170558851Seric 		FallBackMX = newstr(val);
170658851Seric 		break;
170758851Seric 
17088269Seric 	  case 'v':		/* run in verbose mode */
17099381Seric 		Verbose = atobool(val);
17108256Seric 		break;
17118256Seric 
171263837Seric 	  case 'w':		/* if we are best MX, try host directly */
171363837Seric 		TryNullMXList = atobool(val);
171463837Seric 		break;
171561104Seric 
171661104Seric 	    /* 'W' available -- was wizard password */
171761104Seric 
171814879Seric 	  case 'x':		/* load avg at which to auto-queue msgs */
171914879Seric 		QueueLA = atoi(val);
172014879Seric 		break;
172114879Seric 
172214879Seric 	  case 'X':		/* load avg at which to auto-reject connections */
172314879Seric 		RefuseLA = atoi(val);
172414879Seric 		break;
172514879Seric 
172624981Seric 	  case 'y':		/* work recipient factor */
172724981Seric 		WkRecipFact = atoi(val);
172824981Seric 		break;
172924981Seric 
173024981Seric 	  case 'Y':		/* fork jobs during queue runs */
173124952Seric 		ForkQueueRuns = atobool(val);
173224952Seric 		break;
173324952Seric 
173424981Seric 	  case 'z':		/* work message class factor */
173524981Seric 		WkClassFact = atoi(val);
173624981Seric 		break;
173724981Seric 
173824981Seric 	  case 'Z':		/* work time factor */
173924981Seric 		WkTimeFact = atoi(val);
174024981Seric 		break;
174124981Seric 
174267614Seric 	  case O_BSP:		/* SMTP Peers can't handle 2-line greeting */
174367614Seric 		BrokenSmtpPeers = atobool(val);
174467614Seric 		break;
174567614Seric 
174667614Seric 	  case O_SQBH:		/* sort work queue by host first */
174767614Seric 		SortQueueByHost = atobool(val);
174867614Seric 		break;
174967614Seric 
175067707Seric 	  case O_DNICE:		/* delivery nice value */
175167707Seric 		DeliveryNiceness = atoi(val);
175267707Seric 		break;
175367707Seric 
175467707Seric 	  case O_MQA:		/* minimum queue age between deliveries */
175567707Seric 		MinQueueAge = convtime(val, 'm');
175667707Seric 		break;
175767707Seric 
175867707Seric 	  case O_MHSA:		/* maximum age of cached host status */
175967707Seric 		MaxHostStatAge = convtime(val, 'm');
176067707Seric 		break;
176167707Seric 
176267813Seric 	  case O_DEFCHARSET:	/* default character set for mimefying */
176367814Seric 		DefaultCharSet = newstr(val);
176467813Seric 		break;
176567813Seric 
176667848Seric 	  case O_SSFILE:	/* service switch file */
176767848Seric 		ServiceSwitchFile = newstr(val);
176867848Seric 		break;
176967848Seric 
17708256Seric 	  default:
17718256Seric 		break;
17728256Seric 	}
177316878Seric 	if (sticky)
177416878Seric 		setbitn(opt, StickyOpt);
17759188Seric 	return;
17768256Seric }
177710687Seric /*
177810687Seric **  SETCLASS -- set a word into a class
177910687Seric **
178010687Seric **	Parameters:
178110687Seric **		class -- the class to put the word in.
178210687Seric **		word -- the word to enter
178310687Seric **
178410687Seric **	Returns:
178510687Seric **		none.
178610687Seric **
178710687Seric **	Side Effects:
178810687Seric **		puts the word into the symbol table.
178910687Seric */
179010687Seric 
179110687Seric setclass(class, word)
179210687Seric 	int class;
179310687Seric 	char *word;
179410687Seric {
179510687Seric 	register STAB *s;
179610687Seric 
179757943Seric 	if (tTd(37, 8))
179864326Seric 		printf("setclass(%c, %s)\n", class, word);
179910687Seric 	s = stab(word, ST_CLASS, ST_ENTER);
180010687Seric 	setbitn(class, s->s_class);
180110687Seric }
180253654Seric /*
180353654Seric **  MAKEMAPENTRY -- create a map entry
180453654Seric **
180553654Seric **	Parameters:
180653654Seric **		line -- the config file line
180753654Seric **
180853654Seric **	Returns:
180953654Seric **		TRUE if it successfully entered the map entry.
181053654Seric **		FALSE otherwise (usually syntax error).
181153654Seric **
181253654Seric **	Side Effects:
181353654Seric **		Enters the map into the dictionary.
181453654Seric */
181553654Seric 
181653654Seric void
181753654Seric makemapentry(line)
181853654Seric 	char *line;
181953654Seric {
182053654Seric 	register char *p;
182153654Seric 	char *mapname;
182253654Seric 	char *classname;
182364078Seric 	register STAB *s;
182453654Seric 	STAB *class;
182553654Seric 
182658050Seric 	for (p = line; isascii(*p) && isspace(*p); p++)
182753654Seric 		continue;
182858050Seric 	if (!(isascii(*p) && isalnum(*p)))
182953654Seric 	{
183053654Seric 		syserr("readcf: config K line: no map name");
183153654Seric 		return;
183253654Seric 	}
183353654Seric 
183453654Seric 	mapname = p;
183567848Seric 	while ((isascii(*++p) && isalnum(*p)) || *p == '.')
183653654Seric 		continue;
183753654Seric 	if (*p != '\0')
183853654Seric 		*p++ = '\0';
183958050Seric 	while (isascii(*p) && isspace(*p))
184053654Seric 		p++;
184158050Seric 	if (!(isascii(*p) && isalnum(*p)))
184253654Seric 	{
184353654Seric 		syserr("readcf: config K line, map %s: no map class", mapname);
184453654Seric 		return;
184553654Seric 	}
184653654Seric 	classname = p;
184758050Seric 	while (isascii(*++p) && isalnum(*p))
184853654Seric 		continue;
184953654Seric 	if (*p != '\0')
185053654Seric 		*p++ = '\0';
185158050Seric 	while (isascii(*p) && isspace(*p))
185253654Seric 		p++;
185353654Seric 
185453654Seric 	/* look up the class */
185553654Seric 	class = stab(classname, ST_MAPCLASS, ST_FIND);
185653654Seric 	if (class == NULL)
185753654Seric 	{
185853654Seric 		syserr("readcf: map %s: class %s not available", mapname, classname);
185953654Seric 		return;
186053654Seric 	}
186153654Seric 
186253654Seric 	/* enter the map */
186364078Seric 	s = stab(mapname, ST_MAP, ST_ENTER);
186464078Seric 	s->s_map.map_class = &class->s_mapclass;
186564078Seric 	s->s_map.map_mname = newstr(mapname);
186653654Seric 
186764078Seric 	if (class->s_mapclass.map_parse(&s->s_map, p))
186864078Seric 		s->s_map.map_mflags |= MF_VALID;
186964078Seric 
187064078Seric 	if (tTd(37, 5))
187164078Seric 	{
187264078Seric 		printf("map %s, class %s, flags %x, file %s,\n",
187364078Seric 			s->s_map.map_mname, s->s_map.map_class->map_cname,
187464078Seric 			s->s_map.map_mflags,
187564078Seric 			s->s_map.map_file == NULL ? "(null)" : s->s_map.map_file);
187664078Seric 		printf("\tapp %s, domain %s, rebuild %s\n",
187764078Seric 			s->s_map.map_app == NULL ? "(null)" : s->s_map.map_app,
187864078Seric 			s->s_map.map_domain == NULL ? "(null)" : s->s_map.map_domain,
187964078Seric 			s->s_map.map_rebuild == NULL ? "(null)" : s->s_map.map_rebuild);
188064078Seric 	}
188153654Seric }
188258112Seric /*
188367903Seric **  INITTIMEOUTS -- parse and set timeout values
188458112Seric **
188558112Seric **	Parameters:
188658112Seric **		val -- a pointer to the values.  If NULL, do initial
188758112Seric **			settings.
188858112Seric **
188958112Seric **	Returns:
189058112Seric **		none.
189158112Seric **
189258112Seric **	Side Effects:
189358112Seric **		Initializes the TimeOuts structure
189458112Seric */
189558112Seric 
189664255Seric #define SECONDS
189758112Seric #define MINUTES	* 60
189858112Seric #define HOUR	* 3600
189958112Seric 
190067903Seric inittimeouts(val)
190158112Seric 	register char *val;
190258112Seric {
190358112Seric 	register char *p;
190458671Seric 	extern time_t convtime();
190558112Seric 
190658112Seric 	if (val == NULL)
190758112Seric 	{
190858112Seric 		TimeOuts.to_initial = (time_t) 5 MINUTES;
190958112Seric 		TimeOuts.to_helo = (time_t) 5 MINUTES;
191058112Seric 		TimeOuts.to_mail = (time_t) 10 MINUTES;
191158112Seric 		TimeOuts.to_rcpt = (time_t) 1 HOUR;
191258112Seric 		TimeOuts.to_datainit = (time_t) 5 MINUTES;
191358112Seric 		TimeOuts.to_datablock = (time_t) 1 HOUR;
191458112Seric 		TimeOuts.to_datafinal = (time_t) 1 HOUR;
191558112Seric 		TimeOuts.to_rset = (time_t) 5 MINUTES;
191658112Seric 		TimeOuts.to_quit = (time_t) 2 MINUTES;
191758112Seric 		TimeOuts.to_nextcommand = (time_t) 1 HOUR;
191858112Seric 		TimeOuts.to_miscshort = (time_t) 2 MINUTES;
191964255Seric 		TimeOuts.to_ident = (time_t) 30 SECONDS;
192067711Seric 		TimeOuts.to_fileopen = (time_t) 60 SECONDS;
192158112Seric 		return;
192258112Seric 	}
192358112Seric 
192458112Seric 	for (;; val = p)
192558112Seric 	{
192658112Seric 		while (isascii(*val) && isspace(*val))
192758112Seric 			val++;
192858112Seric 		if (*val == '\0')
192958112Seric 			break;
193058112Seric 		for (p = val; *p != '\0' && *p != ','; p++)
193158112Seric 			continue;
193258112Seric 		if (*p != '\0')
193358112Seric 			*p++ = '\0';
193458112Seric 
193558112Seric 		if (isascii(*val) && isdigit(*val))
193658112Seric 		{
193758112Seric 			/* old syntax -- set everything */
193858796Seric 			TimeOuts.to_mail = convtime(val, 'm');
193958112Seric 			TimeOuts.to_rcpt = TimeOuts.to_mail;
194058112Seric 			TimeOuts.to_datainit = TimeOuts.to_mail;
194158112Seric 			TimeOuts.to_datablock = TimeOuts.to_mail;
194258112Seric 			TimeOuts.to_datafinal = TimeOuts.to_mail;
194358112Seric 			TimeOuts.to_nextcommand = TimeOuts.to_mail;
194458112Seric 			continue;
194558112Seric 		}
194658112Seric 		else
194758112Seric 		{
194867711Seric 			register char *q = strchr(val, ':');
194958112Seric 
195067711Seric 			if (q == NULL && (q = strchr(val, '=')) == NULL)
195158112Seric 			{
195258112Seric 				/* syntax error */
195358112Seric 				continue;
195458112Seric 			}
195558112Seric 			*q++ = '\0';
195667903Seric 			settimeout(val, q);
195767903Seric 		}
195867903Seric 	}
195967903Seric }
196067903Seric /*
196167903Seric **  SETTIMEOUT -- set an individual timeout
196267903Seric **
196367903Seric **	Parameters:
196467903Seric **		name -- the name of the timeout.
196567903Seric **		val -- the value of the timeout.
196667903Seric **
196767903Seric **	Returns:
196867903Seric **		none.
196967903Seric */
197058112Seric 
197167903Seric settimeout(name, val)
197267903Seric 	char *name;
197367903Seric 	char *val;
197467903Seric {
197567903Seric 	register char *p;
197667903Seric 	time_t to;
197767903Seric 	extern time_t convtime();
197867903Seric 
197967903Seric 	to = convtime(val, 'm');
198067903Seric 	p = strchr(name, '.');
198167903Seric 	if (p != NULL)
198267903Seric 		*p++ = '\0';
198367903Seric 
198467903Seric 	if (strcasecmp(name, "initial") == 0)
198567903Seric 		TimeOuts.to_initial = to;
198667903Seric 	else if (strcasecmp(name, "mail") == 0)
198767903Seric 		TimeOuts.to_mail = to;
198867903Seric 	else if (strcasecmp(name, "rcpt") == 0)
198967903Seric 		TimeOuts.to_rcpt = to;
199067903Seric 	else if (strcasecmp(name, "datainit") == 0)
199167903Seric 		TimeOuts.to_datainit = to;
199267903Seric 	else if (strcasecmp(name, "datablock") == 0)
199367903Seric 		TimeOuts.to_datablock = to;
199467903Seric 	else if (strcasecmp(name, "datafinal") == 0)
199567903Seric 		TimeOuts.to_datafinal = to;
199667903Seric 	else if (strcasecmp(name, "command") == 0)
199767903Seric 		TimeOuts.to_nextcommand = to;
199867903Seric 	else if (strcasecmp(name, "rset") == 0)
199967903Seric 		TimeOuts.to_rset = to;
200067903Seric 	else if (strcasecmp(name, "helo") == 0)
200167903Seric 		TimeOuts.to_helo = to;
200267903Seric 	else if (strcasecmp(name, "quit") == 0)
200367903Seric 		TimeOuts.to_quit = to;
200467903Seric 	else if (strcasecmp(name, "misc") == 0)
200567903Seric 		TimeOuts.to_miscshort = to;
200667903Seric 	else if (strcasecmp(name, "ident") == 0)
200767903Seric 		TimeOuts.to_ident = to;
200867903Seric 	else if (strcasecmp(name, "fileopen") == 0)
200967903Seric 		TimeOuts.to_fileopen = to;
201067903Seric 	else if (strcasecmp(name, "queuewarn") == 0)
201167903Seric 	{
201267903Seric 		to = convtime(val, 'h');
201367946Seric 		if (p == NULL || strcmp(p, "*") == 0)
201467903Seric 		{
201567903Seric 			TimeOuts.to_q_warning[TOC_NORMAL] = to;
201667903Seric 			TimeOuts.to_q_warning[TOC_URGENT] = to;
201767903Seric 			TimeOuts.to_q_warning[TOC_NONURGENT] = to;
201858112Seric 		}
201967903Seric 		else if (strcasecmp(p, "normal") == 0)
202067903Seric 			TimeOuts.to_q_warning[TOC_NORMAL] = to;
202167903Seric 		else if (strcasecmp(p, "urgent") == 0)
202267903Seric 			TimeOuts.to_q_warning[TOC_URGENT] = to;
202367903Seric 		else if (strcasecmp(p, "non-urgent") == 0)
202467903Seric 			TimeOuts.to_q_warning[TOC_NONURGENT] = to;
202567903Seric 		else
202667903Seric 			syserr("settimeout: invalid queuewarn subtimeout %s", p);
202758112Seric 	}
202867903Seric 	else if (strcasecmp(name, "queuereturn") == 0)
202967903Seric 	{
203067903Seric 		to = convtime(val, 'd');
203167903Seric 		if (p == NULL || strcmp(p, "*") == 0)
203267903Seric 		{
203367903Seric 			TimeOuts.to_q_return[TOC_NORMAL] = to;
203467903Seric 			TimeOuts.to_q_return[TOC_URGENT] = to;
203567903Seric 			TimeOuts.to_q_return[TOC_NONURGENT] = to;
203667903Seric 		}
203767903Seric 		else if (strcasecmp(p, "normal") == 0)
203867903Seric 			TimeOuts.to_q_return[TOC_NORMAL] = to;
203967903Seric 		else if (strcasecmp(p, "urgent") == 0)
204067903Seric 			TimeOuts.to_q_return[TOC_URGENT] = to;
204167903Seric 		else if (strcasecmp(p, "non-urgent") == 0)
204267903Seric 			TimeOuts.to_q_return[TOC_NONURGENT] = to;
204367903Seric 		else
204467903Seric 			syserr("settimeout: invalid queuereturn subtimeout %s", p);
204567903Seric 	}
204667903Seric 	else
204767903Seric 		syserr("settimeout: invalid timeout %s", name);
204858112Seric }
2049