xref: /csrg-svn/usr.sbin/sendmail/src/readcf.c (revision 67615)
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*67615Seric static char sccsid[] = "@(#)readcf.c	8.31 (Berkeley) 08/07/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;
803308Seric 	char buf[MAXLINE];
813308Seric 	register char *p;
823308Seric 	extern char **copyplist();
8352647Seric 	struct stat statb;
845909Seric 	char exbuf[MAXLINE];
8565066Seric 	char pvpbuf[MAXLINE + MAXATOM];
8610709Seric 	extern char *munchstring();
8753654Seric 	extern void makemapentry();
883308Seric 
8952647Seric 	FileName = cfname;
9052647Seric 	LineNumber = 0;
9152647Seric 
923308Seric 	cf = fopen(cfname, "r");
933308Seric 	if (cf == NULL)
943308Seric 	{
9552647Seric 		syserr("cannot open");
963308Seric 		exit(EX_OSFILE);
973308Seric 	}
983308Seric 
9952647Seric 	if (fstat(fileno(cf), &statb) < 0)
10052647Seric 	{
10152647Seric 		syserr("cannot fstat");
10252647Seric 		exit(EX_OSFILE);
10352647Seric 	}
10452647Seric 
10552647Seric 	if (!S_ISREG(statb.st_mode))
10652647Seric 	{
10752647Seric 		syserr("not a plain file");
10852647Seric 		exit(EX_OSFILE);
10952647Seric 	}
11052647Seric 
11152647Seric 	if (OpMode != MD_TEST && bitset(S_IWGRP|S_IWOTH, statb.st_mode))
11252647Seric 	{
11353037Seric 		if (OpMode == MD_DAEMON || OpMode == MD_FREEZE)
11453037Seric 			fprintf(stderr, "%s: WARNING: dangerous write permissions\n",
11553037Seric 				FileName);
11653037Seric #ifdef LOG
11753037Seric 		if (LogLevel > 0)
11853037Seric 			syslog(LOG_CRIT, "%s: WARNING: dangerous write permissions",
11953037Seric 				FileName);
12053037Seric #endif
12152647Seric 	}
12252647Seric 
12359254Seric #ifdef XLA
12459254Seric 	xla_zero();
12559254Seric #endif
12659254Seric 
12757135Seric 	while ((bp = fgetfolded(buf, sizeof buf, cf)) != NULL)
1283308Seric 	{
12957135Seric 		if (bp[0] == '#')
13057135Seric 		{
13157135Seric 			if (bp != buf)
13257135Seric 				free(bp);
13352637Seric 			continue;
13457135Seric 		}
13552637Seric 
13658050Seric 		/* map $ into \201 for macro expansion */
13757135Seric 		for (p = bp; *p != '\0'; p++)
13816157Seric 		{
13957135Seric 			if (*p == '#' && p > bp && ConfigLevel >= 3)
14052647Seric 			{
14152647Seric 				/* this is an on-line comment */
14252647Seric 				register char *e;
14352647Seric 
14458050Seric 				switch (*--p & 0377)
14552647Seric 				{
14658050Seric 				  case MACROEXPAND:
14752647Seric 					/* it's from $# -- let it go through */
14852647Seric 					p++;
14952647Seric 					break;
15052647Seric 
15152647Seric 				  case '\\':
15252647Seric 					/* it's backslash escaped */
15352647Seric 					(void) strcpy(p, p + 1);
15452647Seric 					break;
15552647Seric 
15652647Seric 				  default:
15752647Seric 					/* delete preceeding white space */
15858050Seric 					while (isascii(*p) && isspace(*p) && p > bp)
15952647Seric 						p--;
16056795Seric 					if ((e = strchr(++p, '\n')) != NULL)
16152647Seric 						(void) strcpy(p, e);
16252647Seric 					else
16352647Seric 						p[0] = p[1] = '\0';
16452647Seric 					break;
16552647Seric 				}
16652647Seric 				continue;
16752647Seric 			}
16852647Seric 
16916157Seric 			if (*p != '$')
17016157Seric 				continue;
17116157Seric 
17216157Seric 			if (p[1] == '$')
17316157Seric 			{
17416157Seric 				/* actual dollar sign.... */
17523111Seric 				(void) strcpy(p, p + 1);
17616157Seric 				continue;
17716157Seric 			}
17816157Seric 
17916157Seric 			/* convert to macro expansion character */
18058050Seric 			*p = MACROEXPAND;
18116157Seric 		}
18216157Seric 
18316157Seric 		/* interpret this line */
18464718Seric 		errno = 0;
18557135Seric 		switch (bp[0])
1863308Seric 		{
1873308Seric 		  case '\0':
1883308Seric 		  case '#':		/* comment */
1893308Seric 			break;
1903308Seric 
1913308Seric 		  case 'R':		/* rewriting rule */
19257135Seric 			for (p = &bp[1]; *p != '\0' && *p != '\t'; p++)
1933308Seric 				continue;
1943308Seric 
1953308Seric 			if (*p == '\0')
1965909Seric 			{
19765821Seric 				syserr("invalid rewrite line \"%s\" (tab expected)", bp);
1985909Seric 				break;
1995909Seric 			}
2005909Seric 
2015909Seric 			/* allocate space for the rule header */
2025909Seric 			if (rwp == NULL)
2035909Seric 			{
2045909Seric 				RewriteRules[ruleset] = rwp =
2055909Seric 					(struct rewrite *) xalloc(sizeof *rwp);
2065909Seric 			}
2073308Seric 			else
2083308Seric 			{
2095909Seric 				rwp->r_next = (struct rewrite *) xalloc(sizeof *rwp);
2105909Seric 				rwp = rwp->r_next;
2115909Seric 			}
2125909Seric 			rwp->r_next = NULL;
2133308Seric 
2145909Seric 			/* expand and save the LHS */
2155909Seric 			*p = '\0';
21657135Seric 			expand(&bp[1], exbuf, &exbuf[sizeof exbuf], e);
21765066Seric 			rwp->r_lhs = prescan(exbuf, '\t', pvpbuf,
21865066Seric 					     sizeof pvpbuf, NULL);
21957589Seric 			nfuzzy = 0;
2205909Seric 			if (rwp->r_lhs != NULL)
22157589Seric 			{
22257589Seric 				register char **ap;
22357589Seric 
2245909Seric 				rwp->r_lhs = copyplist(rwp->r_lhs, TRUE);
22557589Seric 
22657589Seric 				/* count the number of fuzzy matches in LHS */
22757589Seric 				for (ap = rwp->r_lhs; *ap != NULL; ap++)
22857589Seric 				{
22958148Seric 					char *botch;
23058148Seric 
23158148Seric 					botch = NULL;
23258050Seric 					switch (**ap & 0377)
23357589Seric 					{
23457589Seric 					  case MATCHZANY:
23557589Seric 					  case MATCHANY:
23657589Seric 					  case MATCHONE:
23757589Seric 					  case MATCHCLASS:
23857589Seric 					  case MATCHNCLASS:
23957589Seric 						nfuzzy++;
24058148Seric 						break;
24158148Seric 
24258148Seric 					  case MATCHREPL:
24358148Seric 						botch = "$0-$9";
24458148Seric 						break;
24558148Seric 
24658148Seric 					  case CANONNET:
24758148Seric 						botch = "$#";
24858148Seric 						break;
24958148Seric 
25058148Seric 					  case CANONUSER:
25158148Seric 						botch = "$:";
25258148Seric 						break;
25358148Seric 
25458148Seric 					  case CALLSUBR:
25558148Seric 						botch = "$>";
25658148Seric 						break;
25758148Seric 
25858148Seric 					  case CONDIF:
25958148Seric 						botch = "$?";
26058148Seric 						break;
26158148Seric 
26258148Seric 					  case CONDELSE:
26358148Seric 						botch = "$|";
26458148Seric 						break;
26558148Seric 
26658148Seric 					  case CONDFI:
26758148Seric 						botch = "$.";
26858148Seric 						break;
26958148Seric 
27058148Seric 					  case HOSTBEGIN:
27158148Seric 						botch = "$[";
27258148Seric 						break;
27358148Seric 
27458148Seric 					  case HOSTEND:
27558148Seric 						botch = "$]";
27658148Seric 						break;
27758148Seric 
27858148Seric 					  case LOOKUPBEGIN:
27958148Seric 						botch = "$(";
28058148Seric 						break;
28158148Seric 
28258148Seric 					  case LOOKUPEND:
28358148Seric 						botch = "$)";
28458148Seric 						break;
28557589Seric 					}
28658148Seric 					if (botch != NULL)
28758148Seric 						syserr("Inappropriate use of %s on LHS",
28858148Seric 							botch);
28957589Seric 				}
29057589Seric 			}
29156678Seric 			else
29256678Seric 				syserr("R line: null LHS");
2935909Seric 
2945909Seric 			/* expand and save the RHS */
2955909Seric 			while (*++p == '\t')
2965909Seric 				continue;
2977231Seric 			q = p;
2987231Seric 			while (*p != '\0' && *p != '\t')
2997231Seric 				p++;
3007231Seric 			*p = '\0';
30155012Seric 			expand(q, exbuf, &exbuf[sizeof exbuf], e);
30265066Seric 			rwp->r_rhs = prescan(exbuf, '\t', pvpbuf,
30365066Seric 					     sizeof pvpbuf, NULL);
3045909Seric 			if (rwp->r_rhs != NULL)
30557589Seric 			{
30657589Seric 				register char **ap;
30757589Seric 
3085909Seric 				rwp->r_rhs = copyplist(rwp->r_rhs, TRUE);
30957589Seric 
31057589Seric 				/* check no out-of-bounds replacements */
31157589Seric 				nfuzzy += '0';
31257589Seric 				for (ap = rwp->r_rhs; *ap != NULL; ap++)
31357589Seric 				{
31458148Seric 					char *botch;
31558148Seric 
31658148Seric 					botch = NULL;
31758148Seric 					switch (**ap & 0377)
31857589Seric 					{
31958148Seric 					  case MATCHREPL:
32058148Seric 						if ((*ap)[1] <= '0' || (*ap)[1] > nfuzzy)
32158148Seric 						{
32258148Seric 							syserr("replacement $%c out of bounds",
32358148Seric 								(*ap)[1]);
32458148Seric 						}
32558148Seric 						break;
32658148Seric 
32758148Seric 					  case MATCHZANY:
32858148Seric 						botch = "$*";
32958148Seric 						break;
33058148Seric 
33158148Seric 					  case MATCHANY:
33258148Seric 						botch = "$+";
33358148Seric 						break;
33458148Seric 
33558148Seric 					  case MATCHONE:
33658148Seric 						botch = "$-";
33758148Seric 						break;
33858148Seric 
33958148Seric 					  case MATCHCLASS:
34058148Seric 						botch = "$=";
34158148Seric 						break;
34258148Seric 
34358148Seric 					  case MATCHNCLASS:
34458148Seric 						botch = "$~";
34558148Seric 						break;
34657589Seric 					}
34758148Seric 					if (botch != NULL)
34858148Seric 						syserr("Inappropriate use of %s on RHS",
34958148Seric 							botch);
35057589Seric 				}
35157589Seric 			}
35256678Seric 			else
35356678Seric 				syserr("R line: null RHS");
3543308Seric 			break;
3553308Seric 
3564072Seric 		  case 'S':		/* select rewriting set */
35764440Seric 			for (p = &bp[1]; isascii(*p) && isspace(*p); p++)
35864440Seric 				continue;
35964440Seric 			if (!isascii(*p) || !isdigit(*p))
36064440Seric 			{
36164440Seric 				syserr("invalid argument to S line: \"%.20s\"",
36264440Seric 					&bp[1]);
36364440Seric 				break;
36464440Seric 			}
36564440Seric 			ruleset = atoi(p);
3668056Seric 			if (ruleset >= MAXRWSETS || ruleset < 0)
3678056Seric 			{
3689381Seric 				syserr("bad ruleset %d (%d max)", ruleset, MAXRWSETS);
3698056Seric 				ruleset = 0;
3708056Seric 			}
3714072Seric 			rwp = NULL;
3724072Seric 			break;
3734072Seric 
3743308Seric 		  case 'D':		/* macro definition */
37564086Seric 			p = munchstring(&bp[2], NULL);
37664086Seric 			define(bp[1], newstr(p), e);
3773308Seric 			break;
3783308Seric 
3793387Seric 		  case 'H':		/* required header line */
38057135Seric 			(void) chompheader(&bp[1], TRUE, e);
3813387Seric 			break;
3823387Seric 
3834061Seric 		  case 'C':		/* word class */
3844432Seric 			/* scan the list of words and set class for all */
38564121Seric 			expand(&bp[2], exbuf, &exbuf[sizeof exbuf], e);
38664121Seric 			for (p = exbuf; *p != '\0'; )
3874061Seric 			{
3884061Seric 				register char *wd;
3894061Seric 				char delim;
3904061Seric 
39158050Seric 				while (*p != '\0' && isascii(*p) && isspace(*p))
3924061Seric 					p++;
3934061Seric 				wd = p;
39458050Seric 				while (*p != '\0' && !(isascii(*p) && isspace(*p)))
3954061Seric 					p++;
3964061Seric 				delim = *p;
3974061Seric 				*p = '\0';
3984061Seric 				if (wd[0] != '\0')
39957135Seric 					setclass(bp[1], wd);
4004061Seric 				*p = delim;
4014061Seric 			}
4024061Seric 			break;
4034061Seric 
40459272Seric 		  case 'F':		/* word class from file */
40564133Seric 			for (p = &bp[2]; isascii(*p) && isspace(*p); )
40664133Seric 				p++;
40764133Seric 			if (p[0] == '-' && p[1] == 'o')
40864133Seric 			{
40964133Seric 				optional = TRUE;
41064133Seric 				while (*p != '\0' && !(isascii(*p) && isspace(*p)))
41164133Seric 					p++;
41264133Seric 				while (isascii(*p) && isspace(*p))
413*67615Seric 					p++;
41464133Seric 			}
41564133Seric 			else
41664133Seric 				optional = FALSE;
41764133Seric 			file = p;
41864133Seric 			while (*p != '\0' && !(isascii(*p) && isspace(*p)))
41964133Seric 				p++;
42059272Seric 			if (*p == '\0')
42159272Seric 				p = "%s";
42259272Seric 			else
42359272Seric 			{
42459272Seric 				*p = '\0';
42559272Seric 				while (isascii(*++p) && isspace(*p))
42659272Seric 					continue;
42759272Seric 			}
42864133Seric 			fileclass(bp[1], file, p, safe, optional);
42959272Seric 			break;
43059272Seric 
43159156Seric #ifdef XLA
43259156Seric 		  case 'L':		/* extended load average description */
43359156Seric 			xla_init(&bp[1]);
43459156Seric 			break;
43559156Seric #endif
43659156Seric 
4374096Seric 		  case 'M':		/* define mailer */
43857135Seric 			makemailer(&bp[1]);
4394096Seric 			break;
4404096Seric 
4418252Seric 		  case 'O':		/* set option */
44258734Seric 			setoption(bp[1], &bp[2], safe, FALSE, e);
4438252Seric 			break;
4448252Seric 
4458252Seric 		  case 'P':		/* set precedence */
4468252Seric 			if (NumPriorities >= MAXPRIORITIES)
4478252Seric 			{
4488547Seric 				toomany('P', MAXPRIORITIES);
4498252Seric 				break;
4508252Seric 			}
45157135Seric 			for (p = &bp[1]; *p != '\0' && *p != '=' && *p != '\t'; p++)
4528252Seric 				continue;
4538252Seric 			if (*p == '\0')
4548252Seric 				goto badline;
4558252Seric 			*p = '\0';
45657135Seric 			Priorities[NumPriorities].pri_name = newstr(&bp[1]);
4578252Seric 			Priorities[NumPriorities].pri_val = atoi(++p);
4588252Seric 			NumPriorities++;
4598252Seric 			break;
4608252Seric 
4618547Seric 		  case 'T':		/* trusted user(s) */
46258161Seric 			/* this option is obsolete, but will be ignored */
4638547Seric 			break;
4648547Seric 
46552645Seric 		  case 'V':		/* configuration syntax version */
46664440Seric 			for (p = &bp[1]; isascii(*p) && isspace(*p); p++)
46764440Seric 				continue;
46864440Seric 			if (!isascii(*p) || !isdigit(*p))
46964440Seric 			{
47064440Seric 				syserr("invalid argument to V line: \"%.20s\"",
47164440Seric 					&bp[1]);
47264440Seric 				break;
47364440Seric 			}
47464718Seric 			ConfigLevel = strtol(p, &ep, 10);
47564279Seric 			if (ConfigLevel >= 5)
47664279Seric 			{
47764279Seric 				/* level 5 configs have short name in $w */
47864279Seric 				p = macvalue('w', e);
47964279Seric 				if (p != NULL && (p = strchr(p, '.')) != NULL)
48064279Seric 					*p = '\0';
48164279Seric 			}
48264718Seric 			if (*ep++ == '/')
48364718Seric 			{
48464718Seric 				/* extract vendor code */
48564718Seric 				for (p = ep; isascii(*p) && isalpha(*p); )
48664718Seric 					p++;
48764718Seric 				*p = '\0';
48864718Seric 
48964718Seric 				if (!setvendor(ep))
49064718Seric 					syserr("invalid V line vendor code: \"%s\"",
49164718Seric 						ep);
49264718Seric 			}
49352645Seric 			break;
49452645Seric 
49553654Seric 		  case 'K':
49657135Seric 			makemapentry(&bp[1]);
49753654Seric 			break;
49853654Seric 
4993308Seric 		  default:
5004061Seric 		  badline:
50157135Seric 			syserr("unknown control line \"%s\"", bp);
5023308Seric 		}
50357135Seric 		if (bp != buf)
50457135Seric 			free(bp);
5053308Seric 	}
50652637Seric 	if (ferror(cf))
50752637Seric 	{
50852647Seric 		syserr("I/O read error", cfname);
50952637Seric 		exit(EX_OSFILE);
51052637Seric 	}
51152637Seric 	fclose(cf);
5129381Seric 	FileName = NULL;
51356836Seric 
51457076Seric 	if (stab("host", ST_MAP, ST_FIND) == NULL)
51557076Seric 	{
51657076Seric 		/* user didn't initialize: set up host map */
51757076Seric 		strcpy(buf, "host host");
51866334Seric #if NAMED_BIND
51957076Seric 		if (ConfigLevel >= 2)
52057076Seric 			strcat(buf, " -a.");
52164947Seric #endif
52257076Seric 		makemapentry(buf);
52357076Seric 	}
5244096Seric }
5254096Seric /*
5268547Seric **  TOOMANY -- signal too many of some option
5278547Seric **
5288547Seric **	Parameters:
5298547Seric **		id -- the id of the error line
5308547Seric **		maxcnt -- the maximum possible values
5318547Seric **
5328547Seric **	Returns:
5338547Seric **		none.
5348547Seric **
5358547Seric **	Side Effects:
5368547Seric **		gives a syserr.
5378547Seric */
5388547Seric 
5398547Seric toomany(id, maxcnt)
5408547Seric 	char id;
5418547Seric 	int maxcnt;
5428547Seric {
5439381Seric 	syserr("too many %c lines, %d max", id, maxcnt);
5448547Seric }
5458547Seric /*
5464432Seric **  FILECLASS -- read members of a class from a file
5474432Seric **
5484432Seric **	Parameters:
5494432Seric **		class -- class to define.
5504432Seric **		filename -- name of file to read.
5514432Seric **		fmt -- scanf string to use for match.
55264133Seric **		safe -- if set, this is a safe read.
55364133Seric **		optional -- if set, it is not an error for the file to
55464133Seric **			not exist.
5554432Seric **
5564432Seric **	Returns:
5574432Seric **		none
5584432Seric **
5594432Seric **	Side Effects:
5604432Seric **
5614432Seric **		puts all lines in filename that match a scanf into
5624432Seric **			the named class.
5634432Seric */
5644432Seric 
56564133Seric fileclass(class, filename, fmt, safe, optional)
5664432Seric 	int class;
5674432Seric 	char *filename;
5684432Seric 	char *fmt;
56954973Seric 	bool safe;
57064133Seric 	bool optional;
5714432Seric {
57225808Seric 	FILE *f;
57354973Seric 	struct stat stbuf;
5744432Seric 	char buf[MAXLINE];
5754432Seric 
57666101Seric 	if (tTd(37, 2))
57766101Seric 		printf("fileclass(%s, fmt=%s)\n", filename, fmt);
57866101Seric 
57966031Seric 	if (filename[0] == '|')
58066031Seric 	{
58166031Seric 		syserr("fileclass: pipes (F%c%s) not supported due to security problems",
58266031Seric 			class, filename);
58366031Seric 		return;
58466031Seric 	}
58554973Seric 	if (stat(filename, &stbuf) < 0)
58654973Seric 	{
58766101Seric 		if (tTd(37, 2))
58866101Seric 			printf("  cannot stat (%s)\n", errstring(errno));
58964133Seric 		if (!optional)
59064133Seric 			syserr("fileclass: cannot stat %s", filename);
59154973Seric 		return;
59254973Seric 	}
59354973Seric 	if (!S_ISREG(stbuf.st_mode))
59454973Seric 	{
59554973Seric 		syserr("fileclass: %s not a regular file", filename);
59654973Seric 		return;
59754973Seric 	}
59854973Seric 	if (!safe && access(filename, R_OK) < 0)
59954973Seric 	{
60054973Seric 		syserr("fileclass: access denied on %s", filename);
60154973Seric 		return;
60254973Seric 	}
60354973Seric 	f = fopen(filename, "r");
6044432Seric 	if (f == NULL)
6054432Seric 	{
60654973Seric 		syserr("fileclass: cannot open %s", filename);
6074432Seric 		return;
6084432Seric 	}
6094432Seric 
6104432Seric 	while (fgets(buf, sizeof buf, f) != NULL)
6114432Seric 	{
6124432Seric 		register STAB *s;
61325808Seric 		register char *p;
61425808Seric # ifdef SCANF
6154432Seric 		char wordbuf[MAXNAME+1];
6164432Seric 
6174432Seric 		if (sscanf(buf, fmt, wordbuf) != 1)
6184432Seric 			continue;
61925808Seric 		p = wordbuf;
62056795Seric # else /* SCANF */
62125808Seric 		p = buf;
62256795Seric # endif /* SCANF */
62325808Seric 
62425808Seric 		/*
62525808Seric 		**  Break up the match into words.
62625808Seric 		*/
62725808Seric 
62825808Seric 		while (*p != '\0')
62925808Seric 		{
63025808Seric 			register char *q;
63125808Seric 
63225808Seric 			/* strip leading spaces */
63358050Seric 			while (isascii(*p) && isspace(*p))
63425808Seric 				p++;
63525808Seric 			if (*p == '\0')
63625808Seric 				break;
63725808Seric 
63825808Seric 			/* find the end of the word */
63925808Seric 			q = p;
64058050Seric 			while (*p != '\0' && !(isascii(*p) && isspace(*p)))
64125808Seric 				p++;
64225808Seric 			if (*p != '\0')
64325808Seric 				*p++ = '\0';
64425808Seric 
64525808Seric 			/* enter the word in the symbol table */
64666101Seric 			setclass(class, q);
64725808Seric 		}
6484432Seric 	}
6494432Seric 
65054973Seric 	(void) fclose(f);
6514432Seric }
6524432Seric /*
6534096Seric **  MAKEMAILER -- define a new mailer.
6544096Seric **
6554096Seric **	Parameters:
65610327Seric **		line -- description of mailer.  This is in labeled
65710327Seric **			fields.  The fields are:
65810327Seric **			   P -- the path to the mailer
65910327Seric **			   F -- the flags associated with the mailer
66010327Seric **			   A -- the argv for this mailer
66110327Seric **			   S -- the sender rewriting set
66210327Seric **			   R -- the recipient rewriting set
66310327Seric **			   E -- the eol string
66410327Seric **			The first word is the canonical name of the mailer.
6654096Seric **
6664096Seric **	Returns:
6674096Seric **		none.
6684096Seric **
6694096Seric **	Side Effects:
6704096Seric **		enters the mailer into the mailer table.
6714096Seric */
6723308Seric 
67321066Seric makemailer(line)
6744096Seric 	char *line;
6754096Seric {
6764096Seric 	register char *p;
6778067Seric 	register struct mailer *m;
6788067Seric 	register STAB *s;
6798067Seric 	int i;
68010327Seric 	char fcode;
68158020Seric 	auto char *endp;
6824096Seric 	extern int NextMailer;
68310327Seric 	extern char **makeargv();
68410327Seric 	extern char *munchstring();
68510701Seric 	extern long atol();
6864096Seric 
68710327Seric 	/* allocate a mailer and set up defaults */
68810327Seric 	m = (struct mailer *) xalloc(sizeof *m);
68910327Seric 	bzero((char *) m, sizeof *m);
69010327Seric 	m->m_eol = "\n";
69167604Seric 	m->m_uid = m->m_gid = 0;
69210327Seric 
69310327Seric 	/* collect the mailer name */
69458050Seric 	for (p = line; *p != '\0' && *p != ',' && !(isascii(*p) && isspace(*p)); p++)
69510327Seric 		continue;
69610327Seric 	if (*p != '\0')
69710327Seric 		*p++ = '\0';
69810327Seric 	m->m_name = newstr(line);
69910327Seric 
70010327Seric 	/* now scan through and assign info from the fields */
70110327Seric 	while (*p != '\0')
70210327Seric 	{
70358333Seric 		auto char *delimptr;
70458333Seric 
70558050Seric 		while (*p != '\0' && (*p == ',' || (isascii(*p) && isspace(*p))))
70610327Seric 			p++;
70710327Seric 
70810327Seric 		/* p now points to field code */
70910327Seric 		fcode = *p;
71010327Seric 		while (*p != '\0' && *p != '=' && *p != ',')
71110327Seric 			p++;
71210327Seric 		if (*p++ != '=')
71310327Seric 		{
71452637Seric 			syserr("mailer %s: `=' expected", m->m_name);
71510327Seric 			return;
71610327Seric 		}
71758050Seric 		while (isascii(*p) && isspace(*p))
71810327Seric 			p++;
71910327Seric 
72010327Seric 		/* p now points to the field body */
72158333Seric 		p = munchstring(p, &delimptr);
72210327Seric 
72310327Seric 		/* install the field into the mailer struct */
72410327Seric 		switch (fcode)
72510327Seric 		{
72610327Seric 		  case 'P':		/* pathname */
72710327Seric 			m->m_mailer = newstr(p);
72810327Seric 			break;
72910327Seric 
73010327Seric 		  case 'F':		/* flags */
73110687Seric 			for (; *p != '\0'; p++)
73258050Seric 				if (!(isascii(*p) && isspace(*p)))
73352637Seric 					setbitn(*p, m->m_flags);
73410327Seric 			break;
73510327Seric 
73610327Seric 		  case 'S':		/* sender rewriting ruleset */
73710327Seric 		  case 'R':		/* recipient rewriting ruleset */
73858020Seric 			i = strtol(p, &endp, 10);
73910327Seric 			if (i < 0 || i >= MAXRWSETS)
74010327Seric 			{
74110327Seric 				syserr("invalid rewrite set, %d max", MAXRWSETS);
74210327Seric 				return;
74310327Seric 			}
74410327Seric 			if (fcode == 'S')
74558020Seric 				m->m_sh_rwset = m->m_se_rwset = i;
74610327Seric 			else
74758020Seric 				m->m_rh_rwset = m->m_re_rwset = i;
74858020Seric 
74958020Seric 			p = endp;
75059985Seric 			if (*p++ == '/')
75158020Seric 			{
75258020Seric 				i = strtol(p, NULL, 10);
75358020Seric 				if (i < 0 || i >= MAXRWSETS)
75458020Seric 				{
75558020Seric 					syserr("invalid rewrite set, %d max",
75658020Seric 						MAXRWSETS);
75758020Seric 					return;
75858020Seric 				}
75958020Seric 				if (fcode == 'S')
76058020Seric 					m->m_sh_rwset = i;
76158020Seric 				else
76258020Seric 					m->m_rh_rwset = i;
76358020Seric 			}
76410327Seric 			break;
76510327Seric 
76610327Seric 		  case 'E':		/* end of line string */
76710327Seric 			m->m_eol = newstr(p);
76810327Seric 			break;
76910327Seric 
77010327Seric 		  case 'A':		/* argument vector */
77110327Seric 			m->m_argv = makeargv(p);
77210327Seric 			break;
77310701Seric 
77410701Seric 		  case 'M':		/* maximum message size */
77510701Seric 			m->m_maxsize = atol(p);
77610701Seric 			break;
77752106Seric 
77852106Seric 		  case 'L':		/* maximum line length */
77952106Seric 			m->m_linelimit = atoi(p);
78052106Seric 			break;
78158935Seric 
78258935Seric 		  case 'D':		/* working directory */
78358935Seric 			m->m_execdir = newstr(p);
78458935Seric 			break;
78567604Seric 
78667604Seric 		  case 'U':		/* user id */
78767604Seric 			if (isascii(*p) && !isdigit(*p))
78867604Seric 			{
78967604Seric 				char *q = p;
79067604Seric 				struct passwd *pw;
79167604Seric 
79267604Seric 				while (isascii(*p) && isalnum(*p))
79367604Seric 					p++;
79467604Seric 				while (isascii(*p) && isspace(*p))
79567604Seric 					*p++ = '\0';
79667604Seric 				if (*p != '\0')
79767604Seric 					*p++ = '\0';
79867604Seric 				pw = getpwnam(q);
79967604Seric 				if (pw == NULL)
80067604Seric 					syserr("readcf: mailer U= flag: unknown user %s", q);
80167604Seric 				else
80267604Seric 				{
80367604Seric 					m->m_uid = pw->pw_uid;
80467604Seric 					m->m_gid = pw->pw_gid;
80567604Seric 				}
80667604Seric 			}
80767604Seric 			else
80867604Seric 			{
80967604Seric 				auto char *q;
81067604Seric 
81167604Seric 				m->m_uid = strtol(p, &q, 0);
81267604Seric 				p = q;
81367604Seric 			}
81467604Seric 			while (isascii(*p) && isspace(*p))
81567604Seric 				p++;
81667604Seric 			if (*p == '\0')
81767604Seric 				break;
81867604Seric 			if (isascii(*p) && !isdigit(*p))
81967604Seric 			{
82067604Seric 				char *q = p;
82167604Seric 				struct group *gr;
82267604Seric 
82367604Seric 				while (isascii(*p) && isalnum(*p))
82467604Seric 					p++;
82567604Seric 				*p++ = '\0';
82667604Seric 				gr = getgrnam(q);
82767604Seric 				if (gr == NULL)
82867604Seric 					syserr("readcf: mailer U= flag: unknown group %s", q);
82967604Seric 				else
83067604Seric 					m->m_gid = gr->gr_gid;
83167604Seric 			}
83267604Seric 			else
83367604Seric 			{
83467604Seric 				m->m_gid = strtol(p, NULL, 0);
83567604Seric 			}
83667604Seric 			break;
83710327Seric 		}
83810327Seric 
83958333Seric 		p = delimptr;
84010327Seric 	}
84110327Seric 
84252106Seric 	/* do some heuristic cleanup for back compatibility */
84352106Seric 	if (bitnset(M_LIMITS, m->m_flags))
84452106Seric 	{
84552106Seric 		if (m->m_linelimit == 0)
84652106Seric 			m->m_linelimit = SMTPLINELIM;
84755418Seric 		if (ConfigLevel < 2)
84852106Seric 			setbitn(M_7BITS, m->m_flags);
84952106Seric 	}
85052106Seric 
85158321Seric 	/* do some rationality checking */
85258321Seric 	if (m->m_argv == NULL)
85358321Seric 	{
85458321Seric 		syserr("M%s: A= argument required", m->m_name);
85558321Seric 		return;
85658321Seric 	}
85758321Seric 	if (m->m_mailer == NULL)
85858321Seric 	{
85958321Seric 		syserr("M%s: P= argument required", m->m_name);
86058321Seric 		return;
86158321Seric 	}
86258321Seric 
8634096Seric 	if (NextMailer >= MAXMAILERS)
8644096Seric 	{
8659381Seric 		syserr("too many mailers defined (%d max)", MAXMAILERS);
8664096Seric 		return;
8674096Seric 	}
86857402Seric 
86910327Seric 	s = stab(m->m_name, ST_MAILER, ST_ENTER);
87057402Seric 	if (s->s_mailer != NULL)
87157402Seric 	{
87257402Seric 		i = s->s_mailer->m_mno;
87357402Seric 		free(s->s_mailer);
87457402Seric 	}
87557402Seric 	else
87657402Seric 	{
87757402Seric 		i = NextMailer++;
87857402Seric 	}
87957402Seric 	Mailer[i] = s->s_mailer = m;
88057454Seric 	m->m_mno = i;
88110327Seric }
88210327Seric /*
88310327Seric **  MUNCHSTRING -- translate a string into internal form.
88410327Seric **
88510327Seric **	Parameters:
88610327Seric **		p -- the string to munch.
88758333Seric **		delimptr -- if non-NULL, set to the pointer of the
88858333Seric **			field delimiter character.
88910327Seric **
89010327Seric **	Returns:
89110327Seric **		the munched string.
89210327Seric */
8934096Seric 
89410327Seric char *
89558333Seric munchstring(p, delimptr)
89610327Seric 	register char *p;
89758333Seric 	char **delimptr;
89810327Seric {
89910327Seric 	register char *q;
90010327Seric 	bool backslash = FALSE;
90110327Seric 	bool quotemode = FALSE;
90210327Seric 	static char buf[MAXLINE];
9034096Seric 
90410327Seric 	for (q = buf; *p != '\0'; p++)
9054096Seric 	{
90610327Seric 		if (backslash)
90710327Seric 		{
90810327Seric 			/* everything is roughly literal */
90910357Seric 			backslash = FALSE;
91010327Seric 			switch (*p)
91110327Seric 			{
91210327Seric 			  case 'r':		/* carriage return */
91310327Seric 				*q++ = '\r';
91410327Seric 				continue;
91510327Seric 
91610327Seric 			  case 'n':		/* newline */
91710327Seric 				*q++ = '\n';
91810327Seric 				continue;
91910327Seric 
92010327Seric 			  case 'f':		/* form feed */
92110327Seric 				*q++ = '\f';
92210327Seric 				continue;
92310327Seric 
92410327Seric 			  case 'b':		/* backspace */
92510327Seric 				*q++ = '\b';
92610327Seric 				continue;
92710327Seric 			}
92810327Seric 			*q++ = *p;
92910327Seric 		}
93010327Seric 		else
93110327Seric 		{
93210327Seric 			if (*p == '\\')
93310327Seric 				backslash = TRUE;
93410327Seric 			else if (*p == '"')
93510327Seric 				quotemode = !quotemode;
93610327Seric 			else if (quotemode || *p != ',')
93710327Seric 				*q++ = *p;
93810327Seric 			else
93910327Seric 				break;
94010327Seric 		}
9414096Seric 	}
9424096Seric 
94358333Seric 	if (delimptr != NULL)
94458333Seric 		*delimptr = p;
94510327Seric 	*q++ = '\0';
94610327Seric 	return (buf);
94710327Seric }
94810327Seric /*
94910327Seric **  MAKEARGV -- break up a string into words
95010327Seric **
95110327Seric **	Parameters:
95210327Seric **		p -- the string to break up.
95310327Seric **
95410327Seric **	Returns:
95510327Seric **		a char **argv (dynamically allocated)
95610327Seric **
95710327Seric **	Side Effects:
95810327Seric **		munges p.
95910327Seric */
9604096Seric 
96110327Seric char **
96210327Seric makeargv(p)
96310327Seric 	register char *p;
96410327Seric {
96510327Seric 	char *q;
96610327Seric 	int i;
96710327Seric 	char **avp;
96810327Seric 	char *argv[MAXPV + 1];
96910327Seric 
97010327Seric 	/* take apart the words */
97110327Seric 	i = 0;
97210327Seric 	while (*p != '\0' && i < MAXPV)
9734096Seric 	{
97410327Seric 		q = p;
97558050Seric 		while (*p != '\0' && !(isascii(*p) && isspace(*p)))
97610327Seric 			p++;
97758050Seric 		while (isascii(*p) && isspace(*p))
97810327Seric 			*p++ = '\0';
97910327Seric 		argv[i++] = newstr(q);
9804096Seric 	}
98110327Seric 	argv[i++] = NULL;
9824096Seric 
98310327Seric 	/* now make a copy of the argv */
98410327Seric 	avp = (char **) xalloc(sizeof *avp * i);
98516893Seric 	bcopy((char *) argv, (char *) avp, sizeof *avp * i);
98610327Seric 
98710327Seric 	return (avp);
9883308Seric }
9893308Seric /*
9903308Seric **  PRINTRULES -- print rewrite rules (for debugging)
9913308Seric **
9923308Seric **	Parameters:
9933308Seric **		none.
9943308Seric **
9953308Seric **	Returns:
9963308Seric **		none.
9973308Seric **
9983308Seric **	Side Effects:
9993308Seric **		prints rewrite rules.
10003308Seric */
10013308Seric 
10023308Seric printrules()
10033308Seric {
10043308Seric 	register struct rewrite *rwp;
10054072Seric 	register int ruleset;
10063308Seric 
10074072Seric 	for (ruleset = 0; ruleset < 10; ruleset++)
10083308Seric 	{
10094072Seric 		if (RewriteRules[ruleset] == NULL)
10104072Seric 			continue;
10118067Seric 		printf("\n----Rule Set %d:", ruleset);
10123308Seric 
10134072Seric 		for (rwp = RewriteRules[ruleset]; rwp != NULL; rwp = rwp->r_next)
10143308Seric 		{
10158067Seric 			printf("\nLHS:");
10168067Seric 			printav(rwp->r_lhs);
10178067Seric 			printf("RHS:");
10188067Seric 			printav(rwp->r_rhs);
10193308Seric 		}
10203308Seric 	}
10213308Seric }
10224319Seric 
10234096Seric /*
10248256Seric **  SETOPTION -- set global processing option
10258256Seric **
10268256Seric **	Parameters:
10278256Seric **		opt -- option name.
10288256Seric **		val -- option value (as a text string).
102921755Seric **		safe -- set if this came from a configuration file.
103021755Seric **			Some options (if set from the command line) will
103121755Seric **			reset the user id to avoid security problems.
10328269Seric **		sticky -- if set, don't let other setoptions override
10338269Seric **			this value.
103458734Seric **		e -- the main envelope.
10358256Seric **
10368256Seric **	Returns:
10378256Seric **		none.
10388256Seric **
10398256Seric **	Side Effects:
10408256Seric **		Sets options as implied by the arguments.
10418256Seric */
10428256Seric 
104310687Seric static BITMAP	StickyOpt;		/* set if option is stuck */
10448269Seric 
104557207Seric 
104666334Seric #if NAMED_BIND
104757207Seric 
104857207Seric struct resolverflags
104957207Seric {
105057207Seric 	char	*rf_name;	/* name of the flag */
105157207Seric 	long	rf_bits;	/* bits to set/clear */
105257207Seric } ResolverFlags[] =
105357207Seric {
105457207Seric 	"debug",	RES_DEBUG,
105557207Seric 	"aaonly",	RES_AAONLY,
105657207Seric 	"usevc",	RES_USEVC,
105757207Seric 	"primary",	RES_PRIMARY,
105857207Seric 	"igntc",	RES_IGNTC,
105957207Seric 	"recurse",	RES_RECURSE,
106057207Seric 	"defnames",	RES_DEFNAMES,
106157207Seric 	"stayopen",	RES_STAYOPEN,
106257207Seric 	"dnsrch",	RES_DNSRCH,
106365583Seric 	"true",		0,		/* to avoid error on old syntax */
106457207Seric 	NULL,		0
106557207Seric };
106657207Seric 
106757207Seric #endif
106857207Seric 
106967614Seric /* codes for options that have no short name */
107067614Seric /* NOTE: some of these values may be in the list of "safe" options below */
107167614Seric #define O_BSP		0x80	/* have broken SMTP peers */
107267614Seric #define O_SQBH		0x81	/* sort queue by host */
107367614Seric 
107467614Seric struct optioninfo
107567614Seric {
107667614Seric 	char	*o_name;	/* long name of option */
107767614Seric 	char	o_code;		/* short name of option */
107867614Seric 	bool	o_safe;		/* safe for random people to use */
107967614Seric } OptionTab[] =
108067614Seric {
108167614Seric 	"SevenBitInput",	'7',	TRUE,
108267614Seric 	"AliasFile",		'A',	FALSE,
108367614Seric 	"AliasWait",		'a',	FALSE,
108467614Seric 	"BlankSub",		'B',	FALSE,
108567614Seric 	"MinFreeBlocks",	'b',	TRUE,
108667614Seric 	"CheckpointInterval",	'C',	TRUE,
108767614Seric 	"HoldExpensive",	'c',	FALSE,
108867614Seric 	"AutoRebuildAliases",	'D',	FALSE,
108967614Seric 	"DeliveryMode",		'd',	TRUE,
109067614Seric 	"ErrorHeader",		'E',	FALSE,
109167614Seric 	"ErrorMode",		'e',	TRUE,
109267614Seric 	"TempFileMode",		'F',	FALSE,
109367614Seric 	"SaveFromLine",		'f',	FALSE,
109467614Seric 	"MatchGECOS",		'G',	FALSE,
109567614Seric 	"HelpFile",		'H',	FALSE,
109667614Seric 	"MaxHopCount",		'h',	FALSE,
109767614Seric 	"NameServerOptions",	'I',	FALSE,
109867614Seric 	"IgnoreDots",		'i',	TRUE,
109967614Seric 	"ForwardPath",		'J',	FALSE,
110067614Seric 	"SendMimeErrors",	'j',	TRUE,
110167614Seric 	"ConnectionCacheSize",	'k',	FALSE,
110267614Seric 	"ConnectionCacheTimeout", 'K',	FALSE,
110367614Seric 	"UseErrorsTo",		'l',	FALSE,
110467614Seric 	"LogLevel",		'L',	FALSE,
110567614Seric 	"MeToo",		'm',	TRUE,
110667614Seric 	"CheckAliases",		'n',	FALSE,
110767614Seric 	"OldStyleHeaders",	'o',	TRUE,
110867614Seric 	"DaemonPortOptions",	'O',	FALSE,
110967614Seric 	"PrivacyOptions",	'p',	TRUE,
111067614Seric 	"PostmasterCopy",	'P',	FALSE,
111167614Seric 	"QueueFactor",		'q',	FALSE,
111267614Seric 	"QueueDirectory",	'Q',	FALSE,
111367614Seric 	"DontPruneRoutes",	'R',	FALSE,
111467614Seric 	"ReadTimeout",		'r',	TRUE,
111567614Seric 	"StatusFile",		'S',	FALSE,
111667614Seric 	"SuperSafe",		's',	TRUE,
111767614Seric 	"QueueTimeout",		'T',	FALSE,
111867614Seric 	"TimeZoneSpec",		't',	FALSE,
111967614Seric 	"UserDatabaseSpec",	'U',	FALSE,
112067614Seric 	"DefaultUser",		'u',	FALSE,
112167614Seric 	"FallbackMXhost",	'V',	FALSE,
112267614Seric 	"Verbose",		'v',	TRUE,
112367614Seric 	"TryNullMXList",	'w',	TRUE,
112467614Seric 	"QueueLA",		'x',	FALSE,
112567614Seric 	"RefuseLA",		'X',	FALSE,
112667614Seric 	"RecipientFactor",	'y',	FALSE,
112767614Seric 	"ForkQueueRuns",	'Y',	FALSE,
112867614Seric 	"ClassFactor",		'z',	FALSE,
112967614Seric 	"TimeFactor",		'Z',	FALSE,
113067614Seric 	"BrokenSmtpPeers",	O_BSP,	TRUE,
113167614Seric 	"SortQueueByHost",	O_SQBH,	TRUE,
113267614Seric 	NULL,			'\0',	FALSE,
113367614Seric };
113467614Seric 
113567614Seric 
113667614Seric 
113758734Seric setoption(opt, val, safe, sticky, e)
113867614Seric 	u_char opt;
11398256Seric 	char *val;
114021755Seric 	bool safe;
11418269Seric 	bool sticky;
114258734Seric 	register ENVELOPE *e;
11438256Seric {
114457207Seric 	register char *p;
114567614Seric 	register struct optioninfo *o;
11468265Seric 	extern bool atobool();
114712633Seric 	extern time_t convtime();
114814879Seric 	extern int QueueLA;
114914879Seric 	extern int RefuseLA;
115064718Seric 	extern bool Warn_Q_option;
115117474Seric 	extern bool trusteduser();
11528256Seric 
115367614Seric 	if (opt == ' ')
115467614Seric 	{
115567614Seric 		/* full word options */
115667614Seric 
115767614Seric 		p = strchr(val, '=');
115867614Seric 		if (p == NULL)
115967614Seric 			p = &val[strlen(val)];
116067614Seric 		while (*--p == ' ')
116167614Seric 			continue;
116267614Seric 		while (*++p == ' ')
116367614Seric 			*p = '\0';
116467614Seric 		if (*p == '=')
116567614Seric 			*p++ = '\0';
116667614Seric 		while (*p == ' ')
116767614Seric 			p++;
116867614Seric 		for (o = OptionTab; o->o_name != NULL; o++)
116967614Seric 		{
117067614Seric 			if (strcasecmp(o->o_name, val) == 0)
117167614Seric 				break;
117267614Seric 		}
117367614Seric 		if (o->o_name == NULL)
117467614Seric 			syserr("readcf: unknown option name %s", val);
117567614Seric 		opt = o->o_code;
117667614Seric 		val = p;
117767614Seric 	}
117867614Seric 	else
117967614Seric 	{
118067614Seric 		for (o = OptionTab; o->o_name != NULL; o++)
118167614Seric 		{
118267614Seric 			if (o->o_code == opt)
118367614Seric 				break;
118467614Seric 		}
118567614Seric 	}
118667614Seric 
11878256Seric 	if (tTd(37, 1))
118867614Seric 		printf("setoption %s (0x%x)=%s",
118967614Seric 			o->o_name == NULL ? "<unknown>" : o->o_name,
119067614Seric 			opt, val);
11918256Seric 
11928256Seric 	/*
11938269Seric 	**  See if this option is preset for us.
11948256Seric 	*/
11958256Seric 
119659731Seric 	if (!sticky && bitnset(opt, StickyOpt))
11978269Seric 	{
11989341Seric 		if (tTd(37, 1))
11999341Seric 			printf(" (ignored)\n");
12008269Seric 		return;
12018269Seric 	}
12028269Seric 
120321755Seric 	/*
120421755Seric 	**  Check to see if this option can be specified by this user.
120521755Seric 	*/
120621755Seric 
120763787Seric 	if (!safe && RealUid == 0)
120821755Seric 		safe = TRUE;
120967614Seric 	if (!safe && !o->o_safe)
121021755Seric 	{
121139111Srick 		if (opt != 'M' || (val[0] != 'r' && val[0] != 's'))
121221755Seric 		{
121336582Sbostic 			if (tTd(37, 1))
121436582Sbostic 				printf(" (unsafe)");
121563787Seric 			if (RealUid != geteuid())
121636582Sbostic 			{
121751210Seric 				if (tTd(37, 1))
121851210Seric 					printf("(Resetting uid)");
121963787Seric 				(void) setgid(RealGid);
122063787Seric 				(void) setuid(RealUid);
122136582Sbostic 			}
122221755Seric 		}
122321755Seric 	}
122451210Seric 	if (tTd(37, 1))
122517985Seric 		printf("\n");
12268269Seric 
122767614Seric 	switch (opt & 0xff)
12288256Seric 	{
122959709Seric 	  case '7':		/* force seven-bit input */
123067546Seric 		SevenBitInput = atobool(val);
123152106Seric 		break;
123252106Seric 
123367546Seric 	  case '8':		/* handling of 8-bit input */
123467546Seric 		switch (*val)
123567546Seric 		{
123667547Seric 		  case 'r':		/* reject 8-bit, don't convert MIME */
123767546Seric 			MimeMode = 0;
123867546Seric 			break;
123967546Seric 
124067547Seric 		  case 'm':		/* convert 8-bit, convert MIME */
124167546Seric 			MimeMode = MM_CVTMIME|MM_MIME8BIT;
124267546Seric 			break;
124367546Seric 
124467547Seric 		  case 'j':		/* "just send 8" */
124567546Seric 			MimeMode = MM_PASS8BIT;
124667546Seric 			break;
124767546Seric 
124867546Seric 		  case 'p':		/* pass 8 bit, convert MIME */
124967546Seric 			MimeMode = MM_PASS8BIT|MM_CVTMIME;
125067546Seric 			break;
125167546Seric 
125267546Seric 		  case 's':		/* strict adherence */
125367546Seric 			MimeMode = MM_CVTMIME;
125467546Seric 			break;
125567546Seric 
125667547Seric 		  case 'a':		/* encode 8 bit if available */
125767546Seric 			MimeMode = MM_MIME8BIT|MM_PASS8BIT|MM_CVTMIME;
125867546Seric 			break;
125967546Seric 
126067547Seric 		  case 'c':		/* convert 8 bit to MIME, never 7 bit */
126167547Seric 			MimeMode = MM_MIME8BIT;
126267547Seric 			break;
126367547Seric 
126467546Seric 		  default:
126567546Seric 			syserr("Unknown 8-bit mode %c", *val);
126667546Seric 			exit(EX_USAGE);
126767546Seric 		}
126867546Seric 		break;
126967546Seric 
12708256Seric 	  case 'A':		/* set default alias file */
12719381Seric 		if (val[0] == '\0')
127259672Seric 			setalias("aliases");
12739381Seric 		else
127459672Seric 			setalias(val);
12758256Seric 		break;
12768256Seric 
127717474Seric 	  case 'a':		/* look N minutes for "@:@" in alias file */
127817474Seric 		if (val[0] == '\0')
127964796Seric 			SafeAlias = 5 * 60;		/* five minutes */
128017474Seric 		else
128164796Seric 			SafeAlias = convtime(val, 'm');
128217474Seric 		break;
128317474Seric 
128416843Seric 	  case 'B':		/* substitution for blank character */
128516843Seric 		SpaceSub = val[0];
128616843Seric 		if (SpaceSub == '\0')
128716843Seric 			SpaceSub = ' ';
128816843Seric 		break;
128916843Seric 
129059283Seric 	  case 'b':		/* min blocks free on queue fs/max msg size */
129159283Seric 		p = strchr(val, '/');
129259283Seric 		if (p != NULL)
129359283Seric 		{
129459283Seric 			*p++ = '\0';
129559283Seric 			MaxMessageSize = atol(p);
129659283Seric 		}
129758082Seric 		MinBlocksFree = atol(val);
129858082Seric 		break;
129958082Seric 
13009284Seric 	  case 'c':		/* don't connect to "expensive" mailers */
13019381Seric 		NoConnect = atobool(val);
13029284Seric 		break;
13039284Seric 
130451305Seric 	  case 'C':		/* checkpoint every N addresses */
130551305Seric 		CheckpointInterval = atoi(val);
130624944Seric 		break;
130724944Seric 
13089284Seric 	  case 'd':		/* delivery mode */
13099284Seric 		switch (*val)
13108269Seric 		{
13119284Seric 		  case '\0':
131258734Seric 			e->e_sendmode = SM_DELIVER;
13138269Seric 			break;
13148269Seric 
131510755Seric 		  case SM_QUEUE:	/* queue only */
131610755Seric #ifndef QUEUE
131710755Seric 			syserr("need QUEUE to set -odqueue");
131856795Seric #endif /* QUEUE */
131910755Seric 			/* fall through..... */
132010755Seric 
13219284Seric 		  case SM_DELIVER:	/* do everything */
13229284Seric 		  case SM_FORK:		/* fork after verification */
132358734Seric 			e->e_sendmode = *val;
13248269Seric 			break;
13258269Seric 
13268269Seric 		  default:
13279284Seric 			syserr("Unknown delivery mode %c", *val);
13288269Seric 			exit(EX_USAGE);
13298269Seric 		}
13308269Seric 		break;
13318269Seric 
13329146Seric 	  case 'D':		/* rebuild alias database as needed */
13339381Seric 		AutoRebuild = atobool(val);
13349146Seric 		break;
13359146Seric 
133655372Seric 	  case 'E':		/* error message header/header file */
133755379Seric 		if (*val != '\0')
133855379Seric 			ErrMsgFile = newstr(val);
133955372Seric 		break;
134055372Seric 
13418269Seric 	  case 'e':		/* set error processing mode */
13428269Seric 		switch (*val)
13438269Seric 		{
13449381Seric 		  case EM_QUIET:	/* be silent about it */
13459381Seric 		  case EM_MAIL:		/* mail back */
13469381Seric 		  case EM_BERKNET:	/* do berknet error processing */
13479381Seric 		  case EM_WRITE:	/* write back (or mail) */
13489381Seric 		  case EM_PRINT:	/* print errors normally (default) */
134958734Seric 			e->e_errormode = *val;
13508269Seric 			break;
13518269Seric 		}
13528269Seric 		break;
13538269Seric 
13549049Seric 	  case 'F':		/* file mode */
135517975Seric 		FileMode = atooct(val) & 0777;
13569049Seric 		break;
13579049Seric 
13588269Seric 	  case 'f':		/* save Unix-style From lines on front */
13599381Seric 		SaveFrom = atobool(val);
13608269Seric 		break;
13618269Seric 
136253735Seric 	  case 'G':		/* match recipients against GECOS field */
136353735Seric 		MatchGecos = atobool(val);
136453735Seric 		break;
136553735Seric 
13668256Seric 	  case 'g':		/* default gid */
136764133Seric 		if (isascii(*val) && isdigit(*val))
136864133Seric 			DefGid = atoi(val);
136964133Seric 		else
137064133Seric 		{
137164133Seric 			register struct group *gr;
137264133Seric 
137364133Seric 			DefGid = -1;
137464133Seric 			gr = getgrnam(val);
137564133Seric 			if (gr == NULL)
137664133Seric 				syserr("readcf: option g: unknown group %s", val);
137764133Seric 			else
137864133Seric 				DefGid = gr->gr_gid;
137964133Seric 		}
13808256Seric 		break;
13818256Seric 
13828256Seric 	  case 'H':		/* help file */
13839381Seric 		if (val[0] == '\0')
13848269Seric 			HelpFile = "sendmail.hf";
13859381Seric 		else
13869381Seric 			HelpFile = newstr(val);
13878256Seric 		break;
13888256Seric 
138951305Seric 	  case 'h':		/* maximum hop count */
139051305Seric 		MaxHopCount = atoi(val);
139151305Seric 		break;
139251305Seric 
139335651Seric 	  case 'I':		/* use internet domain name server */
139466334Seric #if NAMED_BIND
139557207Seric 		UseNameServer = TRUE;
139657207Seric 		for (p = val; *p != 0; )
139757207Seric 		{
139857207Seric 			bool clearmode;
139957207Seric 			char *q;
140057207Seric 			struct resolverflags *rfp;
140157207Seric 
140257207Seric 			while (*p == ' ')
140357207Seric 				p++;
140457207Seric 			if (*p == '\0')
140557207Seric 				break;
140657207Seric 			clearmode = FALSE;
140757207Seric 			if (*p == '-')
140857207Seric 				clearmode = TRUE;
140957207Seric 			else if (*p != '+')
141057207Seric 				p--;
141157207Seric 			p++;
141257207Seric 			q = p;
141358050Seric 			while (*p != '\0' && !(isascii(*p) && isspace(*p)))
141457207Seric 				p++;
141557207Seric 			if (*p != '\0')
141657207Seric 				*p++ = '\0';
141757207Seric 			for (rfp = ResolverFlags; rfp->rf_name != NULL; rfp++)
141857207Seric 			{
141957207Seric 				if (strcasecmp(q, rfp->rf_name) == 0)
142057207Seric 					break;
142157207Seric 			}
142264923Seric 			if (rfp->rf_name == NULL)
142364923Seric 				syserr("readcf: I option value %s unrecognized", q);
142464923Seric 			else if (clearmode)
142557207Seric 				_res.options &= ~rfp->rf_bits;
142657207Seric 			else
142757207Seric 				_res.options |= rfp->rf_bits;
142857207Seric 		}
142957207Seric 		if (tTd(8, 2))
143057207Seric 			printf("_res.options = %x\n", _res.options);
143157207Seric #else
143257207Seric 		usrerr("name server (I option) specified but BIND not compiled in");
143357207Seric #endif
143435651Seric 		break;
143535651Seric 
14368269Seric 	  case 'i':		/* ignore dot lines in message */
14379381Seric 		IgnrDot = atobool(val);
14388269Seric 		break;
14398269Seric 
144059730Seric 	  case 'j':		/* send errors in MIME (RFC 1341) format */
144159730Seric 		SendMIMEErrors = atobool(val);
144259730Seric 		break;
144359730Seric 
144457136Seric 	  case 'J':		/* .forward search path */
144557136Seric 		ForwardPath = newstr(val);
144657136Seric 		break;
144757136Seric 
144854967Seric 	  case 'k':		/* connection cache size */
144954967Seric 		MaxMciCache = atoi(val);
145056215Seric 		if (MaxMciCache < 0)
145156215Seric 			MaxMciCache = 0;
145254967Seric 		break;
145354967Seric 
145454967Seric 	  case 'K':		/* connection cache timeout */
145558796Seric 		MciCacheTimeout = convtime(val, 'm');
145654967Seric 		break;
145754967Seric 
145861104Seric 	  case 'l':		/* use Errors-To: header */
145961104Seric 		UseErrorsTo = atobool(val);
146061104Seric 		break;
146161104Seric 
14628256Seric 	  case 'L':		/* log level */
146364140Seric 		if (safe || LogLevel < atoi(val))
146464140Seric 			LogLevel = atoi(val);
14658256Seric 		break;
14668256Seric 
14678269Seric 	  case 'M':		/* define macro */
14689381Seric 		define(val[0], newstr(&val[1]), CurEnv);
146916878Seric 		sticky = FALSE;
14708269Seric 		break;
14718269Seric 
14728269Seric 	  case 'm':		/* send to me too */
14739381Seric 		MeToo = atobool(val);
14748269Seric 		break;
14758269Seric 
147625820Seric 	  case 'n':		/* validate RHS in newaliases */
147725820Seric 		CheckAliases = atobool(val);
147825820Seric 		break;
147925820Seric 
148061104Seric 	    /* 'N' available -- was "net name" */
148161104Seric 
148258851Seric 	  case 'O':		/* daemon options */
148358851Seric 		setdaemonoptions(val);
148458851Seric 		break;
148558851Seric 
14868269Seric 	  case 'o':		/* assume old style headers */
14879381Seric 		if (atobool(val))
14889341Seric 			CurEnv->e_flags |= EF_OLDSTYLE;
14899341Seric 		else
14909341Seric 			CurEnv->e_flags &= ~EF_OLDSTYLE;
14918269Seric 		break;
14928269Seric 
149358082Seric 	  case 'p':		/* select privacy level */
149458082Seric 		p = val;
149558082Seric 		for (;;)
149658082Seric 		{
149758082Seric 			register struct prival *pv;
149858082Seric 			extern struct prival PrivacyValues[];
149958082Seric 
150058082Seric 			while (isascii(*p) && (isspace(*p) || ispunct(*p)))
150158082Seric 				p++;
150258082Seric 			if (*p == '\0')
150358082Seric 				break;
150458082Seric 			val = p;
150558082Seric 			while (isascii(*p) && isalnum(*p))
150658082Seric 				p++;
150758082Seric 			if (*p != '\0')
150858082Seric 				*p++ = '\0';
150958082Seric 
151058082Seric 			for (pv = PrivacyValues; pv->pv_name != NULL; pv++)
151158082Seric 			{
151258082Seric 				if (strcasecmp(val, pv->pv_name) == 0)
151358082Seric 					break;
151458082Seric 			}
151558886Seric 			if (pv->pv_name == NULL)
151658886Seric 				syserr("readcf: Op line: %s unrecognized", val);
151758082Seric 			PrivacyFlags |= pv->pv_flag;
151858082Seric 		}
151958082Seric 		break;
152058082Seric 
152124944Seric 	  case 'P':		/* postmaster copy address for returned mail */
152224944Seric 		PostMasterCopy = newstr(val);
152324944Seric 		break;
152424944Seric 
152524944Seric 	  case 'q':		/* slope of queue only function */
152624944Seric 		QueueFactor = atoi(val);
152724944Seric 		break;
152824944Seric 
15298256Seric 	  case 'Q':		/* queue directory */
15309381Seric 		if (val[0] == '\0')
15318269Seric 			QueueDir = "mqueue";
15329381Seric 		else
15339381Seric 			QueueDir = newstr(val);
153458789Seric 		if (RealUid != 0 && !safe)
153564718Seric 			Warn_Q_option = TRUE;
15368256Seric 		break;
15378256Seric 
153858148Seric 	  case 'R':		/* don't prune routes */
153958148Seric 		DontPruneRoutes = atobool(val);
154058148Seric 		break;
154158148Seric 
15428256Seric 	  case 'r':		/* read timeout */
154358112Seric 		settimeouts(val);
15448256Seric 		break;
15458256Seric 
15468256Seric 	  case 'S':		/* status file */
15479381Seric 		if (val[0] == '\0')
15488269Seric 			StatFile = "sendmail.st";
15499381Seric 		else
15509381Seric 			StatFile = newstr(val);
15518256Seric 		break;
15528256Seric 
15538265Seric 	  case 's':		/* be super safe, even if expensive */
15549381Seric 		SuperSafe = atobool(val);
15558256Seric 		break;
15568256Seric 
15578256Seric 	  case 'T':		/* queue timeout */
155858737Seric 		p = strchr(val, '/');
155958737Seric 		if (p != NULL)
156058737Seric 		{
156158737Seric 			*p++ = '\0';
156258796Seric 			TimeOuts.to_q_warning = convtime(p, 'd');
156358737Seric 		}
156458796Seric 		TimeOuts.to_q_return = convtime(val, 'h');
156554967Seric 		break;
15668256Seric 
15678265Seric 	  case 't':		/* time zone name */
156852106Seric 		TimeZoneSpec = newstr(val);
15698265Seric 		break;
15708265Seric 
157150556Seric 	  case 'U':		/* location of user database */
157251360Seric 		UdbSpec = newstr(val);
157350556Seric 		break;
157450556Seric 
15758256Seric 	  case 'u':		/* set default uid */
157664133Seric 		if (isascii(*val) && isdigit(*val))
157764133Seric 			DefUid = atoi(val);
157864133Seric 		else
157964133Seric 		{
158064133Seric 			register struct passwd *pw;
158164133Seric 
158264133Seric 			DefUid = -1;
158364133Seric 			pw = getpwnam(val);
158464133Seric 			if (pw == NULL)
158564133Seric 				syserr("readcf: option u: unknown user %s", val);
158664133Seric 			else
158764133Seric 				DefUid = pw->pw_uid;
158864133Seric 		}
158940973Sbostic 		setdefuser();
15908256Seric 		break;
15918256Seric 
159258851Seric 	  case 'V':		/* fallback MX host */
159358851Seric 		FallBackMX = newstr(val);
159458851Seric 		break;
159558851Seric 
15968269Seric 	  case 'v':		/* run in verbose mode */
15979381Seric 		Verbose = atobool(val);
15988256Seric 		break;
15998256Seric 
160063837Seric 	  case 'w':		/* if we are best MX, try host directly */
160163837Seric 		TryNullMXList = atobool(val);
160263837Seric 		break;
160361104Seric 
160461104Seric 	    /* 'W' available -- was wizard password */
160561104Seric 
160614879Seric 	  case 'x':		/* load avg at which to auto-queue msgs */
160714879Seric 		QueueLA = atoi(val);
160814879Seric 		break;
160914879Seric 
161014879Seric 	  case 'X':		/* load avg at which to auto-reject connections */
161114879Seric 		RefuseLA = atoi(val);
161214879Seric 		break;
161314879Seric 
161424981Seric 	  case 'y':		/* work recipient factor */
161524981Seric 		WkRecipFact = atoi(val);
161624981Seric 		break;
161724981Seric 
161824981Seric 	  case 'Y':		/* fork jobs during queue runs */
161924952Seric 		ForkQueueRuns = atobool(val);
162024952Seric 		break;
162124952Seric 
162224981Seric 	  case 'z':		/* work message class factor */
162324981Seric 		WkClassFact = atoi(val);
162424981Seric 		break;
162524981Seric 
162624981Seric 	  case 'Z':		/* work time factor */
162724981Seric 		WkTimeFact = atoi(val);
162824981Seric 		break;
162924981Seric 
163067614Seric 	  case O_BSP:		/* SMTP Peers can't handle 2-line greeting */
163167614Seric 		BrokenSmtpPeers = atobool(val);
163267614Seric 		break;
163367614Seric 
163467614Seric 	  case O_SQBH:		/* sort work queue by host first */
163567614Seric 		SortQueueByHost = atobool(val);
163667614Seric 		break;
163767614Seric 
16388256Seric 	  default:
16398256Seric 		break;
16408256Seric 	}
164116878Seric 	if (sticky)
164216878Seric 		setbitn(opt, StickyOpt);
16439188Seric 	return;
16448256Seric }
164510687Seric /*
164610687Seric **  SETCLASS -- set a word into a class
164710687Seric **
164810687Seric **	Parameters:
164910687Seric **		class -- the class to put the word in.
165010687Seric **		word -- the word to enter
165110687Seric **
165210687Seric **	Returns:
165310687Seric **		none.
165410687Seric **
165510687Seric **	Side Effects:
165610687Seric **		puts the word into the symbol table.
165710687Seric */
165810687Seric 
165910687Seric setclass(class, word)
166010687Seric 	int class;
166110687Seric 	char *word;
166210687Seric {
166310687Seric 	register STAB *s;
166410687Seric 
166557943Seric 	if (tTd(37, 8))
166664326Seric 		printf("setclass(%c, %s)\n", class, word);
166710687Seric 	s = stab(word, ST_CLASS, ST_ENTER);
166810687Seric 	setbitn(class, s->s_class);
166910687Seric }
167053654Seric /*
167153654Seric **  MAKEMAPENTRY -- create a map entry
167253654Seric **
167353654Seric **	Parameters:
167453654Seric **		line -- the config file line
167553654Seric **
167653654Seric **	Returns:
167753654Seric **		TRUE if it successfully entered the map entry.
167853654Seric **		FALSE otherwise (usually syntax error).
167953654Seric **
168053654Seric **	Side Effects:
168153654Seric **		Enters the map into the dictionary.
168253654Seric */
168353654Seric 
168453654Seric void
168553654Seric makemapentry(line)
168653654Seric 	char *line;
168753654Seric {
168853654Seric 	register char *p;
168953654Seric 	char *mapname;
169053654Seric 	char *classname;
169164078Seric 	register STAB *s;
169253654Seric 	STAB *class;
169353654Seric 
169458050Seric 	for (p = line; isascii(*p) && isspace(*p); p++)
169553654Seric 		continue;
169658050Seric 	if (!(isascii(*p) && isalnum(*p)))
169753654Seric 	{
169853654Seric 		syserr("readcf: config K line: no map name");
169953654Seric 		return;
170053654Seric 	}
170153654Seric 
170253654Seric 	mapname = p;
170358050Seric 	while (isascii(*++p) && isalnum(*p))
170453654Seric 		continue;
170553654Seric 	if (*p != '\0')
170653654Seric 		*p++ = '\0';
170758050Seric 	while (isascii(*p) && isspace(*p))
170853654Seric 		p++;
170958050Seric 	if (!(isascii(*p) && isalnum(*p)))
171053654Seric 	{
171153654Seric 		syserr("readcf: config K line, map %s: no map class", mapname);
171253654Seric 		return;
171353654Seric 	}
171453654Seric 	classname = p;
171558050Seric 	while (isascii(*++p) && isalnum(*p))
171653654Seric 		continue;
171753654Seric 	if (*p != '\0')
171853654Seric 		*p++ = '\0';
171958050Seric 	while (isascii(*p) && isspace(*p))
172053654Seric 		p++;
172153654Seric 
172253654Seric 	/* look up the class */
172353654Seric 	class = stab(classname, ST_MAPCLASS, ST_FIND);
172453654Seric 	if (class == NULL)
172553654Seric 	{
172653654Seric 		syserr("readcf: map %s: class %s not available", mapname, classname);
172753654Seric 		return;
172853654Seric 	}
172953654Seric 
173053654Seric 	/* enter the map */
173164078Seric 	s = stab(mapname, ST_MAP, ST_ENTER);
173264078Seric 	s->s_map.map_class = &class->s_mapclass;
173364078Seric 	s->s_map.map_mname = newstr(mapname);
173453654Seric 
173564078Seric 	if (class->s_mapclass.map_parse(&s->s_map, p))
173664078Seric 		s->s_map.map_mflags |= MF_VALID;
173764078Seric 
173864078Seric 	if (tTd(37, 5))
173964078Seric 	{
174064078Seric 		printf("map %s, class %s, flags %x, file %s,\n",
174164078Seric 			s->s_map.map_mname, s->s_map.map_class->map_cname,
174264078Seric 			s->s_map.map_mflags,
174364078Seric 			s->s_map.map_file == NULL ? "(null)" : s->s_map.map_file);
174464078Seric 		printf("\tapp %s, domain %s, rebuild %s\n",
174564078Seric 			s->s_map.map_app == NULL ? "(null)" : s->s_map.map_app,
174664078Seric 			s->s_map.map_domain == NULL ? "(null)" : s->s_map.map_domain,
174764078Seric 			s->s_map.map_rebuild == NULL ? "(null)" : s->s_map.map_rebuild);
174864078Seric 	}
174953654Seric }
175058112Seric /*
175158112Seric **  SETTIMEOUTS -- parse and set timeout values
175258112Seric **
175358112Seric **	Parameters:
175458112Seric **		val -- a pointer to the values.  If NULL, do initial
175558112Seric **			settings.
175658112Seric **
175758112Seric **	Returns:
175858112Seric **		none.
175958112Seric **
176058112Seric **	Side Effects:
176158112Seric **		Initializes the TimeOuts structure
176258112Seric */
176358112Seric 
176464255Seric #define SECONDS
176558112Seric #define MINUTES	* 60
176658112Seric #define HOUR	* 3600
176758112Seric 
176858112Seric settimeouts(val)
176958112Seric 	register char *val;
177058112Seric {
177158112Seric 	register char *p;
177258671Seric 	extern time_t convtime();
177358112Seric 
177458112Seric 	if (val == NULL)
177558112Seric 	{
177658112Seric 		TimeOuts.to_initial = (time_t) 5 MINUTES;
177758112Seric 		TimeOuts.to_helo = (time_t) 5 MINUTES;
177858112Seric 		TimeOuts.to_mail = (time_t) 10 MINUTES;
177958112Seric 		TimeOuts.to_rcpt = (time_t) 1 HOUR;
178058112Seric 		TimeOuts.to_datainit = (time_t) 5 MINUTES;
178158112Seric 		TimeOuts.to_datablock = (time_t) 1 HOUR;
178258112Seric 		TimeOuts.to_datafinal = (time_t) 1 HOUR;
178358112Seric 		TimeOuts.to_rset = (time_t) 5 MINUTES;
178458112Seric 		TimeOuts.to_quit = (time_t) 2 MINUTES;
178558112Seric 		TimeOuts.to_nextcommand = (time_t) 1 HOUR;
178658112Seric 		TimeOuts.to_miscshort = (time_t) 2 MINUTES;
178764255Seric 		TimeOuts.to_ident = (time_t) 30 SECONDS;
178858112Seric 		return;
178958112Seric 	}
179058112Seric 
179158112Seric 	for (;; val = p)
179258112Seric 	{
179358112Seric 		while (isascii(*val) && isspace(*val))
179458112Seric 			val++;
179558112Seric 		if (*val == '\0')
179658112Seric 			break;
179758112Seric 		for (p = val; *p != '\0' && *p != ','; p++)
179858112Seric 			continue;
179958112Seric 		if (*p != '\0')
180058112Seric 			*p++ = '\0';
180158112Seric 
180258112Seric 		if (isascii(*val) && isdigit(*val))
180358112Seric 		{
180458112Seric 			/* old syntax -- set everything */
180558796Seric 			TimeOuts.to_mail = convtime(val, 'm');
180658112Seric 			TimeOuts.to_rcpt = TimeOuts.to_mail;
180758112Seric 			TimeOuts.to_datainit = TimeOuts.to_mail;
180858112Seric 			TimeOuts.to_datablock = TimeOuts.to_mail;
180958112Seric 			TimeOuts.to_datafinal = TimeOuts.to_mail;
181058112Seric 			TimeOuts.to_nextcommand = TimeOuts.to_mail;
181158112Seric 			continue;
181258112Seric 		}
181358112Seric 		else
181458112Seric 		{
181558112Seric 			register char *q = strchr(val, '=');
181658112Seric 			time_t to;
181758112Seric 
181858112Seric 			if (q == NULL)
181958112Seric 			{
182058112Seric 				/* syntax error */
182158112Seric 				continue;
182258112Seric 			}
182358112Seric 			*q++ = '\0';
182458796Seric 			to = convtime(q, 'm');
182558112Seric 
182658112Seric 			if (strcasecmp(val, "initial") == 0)
182758112Seric 				TimeOuts.to_initial = to;
182858112Seric 			else if (strcasecmp(val, "mail") == 0)
182958112Seric 				TimeOuts.to_mail = to;
183058112Seric 			else if (strcasecmp(val, "rcpt") == 0)
183158112Seric 				TimeOuts.to_rcpt = to;
183258112Seric 			else if (strcasecmp(val, "datainit") == 0)
183358112Seric 				TimeOuts.to_datainit = to;
183458112Seric 			else if (strcasecmp(val, "datablock") == 0)
183558112Seric 				TimeOuts.to_datablock = to;
183658112Seric 			else if (strcasecmp(val, "datafinal") == 0)
183758112Seric 				TimeOuts.to_datafinal = to;
183858112Seric 			else if (strcasecmp(val, "command") == 0)
183958112Seric 				TimeOuts.to_nextcommand = to;
184058112Seric 			else if (strcasecmp(val, "rset") == 0)
184158112Seric 				TimeOuts.to_rset = to;
184258112Seric 			else if (strcasecmp(val, "helo") == 0)
184358112Seric 				TimeOuts.to_helo = to;
184458112Seric 			else if (strcasecmp(val, "quit") == 0)
184558112Seric 				TimeOuts.to_quit = to;
184658112Seric 			else if (strcasecmp(val, "misc") == 0)
184758112Seric 				TimeOuts.to_miscshort = to;
184864255Seric 			else if (strcasecmp(val, "ident") == 0)
184964255Seric 				TimeOuts.to_ident = to;
185058112Seric 			else
185158112Seric 				syserr("settimeouts: invalid timeout %s", val);
185258112Seric 		}
185358112Seric 	}
185458112Seric }
1855