readcf.c revision 66494
1/*
2 * Copyright (c) 1998-2000 Sendmail, Inc. and its suppliers.
3 *	All rights reserved.
4 * Copyright (c) 1983, 1995-1997 Eric P. Allman.  All rights reserved.
5 * Copyright (c) 1988, 1993
6 *	The Regents of the University of California.  All rights reserved.
7 *
8 * By using this file, you agree to the terms and conditions set
9 * forth in the LICENSE file which can be found at the top level of
10 * the sendmail distribution.
11 *
12 */
13
14#ifndef lint
15static char id[] = "@(#)$Id: readcf.c,v 8.382.4.27 2000/09/28 01:31:16 gshapiro Exp $";
16#endif /* ! lint */
17
18#include <sendmail.h>
19
20
21#if NETINET || NETINET6
22# include <arpa/inet.h>
23#endif /* NETINET || NETINET6 */
24
25#define SECONDS
26#define MINUTES	* 60
27#define HOUR	* 3600
28#define HOURS	HOUR
29
30static void	fileclass __P((int, char *, char *, bool, bool));
31static char	**makeargv __P((char *));
32static void	settimeout __P((char *, char *, bool));
33static void	toomany __P((int, int));
34
35/*
36**  READCF -- read configuration file.
37**
38**	This routine reads the configuration file and builds the internal
39**	form.
40**
41**	The file is formatted as a sequence of lines, each taken
42**	atomically.  The first character of each line describes how
43**	the line is to be interpreted.  The lines are:
44**		Dxval		Define macro x to have value val.
45**		Cxword		Put word into class x.
46**		Fxfile [fmt]	Read file for lines to put into
47**				class x.  Use scanf string 'fmt'
48**				or "%s" if not present.  Fmt should
49**				only produce one string-valued result.
50**		Hname: value	Define header with field-name 'name'
51**				and value as specified; this will be
52**				macro expanded immediately before
53**				use.
54**		Sn		Use rewriting set n.
55**		Rlhs rhs	Rewrite addresses that match lhs to
56**				be rhs.
57**		Mn arg=val...	Define mailer.  n is the internal name.
58**				Args specify mailer parameters.
59**		Oxvalue		Set option x to value.
60**		Pname=value	Set precedence name to value.
61**		Vversioncode[/vendorcode]
62**				Version level/vendor name of
63**				configuration syntax.
64**		Kmapname mapclass arguments....
65**				Define keyed lookup of a given class.
66**				Arguments are class dependent.
67**		Eenvar=value	Set the environment value to the given value.
68**
69**	Parameters:
70**		cfname -- configuration file name.
71**		safe -- TRUE if this is the system config file;
72**			FALSE otherwise.
73**		e -- the main envelope.
74**
75**	Returns:
76**		none.
77**
78**	Side Effects:
79**		Builds several internal tables.
80*/
81
82void
83readcf(cfname, safe, e)
84	char *cfname;
85	bool safe;
86	register ENVELOPE *e;
87{
88	FILE *cf;
89	int ruleset = -1;
90	char *q;
91	struct rewrite *rwp = NULL;
92	char *bp;
93	auto char *ep;
94	int nfuzzy;
95	char *file;
96	bool optional;
97	int mid;
98	register char *p;
99	long sff = SFF_OPENASROOT;
100	struct stat statb;
101	char buf[MAXLINE];
102	char exbuf[MAXLINE];
103	char pvpbuf[MAXLINE + MAXATOM];
104	static char *null_list[1] = { NULL };
105	extern u_char TokTypeNoC[];
106
107	FileName = cfname;
108	LineNumber = 0;
109
110	if (DontLockReadFiles)
111		sff |= SFF_NOLOCK;
112	cf = safefopen(cfname, O_RDONLY, 0444, sff);
113	if (cf == NULL)
114	{
115		syserr("cannot open");
116		finis(FALSE, EX_OSFILE);
117	}
118
119	if (fstat(fileno(cf), &statb) < 0)
120	{
121		syserr("cannot fstat");
122		finis(FALSE, EX_OSFILE);
123	}
124
125	if (!S_ISREG(statb.st_mode))
126	{
127		syserr("not a plain file");
128		finis(FALSE, EX_OSFILE);
129	}
130
131	if (OpMode != MD_TEST && bitset(S_IWGRP|S_IWOTH, statb.st_mode))
132	{
133		if (OpMode == MD_DAEMON || OpMode == MD_INITALIAS)
134			fprintf(stderr, "%s: WARNING: dangerous write permissions\n",
135				FileName);
136		if (LogLevel > 0)
137			sm_syslog(LOG_CRIT, NOQID,
138				  "%s: WARNING: dangerous write permissions",
139				  FileName);
140	}
141
142#ifdef XLA
143	xla_zero();
144#endif /* XLA */
145
146	while ((bp = fgetfolded(buf, sizeof buf, cf)) != NULL)
147	{
148		if (bp[0] == '#')
149		{
150			if (bp != buf)
151				free(bp);
152			continue;
153		}
154
155		/* do macro expansion mappings */
156		translate_dollars(bp);
157
158		/* interpret this line */
159		errno = 0;
160		switch (bp[0])
161		{
162		  case '\0':
163		  case '#':		/* comment */
164			break;
165
166		  case 'R':		/* rewriting rule */
167			if (ruleset < 0)
168			{
169				syserr("missing valid ruleset for \"%s\"", bp);
170				break;
171			}
172			for (p = &bp[1]; *p != '\0' && *p != '\t'; p++)
173				continue;
174
175			if (*p == '\0')
176			{
177				syserr("invalid rewrite line \"%s\" (tab expected)", bp);
178				break;
179			}
180
181			/* allocate space for the rule header */
182			if (rwp == NULL)
183			{
184				RewriteRules[ruleset] = rwp =
185					(struct rewrite *) xalloc(sizeof *rwp);
186			}
187			else
188			{
189				rwp->r_next = (struct rewrite *) xalloc(sizeof *rwp);
190				rwp = rwp->r_next;
191			}
192			rwp->r_next = NULL;
193
194			/* expand and save the LHS */
195			*p = '\0';
196			expand(&bp[1], exbuf, sizeof exbuf, e);
197			rwp->r_lhs = prescan(exbuf, '\t', pvpbuf,
198					     sizeof pvpbuf, NULL,
199					     ConfigLevel >= 9 ? TokTypeNoC : NULL);
200			nfuzzy = 0;
201			if (rwp->r_lhs != NULL)
202			{
203				register char **ap;
204
205				rwp->r_lhs = copyplist(rwp->r_lhs, TRUE);
206
207				/* count the number of fuzzy matches in LHS */
208				for (ap = rwp->r_lhs; *ap != NULL; ap++)
209				{
210					char *botch;
211
212					botch = NULL;
213					switch (**ap & 0377)
214					{
215					  case MATCHZANY:
216					  case MATCHANY:
217					  case MATCHONE:
218					  case MATCHCLASS:
219					  case MATCHNCLASS:
220						nfuzzy++;
221						break;
222
223					  case MATCHREPL:
224						botch = "$0-$9";
225						break;
226
227					  case CANONUSER:
228						botch = "$:";
229						break;
230
231					  case CALLSUBR:
232						botch = "$>";
233						break;
234
235					  case CONDIF:
236						botch = "$?";
237						break;
238
239					  case CONDFI:
240						botch = "$.";
241						break;
242
243					  case HOSTBEGIN:
244						botch = "$[";
245						break;
246
247					  case HOSTEND:
248						botch = "$]";
249						break;
250
251					  case LOOKUPBEGIN:
252						botch = "$(";
253						break;
254
255					  case LOOKUPEND:
256						botch = "$)";
257						break;
258					}
259					if (botch != NULL)
260						syserr("Inappropriate use of %s on LHS",
261							botch);
262				}
263				rwp->r_line = LineNumber;
264			}
265			else
266			{
267				syserr("R line: null LHS");
268				rwp->r_lhs = null_list;
269			}
270
271			/* expand and save the RHS */
272			while (*++p == '\t')
273				continue;
274			q = p;
275			while (*p != '\0' && *p != '\t')
276				p++;
277			*p = '\0';
278			expand(q, exbuf, sizeof exbuf, e);
279			rwp->r_rhs = prescan(exbuf, '\t', pvpbuf,
280					     sizeof pvpbuf, NULL,
281					     ConfigLevel >= 9 ? TokTypeNoC : NULL);
282			if (rwp->r_rhs != NULL)
283			{
284				register char **ap;
285
286				rwp->r_rhs = copyplist(rwp->r_rhs, TRUE);
287
288				/* check no out-of-bounds replacements */
289				nfuzzy += '0';
290				for (ap = rwp->r_rhs; *ap != NULL; ap++)
291				{
292					char *botch;
293
294					botch = NULL;
295					switch (**ap & 0377)
296					{
297					  case MATCHREPL:
298						if ((*ap)[1] <= '0' || (*ap)[1] > nfuzzy)
299						{
300							syserr("replacement $%c out of bounds",
301								(*ap)[1]);
302						}
303						break;
304
305					  case MATCHZANY:
306						botch = "$*";
307						break;
308
309					  case MATCHANY:
310						botch = "$+";
311						break;
312
313					  case MATCHONE:
314						botch = "$-";
315						break;
316
317					  case MATCHCLASS:
318						botch = "$=";
319						break;
320
321					  case MATCHNCLASS:
322						botch = "$~";
323						break;
324					}
325					if (botch != NULL)
326						syserr("Inappropriate use of %s on RHS",
327							botch);
328				}
329			}
330			else
331			{
332				syserr("R line: null RHS");
333				rwp->r_rhs = null_list;
334			}
335			break;
336
337		  case 'S':		/* select rewriting set */
338			expand(&bp[1], exbuf, sizeof exbuf, e);
339			ruleset = strtorwset(exbuf, NULL, ST_ENTER);
340			if (ruleset < 0)
341				break;
342
343			rwp = RewriteRules[ruleset];
344			if (rwp != NULL)
345			{
346				if (OpMode == MD_TEST)
347					printf("WARNING: Ruleset %s has multiple definitions\n",
348					       &bp[1]);
349				if (tTd(37, 1))
350					dprintf("WARNING: Ruleset %s has multiple definitions\n",
351						&bp[1]);
352				while (rwp->r_next != NULL)
353					rwp = rwp->r_next;
354			}
355			break;
356
357		  case 'D':		/* macro definition */
358			mid = macid(&bp[1], &ep);
359			p = munchstring(ep, NULL, '\0');
360			define(mid, newstr(p), e);
361			break;
362
363		  case 'H':		/* required header line */
364			(void) chompheader(&bp[1], CHHDR_DEF, NULL, e);
365			break;
366
367		  case 'C':		/* word class */
368		  case 'T':		/* trusted user (set class `t') */
369			if (bp[0] == 'C')
370			{
371				mid = macid(&bp[1], &ep);
372				expand(ep, exbuf, sizeof exbuf, e);
373				p = exbuf;
374			}
375			else
376			{
377				mid = 't';
378				p = &bp[1];
379			}
380			while (*p != '\0')
381			{
382				register char *wd;
383				char delim;
384
385				while (*p != '\0' && isascii(*p) && isspace(*p))
386					p++;
387				wd = p;
388				while (*p != '\0' && !(isascii(*p) && isspace(*p)))
389					p++;
390				delim = *p;
391				*p = '\0';
392				if (wd[0] != '\0')
393					setclass(mid, wd);
394				*p = delim;
395			}
396			break;
397
398		  case 'F':		/* word class from file */
399			mid = macid(&bp[1], &ep);
400			for (p = ep; isascii(*p) && isspace(*p); )
401				p++;
402			if (p[0] == '-' && p[1] == 'o')
403			{
404				optional = TRUE;
405				while (*p != '\0' && !(isascii(*p) && isspace(*p)))
406					p++;
407				while (isascii(*p) && isspace(*p))
408					p++;
409			}
410			else
411				optional = FALSE;
412
413			file = p;
414			q = p;
415			while (*q != '\0' && !(isascii(*q) && isspace(*q)))
416				q++;
417			if (*file == '|')
418				p = "%s";
419			else
420			{
421				p = q;
422				if (*p == '\0')
423					p = "%s";
424				else
425				{
426					*p = '\0';
427					while (isascii(*++p) && isspace(*p))
428						continue;
429				}
430			}
431			fileclass(mid, file, p, safe, optional);
432			break;
433
434#ifdef XLA
435		  case 'L':		/* extended load average description */
436			xla_init(&bp[1]);
437			break;
438#endif /* XLA */
439
440#if defined(SUN_EXTENSIONS) && defined(SUN_LOOKUP_MACRO)
441		  case 'L':		/* lookup macro */
442		  case 'G':		/* lookup class */
443			/* reserved for Sun -- NIS+ database lookup */
444			if (VendorCode != VENDOR_SUN)
445				goto badline;
446			sun_lg_config_line(bp, e);
447			break;
448#endif /* defined(SUN_EXTENSIONS) && defined(SUN_LOOKUP_MACRO) */
449
450		  case 'M':		/* define mailer */
451			makemailer(&bp[1]);
452			break;
453
454		  case 'O':		/* set option */
455			setoption(bp[1], &bp[2], safe, FALSE, e);
456			break;
457
458		  case 'P':		/* set precedence */
459			if (NumPriorities >= MAXPRIORITIES)
460			{
461				toomany('P', MAXPRIORITIES);
462				break;
463			}
464			for (p = &bp[1]; *p != '\0' && *p != '='; p++)
465				continue;
466			if (*p == '\0')
467				goto badline;
468			*p = '\0';
469			Priorities[NumPriorities].pri_name = newstr(&bp[1]);
470			Priorities[NumPriorities].pri_val = atoi(++p);
471			NumPriorities++;
472			break;
473
474		  case 'V':		/* configuration syntax version */
475			for (p = &bp[1]; isascii(*p) && isspace(*p); p++)
476				continue;
477			if (!isascii(*p) || !isdigit(*p))
478			{
479				syserr("invalid argument to V line: \"%.20s\"",
480					&bp[1]);
481				break;
482			}
483			ConfigLevel = strtol(p, &ep, 10);
484
485			/*
486			**  Do heuristic tweaking for back compatibility.
487			*/
488
489			if (ConfigLevel >= 5)
490			{
491				/* level 5 configs have short name in $w */
492				p = macvalue('w', e);
493				if (p != NULL && (p = strchr(p, '.')) != NULL)
494					*p = '\0';
495				define('w', macvalue('w', e), e);
496			}
497			if (ConfigLevel >= 6)
498			{
499				ColonOkInAddr = FALSE;
500			}
501
502			/*
503			**  Look for vendor code.
504			*/
505
506			if (*ep++ == '/')
507			{
508				/* extract vendor code */
509				for (p = ep; isascii(*p) && isalpha(*p); )
510					p++;
511				*p = '\0';
512
513				if (!setvendor(ep))
514					syserr("invalid V line vendor code: \"%s\"",
515						ep);
516			}
517			break;
518
519		  case 'K':
520			expand(&bp[1], exbuf, sizeof exbuf, e);
521			(void) makemapentry(exbuf);
522			break;
523
524		  case 'E':
525			p = strchr(bp, '=');
526			if (p != NULL)
527				*p++ = '\0';
528			setuserenv(&bp[1], p);
529			break;
530
531#if _FFR_MILTER
532		  case 'X':		/* mail filter */
533			milter_setup(&bp[1]);
534			break;
535#endif /* _FFR_MILTER */
536
537		  default:
538		  badline:
539			syserr("unknown configuration line \"%s\"", bp);
540		}
541		if (bp != buf)
542			free(bp);
543	}
544	if (ferror(cf))
545	{
546		syserr("I/O read error");
547		finis(FALSE, EX_OSFILE);
548	}
549	(void) fclose(cf);
550	FileName = NULL;
551
552	/* initialize host maps from local service tables */
553	inithostmaps();
554
555	/* initialize daemon (if not defined yet) */
556	initdaemon();
557
558	/* determine if we need to do special name-server frotz */
559	{
560		int nmaps;
561		char *maptype[MAXMAPSTACK];
562		short mapreturn[MAXMAPACTIONS];
563
564		nmaps = switch_map_find("hosts", maptype, mapreturn);
565		UseNameServer = FALSE;
566		if (nmaps > 0 && nmaps <= MAXMAPSTACK)
567		{
568			register int mapno;
569
570			for (mapno = 0; mapno < nmaps && !UseNameServer; mapno++)
571			{
572				if (strcmp(maptype[mapno], "dns") == 0)
573					UseNameServer = TRUE;
574			}
575		}
576
577#ifdef HESIOD
578		nmaps = switch_map_find("passwd", maptype, mapreturn);
579		UseHesiod = FALSE;
580		if (nmaps > 0 && nmaps <= MAXMAPSTACK)
581		{
582			register int mapno;
583
584			for (mapno = 0; mapno < nmaps && !UseHesiod; mapno++)
585			{
586				if (strcmp(maptype[mapno], "hesiod") == 0)
587					UseHesiod = TRUE;
588			}
589		}
590#endif /* HESIOD */
591	}
592}
593/*
594**  TRANSLATE_DOLLARS -- convert $x into internal form
595**
596**	Actually does all appropriate pre-processing of a config line
597**	to turn it into internal form.
598**
599**	Parameters:
600**		bp -- the buffer to translate.
601**
602**	Returns:
603**		None.  The buffer is translated in place.  Since the
604**		translations always make the buffer shorter, this is
605**		safe without a size parameter.
606*/
607
608void
609translate_dollars(bp)
610	char *bp;
611{
612	register char *p;
613	auto char *ep;
614
615	for (p = bp; *p != '\0'; p++)
616	{
617		if (*p == '#' && p > bp && ConfigLevel >= 3)
618		{
619			/* this is an on-line comment */
620			register char *e;
621
622			switch (*--p & 0377)
623			{
624			  case MACROEXPAND:
625				/* it's from $# -- let it go through */
626				p++;
627				break;
628
629			  case '\\':
630				/* it's backslash escaped */
631				(void) strlcpy(p, p + 1, strlen(p));
632				break;
633
634			  default:
635				/* delete leading white space */
636				while (isascii(*p) && isspace(*p) &&
637				       *p != '\n' && p > bp)
638					p--;
639				if ((e = strchr(++p, '\n')) != NULL)
640					(void) strlcpy(p, e, strlen(p));
641				else
642					*p-- = '\0';
643				break;
644			}
645			continue;
646		}
647
648		if (*p != '$' || p[1] == '\0')
649			continue;
650
651		if (p[1] == '$')
652		{
653			/* actual dollar sign.... */
654			(void) strlcpy(p, p + 1, strlen(p));
655			continue;
656		}
657
658		/* convert to macro expansion character */
659		*p++ = MACROEXPAND;
660
661		/* special handling for $=, $~, $&, and $? */
662		if (*p == '=' || *p == '~' || *p == '&' || *p == '?')
663			p++;
664
665		/* convert macro name to code */
666		*p = macid(p, &ep);
667		if (ep != p + 1)
668			(void) strlcpy(p + 1, ep, strlen(p + 1));
669	}
670
671	/* strip trailing white space from the line */
672	while (--p > bp && isascii(*p) && isspace(*p))
673		*p = '\0';
674}
675/*
676**  TOOMANY -- signal too many of some option
677**
678**	Parameters:
679**		id -- the id of the error line
680**		maxcnt -- the maximum possible values
681**
682**	Returns:
683**		none.
684**
685**	Side Effects:
686**		gives a syserr.
687*/
688
689static void
690toomany(id, maxcnt)
691	int id;
692	int maxcnt;
693{
694	syserr("too many %c lines, %d max", id, maxcnt);
695}
696/*
697**  FILECLASS -- read members of a class from a file
698**
699**	Parameters:
700**		class -- class to define.
701**		filename -- name of file to read.
702**		fmt -- scanf string to use for match.
703**		safe -- if set, this is a safe read.
704**		optional -- if set, it is not an error for the file to
705**			not exist.
706**
707**	Returns:
708**		none
709**
710**	Side Effects:
711**
712**		puts all lines in filename that match a scanf into
713**			the named class.
714*/
715
716static void
717fileclass(class, filename, fmt, safe, optional)
718	int class;
719	char *filename;
720	char *fmt;
721	bool safe;
722	bool optional;
723{
724	FILE *f;
725	long sff;
726	pid_t pid;
727	register char *p;
728	char buf[MAXLINE];
729
730	if (tTd(37, 2))
731		dprintf("fileclass(%s, fmt=%s)\n", filename, fmt);
732
733	if (filename[0] == '|')
734	{
735		auto int fd;
736		int i;
737		char *argv[MAXPV + 1];
738
739		i = 0;
740		for (p = strtok(&filename[1], " \t"); p != NULL; p = strtok(NULL, " \t"))
741		{
742			if (i >= MAXPV)
743				break;
744			argv[i++] = p;
745		}
746		argv[i] = NULL;
747		pid = prog_open(argv, &fd, CurEnv);
748		if (pid < 0)
749			f = NULL;
750		else
751			f = fdopen(fd, "r");
752	}
753	else
754	{
755		pid = -1;
756		sff = SFF_REGONLY;
757		if (!bitnset(DBS_CLASSFILEINUNSAFEDIRPATH, DontBlameSendmail))
758			sff |= SFF_SAFEDIRPATH;
759		if (!bitnset(DBS_LINKEDCLASSFILEINWRITABLEDIR,
760			     DontBlameSendmail))
761			sff |= SFF_NOWLINK;
762		if (safe)
763			sff |= SFF_OPENASROOT;
764		if (DontLockReadFiles)
765			sff |= SFF_NOLOCK;
766		f = safefopen(filename, O_RDONLY, 0, sff);
767	}
768	if (f == NULL)
769	{
770		if (!optional)
771			syserr("fileclass: cannot open '%s'", filename);
772		return;
773	}
774
775	while (fgets(buf, sizeof buf, f) != NULL)
776	{
777#if SCANF
778		char wordbuf[MAXLINE + 1];
779#endif /* SCANF */
780
781		if (buf[0] == '#')
782			continue;
783#if SCANF
784		if (sscanf(buf, fmt, wordbuf) != 1)
785			continue;
786		p = wordbuf;
787#else /* SCANF */
788		p = buf;
789#endif /* SCANF */
790
791		/*
792		**  Break up the match into words.
793		*/
794
795		while (*p != '\0')
796		{
797			register char *q;
798
799			/* strip leading spaces */
800			while (isascii(*p) && isspace(*p))
801				p++;
802			if (*p == '\0')
803				break;
804
805			/* find the end of the word */
806			q = p;
807			while (*p != '\0' && !(isascii(*p) && isspace(*p)))
808				p++;
809			if (*p != '\0')
810				*p++ = '\0';
811
812			/* enter the word in the symbol table */
813			setclass(class, q);
814		}
815	}
816
817	(void) fclose(f);
818	if (pid > 0)
819		(void) waitfor(pid);
820}
821/*
822**  MAKEMAILER -- define a new mailer.
823**
824**	Parameters:
825**		line -- description of mailer.  This is in labeled
826**			fields.  The fields are:
827**			   A -- the argv for this mailer
828**			   C -- the character set for MIME conversions
829**			   D -- the directory to run in
830**			   E -- the eol string
831**			   F -- the flags associated with the mailer
832**			   L -- the maximum line length
833**			   M -- the maximum message size
834**			   N -- the niceness at which to run
835**			   P -- the path to the mailer
836**			   R -- the recipient rewriting set
837**			   S -- the sender rewriting set
838**			   T -- the mailer type (for DSNs)
839**			   U -- the uid to run as
840**			   W -- the time to wait at the end
841**			The first word is the canonical name of the mailer.
842**
843**	Returns:
844**		none.
845**
846**	Side Effects:
847**		enters the mailer into the mailer table.
848*/
849
850void
851makemailer(line)
852	char *line;
853{
854	register char *p;
855	register struct mailer *m;
856	register STAB *s;
857	int i;
858	char fcode;
859	auto char *endp;
860	extern int NextMailer;
861
862	/* allocate a mailer and set up defaults */
863	m = (struct mailer *) xalloc(sizeof *m);
864	memset((char *) m, '\0', sizeof *m);
865
866	/* collect the mailer name */
867	for (p = line; *p != '\0' && *p != ',' && !(isascii(*p) && isspace(*p)); p++)
868		continue;
869	if (*p != '\0')
870		*p++ = '\0';
871	if (line[0] == '\0')
872		syserr("name required for mailer");
873	m->m_name = newstr(line);
874
875	/* now scan through and assign info from the fields */
876	while (*p != '\0')
877	{
878		auto char *delimptr;
879
880		while (*p != '\0' && (*p == ',' || (isascii(*p) && isspace(*p))))
881			p++;
882
883		/* p now points to field code */
884		fcode = *p;
885		while (*p != '\0' && *p != '=' && *p != ',')
886			p++;
887		if (*p++ != '=')
888		{
889			syserr("mailer %s: `=' expected", m->m_name);
890			return;
891		}
892		while (isascii(*p) && isspace(*p))
893			p++;
894
895		/* p now points to the field body */
896		p = munchstring(p, &delimptr, ',');
897
898		/* install the field into the mailer struct */
899		switch (fcode)
900		{
901		  case 'P':		/* pathname */
902			if (*p == '\0')
903				syserr("mailer %s: empty path name", m->m_name);
904			m->m_mailer = newstr(p);
905			break;
906
907		  case 'F':		/* flags */
908			for (; *p != '\0'; p++)
909				if (!(isascii(*p) && isspace(*p)))
910					setbitn(*p, m->m_flags);
911			break;
912
913		  case 'S':		/* sender rewriting ruleset */
914		  case 'R':		/* recipient rewriting ruleset */
915			i = strtorwset(p, &endp, ST_ENTER);
916			if (i < 0)
917				return;
918			if (fcode == 'S')
919				m->m_sh_rwset = m->m_se_rwset = i;
920			else
921				m->m_rh_rwset = m->m_re_rwset = i;
922
923			p = endp;
924			if (*p++ == '/')
925			{
926				i = strtorwset(p, NULL, ST_ENTER);
927				if (i < 0)
928					return;
929				if (fcode == 'S')
930					m->m_sh_rwset = i;
931				else
932					m->m_rh_rwset = i;
933			}
934			break;
935
936		  case 'E':		/* end of line string */
937			if (*p == '\0')
938				syserr("mailer %s: null end-of-line string",
939					m->m_name);
940			m->m_eol = newstr(p);
941			break;
942
943		  case 'A':		/* argument vector */
944			if (*p == '\0')
945				syserr("mailer %s: null argument vector",
946					m->m_name);
947			m->m_argv = makeargv(p);
948			break;
949
950		  case 'M':		/* maximum message size */
951			m->m_maxsize = atol(p);
952			break;
953
954		  case 'm':		/* maximum messages per connection */
955			m->m_maxdeliveries = atoi(p);
956			break;
957
958#if _FFR_DYNAMIC_TOBUF
959		  case 'r':		/* max recipient per envelope */
960			m->m_maxrcpt = atoi(p);
961			break;
962#endif /* _FFR_DYNAMIC_TOBUF */
963
964		  case 'L':		/* maximum line length */
965			m->m_linelimit = atoi(p);
966			if (m->m_linelimit < 0)
967				m->m_linelimit = 0;
968			break;
969
970		  case 'N':		/* run niceness */
971			m->m_nice = atoi(p);
972			break;
973
974		  case 'D':		/* working directory */
975			if (*p == '\0')
976				syserr("mailer %s: null working directory",
977					m->m_name);
978			m->m_execdir = newstr(p);
979			break;
980
981		  case 'C':		/* default charset */
982			if (*p == '\0')
983				syserr("mailer %s: null charset", m->m_name);
984			m->m_defcharset = newstr(p);
985			break;
986
987		  case 'T':		/* MTA-Name/Address/Diagnostic types */
988			/* extract MTA name type; default to "dns" */
989			m->m_mtatype = newstr(p);
990			p = strchr(m->m_mtatype, '/');
991			if (p != NULL)
992			{
993				*p++ = '\0';
994				if (*p == '\0')
995					p = NULL;
996			}
997			if (*m->m_mtatype == '\0')
998				m->m_mtatype = "dns";
999
1000			/* extract address type; default to "rfc822" */
1001			m->m_addrtype = p;
1002			if (p != NULL)
1003				p = strchr(p, '/');
1004			if (p != NULL)
1005			{
1006				*p++ = '\0';
1007				if (*p == '\0')
1008					p = NULL;
1009			}
1010			if (m->m_addrtype == NULL || *m->m_addrtype == '\0')
1011				m->m_addrtype = "rfc822";
1012
1013			/* extract diagnostic type; default to "smtp" */
1014			m->m_diagtype = p;
1015			if (m->m_diagtype == NULL || *m->m_diagtype == '\0')
1016				m->m_diagtype = "smtp";
1017			break;
1018
1019		  case 'U':		/* user id */
1020			if (isascii(*p) && !isdigit(*p))
1021			{
1022				char *q = p;
1023				struct passwd *pw;
1024
1025				while (*p != '\0' && isascii(*p) &&
1026				       (isalnum(*p) || strchr("-_", *p) != NULL))
1027					p++;
1028				while (isascii(*p) && isspace(*p))
1029					*p++ = '\0';
1030				if (*p != '\0')
1031					*p++ = '\0';
1032				if (*q == '\0')
1033					syserr("mailer %s: null user name",
1034						m->m_name);
1035				pw = sm_getpwnam(q);
1036				if (pw == NULL)
1037					syserr("readcf: mailer U= flag: unknown user %s", q);
1038				else
1039				{
1040					m->m_uid = pw->pw_uid;
1041					m->m_gid = pw->pw_gid;
1042				}
1043			}
1044			else
1045			{
1046				auto char *q;
1047
1048				m->m_uid = strtol(p, &q, 0);
1049				p = q;
1050				while (isascii(*p) && isspace(*p))
1051					p++;
1052				if (*p != '\0')
1053					p++;
1054			}
1055			while (isascii(*p) && isspace(*p))
1056				p++;
1057			if (*p == '\0')
1058				break;
1059			if (isascii(*p) && !isdigit(*p))
1060			{
1061				char *q = p;
1062				struct group *gr;
1063
1064				while (isascii(*p) && isalnum(*p))
1065					p++;
1066				*p++ = '\0';
1067				if (*q == '\0')
1068					syserr("mailer %s: null group name",
1069						m->m_name);
1070				gr = getgrnam(q);
1071				if (gr == NULL)
1072					syserr("readcf: mailer U= flag: unknown group %s", q);
1073				else
1074					m->m_gid = gr->gr_gid;
1075			}
1076			else
1077			{
1078				m->m_gid = strtol(p, NULL, 0);
1079			}
1080			break;
1081
1082		  case 'W':		/* wait timeout */
1083			m->m_wait = convtime(p, 's');
1084			break;
1085
1086		  case '/':		/* new root directory */
1087			if (*p == '\0')
1088				syserr("mailer %s: null root directory",
1089					m->m_name);
1090			else
1091				m->m_rootdir = newstr(p);
1092			break;
1093
1094		  default:
1095			syserr("M%s: unknown mailer equate %c=",
1096			       m->m_name, fcode);
1097			break;
1098		}
1099
1100		p = delimptr;
1101	}
1102
1103	/* do some rationality checking */
1104	if (m->m_argv == NULL)
1105	{
1106		syserr("M%s: A= argument required", m->m_name);
1107		return;
1108	}
1109	if (m->m_mailer == NULL)
1110	{
1111		syserr("M%s: P= argument required", m->m_name);
1112		return;
1113	}
1114
1115	if (NextMailer >= MAXMAILERS)
1116	{
1117		syserr("too many mailers defined (%d max)", MAXMAILERS);
1118		return;
1119	}
1120
1121#if _FFR_DYNAMIC_TOBUF
1122	if (m->m_maxrcpt <= 0)
1123		m->m_maxrcpt = DEFAULT_MAX_RCPT;
1124#endif /* _FFR_DYNAMIC_TOBUF */
1125
1126	/* do some heuristic cleanup for back compatibility */
1127	if (bitnset(M_LIMITS, m->m_flags))
1128	{
1129		if (m->m_linelimit == 0)
1130			m->m_linelimit = SMTPLINELIM;
1131		if (ConfigLevel < 2)
1132			setbitn(M_7BITS, m->m_flags);
1133	}
1134
1135	if (strcmp(m->m_mailer, "[TCP]") == 0)
1136	{
1137#if _FFR_REMOVE_TCP_MAILER_PATH
1138		syserr("M%s: P=[TCP] is deprecated, use P=[IPC] instead\n",
1139		       m->m_name);
1140#else /* _FFR_REMOVE_TCP_MAILER_PATH */
1141		printf("M%s: Warning: P=[TCP] is deprecated, use P=[IPC] instead\n",
1142		       m->m_name);
1143#endif /* _FFR_REMOVE_TCP_MAILER_PATH */
1144	}
1145
1146	if (strcmp(m->m_mailer, "[IPC]") == 0
1147#if !_FFR_REMOVE_TCP_MAILER_PATH
1148	    || strcmp(m->m_mailer, "[TCP]") == 0
1149#endif /* !_FFR_REMOVE_TCP_MAILER_PATH */
1150	    )
1151	{
1152		/* Use the second argument for host or path to socket */
1153		if (m->m_argv[0] == NULL || m->m_argv[1] == NULL ||
1154		    m->m_argv[1][0] == '\0')
1155		{
1156			syserr("M%s: too few parameters for %s mailer",
1157			       m->m_name, m->m_mailer);
1158		}
1159		if (strcmp(m->m_argv[0], "TCP") != 0
1160#if NETUNIX
1161		    && strcmp(m->m_argv[0], "FILE") != 0
1162#endif /* NETUNIX */
1163#if !_FFR_DEPRECATE_IPC_MAILER_ARG
1164		    && strcmp(m->m_argv[0], "IPC") != 0
1165#endif /* !_FFR_DEPRECATE_IPC_MAILER_ARG */
1166		    )
1167		{
1168			printf("M%s: Warning: first argument in %s mailer must be %s\n",
1169			       m->m_name, m->m_mailer,
1170#if NETUNIX
1171			       "TCP or FILE"
1172#else /* NETUNIX */
1173			       "TCP"
1174#endif /* NETUNIX */
1175			       );
1176		}
1177
1178	}
1179	else if (strcmp(m->m_mailer, "[FILE]") == 0)
1180	{
1181		/* Use the second argument for filename */
1182		if (m->m_argv[0] == NULL || m->m_argv[1] == NULL ||
1183		    m->m_argv[2] != NULL)
1184		{
1185			syserr("M%s: too %s parameters for [FILE] mailer",
1186			       m->m_name,
1187			       (m->m_argv[0] == NULL ||
1188				m->m_argv[1] == NULL) ? "few" : "many");
1189		}
1190		else if (strcmp(m->m_argv[0], "FILE") != 0)
1191		{
1192			syserr("M%s: first argument in [FILE] mailer must be FILE",
1193			       m->m_name);
1194		}
1195	}
1196
1197	if (strcmp(m->m_mailer, "[IPC]") == 0 ||
1198	    strcmp(m->m_mailer, "[TCP]") == 0)
1199	{
1200		if (m->m_mtatype == NULL)
1201			m->m_mtatype = "dns";
1202		if (m->m_addrtype == NULL)
1203			m->m_addrtype = "rfc822";
1204		if (m->m_diagtype == NULL)
1205		{
1206			if (m->m_argv[0] != NULL &&
1207			    strcmp(m->m_argv[0], "FILE") == 0)
1208				m->m_diagtype = "x-unix";
1209			else
1210				m->m_diagtype = "smtp";
1211		}
1212	}
1213
1214	if (m->m_eol == NULL)
1215	{
1216		char **pp;
1217
1218		/* default for SMTP is \r\n; use \n for local delivery */
1219		for (pp = m->m_argv; *pp != NULL; pp++)
1220		{
1221			for (p = *pp; *p != '\0'; )
1222			{
1223				if ((*p++ & 0377) == MACROEXPAND && *p == 'u')
1224					break;
1225			}
1226			if (*p != '\0')
1227				break;
1228		}
1229		if (*pp == NULL)
1230			m->m_eol = "\r\n";
1231		else
1232			m->m_eol = "\n";
1233	}
1234
1235	/* enter the mailer into the symbol table */
1236	s = stab(m->m_name, ST_MAILER, ST_ENTER);
1237	if (s->s_mailer != NULL)
1238	{
1239		i = s->s_mailer->m_mno;
1240		free(s->s_mailer);
1241	}
1242	else
1243	{
1244		i = NextMailer++;
1245	}
1246	Mailer[i] = s->s_mailer = m;
1247	m->m_mno = i;
1248}
1249/*
1250**  MUNCHSTRING -- translate a string into internal form.
1251**
1252**	Parameters:
1253**		p -- the string to munch.
1254**		delimptr -- if non-NULL, set to the pointer of the
1255**			field delimiter character.
1256**		delim -- the delimiter for the field.
1257**
1258**	Returns:
1259**		the munched string.
1260**
1261**	Side Effects:
1262**		the munched string is a local static buffer.
1263**		it must be copied before the function is called again.
1264*/
1265
1266char *
1267munchstring(p, delimptr, delim)
1268	register char *p;
1269	char **delimptr;
1270	int delim;
1271{
1272	register char *q;
1273	bool backslash = FALSE;
1274	bool quotemode = FALSE;
1275	static char buf[MAXLINE];
1276
1277	for (q = buf; *p != '\0' && q < &buf[sizeof buf - 1]; p++)
1278	{
1279		if (backslash)
1280		{
1281			/* everything is roughly literal */
1282			backslash = FALSE;
1283			switch (*p)
1284			{
1285			  case 'r':		/* carriage return */
1286				*q++ = '\r';
1287				continue;
1288
1289			  case 'n':		/* newline */
1290				*q++ = '\n';
1291				continue;
1292
1293			  case 'f':		/* form feed */
1294				*q++ = '\f';
1295				continue;
1296
1297			  case 'b':		/* backspace */
1298				*q++ = '\b';
1299				continue;
1300			}
1301			*q++ = *p;
1302		}
1303		else
1304		{
1305			if (*p == '\\')
1306				backslash = TRUE;
1307			else if (*p == '"')
1308				quotemode = !quotemode;
1309			else if (quotemode || *p != delim)
1310				*q++ = *p;
1311			else
1312				break;
1313		}
1314	}
1315
1316	if (delimptr != NULL)
1317		*delimptr = p;
1318	*q++ = '\0';
1319	return buf;
1320}
1321/*
1322**  MAKEARGV -- break up a string into words
1323**
1324**	Parameters:
1325**		p -- the string to break up.
1326**
1327**	Returns:
1328**		a char **argv (dynamically allocated)
1329**
1330**	Side Effects:
1331**		munges p.
1332*/
1333
1334static char **
1335makeargv(p)
1336	register char *p;
1337{
1338	char *q;
1339	int i;
1340	char **avp;
1341	char *argv[MAXPV + 1];
1342
1343	/* take apart the words */
1344	i = 0;
1345	while (*p != '\0' && i < MAXPV)
1346	{
1347		q = p;
1348		while (*p != '\0' && !(isascii(*p) && isspace(*p)))
1349			p++;
1350		while (isascii(*p) && isspace(*p))
1351			*p++ = '\0';
1352		argv[i++] = newstr(q);
1353	}
1354	argv[i++] = NULL;
1355
1356	/* now make a copy of the argv */
1357	avp = (char **) xalloc(sizeof *avp * i);
1358	memmove((char *) avp, (char *) argv, sizeof *avp * i);
1359
1360	return avp;
1361}
1362/*
1363**  PRINTRULES -- print rewrite rules (for debugging)
1364**
1365**	Parameters:
1366**		none.
1367**
1368**	Returns:
1369**		none.
1370**
1371**	Side Effects:
1372**		prints rewrite rules.
1373*/
1374
1375void
1376printrules()
1377{
1378	register struct rewrite *rwp;
1379	register int ruleset;
1380
1381	for (ruleset = 0; ruleset < 10; ruleset++)
1382	{
1383		if (RewriteRules[ruleset] == NULL)
1384			continue;
1385		printf("\n----Rule Set %d:", ruleset);
1386
1387		for (rwp = RewriteRules[ruleset]; rwp != NULL; rwp = rwp->r_next)
1388		{
1389			printf("\nLHS:");
1390			printav(rwp->r_lhs);
1391			printf("RHS:");
1392			printav(rwp->r_rhs);
1393		}
1394	}
1395}
1396/*
1397**  PRINTMAILER -- print mailer structure (for debugging)
1398**
1399**	Parameters:
1400**		m -- the mailer to print
1401**
1402**	Returns:
1403**		none.
1404*/
1405
1406void
1407printmailer(m)
1408	register MAILER *m;
1409{
1410	int j;
1411
1412	printf("mailer %d (%s): P=%s S=", m->m_mno, m->m_name, m->m_mailer);
1413	if (RuleSetNames[m->m_se_rwset] == NULL)
1414		printf("%d/", m->m_se_rwset);
1415	else
1416		printf("%s/", RuleSetNames[m->m_se_rwset]);
1417	if (RuleSetNames[m->m_sh_rwset] == NULL)
1418		printf("%d R=", m->m_sh_rwset);
1419	else
1420		printf("%s R=", RuleSetNames[m->m_sh_rwset]);
1421	if (RuleSetNames[m->m_re_rwset] == NULL)
1422		printf("%d/", m->m_re_rwset);
1423	else
1424		printf("%s/", RuleSetNames[m->m_re_rwset]);
1425	if (RuleSetNames[m->m_rh_rwset] == NULL)
1426		printf("%d ", m->m_rh_rwset);
1427	else
1428		printf("%s ", RuleSetNames[m->m_rh_rwset]);
1429	printf("M=%ld U=%d:%d F=", m->m_maxsize,
1430	       (int) m->m_uid, (int) m->m_gid);
1431	for (j = '\0'; j <= '\177'; j++)
1432		if (bitnset(j, m->m_flags))
1433			(void) putchar(j);
1434	printf(" L=%d E=", m->m_linelimit);
1435	xputs(m->m_eol);
1436	if (m->m_defcharset != NULL)
1437		printf(" C=%s", m->m_defcharset);
1438	printf(" T=%s/%s/%s",
1439		m->m_mtatype == NULL ? "<undefined>" : m->m_mtatype,
1440		m->m_addrtype == NULL ? "<undefined>" : m->m_addrtype,
1441		m->m_diagtype == NULL ? "<undefined>" : m->m_diagtype);
1442#if _FFR_DYNAMIC_TOBUF
1443	printf(" r=%d", m->m_maxrcpt);
1444#endif /* _FFR_DYNAMIC_TOBUF */
1445	if (m->m_argv != NULL)
1446	{
1447		char **a = m->m_argv;
1448
1449		printf(" A=");
1450		while (*a != NULL)
1451		{
1452			if (a != m->m_argv)
1453				printf(" ");
1454			xputs(*a++);
1455		}
1456	}
1457	printf("\n");
1458}
1459/*
1460**  SETOPTION -- set global processing option
1461**
1462**	Parameters:
1463**		opt -- option name.
1464**		val -- option value (as a text string).
1465**		safe -- set if this came from a configuration file.
1466**			Some options (if set from the command line) will
1467**			reset the user id to avoid security problems.
1468**		sticky -- if set, don't let other setoptions override
1469**			this value.
1470**		e -- the main envelope.
1471**
1472**	Returns:
1473**		none.
1474**
1475**	Side Effects:
1476**		Sets options as implied by the arguments.
1477*/
1478
1479static BITMAP256	StickyOpt;		/* set if option is stuck */
1480
1481#if NAMED_BIND
1482
1483static struct resolverflags
1484{
1485	char	*rf_name;	/* name of the flag */
1486	long	rf_bits;	/* bits to set/clear */
1487} ResolverFlags[] =
1488{
1489	{ "debug",	RES_DEBUG	},
1490	{ "aaonly",	RES_AAONLY	},
1491	{ "usevc",	RES_USEVC	},
1492	{ "primary",	RES_PRIMARY	},
1493	{ "igntc",	RES_IGNTC	},
1494	{ "recurse",	RES_RECURSE	},
1495	{ "defnames",	RES_DEFNAMES	},
1496	{ "stayopen",	RES_STAYOPEN	},
1497	{ "dnsrch",	RES_DNSRCH	},
1498	{ "true",	0		},	/* avoid error on old syntax */
1499	{ NULL,		0		}
1500};
1501
1502#endif /* NAMED_BIND */
1503
1504#define OI_NONE		0	/* no special treatment */
1505#define OI_SAFE		0x0001	/* safe for random people to use */
1506#define OI_SUBOPT	0x0002	/* option has suboptions */
1507
1508static struct optioninfo
1509{
1510	char	*o_name;	/* long name of option */
1511	u_char	o_code;		/* short name of option */
1512	u_short	o_flags;	/* option flags */
1513} OptionTab[] =
1514{
1515#if defined(SUN_EXTENSIONS) && defined(REMOTE_MODE)
1516	{ "RemoteMode",			'>',		OI_NONE	},
1517#endif /* defined(SUN_EXTENSIONS) && defined(REMOTE_MODE) */
1518	{ "SevenBitInput",		'7',		OI_SAFE	},
1519#if MIME8TO7
1520	{ "EightBitMode",		'8',		OI_SAFE	},
1521#endif /* MIME8TO7 */
1522	{ "AliasFile",			'A',		OI_NONE	},
1523	{ "AliasWait",			'a',		OI_NONE	},
1524	{ "BlankSub",			'B',		OI_NONE	},
1525	{ "MinFreeBlocks",		'b',		OI_SAFE	},
1526	{ "CheckpointInterval",		'C',		OI_SAFE	},
1527	{ "HoldExpensive",		'c',		OI_NONE	},
1528#if !_FFR_REMOVE_AUTOREBUILD
1529	{ "AutoRebuildAliases",		'D',		OI_NONE	},
1530#endif /* !_FFR_REMOVE_AUTOREBUILD */
1531	{ "DeliveryMode",		'd',		OI_SAFE	},
1532	{ "ErrorHeader",		'E',		OI_NONE	},
1533	{ "ErrorMode",			'e',		OI_SAFE	},
1534	{ "TempFileMode",		'F',		OI_NONE	},
1535	{ "SaveFromLine",		'f',		OI_NONE	},
1536	{ "MatchGECOS",			'G',		OI_NONE	},
1537	{ "HelpFile",			'H',		OI_NONE	},
1538	{ "MaxHopCount",		'h',		OI_NONE	},
1539	{ "ResolverOptions",		'I',		OI_NONE	},
1540	{ "IgnoreDots",			'i',		OI_SAFE	},
1541	{ "ForwardPath",		'J',		OI_NONE	},
1542	{ "SendMimeErrors",		'j',		OI_SAFE	},
1543	{ "ConnectionCacheSize",	'k',		OI_NONE	},
1544	{ "ConnectionCacheTimeout",	'K',		OI_NONE	},
1545	{ "UseErrorsTo",		'l',		OI_NONE	},
1546	{ "LogLevel",			'L',		OI_SAFE	},
1547	{ "MeToo",			'm',		OI_SAFE	},
1548	{ "CheckAliases",		'n',		OI_NONE	},
1549	{ "OldStyleHeaders",		'o',		OI_SAFE	},
1550	{ "DaemonPortOptions",		'O',		OI_NONE	},
1551	{ "PrivacyOptions",		'p',		OI_SAFE	},
1552	{ "PostmasterCopy",		'P',		OI_NONE	},
1553	{ "QueueFactor",		'q',		OI_NONE	},
1554	{ "QueueDirectory",		'Q',		OI_NONE	},
1555	{ "DontPruneRoutes",		'R',		OI_NONE	},
1556	{ "Timeout",			'r',		OI_SUBOPT },
1557	{ "StatusFile",			'S',		OI_NONE	},
1558	{ "SuperSafe",			's',		OI_SAFE	},
1559	{ "QueueTimeout",		'T',		OI_NONE	},
1560	{ "TimeZoneSpec",		't',		OI_NONE	},
1561	{ "UserDatabaseSpec",		'U',		OI_NONE	},
1562	{ "DefaultUser",		'u',		OI_NONE	},
1563	{ "FallbackMXhost",		'V',		OI_NONE	},
1564	{ "Verbose",			'v',		OI_SAFE	},
1565	{ "TryNullMXList",		'w',		OI_NONE	},
1566	{ "QueueLA",			'x',		OI_NONE	},
1567	{ "RefuseLA",			'X',		OI_NONE	},
1568	{ "RecipientFactor",		'y',		OI_NONE	},
1569	{ "ForkEachJob",		'Y',		OI_NONE	},
1570	{ "ClassFactor",		'z',		OI_NONE	},
1571	{ "RetryFactor",		'Z',		OI_NONE	},
1572#define O_QUEUESORTORD	0x81
1573	{ "QueueSortOrder",		O_QUEUESORTORD,	OI_SAFE	},
1574#define O_HOSTSFILE	0x82
1575	{ "HostsFile",			O_HOSTSFILE,	OI_NONE	},
1576#define O_MQA		0x83
1577	{ "MinQueueAge",		O_MQA,		OI_SAFE	},
1578#define O_DEFCHARSET	0x85
1579	{ "DefaultCharSet",		O_DEFCHARSET,	OI_SAFE	},
1580#define O_SSFILE	0x86
1581	{ "ServiceSwitchFile",		O_SSFILE,	OI_NONE	},
1582#define O_DIALDELAY	0x87
1583	{ "DialDelay",			O_DIALDELAY,	OI_SAFE	},
1584#define O_NORCPTACTION	0x88
1585	{ "NoRecipientAction",		O_NORCPTACTION,	OI_SAFE	},
1586#define O_SAFEFILEENV	0x89
1587	{ "SafeFileEnvironment",	O_SAFEFILEENV,	OI_NONE	},
1588#define O_MAXMSGSIZE	0x8a
1589	{ "MaxMessageSize",		O_MAXMSGSIZE,	OI_NONE	},
1590#define O_COLONOKINADDR	0x8b
1591	{ "ColonOkInAddr",		O_COLONOKINADDR, OI_SAFE },
1592#define O_MAXQUEUERUN	0x8c
1593	{ "MaxQueueRunSize",		O_MAXQUEUERUN,	OI_SAFE	},
1594#define O_MAXCHILDREN	0x8d
1595	{ "MaxDaemonChildren",		O_MAXCHILDREN,	OI_NONE	},
1596#define O_KEEPCNAMES	0x8e
1597	{ "DontExpandCnames",		O_KEEPCNAMES,	OI_NONE	},
1598#define O_MUSTQUOTE	0x8f
1599	{ "MustQuoteChars",		O_MUSTQUOTE,	OI_NONE	},
1600#define O_SMTPGREETING	0x90
1601	{ "SmtpGreetingMessage",	O_SMTPGREETING,	OI_NONE	},
1602#define O_UNIXFROM	0x91
1603	{ "UnixFromLine",		O_UNIXFROM,	OI_NONE	},
1604#define O_OPCHARS	0x92
1605	{ "OperatorChars",		O_OPCHARS,	OI_NONE	},
1606#define O_DONTINITGRPS	0x93
1607	{ "DontInitGroups",		O_DONTINITGRPS,	OI_NONE	},
1608#define O_SLFH		0x94
1609	{ "SingleLineFromHeader",	O_SLFH,		OI_SAFE	},
1610#define O_ABH		0x95
1611	{ "AllowBogusHELO",		O_ABH,		OI_SAFE	},
1612#define O_CONNTHROT	0x97
1613	{ "ConnectionRateThrottle",	O_CONNTHROT,	OI_NONE	},
1614#define O_UGW		0x99
1615	{ "UnsafeGroupWrites",		O_UGW,		OI_NONE	},
1616#define O_DBLBOUNCE	0x9a
1617	{ "DoubleBounceAddress",	O_DBLBOUNCE,	OI_NONE	},
1618#define O_HSDIR		0x9b
1619	{ "HostStatusDirectory",	O_HSDIR,	OI_NONE	},
1620#define O_SINGTHREAD	0x9c
1621	{ "SingleThreadDelivery",	O_SINGTHREAD,	OI_NONE	},
1622#define O_RUNASUSER	0x9d
1623	{ "RunAsUser",			O_RUNASUSER,	OI_NONE	},
1624#define O_DSN_RRT	0x9e
1625	{ "RrtImpliesDsn",		O_DSN_RRT,	OI_NONE	},
1626#define O_PIDFILE	0x9f
1627	{ "PidFile",			O_PIDFILE,	OI_NONE	},
1628#define O_DONTBLAMESENDMAIL	0xa0
1629	{ "DontBlameSendmail",		O_DONTBLAMESENDMAIL,	OI_NONE	},
1630#define O_DPI		0xa1
1631	{ "DontProbeInterfaces",	O_DPI,		OI_NONE	},
1632#define O_MAXRCPT	0xa2
1633	{ "MaxRecipientsPerMessage",	O_MAXRCPT,	OI_SAFE	},
1634#define O_DEADLETTER	0xa3
1635	{ "DeadLetterDrop",		O_DEADLETTER,	OI_NONE	},
1636#if _FFR_DONTLOCKFILESFORREAD_OPTION
1637# define O_DONTLOCK	0xa4
1638	{ "DontLockFilesForRead",	O_DONTLOCK,	OI_NONE	},
1639#endif /* _FFR_DONTLOCKFILESFORREAD_OPTION */
1640#define O_MAXALIASRCSN	0xa5
1641	{ "MaxAliasRecursion",		O_MAXALIASRCSN,	OI_NONE	},
1642#define O_CNCTONLYTO	0xa6
1643	{ "ConnectOnlyTo",		O_CNCTONLYTO,	OI_NONE	},
1644#define O_TRUSTUSER	0xa7
1645	{ "TrustedUser",		O_TRUSTUSER,	OI_NONE	},
1646#define O_MAXMIMEHDRLEN	0xa8
1647	{ "MaxMimeHeaderLength",	O_MAXMIMEHDRLEN,	OI_NONE	},
1648#define O_CONTROLSOCKET	0xa9
1649	{ "ControlSocketName",		O_CONTROLSOCKET,	OI_NONE	},
1650#define O_MAXHDRSLEN	0xaa
1651	{ "MaxHeadersLength",		O_MAXHDRSLEN,	OI_NONE	},
1652#if _FFR_MAX_FORWARD_ENTRIES
1653# define O_MAXFORWARD	0xab
1654	{ "MaxForwardEntries",		O_MAXFORWARD,	OI_NONE	},
1655#endif /* _FFR_MAX_FORWARD_ENTRIES */
1656#define O_PROCTITLEPREFIX	0xac
1657	{ "ProcessTitlePrefix",		O_PROCTITLEPREFIX,	OI_NONE	},
1658#define O_SASLINFO	0xad
1659#if _FFR_ALLOW_SASLINFO
1660	{ "DefaultAuthInfo",		O_SASLINFO,	OI_SAFE	},
1661#else /* _FFR_ALLOW_SASLINFO */
1662	{ "DefaultAuthInfo",		O_SASLINFO,	OI_NONE	},
1663#endif /* _FFR_ALLOW_SASLINFO */
1664#define O_SASLMECH	0xae
1665	{ "AuthMechanisms",		O_SASLMECH,	OI_NONE	},
1666#define O_CLIENTPORT	0xaf
1667	{ "ClientPortOptions",		O_CLIENTPORT,	OI_NONE	},
1668#define O_DF_BUFSIZE	0xb0
1669	{ "DataFileBufferSize",		O_DF_BUFSIZE,	OI_NONE	},
1670#define O_XF_BUFSIZE	0xb1
1671	{ "XscriptFileBufferSize",	O_XF_BUFSIZE,	OI_NONE	},
1672# define O_LDAPDEFAULTSPEC	0xb2
1673	{ "LDAPDefaultSpec",		O_LDAPDEFAULTSPEC,	OI_NONE	},
1674#if _FFR_QUEUEDELAY
1675#define O_QUEUEDELAY	0xb3
1676	{ "QueueDelay",			O_QUEUEDELAY,	OI_NONE	},
1677#endif /* _FFR_QUEUEDELAY */
1678# define O_SRVCERTFILE	0xb4
1679	{ "ServerCertFile",		O_SRVCERTFILE,	OI_NONE	},
1680# define O_SRVKEYFILE	0xb5
1681	{ "Serverkeyfile",		O_SRVKEYFILE,	OI_NONE	},
1682# define O_CLTCERTFILE	0xb6
1683	{ "ClientCertFile",		O_CLTCERTFILE,	OI_NONE	},
1684# define O_CLTKEYFILE	0xb7
1685	{ "Clientkeyfile",		O_CLTKEYFILE,	OI_NONE	},
1686# define O_CACERTFILE	0xb8
1687	{ "CACERTFile",			O_CACERTFILE,	OI_NONE	},
1688# define O_CACERTPATH	0xb9
1689	{ "CACERTPath",			O_CACERTPATH,	OI_NONE	},
1690# define O_DHPARAMS	0xba
1691	{ "DHParameters",		O_DHPARAMS,	OI_NONE	},
1692#if _FFR_MILTER
1693#define O_INPUTMILTER	0xbb
1694	{ "InputMailFilters",		O_INPUTMILTER,	OI_NONE	},
1695#define O_MILTER	0xbc
1696	{ "Milter",			O_MILTER,	OI_SUBOPT	},
1697#endif /* _FFR_MILTER */
1698#define O_SASLOPTS	0xbd
1699	{ "AuthOptions",		O_SASLOPTS,	OI_NONE	},
1700#if _FFR_QUEUE_FILE_MODE
1701#define O_QUEUE_FILE_MODE	0xbe
1702	{ "QueueFileMode",		O_QUEUE_FILE_MODE, OI_NONE	},
1703#endif /* _FFR_QUEUE_FILE_MODE */
1704# if _FFR_TLS_1
1705# define O_DHPARAMS5	0xbf
1706	{ "DHParameters512",		O_DHPARAMS5,	OI_NONE	},
1707# define O_CIPHERLIST	0xc0
1708	{ "CipherList",			O_CIPHERLIST,	OI_NONE	},
1709# endif /* _FFR_TLS_1 */
1710# define O_RANDFILE	0xc1
1711	{ "RandFile",			O_RANDFILE,	OI_NONE	},
1712	{ NULL,				'\0',		OI_NONE	}
1713};
1714
1715void
1716setoption(opt, val, safe, sticky, e)
1717	int opt;
1718	char *val;
1719	bool safe;
1720	bool sticky;
1721	register ENVELOPE *e;
1722{
1723	register char *p;
1724	register struct optioninfo *o;
1725	char *subopt;
1726	int mid;
1727	bool can_setuid = RunAsUid == 0;
1728	auto char *ep;
1729	char buf[50];
1730	extern bool Warn_Q_option;
1731#if _FFR_ALLOW_SASLINFO
1732	extern int SubmitMode;
1733#endif /* _FFR_ALLOW_SASLINFO */
1734
1735	errno = 0;
1736	if (opt == ' ')
1737	{
1738		/* full word options */
1739		struct optioninfo *sel;
1740
1741		p = strchr(val, '=');
1742		if (p == NULL)
1743			p = &val[strlen(val)];
1744		while (*--p == ' ')
1745			continue;
1746		while (*++p == ' ')
1747			*p = '\0';
1748		if (p == val)
1749		{
1750			syserr("readcf: null option name");
1751			return;
1752		}
1753		if (*p == '=')
1754			*p++ = '\0';
1755		while (*p == ' ')
1756			p++;
1757		subopt = strchr(val, '.');
1758		if (subopt != NULL)
1759			*subopt++ = '\0';
1760		sel = NULL;
1761		for (o = OptionTab; o->o_name != NULL; o++)
1762		{
1763			if (strncasecmp(o->o_name, val, strlen(val)) != 0)
1764				continue;
1765			if (strlen(o->o_name) == strlen(val))
1766			{
1767				/* completely specified -- this must be it */
1768				sel = NULL;
1769				break;
1770			}
1771			if (sel != NULL)
1772				break;
1773			sel = o;
1774		}
1775		if (sel != NULL && o->o_name == NULL)
1776			o = sel;
1777		else if (o->o_name == NULL)
1778		{
1779			syserr("readcf: unknown option name %s", val);
1780			return;
1781		}
1782		else if (sel != NULL)
1783		{
1784			syserr("readcf: ambiguous option name %s (matches %s and %s)",
1785				val, sel->o_name, o->o_name);
1786			return;
1787		}
1788		if (strlen(val) != strlen(o->o_name))
1789		{
1790			int oldVerbose = Verbose;
1791
1792			Verbose = 1;
1793			message("Option %s used as abbreviation for %s",
1794				val, o->o_name);
1795			Verbose = oldVerbose;
1796		}
1797		opt = o->o_code;
1798		val = p;
1799	}
1800	else
1801	{
1802		for (o = OptionTab; o->o_name != NULL; o++)
1803		{
1804			if (o->o_code == opt)
1805				break;
1806		}
1807		subopt = NULL;
1808	}
1809
1810	if (subopt != NULL && !bitset(OI_SUBOPT, o->o_flags))
1811	{
1812		if (tTd(37, 1))
1813			dprintf("setoption: %s does not support suboptions, ignoring .%s\n",
1814				o->o_name == NULL ? "<unknown>" : o->o_name,
1815				subopt);
1816		subopt = NULL;
1817	}
1818
1819	if (tTd(37, 1))
1820	{
1821		dprintf(isascii(opt) && isprint(opt) ?
1822			"setoption %s (%c)%s%s=" :
1823			"setoption %s (0x%x)%s%s=",
1824			o->o_name == NULL ? "<unknown>" : o->o_name,
1825			opt,
1826			subopt == NULL ? "" : ".",
1827			subopt == NULL ? "" : subopt);
1828		xputs(val);
1829	}
1830
1831	/*
1832	**  See if this option is preset for us.
1833	*/
1834
1835	if (!sticky && bitnset(opt, StickyOpt))
1836	{
1837		if (tTd(37, 1))
1838			dprintf(" (ignored)\n");
1839		return;
1840	}
1841
1842	/*
1843	**  Check to see if this option can be specified by this user.
1844	*/
1845
1846	if (!safe && RealUid == 0)
1847		safe = TRUE;
1848	if (!safe && !bitset(OI_SAFE, o->o_flags))
1849	{
1850		if (opt != 'M' || (val[0] != 'r' && val[0] != 's'))
1851		{
1852			int dp;
1853
1854			if (tTd(37, 1))
1855				dprintf(" (unsafe)");
1856			dp = drop_privileges(TRUE);
1857			setstat(dp);
1858		}
1859	}
1860	if (tTd(37, 1))
1861		dprintf("\n");
1862
1863	switch (opt & 0xff)
1864	{
1865	  case '7':		/* force seven-bit input */
1866		SevenBitInput = atobool(val);
1867		break;
1868
1869#if MIME8TO7
1870	  case '8':		/* handling of 8-bit input */
1871		switch (*val)
1872		{
1873		  case 'm':		/* convert 8-bit, convert MIME */
1874			MimeMode = MM_CVTMIME|MM_MIME8BIT;
1875			break;
1876
1877		  case 'p':		/* pass 8 bit, convert MIME */
1878			MimeMode = MM_CVTMIME|MM_PASS8BIT;
1879			break;
1880
1881		  case 's':		/* strict adherence */
1882			MimeMode = MM_CVTMIME;
1883			break;
1884
1885# if 0
1886		  case 'r':		/* reject 8-bit, don't convert MIME */
1887			MimeMode = 0;
1888			break;
1889
1890		  case 'j':		/* "just send 8" */
1891			MimeMode = MM_PASS8BIT;
1892			break;
1893
1894		  case 'a':		/* encode 8 bit if available */
1895			MimeMode = MM_MIME8BIT|MM_PASS8BIT|MM_CVTMIME;
1896			break;
1897
1898		  case 'c':		/* convert 8 bit to MIME, never 7 bit */
1899			MimeMode = MM_MIME8BIT;
1900			break;
1901# endif /* 0 */
1902
1903		  default:
1904			syserr("Unknown 8-bit mode %c", *val);
1905			finis(FALSE, EX_USAGE);
1906		}
1907		break;
1908#endif /* MIME8TO7 */
1909
1910	  case 'A':		/* set default alias file */
1911		if (val[0] == '\0')
1912			setalias("aliases");
1913		else
1914			setalias(val);
1915		break;
1916
1917	  case 'a':		/* look N minutes for "@:@" in alias file */
1918		if (val[0] == '\0')
1919			SafeAlias = 5 * 60;		/* five minutes */
1920		else
1921			SafeAlias = convtime(val, 'm');
1922		break;
1923
1924	  case 'B':		/* substitution for blank character */
1925		SpaceSub = val[0];
1926		if (SpaceSub == '\0')
1927			SpaceSub = ' ';
1928		break;
1929
1930	  case 'b':		/* min blocks free on queue fs/max msg size */
1931		p = strchr(val, '/');
1932		if (p != NULL)
1933		{
1934			*p++ = '\0';
1935			MaxMessageSize = atol(p);
1936		}
1937		MinBlocksFree = atol(val);
1938		break;
1939
1940	  case 'c':		/* don't connect to "expensive" mailers */
1941		NoConnect = atobool(val);
1942		break;
1943
1944	  case 'C':		/* checkpoint every N addresses */
1945		CheckpointInterval = atoi(val);
1946		break;
1947
1948	  case 'd':		/* delivery mode */
1949		switch (*val)
1950		{
1951		  case '\0':
1952			set_delivery_mode(SM_DELIVER, e);
1953			break;
1954
1955		  case SM_QUEUE:	/* queue only */
1956		  case SM_DEFER:	/* queue only and defer map lookups */
1957#if !QUEUE
1958			syserr("need QUEUE to set -odqueue or -oddefer");
1959#endif /* !QUEUE */
1960			/* FALLTHROUGH */
1961
1962		  case SM_DELIVER:	/* do everything */
1963		  case SM_FORK:		/* fork after verification */
1964			set_delivery_mode(*val, e);
1965			break;
1966
1967		  default:
1968			syserr("Unknown delivery mode %c", *val);
1969			finis(FALSE, EX_USAGE);
1970		}
1971		break;
1972
1973#if !_FFR_REMOVE_AUTOREBUILD
1974	  case 'D':		/* rebuild alias database as needed */
1975		AutoRebuild = atobool(val);
1976		break;
1977#endif /* !_FFR_REMOVE_AUTOREBUILD */
1978
1979	  case 'E':		/* error message header/header file */
1980		if (*val != '\0')
1981			ErrMsgFile = newstr(val);
1982		break;
1983
1984	  case 'e':		/* set error processing mode */
1985		switch (*val)
1986		{
1987		  case EM_QUIET:	/* be silent about it */
1988		  case EM_MAIL:		/* mail back */
1989		  case EM_BERKNET:	/* do berknet error processing */
1990		  case EM_WRITE:	/* write back (or mail) */
1991		  case EM_PRINT:	/* print errors normally (default) */
1992			e->e_errormode = *val;
1993			break;
1994		}
1995		break;
1996
1997	  case 'F':		/* file mode */
1998		FileMode = atooct(val) & 0777;
1999		break;
2000
2001	  case 'f':		/* save Unix-style From lines on front */
2002		SaveFrom = atobool(val);
2003		break;
2004
2005	  case 'G':		/* match recipients against GECOS field */
2006		MatchGecos = atobool(val);
2007		break;
2008
2009	  case 'g':		/* default gid */
2010  g_opt:
2011		if (isascii(*val) && isdigit(*val))
2012			DefGid = atoi(val);
2013		else
2014		{
2015			register struct group *gr;
2016
2017			DefGid = -1;
2018			gr = getgrnam(val);
2019			if (gr == NULL)
2020				syserr("readcf: option %c: unknown group %s",
2021					opt, val);
2022			else
2023				DefGid = gr->gr_gid;
2024		}
2025		break;
2026
2027	  case 'H':		/* help file */
2028		if (val[0] == '\0')
2029			HelpFile = "helpfile";
2030		else
2031			HelpFile = newstr(val);
2032		break;
2033
2034	  case 'h':		/* maximum hop count */
2035		MaxHopCount = atoi(val);
2036		break;
2037
2038	  case 'I':		/* use internet domain name server */
2039#if NAMED_BIND
2040		for (p = val; *p != 0; )
2041		{
2042			bool clearmode;
2043			char *q;
2044			struct resolverflags *rfp;
2045
2046			while (*p == ' ')
2047				p++;
2048			if (*p == '\0')
2049				break;
2050			clearmode = FALSE;
2051			if (*p == '-')
2052				clearmode = TRUE;
2053			else if (*p != '+')
2054				p--;
2055			p++;
2056			q = p;
2057			while (*p != '\0' && !(isascii(*p) && isspace(*p)))
2058				p++;
2059			if (*p != '\0')
2060				*p++ = '\0';
2061			if (strcasecmp(q, "HasWildcardMX") == 0)
2062			{
2063				HasWildcardMX = !clearmode;
2064				continue;
2065			}
2066			for (rfp = ResolverFlags; rfp->rf_name != NULL; rfp++)
2067			{
2068				if (strcasecmp(q, rfp->rf_name) == 0)
2069					break;
2070			}
2071			if (rfp->rf_name == NULL)
2072				syserr("readcf: I option value %s unrecognized", q);
2073			else if (clearmode)
2074				_res.options &= ~rfp->rf_bits;
2075			else
2076				_res.options |= rfp->rf_bits;
2077		}
2078		if (tTd(8, 2))
2079			dprintf("_res.options = %x, HasWildcardMX = %d\n",
2080				(u_int) _res.options, HasWildcardMX);
2081#else /* NAMED_BIND */
2082		usrerr("name server (I option) specified but BIND not compiled in");
2083#endif /* NAMED_BIND */
2084		break;
2085
2086	  case 'i':		/* ignore dot lines in message */
2087		IgnrDot = atobool(val);
2088		break;
2089
2090	  case 'j':		/* send errors in MIME (RFC 1341) format */
2091		SendMIMEErrors = atobool(val);
2092		break;
2093
2094	  case 'J':		/* .forward search path */
2095		ForwardPath = newstr(val);
2096		break;
2097
2098	  case 'k':		/* connection cache size */
2099		MaxMciCache = atoi(val);
2100		if (MaxMciCache < 0)
2101			MaxMciCache = 0;
2102		break;
2103
2104	  case 'K':		/* connection cache timeout */
2105		MciCacheTimeout = convtime(val, 'm');
2106		break;
2107
2108	  case 'l':		/* use Errors-To: header */
2109		UseErrorsTo = atobool(val);
2110		break;
2111
2112	  case 'L':		/* log level */
2113		if (safe || LogLevel < atoi(val))
2114			LogLevel = atoi(val);
2115		break;
2116
2117	  case 'M':		/* define macro */
2118		mid = macid(val, &ep);
2119		p = newstr(ep);
2120		if (!safe)
2121			cleanstrcpy(p, p, MAXNAME);
2122		define(mid, p, CurEnv);
2123		sticky = FALSE;
2124		break;
2125
2126	  case 'm':		/* send to me too */
2127		MeToo = atobool(val);
2128		break;
2129
2130	  case 'n':		/* validate RHS in newaliases */
2131		CheckAliases = atobool(val);
2132		break;
2133
2134	    /* 'N' available -- was "net name" */
2135
2136	  case 'O':		/* daemon options */
2137#if DAEMON
2138		if (!setdaemonoptions(val))
2139		{
2140			syserr("too many daemons defined (%d max)", MAXDAEMONS);
2141		}
2142#else /* DAEMON */
2143		syserr("DaemonPortOptions (O option) set but DAEMON not compiled in");
2144#endif /* DAEMON */
2145		break;
2146
2147	  case 'o':		/* assume old style headers */
2148		if (atobool(val))
2149			CurEnv->e_flags |= EF_OLDSTYLE;
2150		else
2151			CurEnv->e_flags &= ~EF_OLDSTYLE;
2152		break;
2153
2154	  case 'p':		/* select privacy level */
2155		p = val;
2156		for (;;)
2157		{
2158			register struct prival *pv;
2159			extern struct prival PrivacyValues[];
2160
2161			while (isascii(*p) && (isspace(*p) || ispunct(*p)))
2162				p++;
2163			if (*p == '\0')
2164				break;
2165			val = p;
2166			while (isascii(*p) && isalnum(*p))
2167				p++;
2168			if (*p != '\0')
2169				*p++ = '\0';
2170
2171			for (pv = PrivacyValues; pv->pv_name != NULL; pv++)
2172			{
2173				if (strcasecmp(val, pv->pv_name) == 0)
2174					break;
2175			}
2176			if (pv->pv_name == NULL)
2177				syserr("readcf: Op line: %s unrecognized", val);
2178			PrivacyFlags |= pv->pv_flag;
2179		}
2180		sticky = FALSE;
2181		break;
2182
2183	  case 'P':		/* postmaster copy address for returned mail */
2184		PostMasterCopy = newstr(val);
2185		break;
2186
2187	  case 'q':		/* slope of queue only function */
2188		QueueFactor = atoi(val);
2189		break;
2190
2191	  case 'Q':		/* queue directory */
2192		if (val[0] == '\0')
2193		{
2194			QueueDir = "mqueue";
2195		}
2196		else
2197		{
2198			QueueDir = newstr(val);
2199		}
2200		if (RealUid != 0 && !safe)
2201			Warn_Q_option = TRUE;
2202		break;
2203
2204	  case 'R':		/* don't prune routes */
2205		DontPruneRoutes = atobool(val);
2206		break;
2207
2208	  case 'r':		/* read timeout */
2209		if (subopt == NULL)
2210			inittimeouts(val, sticky);
2211		else
2212			settimeout(subopt, val, sticky);
2213		break;
2214
2215	  case 'S':		/* status file */
2216		if (val[0] == '\0')
2217			StatFile = "statistics";
2218		else
2219			StatFile = newstr(val);
2220		break;
2221
2222	  case 's':		/* be super safe, even if expensive */
2223		SuperSafe = atobool(val);
2224		break;
2225
2226	  case 'T':		/* queue timeout */
2227		p = strchr(val, '/');
2228		if (p != NULL)
2229		{
2230			*p++ = '\0';
2231			settimeout("queuewarn", p, sticky);
2232		}
2233		settimeout("queuereturn", val, sticky);
2234		break;
2235
2236	  case 't':		/* time zone name */
2237		TimeZoneSpec = newstr(val);
2238		break;
2239
2240	  case 'U':		/* location of user database */
2241		UdbSpec = newstr(val);
2242		break;
2243
2244	  case 'u':		/* set default uid */
2245		for (p = val; *p != '\0'; p++)
2246		{
2247			if (*p == '.' || *p == '/' || *p == ':')
2248			{
2249				*p++ = '\0';
2250				break;
2251			}
2252		}
2253		if (isascii(*val) && isdigit(*val))
2254		{
2255			DefUid = atoi(val);
2256			setdefuser();
2257		}
2258		else
2259		{
2260			register struct passwd *pw;
2261
2262			DefUid = -1;
2263			pw = sm_getpwnam(val);
2264			if (pw == NULL)
2265				syserr("readcf: option u: unknown user %s", val);
2266			else
2267			{
2268				DefUid = pw->pw_uid;
2269				DefGid = pw->pw_gid;
2270				DefUser = newstr(pw->pw_name);
2271			}
2272		}
2273
2274#ifdef UID_MAX
2275		if (DefUid > UID_MAX)
2276		{
2277			syserr("readcf: option u: uid value (%ld) > UID_MAX (%ld); ignored",
2278				DefUid, UID_MAX);
2279		}
2280#endif /* UID_MAX */
2281
2282		/* handle the group if it is there */
2283		if (*p == '\0')
2284			break;
2285		val = p;
2286		goto g_opt;
2287
2288	  case 'V':		/* fallback MX host */
2289		if (val[0] != '\0')
2290			FallBackMX = newstr(val);
2291		break;
2292
2293	  case 'v':		/* run in verbose mode */
2294		Verbose = atobool(val) ? 1 : 0;
2295		break;
2296
2297	  case 'w':		/* if we are best MX, try host directly */
2298		TryNullMXList = atobool(val);
2299		break;
2300
2301	    /* 'W' available -- was wizard password */
2302
2303	  case 'x':		/* load avg at which to auto-queue msgs */
2304		QueueLA = atoi(val);
2305		break;
2306
2307	  case 'X':		/* load avg at which to auto-reject connections */
2308		RefuseLA = atoi(val);
2309		break;
2310
2311	  case 'y':		/* work recipient factor */
2312		WkRecipFact = atoi(val);
2313		break;
2314
2315	  case 'Y':		/* fork jobs during queue runs */
2316		ForkQueueRuns = atobool(val);
2317		break;
2318
2319	  case 'z':		/* work message class factor */
2320		WkClassFact = atoi(val);
2321		break;
2322
2323	  case 'Z':		/* work time factor */
2324		WkTimeFact = atoi(val);
2325		break;
2326
2327
2328	  case O_QUEUESORTORD:	/* queue sorting order */
2329		switch (*val)
2330		{
2331		  case 'h':	/* Host first */
2332		  case 'H':
2333			QueueSortOrder = QSO_BYHOST;
2334			break;
2335
2336		  case 'p':	/* Priority order */
2337		  case 'P':
2338			QueueSortOrder = QSO_BYPRIORITY;
2339			break;
2340
2341		  case 't':	/* Submission time */
2342		  case 'T':
2343			QueueSortOrder = QSO_BYTIME;
2344			break;
2345
2346		  case 'f':	/* File Name */
2347		  case 'F':
2348			QueueSortOrder = QSO_BYFILENAME;
2349			break;
2350
2351		  default:
2352			syserr("Invalid queue sort order \"%s\"", val);
2353		}
2354		break;
2355
2356#if _FFR_QUEUEDELAY
2357	  case O_QUEUEDELAY:	/* queue delay algorithm */
2358		switch (*val)
2359		{
2360		  case 'e':	/* exponential */
2361		  case 'E':
2362			QueueAlg = QD_EXP;
2363			QueueInitDelay = 10 MINUTES;
2364			QueueMaxDelay = 2 HOURS;
2365			p = strchr(val, '/');
2366			if (p != NULL)
2367			{
2368				char *q;
2369
2370				*p++ = '\0';
2371				q = strchr(p, '/');
2372				if (q != NULL)
2373					*q++ = '\0';
2374				QueueInitDelay = convtime(p, 's');
2375				if (q != NULL)
2376				{
2377					QueueMaxDelay = convtime(q, 's');
2378				}
2379			}
2380			break;
2381
2382		  case 'l':	/* linear */
2383		  case 'L':
2384			QueueAlg = QD_LINEAR;
2385			break;
2386
2387		  default:
2388			syserr("Invalid queue delay algorithm \"%s\"", val);
2389		}
2390		break;
2391#endif /* _FFR_QUEUEDELAY */
2392
2393	  case O_HOSTSFILE:	/* pathname of /etc/hosts file */
2394		HostsFile = newstr(val);
2395		break;
2396
2397	  case O_MQA:		/* minimum queue age between deliveries */
2398		MinQueueAge = convtime(val, 'm');
2399		break;
2400
2401	  case O_DEFCHARSET:	/* default character set for mimefying */
2402		DefaultCharSet = newstr(denlstring(val, TRUE, TRUE));
2403		break;
2404
2405	  case O_SSFILE:	/* service switch file */
2406		ServiceSwitchFile = newstr(val);
2407		break;
2408
2409	  case O_DIALDELAY:	/* delay for dial-on-demand operation */
2410		DialDelay = convtime(val, 's');
2411		break;
2412
2413	  case O_NORCPTACTION:	/* what to do if no recipient */
2414		if (strcasecmp(val, "none") == 0)
2415			NoRecipientAction = NRA_NO_ACTION;
2416		else if (strcasecmp(val, "add-to") == 0)
2417			NoRecipientAction = NRA_ADD_TO;
2418		else if (strcasecmp(val, "add-apparently-to") == 0)
2419			NoRecipientAction = NRA_ADD_APPARENTLY_TO;
2420		else if (strcasecmp(val, "add-bcc") == 0)
2421			NoRecipientAction = NRA_ADD_BCC;
2422		else if (strcasecmp(val, "add-to-undisclosed") == 0)
2423			NoRecipientAction = NRA_ADD_TO_UNDISCLOSED;
2424		else
2425			syserr("Invalid NoRecipientAction: %s", val);
2426		break;
2427
2428	  case O_SAFEFILEENV:	/* chroot() environ for writing to files */
2429		SafeFileEnv = newstr(val);
2430		break;
2431
2432	  case O_MAXMSGSIZE:	/* maximum message size */
2433		MaxMessageSize = atol(val);
2434		break;
2435
2436	  case O_COLONOKINADDR:	/* old style handling of colon addresses */
2437		ColonOkInAddr = atobool(val);
2438		break;
2439
2440	  case O_MAXQUEUERUN:	/* max # of jobs in a single queue run */
2441		MaxQueueRun = atol(val);
2442		break;
2443
2444	  case O_MAXCHILDREN:	/* max # of children of daemon */
2445		MaxChildren = atoi(val);
2446		break;
2447
2448#if _FFR_MAX_FORWARD_ENTRIES
2449	  case O_MAXFORWARD:	/* max # of forward entries */
2450		MaxForwardEntries = atoi(val);
2451		break;
2452#endif /* _FFR_MAX_FORWARD_ENTRIES */
2453
2454	  case O_KEEPCNAMES:	/* don't expand CNAME records */
2455		DontExpandCnames = atobool(val);
2456		break;
2457
2458	  case O_MUSTQUOTE:	/* must quote these characters in phrases */
2459		(void) strlcpy(buf, "@,;:\\()[]", sizeof buf);
2460		if (strlen(val) < (SIZE_T) sizeof buf - 10)
2461			(void) strlcat(buf, val, sizeof buf);
2462		else
2463			printf("Warning: MustQuoteChars too long, ignored.\n");
2464		MustQuoteChars = newstr(buf);
2465		break;
2466
2467	  case O_SMTPGREETING:	/* SMTP greeting message (old $e macro) */
2468		SmtpGreeting = newstr(munchstring(val, NULL, '\0'));
2469		break;
2470
2471	  case O_UNIXFROM:	/* UNIX From_ line (old $l macro) */
2472		UnixFromLine = newstr(munchstring(val, NULL, '\0'));
2473		break;
2474
2475	  case O_OPCHARS:	/* operator characters (old $o macro) */
2476		if (OperatorChars != NULL)
2477			printf("Warning: OperatorChars is being redefined.\n         It should only be set before ruleset definitions.\n");
2478		OperatorChars = newstr(munchstring(val, NULL, '\0'));
2479		break;
2480
2481	  case O_DONTINITGRPS:	/* don't call initgroups(3) */
2482		DontInitGroups = atobool(val);
2483		break;
2484
2485	  case O_SLFH:		/* make sure from fits on one line */
2486		SingleLineFromHeader = atobool(val);
2487		break;
2488
2489	  case O_ABH:		/* allow HELO commands with syntax errors */
2490		AllowBogusHELO = atobool(val);
2491		break;
2492
2493	  case O_CONNTHROT:	/* connection rate throttle */
2494		ConnRateThrottle = atoi(val);
2495		break;
2496
2497	  case O_UGW:		/* group writable files are unsafe */
2498		if (!atobool(val))
2499		{
2500			setbitn(DBS_GROUPWRITABLEFORWARDFILESAFE,
2501				DontBlameSendmail);
2502			setbitn(DBS_GROUPWRITABLEINCLUDEFILESAFE,
2503				DontBlameSendmail);
2504		}
2505		break;
2506
2507	  case O_DBLBOUNCE:	/* address to which to send double bounces */
2508		if (val[0] != '\0')
2509			DoubleBounceAddr = newstr(val);
2510		else
2511			syserr("readcf: option DoubleBounceAddress: value required");
2512		break;
2513
2514	  case O_HSDIR:		/* persistent host status directory */
2515		if (val[0] != '\0')
2516			HostStatDir = newstr(val);
2517		break;
2518
2519	  case O_SINGTHREAD:	/* single thread deliveries (requires hsdir) */
2520		SingleThreadDelivery = atobool(val);
2521		break;
2522
2523	  case O_RUNASUSER:	/* run bulk of code as this user */
2524		for (p = val; *p != '\0'; p++)
2525		{
2526			if (*p == '.' || *p == '/' || *p == ':')
2527			{
2528				*p++ = '\0';
2529				break;
2530			}
2531		}
2532		if (isascii(*val) && isdigit(*val))
2533		{
2534			if (can_setuid)
2535				RunAsUid = atoi(val);
2536		}
2537		else
2538		{
2539			register struct passwd *pw;
2540
2541			pw = sm_getpwnam(val);
2542			if (pw == NULL)
2543				syserr("readcf: option RunAsUser: unknown user %s", val);
2544			else if (can_setuid)
2545			{
2546				if (*p == '\0')
2547					RunAsUserName = newstr(val);
2548				RunAsUid = pw->pw_uid;
2549				RunAsGid = pw->pw_gid;
2550			}
2551		}
2552#ifdef UID_MAX
2553		if (RunAsUid > UID_MAX)
2554		{
2555			syserr("readcf: option RunAsUser: uid value (%ld) > UID_MAX (%ld); ignored",
2556				RunAsUid, UID_MAX);
2557		}
2558#endif /* UID_MAX */
2559		if (*p != '\0')
2560		{
2561			if (isascii(*p) && isdigit(*p))
2562			{
2563				if (can_setuid)
2564					RunAsGid = atoi(p);
2565			}
2566			else
2567			{
2568				register struct group *gr;
2569
2570				gr = getgrnam(p);
2571				if (gr == NULL)
2572					syserr("readcf: option RunAsUser: unknown group %s",
2573						p);
2574				else if (can_setuid)
2575					RunAsGid = gr->gr_gid;
2576			}
2577		}
2578		if (tTd(47, 5))
2579			dprintf("readcf: RunAsUser = %d:%d\n",
2580				(int)RunAsUid, (int)RunAsGid);
2581		break;
2582
2583	  case O_DSN_RRT:
2584		RrtImpliesDsn = atobool(val);
2585		break;
2586
2587	  case O_PIDFILE:
2588		if (PidFile != NULL)
2589			free(PidFile);
2590		PidFile = newstr(val);
2591		break;
2592
2593	case O_DONTBLAMESENDMAIL:
2594		p = val;
2595		for (;;)
2596		{
2597			register struct dbsval *dbs;
2598			extern struct dbsval DontBlameSendmailValues[];
2599
2600			while (isascii(*p) && (isspace(*p) || ispunct(*p)))
2601				p++;
2602			if (*p == '\0')
2603				break;
2604			val = p;
2605			while (isascii(*p) && isalnum(*p))
2606				p++;
2607			if (*p != '\0')
2608				*p++ = '\0';
2609
2610			for (dbs = DontBlameSendmailValues;
2611			     dbs->dbs_name != NULL; dbs++)
2612			{
2613				if (strcasecmp(val, dbs->dbs_name) == 0)
2614					break;
2615			}
2616			if (dbs->dbs_name == NULL)
2617				syserr("readcf: DontBlameSendmail option: %s unrecognized", val);
2618			else if (dbs->dbs_flag == DBS_SAFE)
2619				clrbitmap(DontBlameSendmail);
2620			else
2621				setbitn(dbs->dbs_flag, DontBlameSendmail);
2622		}
2623		sticky = FALSE;
2624		break;
2625
2626	  case O_DPI:
2627		DontProbeInterfaces = atobool(val);
2628		break;
2629
2630	  case O_MAXRCPT:
2631		MaxRcptPerMsg = atoi(val);
2632		break;
2633
2634	  case O_DEADLETTER:
2635		if (DeadLetterDrop != NULL)
2636			free(DeadLetterDrop);
2637		DeadLetterDrop = newstr(val);
2638		break;
2639
2640#if _FFR_DONTLOCKFILESFORREAD_OPTION
2641	  case O_DONTLOCK:
2642		DontLockReadFiles = atobool(val);
2643		break;
2644#endif /* _FFR_DONTLOCKFILESFORREAD_OPTION */
2645
2646	  case O_MAXALIASRCSN:
2647		MaxAliasRecursion = atoi(val);
2648		break;
2649
2650	  case O_CNCTONLYTO:
2651		/* XXX should probably use gethostbyname */
2652#if NETINET || NETINET6
2653# if NETINET6
2654		if (inet_addr(val) == INADDR_NONE)
2655		{
2656			ConnectOnlyTo.sa.sa_family = AF_INET6;
2657			if (inet_pton(AF_INET6, val,
2658				      &ConnectOnlyTo.sin6.sin6_addr) != 1)
2659				syserr("readcf: option ConnectOnlyTo: invalid IP address %s",
2660				       val);
2661		}
2662		else
2663# endif /* NETINET6 */
2664		{
2665			ConnectOnlyTo.sa.sa_family = AF_INET;
2666			ConnectOnlyTo.sin.sin_addr.s_addr = inet_addr(val);
2667		}
2668#endif /* NETINET || NETINET6 */
2669		break;
2670
2671	  case O_TRUSTUSER:
2672#if HASFCHOWN
2673		if (isascii(*val) && isdigit(*val))
2674			TrustedUid = atoi(val);
2675		else
2676		{
2677			register struct passwd *pw;
2678
2679			TrustedUid = 0;
2680			pw = sm_getpwnam(val);
2681			if (pw == NULL)
2682				syserr("readcf: option TrustedUser: unknown user %s", val);
2683			else
2684				TrustedUid = pw->pw_uid;
2685		}
2686
2687# ifdef UID_MAX
2688		if (TrustedUid > UID_MAX)
2689		{
2690			syserr("readcf: option TrustedUser: uid value (%ld) > UID_MAX (%ld)",
2691				TrustedUid, UID_MAX);
2692			TrustedUid = 0;
2693		}
2694# endif /* UID_MAX */
2695#else /* HASFCHOWN */
2696		syserr("readcf: option TrustedUser: can not be used on systems which do not support fchown()");
2697#endif /* HASFCHOWN */
2698		break;
2699
2700	  case O_MAXMIMEHDRLEN:
2701		p = strchr(val, '/');
2702		if (p != NULL)
2703			*p++ = '\0';
2704		MaxMimeHeaderLength = atoi(val);
2705		if (p != NULL && *p != '\0')
2706			MaxMimeFieldLength = atoi(p);
2707		else
2708			MaxMimeFieldLength = MaxMimeHeaderLength / 2;
2709
2710		if (MaxMimeHeaderLength < 0)
2711			MaxMimeHeaderLength = 0;
2712		else if (MaxMimeHeaderLength < 128)
2713			printf("Warning: MaxMimeHeaderLength: header length limit set lower than 128\n");
2714
2715		if (MaxMimeFieldLength < 0)
2716			MaxMimeFieldLength = 0;
2717		else if (MaxMimeFieldLength < 40)
2718			printf("Warning: MaxMimeHeaderLength: field length limit set lower than 40\n");
2719		break;
2720
2721	  case O_CONTROLSOCKET:
2722		if (ControlSocketName != NULL)
2723			free(ControlSocketName);
2724		ControlSocketName = newstr(val);
2725		break;
2726
2727	  case O_MAXHDRSLEN:
2728		MaxHeadersLength = atoi(val);
2729
2730		if (MaxHeadersLength > 0 &&
2731		    MaxHeadersLength < (MAXHDRSLEN / 2))
2732			printf("Warning: MaxHeadersLength: headers length limit set lower than %d\n", (MAXHDRSLEN / 2));
2733		break;
2734
2735	  case O_PROCTITLEPREFIX:
2736		if (ProcTitlePrefix != NULL)
2737			free(ProcTitlePrefix);
2738		ProcTitlePrefix = newstr(val);
2739		break;
2740
2741#if SASL
2742	  case O_SASLINFO:
2743#if _FFR_ALLOW_SASLINFO
2744		/*
2745		**  Allow users to select their own authinfo file.
2746		**  However, this is not a "perfect" solution.
2747		**  If mail is queued, the authentication info
2748		**  will not be used in subsequent delivery attempts.
2749		**  If we really want to support this, then it has
2750		**  to be stored in the queue file.
2751		*/
2752		if (!bitset(SUBMIT_MSA, SubmitMode) && RealUid != 0 &&
2753		    RunAsUid != RealUid)
2754		{
2755			errno = 0;
2756			syserr("Error: %s only allowed with -U\n",
2757				o->o_name == NULL ? "<unknown>" : o->o_name);
2758			ExitStat = EX_USAGE;
2759			break;
2760		}
2761#endif /* _FFR_ALLOW_SASLINFO */
2762		if (SASLInfo != NULL)
2763			free(SASLInfo);
2764		SASLInfo = newstr(val);
2765		break;
2766
2767	  case O_SASLMECH:
2768		if (AuthMechanisms != NULL)
2769			free(AuthMechanisms);
2770		if (*val != '\0')
2771			AuthMechanisms = newstr(val);
2772		else
2773			AuthMechanisms = NULL;
2774		break;
2775
2776	  case O_SASLOPTS:
2777		while (val != NULL && *val != '\0')
2778		{
2779			switch(*val)
2780			{
2781			  case 'A':
2782				SASLOpts |= SASL_AUTH_AUTH;
2783				break;
2784# if _FFR_SASL_OPTS
2785			  case 'a':
2786				SASLOpts |= SASL_SEC_NOACTIVE;
2787				break;
2788			  case 'c':
2789				SASLOpts |= SASL_SEC_PASS_CREDENTIALS;
2790				break;
2791			  case 'd':
2792				SASLOpts |= SASL_SEC_NODICTIONARY;
2793				break;
2794			  case 'f':
2795				SASLOpts |= SASL_SEC_FORWARD_SECRECY;
2796				break;
2797			  case 'p':
2798				SASLOpts |= SASL_SEC_NOPLAINTEXT;
2799				break;
2800			  case 'y':
2801				SASLOpts |= SASL_SEC_NOANONYMOUS;
2802				break;
2803# endif /* _FFR_SASL_OPTS */
2804			  default:
2805				printf("Warning: Option: %s unknown parameter '%c'\n",
2806					o->o_name == NULL ? "<unknown>"
2807							  : o->o_name,
2808					(isascii(*val) && isprint(*val)) ? *val
2809									 : '?');
2810				break;
2811			}
2812			++val;
2813			val = strpbrk(val, ", \t");
2814			if (val != NULL)
2815				++val;
2816		}
2817		break;
2818
2819#else /* SASL */
2820	  case O_SASLINFO:
2821	  case O_SASLMECH:
2822	  case O_SASLOPTS:
2823		printf("Warning: Option: %s requires SASL support (-DSASL)\n",
2824			o->o_name == NULL ? "<unknown>" : o->o_name);
2825		break;
2826#endif /* SASL */
2827
2828#if STARTTLS
2829	  case O_SRVCERTFILE:
2830		if (SrvCERTfile != NULL)
2831			free(SrvCERTfile);
2832		SrvCERTfile = newstr(val);
2833		break;
2834
2835	  case O_SRVKEYFILE:
2836		if (Srvkeyfile != NULL)
2837			free(Srvkeyfile);
2838		Srvkeyfile = newstr(val);
2839		break;
2840
2841	  case O_CLTCERTFILE:
2842		if (CltCERTfile != NULL)
2843			free(CltCERTfile);
2844		CltCERTfile = newstr(val);
2845		break;
2846
2847	  case O_CLTKEYFILE:
2848		if (Cltkeyfile != NULL)
2849			free(Cltkeyfile);
2850		Cltkeyfile = newstr(val);
2851		break;
2852
2853	  case O_CACERTFILE:
2854		if (CACERTfile != NULL)
2855			free(CACERTfile);
2856		CACERTfile = newstr(val);
2857		break;
2858
2859	  case O_CACERTPATH:
2860		if (CACERTpath != NULL)
2861			free(CACERTpath);
2862		CACERTpath = newstr(val);
2863		break;
2864
2865	  case O_DHPARAMS:
2866		if (DHParams != NULL)
2867			free(DHParams);
2868		DHParams = newstr(val);
2869		break;
2870
2871#  if _FFR_TLS_1
2872	  case O_DHPARAMS5:
2873		if (DHParams5 != NULL)
2874			free(DHParams5);
2875		DHParams5 = newstr(val);
2876		break;
2877
2878	  case O_CIPHERLIST:
2879		if (CipherList != NULL)
2880			free(CipherList);
2881		CipherList = newstr(val);
2882		break;
2883#  endif /* _FFR_TLS_1 */
2884
2885	  case O_RANDFILE:
2886		if (RandFile != NULL)
2887			free(RandFile);
2888		RandFile= newstr(val);
2889		break;
2890
2891# else /* STARTTLS */
2892	  case O_SRVCERTFILE:
2893	  case O_SRVKEYFILE:
2894	  case O_CLTCERTFILE:
2895	  case O_CLTKEYFILE:
2896	  case O_CACERTFILE:
2897	  case O_CACERTPATH:
2898	  case O_DHPARAMS:
2899#  if _FFR_TLS_1
2900	  case O_DHPARAMS5:
2901	  case O_CIPHERLIST:
2902#  endif /* _FFR_TLS_1 */
2903	  case O_RANDFILE:
2904		printf("Warning: Option: %s requires TLS support\n",
2905			o->o_name == NULL ? "<unknown>" : o->o_name);
2906		break;
2907
2908# endif /* STARTTLS */
2909
2910	  case O_CLIENTPORT:
2911#if DAEMON
2912		setclientoptions(val);
2913#else /* DAEMON */
2914		syserr("ClientPortOptions (O option) set but DAEMON not compiled in");
2915#endif /* DAEMON */
2916		break;
2917
2918	  case O_DF_BUFSIZE:
2919		DataFileBufferSize = atoi(val);
2920		break;
2921
2922	  case O_XF_BUFSIZE:
2923		XscriptFileBufferSize = atoi(val);
2924		break;
2925
2926	  case O_LDAPDEFAULTSPEC:
2927#ifdef LDAPMAP
2928		ldapmap_set_defaults(val);
2929#else /* LDAPMAP */
2930		printf("Warning: Option: %s requires LDAP support (-DLDAPMAP)\n",
2931			o->o_name == NULL ? "<unknown>" : o->o_name);
2932#endif /* LDAPMAP */
2933		break;
2934
2935#if _FFR_MILTER
2936	  case O_INPUTMILTER:
2937		InputFilterList = newstr(val);
2938		break;
2939
2940	  case O_MILTER:
2941		milter_set_option(subopt, val, sticky);
2942		break;
2943#endif /* _FFR_MILTER */
2944
2945#if _FFR_QUEUE_FILE_MODE
2946	  case O_QUEUE_FILE_MODE:	/* queue file mode */
2947		QueueFileMode = atooct(val) & 0777;
2948		break;
2949#endif /* _FFR_QUEUE_FILE_MODE */
2950
2951	  default:
2952		if (tTd(37, 1))
2953		{
2954			if (isascii(opt) && isprint(opt))
2955				dprintf("Warning: option %c unknown\n", opt);
2956			else
2957				dprintf("Warning: option 0x%x unknown\n", opt);
2958		}
2959		break;
2960	}
2961
2962	/*
2963	**  Options with suboptions are responsible for taking care
2964	**  of sticky-ness (e.g., that a command line setting is kept
2965	**  when reading in the sendmail.cf file).  This has to be done
2966	**  when the suboptions are parsed since each suboption must be
2967	**  sticky, not the root option.
2968	*/
2969
2970	if (sticky && !bitset(OI_SUBOPT, o->o_flags))
2971		setbitn(opt, StickyOpt);
2972}
2973/*
2974**  SETCLASS -- set a string into a class
2975**
2976**	Parameters:
2977**		class -- the class to put the string in.
2978**		str -- the string to enter
2979**
2980**	Returns:
2981**		none.
2982**
2983**	Side Effects:
2984**		puts the word into the symbol table.
2985*/
2986
2987void
2988setclass(class, str)
2989	int class;
2990	char *str;
2991{
2992	register STAB *s;
2993
2994	if ((*str & 0377) == MATCHCLASS)
2995	{
2996		int mid;
2997
2998		str++;
2999		mid = macid(str, NULL);
3000		if (mid == '\0')
3001			return;
3002
3003		if (tTd(37, 8))
3004			dprintf("setclass(%s, $=%s)\n",
3005				macname(class), macname(mid));
3006		copy_class(mid, class);
3007	}
3008	else
3009	{
3010		if (tTd(37, 8))
3011			dprintf("setclass(%s, %s)\n", macname(class), str);
3012
3013		s = stab(str, ST_CLASS, ST_ENTER);
3014		setbitn(class, s->s_class);
3015	}
3016}
3017/*
3018**  MAKEMAPENTRY -- create a map entry
3019**
3020**	Parameters:
3021**		line -- the config file line
3022**
3023**	Returns:
3024**		A pointer to the map that has been created.
3025**		NULL if there was a syntax error.
3026**
3027**	Side Effects:
3028**		Enters the map into the dictionary.
3029*/
3030
3031MAP *
3032makemapentry(line)
3033	char *line;
3034{
3035	register char *p;
3036	char *mapname;
3037	char *classname;
3038	register STAB *s;
3039	STAB *class;
3040
3041	for (p = line; isascii(*p) && isspace(*p); p++)
3042		continue;
3043	if (!(isascii(*p) && isalnum(*p)))
3044	{
3045		syserr("readcf: config K line: no map name");
3046		return NULL;
3047	}
3048
3049	mapname = p;
3050	while ((isascii(*++p) && isalnum(*p)) || *p == '_' || *p == '.')
3051		continue;
3052	if (*p != '\0')
3053		*p++ = '\0';
3054	while (isascii(*p) && isspace(*p))
3055		p++;
3056	if (!(isascii(*p) && isalnum(*p)))
3057	{
3058		syserr("readcf: config K line, map %s: no map class", mapname);
3059		return NULL;
3060	}
3061	classname = p;
3062	while (isascii(*++p) && isalnum(*p))
3063		continue;
3064	if (*p != '\0')
3065		*p++ = '\0';
3066	while (isascii(*p) && isspace(*p))
3067		p++;
3068
3069	/* look up the class */
3070	class = stab(classname, ST_MAPCLASS, ST_FIND);
3071	if (class == NULL)
3072	{
3073		syserr("readcf: map %s: class %s not available", mapname, classname);
3074		return NULL;
3075	}
3076
3077	/* enter the map */
3078	s = stab(mapname, ST_MAP, ST_ENTER);
3079	s->s_map.map_class = &class->s_mapclass;
3080	s->s_map.map_mname = newstr(mapname);
3081
3082	if (class->s_mapclass.map_parse(&s->s_map, p))
3083		s->s_map.map_mflags |= MF_VALID;
3084
3085	if (tTd(37, 5))
3086	{
3087		dprintf("map %s, class %s, flags %lx, file %s,\n",
3088			s->s_map.map_mname, s->s_map.map_class->map_cname,
3089			s->s_map.map_mflags,
3090			s->s_map.map_file == NULL ? "(null)" : s->s_map.map_file);
3091		dprintf("\tapp %s, domain %s, rebuild %s\n",
3092			s->s_map.map_app == NULL ? "(null)" : s->s_map.map_app,
3093			s->s_map.map_domain == NULL ? "(null)" : s->s_map.map_domain,
3094			s->s_map.map_rebuild == NULL ? "(null)" : s->s_map.map_rebuild);
3095	}
3096
3097	return &s->s_map;
3098}
3099/*
3100**  STRTORWSET -- convert string to rewriting set number
3101**
3102**	Parameters:
3103**		p -- the pointer to the string to decode.
3104**		endp -- if set, store the trailing delimiter here.
3105**		stabmode -- ST_ENTER to create this entry, ST_FIND if
3106**			it must already exist.
3107**
3108**	Returns:
3109**		The appropriate ruleset number.
3110**		-1 if it is not valid (error already printed)
3111*/
3112
3113int
3114strtorwset(p, endp, stabmode)
3115	char *p;
3116	char **endp;
3117	int stabmode;
3118{
3119	int ruleset;
3120	static int nextruleset = MAXRWSETS;
3121
3122	while (isascii(*p) && isspace(*p))
3123		p++;
3124	if (!isascii(*p))
3125	{
3126		syserr("invalid ruleset name: \"%.20s\"", p);
3127		return -1;
3128	}
3129	if (isdigit(*p))
3130	{
3131		ruleset = strtol(p, endp, 10);
3132		if (ruleset >= MAXRWSETS / 2 || ruleset < 0)
3133		{
3134			syserr("bad ruleset %d (%d max)",
3135				ruleset, MAXRWSETS / 2);
3136			ruleset = -1;
3137		}
3138	}
3139	else
3140	{
3141		STAB *s;
3142		char delim;
3143		char *q = NULL;
3144
3145		q = p;
3146		while (*p != '\0' && isascii(*p) &&
3147		       (isalnum(*p) || *p == '_'))
3148			p++;
3149		if (q == p || !(isascii(*q) && isalpha(*q)))
3150		{
3151			/* no valid characters */
3152			syserr("invalid ruleset name: \"%.20s\"", q);
3153			return -1;
3154		}
3155		while (isascii(*p) && isspace(*p))
3156			*p++ = '\0';
3157		delim = *p;
3158		if (delim != '\0')
3159			*p = '\0';
3160		s = stab(q, ST_RULESET, stabmode);
3161		if (delim != '\0')
3162			*p = delim;
3163
3164		if (s == NULL)
3165			return -1;
3166
3167		if (stabmode == ST_ENTER && delim == '=')
3168		{
3169			while (isascii(*++p) && isspace(*p))
3170				continue;
3171			if (!(isascii(*p) && isdigit(*p)))
3172			{
3173				syserr("bad ruleset definition \"%s\" (number required after `=')", q);
3174				ruleset = -1;
3175			}
3176			else
3177			{
3178				ruleset = strtol(p, endp, 10);
3179				if (ruleset >= MAXRWSETS / 2 || ruleset < 0)
3180				{
3181					syserr("bad ruleset number %d in \"%s\" (%d max)",
3182						ruleset, q, MAXRWSETS / 2);
3183					ruleset = -1;
3184				}
3185			}
3186		}
3187		else
3188		{
3189			if (endp != NULL)
3190				*endp = p;
3191			if (s->s_ruleset >= 0)
3192				ruleset = s->s_ruleset;
3193			else if ((ruleset = --nextruleset) < MAXRWSETS / 2)
3194			{
3195				syserr("%s: too many named rulesets (%d max)",
3196					q, MAXRWSETS / 2);
3197				ruleset = -1;
3198			}
3199		}
3200		if (s->s_ruleset >= 0 &&
3201		    ruleset >= 0 &&
3202		    ruleset != s->s_ruleset)
3203		{
3204			syserr("%s: ruleset changed value (old %d, new %d)",
3205				q, s->s_ruleset, ruleset);
3206			ruleset = s->s_ruleset;
3207		}
3208		else if (ruleset >= 0)
3209		{
3210			s->s_ruleset = ruleset;
3211		}
3212		if (stabmode == ST_ENTER)
3213		{
3214			char *h = NULL;
3215
3216			if (RuleSetNames[ruleset] != NULL)
3217				free(RuleSetNames[ruleset]);
3218			if (delim != '\0' && (h = strchr(q, delim)) != NULL)
3219				*h = '\0';
3220			RuleSetNames[ruleset] = newstr(q);
3221			if (delim == '/' && h != NULL)
3222				*h = delim;	/* put back delim */
3223		}
3224	}
3225	return ruleset;
3226}
3227/*
3228**  SETTIMEOUT -- set an individual timeout
3229**
3230**	Parameters:
3231**		name -- the name of the timeout.
3232**		val -- the value of the timeout.
3233**		sticky -- if set, don't let other setoptions override
3234**			this value.
3235**
3236**	Returns:
3237**		none.
3238*/
3239
3240/* set if Timeout sub-option is stuck */
3241static BITMAP256	StickyTimeoutOpt;
3242
3243static struct timeoutinfo
3244{
3245	char	*to_name;	/* long name of timeout */
3246	u_char	to_code;	/* code for option */
3247} TimeOutTab[] =
3248{
3249#define TO_INITIAL			0x01
3250	{ "initial",			TO_INITIAL			},
3251#define TO_MAIL				0x02
3252	{ "mail",			TO_MAIL				},
3253#define TO_RCPT				0x03
3254	{ "rcpt",			TO_RCPT				},
3255#define TO_DATAINIT			0x04
3256	{ "datainit",			TO_DATAINIT			},
3257#define TO_DATABLOCK			0x05
3258	{ "datablock",			TO_DATABLOCK			},
3259#define TO_DATAFINAL			0x06
3260	{ "datafinal",			TO_DATAFINAL			},
3261#define TO_COMMAND			0x07
3262	{ "command",			TO_COMMAND			},
3263#define TO_RSET				0x08
3264	{ "rset",			TO_RSET				},
3265#define TO_HELO				0x09
3266	{ "helo",			TO_HELO				},
3267#define TO_QUIT				0x0A
3268	{ "quit",			TO_QUIT				},
3269#define TO_MISC				0x0B
3270	{ "misc",			TO_MISC				},
3271#define TO_IDENT			0x0C
3272	{ "ident",			TO_IDENT			},
3273#define TO_FILEOPEN			0x0D
3274	{ "fileopen",			TO_FILEOPEN			},
3275#define TO_CONNECT			0x0E
3276	{ "connect",			TO_CONNECT			},
3277#define TO_ICONNECT			0x0F
3278	{ "iconnect",			TO_ICONNECT			},
3279#define TO_QUEUEWARN			0x10
3280	{ "queuewarn",			TO_QUEUEWARN			},
3281	{ "queuewarn.*",		TO_QUEUEWARN			},
3282#define TO_QUEUEWARN_NORMAL		0x11
3283	{ "queuewarn.normal",		TO_QUEUEWARN_NORMAL		},
3284#define TO_QUEUEWARN_URGENT		0x12
3285	{ "queuewarn.urgent",		TO_QUEUEWARN_URGENT		},
3286#define TO_QUEUEWARN_NON_URGENT		0x13
3287	{ "queuewarn.non-urgent",	TO_QUEUEWARN_NON_URGENT		},
3288#define TO_QUEUERETURN			0x14
3289	{ "queuereturn",		TO_QUEUERETURN			},
3290	{ "queuereturn.*",		TO_QUEUERETURN			},
3291#define TO_QUEUERETURN_NORMAL		0x15
3292	{ "queuereturn.normal",		TO_QUEUERETURN_NORMAL		},
3293#define TO_QUEUERETURN_URGENT		0x16
3294	{ "queuereturn.urgent",		TO_QUEUERETURN_URGENT		},
3295#define TO_QUEUERETURN_NON_URGENT	0x17
3296	{ "queuereturn.non-urgent",	TO_QUEUERETURN_NON_URGENT	},
3297#define TO_HOSTSTATUS			0x18
3298	{ "hoststatus",			TO_HOSTSTATUS			},
3299#define TO_RESOLVER_RETRANS		0x19
3300	{ "resolver.retrans",		TO_RESOLVER_RETRANS		},
3301#define TO_RESOLVER_RETRANS_NORMAL	0x1A
3302	{ "resolver.retrans.normal",	TO_RESOLVER_RETRANS_NORMAL	},
3303#define TO_RESOLVER_RETRANS_FIRST	0x1B
3304	{ "resolver.retrans.first",	TO_RESOLVER_RETRANS_FIRST	},
3305#define TO_RESOLVER_RETRY		0x1C
3306	{ "resolver.retry",		TO_RESOLVER_RETRY		},
3307#define TO_RESOLVER_RETRY_NORMAL	0x1D
3308	{ "resolver.retry.normal",	TO_RESOLVER_RETRY_NORMAL	},
3309#define TO_RESOLVER_RETRY_FIRST		0x1E
3310	{ "resolver.retry.first",	TO_RESOLVER_RETRY_FIRST		},
3311#define TO_CONTROL			0x1F
3312	{ "control",			TO_CONTROL			},
3313	{ NULL,				0				},
3314};
3315
3316
3317static void
3318settimeout(name, val, sticky)
3319	char *name;
3320	char *val;
3321	bool sticky;
3322{
3323	register struct timeoutinfo *to;
3324	int i;
3325	time_t toval;
3326
3327	if (tTd(37, 2))
3328		dprintf("settimeout(%s = %s)", name, val);
3329
3330	for (to = TimeOutTab; to->to_name != NULL; to++)
3331	{
3332		if (strcasecmp(to->to_name, name) == 0)
3333			break;
3334	}
3335
3336	if (to->to_name == NULL)
3337		syserr("settimeout: invalid timeout %s", name);
3338
3339	/*
3340	**  See if this option is preset for us.
3341	*/
3342
3343	if (!sticky && bitnset(to->to_code, StickyTimeoutOpt))
3344	{
3345		if (tTd(37, 2))
3346			dprintf(" (ignored)\n");
3347		return;
3348	}
3349
3350	if (tTd(37, 2))
3351		dprintf("\n");
3352
3353	toval = convtime(val, 'm');
3354
3355	switch (to->to_code)
3356	{
3357	  case TO_INITIAL:
3358		TimeOuts.to_initial = toval;
3359		break;
3360
3361	  case TO_MAIL:
3362		TimeOuts.to_mail = toval;
3363		break;
3364
3365	  case TO_RCPT:
3366		TimeOuts.to_rcpt = toval;
3367		break;
3368
3369	  case TO_DATAINIT:
3370		TimeOuts.to_datainit = toval;
3371		break;
3372
3373	  case TO_DATABLOCK:
3374		TimeOuts.to_datablock = toval;
3375		break;
3376
3377	  case TO_DATAFINAL:
3378		TimeOuts.to_datafinal = toval;
3379		break;
3380
3381	  case TO_COMMAND:
3382		TimeOuts.to_nextcommand = toval;
3383		break;
3384
3385	  case TO_RSET:
3386		TimeOuts.to_rset = toval;
3387		break;
3388
3389	  case TO_HELO:
3390		TimeOuts.to_helo = toval;
3391		break;
3392
3393	  case TO_QUIT:
3394		TimeOuts.to_quit = toval;
3395		break;
3396
3397	  case TO_MISC:
3398		TimeOuts.to_miscshort = toval;
3399		break;
3400
3401	  case TO_IDENT:
3402		TimeOuts.to_ident = toval;
3403		break;
3404
3405	  case TO_FILEOPEN:
3406		TimeOuts.to_fileopen = toval;
3407		break;
3408
3409	  case TO_CONNECT:
3410		TimeOuts.to_connect = toval;
3411		break;
3412
3413	  case TO_ICONNECT:
3414		TimeOuts.to_iconnect = toval;
3415		break;
3416
3417	  case TO_QUEUEWARN:
3418		toval = convtime(val, 'h');
3419		TimeOuts.to_q_warning[TOC_NORMAL] = toval;
3420		TimeOuts.to_q_warning[TOC_URGENT] = toval;
3421		TimeOuts.to_q_warning[TOC_NONURGENT] = toval;
3422		break;
3423
3424	  case TO_QUEUEWARN_NORMAL:
3425		toval = convtime(val, 'h');
3426		TimeOuts.to_q_warning[TOC_NORMAL] = toval;
3427		break;
3428
3429	  case TO_QUEUEWARN_URGENT:
3430		toval = convtime(val, 'h');
3431		TimeOuts.to_q_warning[TOC_URGENT] = toval;
3432		break;
3433
3434	  case TO_QUEUEWARN_NON_URGENT:
3435		toval = convtime(val, 'h');
3436		TimeOuts.to_q_warning[TOC_NONURGENT] = toval;
3437		break;
3438
3439	  case TO_QUEUERETURN:
3440		toval = convtime(val, 'd');
3441		TimeOuts.to_q_return[TOC_NORMAL] = toval;
3442		TimeOuts.to_q_return[TOC_URGENT] = toval;
3443		TimeOuts.to_q_return[TOC_NONURGENT] = toval;
3444		break;
3445
3446	  case TO_QUEUERETURN_NORMAL:
3447		toval = convtime(val, 'd');
3448		TimeOuts.to_q_return[TOC_NORMAL] = toval;
3449		break;
3450
3451	  case TO_QUEUERETURN_URGENT:
3452		toval = convtime(val, 'd');
3453		TimeOuts.to_q_return[TOC_URGENT] = toval;
3454		break;
3455
3456	  case TO_QUEUERETURN_NON_URGENT:
3457		toval = convtime(val, 'd');
3458		TimeOuts.to_q_return[TOC_NONURGENT] = toval;
3459		break;
3460
3461
3462	  case TO_HOSTSTATUS:
3463		MciInfoTimeout = toval;
3464		break;
3465
3466	  case TO_RESOLVER_RETRANS:
3467		toval = convtime(val, 's');
3468		TimeOuts.res_retrans[RES_TO_DEFAULT] = toval;
3469		TimeOuts.res_retrans[RES_TO_FIRST] = toval;
3470		TimeOuts.res_retrans[RES_TO_NORMAL] = toval;
3471		break;
3472
3473	  case TO_RESOLVER_RETRY:
3474		i = atoi(val);
3475		TimeOuts.res_retry[RES_TO_DEFAULT] = i;
3476		TimeOuts.res_retry[RES_TO_FIRST] = i;
3477		TimeOuts.res_retry[RES_TO_NORMAL] = i;
3478		break;
3479
3480	  case TO_RESOLVER_RETRANS_NORMAL:
3481		TimeOuts.res_retrans[RES_TO_NORMAL] = convtime(val, 's');
3482		break;
3483
3484	  case TO_RESOLVER_RETRY_NORMAL:
3485		TimeOuts.res_retry[RES_TO_NORMAL] = atoi(val);
3486		break;
3487
3488	  case TO_RESOLVER_RETRANS_FIRST:
3489		TimeOuts.res_retrans[RES_TO_FIRST] = convtime(val, 's');
3490		break;
3491
3492	  case TO_RESOLVER_RETRY_FIRST:
3493		TimeOuts.res_retry[RES_TO_FIRST] = atoi(val);
3494		break;
3495
3496	  case TO_CONTROL:
3497		TimeOuts.to_control = toval;
3498		break;
3499
3500	  default:
3501		syserr("settimeout: invalid timeout %s", name);
3502		break;
3503	}
3504
3505	if (sticky)
3506		setbitn(to->to_code, StickyTimeoutOpt);
3507}
3508/*
3509**  INITTIMEOUTS -- parse and set timeout values
3510**
3511**	Parameters:
3512**		val -- a pointer to the values.  If NULL, do initial
3513**			settings.
3514**		sticky -- if set, don't let other setoptions override
3515**			this suboption value.
3516**
3517**	Returns:
3518**		none.
3519**
3520**	Side Effects:
3521**		Initializes the TimeOuts structure
3522*/
3523
3524void
3525inittimeouts(val, sticky)
3526	register char *val;
3527	bool sticky;
3528{
3529	register char *p;
3530
3531	if (tTd(37, 2))
3532		dprintf("inittimeouts(%s)\n", val == NULL ? "<NULL>" : val);
3533	if (val == NULL)
3534	{
3535		TimeOuts.to_connect = (time_t) 0 SECONDS;
3536		TimeOuts.to_initial = (time_t) 5 MINUTES;
3537		TimeOuts.to_helo = (time_t) 5 MINUTES;
3538		TimeOuts.to_mail = (time_t) 10 MINUTES;
3539		TimeOuts.to_rcpt = (time_t) 1 HOUR;
3540		TimeOuts.to_datainit = (time_t) 5 MINUTES;
3541		TimeOuts.to_datablock = (time_t) 1 HOUR;
3542		TimeOuts.to_datafinal = (time_t) 1 HOUR;
3543		TimeOuts.to_rset = (time_t) 5 MINUTES;
3544		TimeOuts.to_quit = (time_t) 2 MINUTES;
3545		TimeOuts.to_nextcommand = (time_t) 1 HOUR;
3546		TimeOuts.to_miscshort = (time_t) 2 MINUTES;
3547#if IDENTPROTO
3548		TimeOuts.to_ident = (time_t) 5 SECONDS;
3549#else /* IDENTPROTO */
3550		TimeOuts.to_ident = (time_t) 0 SECONDS;
3551#endif /* IDENTPROTO */
3552		TimeOuts.to_fileopen = (time_t) 60 SECONDS;
3553		TimeOuts.to_control = (time_t) 2 MINUTES;
3554		if (tTd(37, 5))
3555		{
3556			dprintf("Timeouts:\n");
3557			dprintf("  connect = %ld\n", (long)TimeOuts.to_connect);
3558			dprintf("  initial = %ld\n", (long)TimeOuts.to_initial);
3559			dprintf("  helo = %ld\n", (long)TimeOuts.to_helo);
3560			dprintf("  mail = %ld\n", (long)TimeOuts.to_mail);
3561			dprintf("  rcpt = %ld\n", (long)TimeOuts.to_rcpt);
3562			dprintf("  datainit = %ld\n", (long)TimeOuts.to_datainit);
3563			dprintf("  datablock = %ld\n", (long)TimeOuts.to_datablock);
3564			dprintf("  datafinal = %ld\n", (long)TimeOuts.to_datafinal);
3565			dprintf("  rset = %ld\n", (long)TimeOuts.to_rset);
3566			dprintf("  quit = %ld\n", (long)TimeOuts.to_quit);
3567			dprintf("  nextcommand = %ld\n", (long)TimeOuts.to_nextcommand);
3568			dprintf("  miscshort = %ld\n", (long)TimeOuts.to_miscshort);
3569			dprintf("  ident = %ld\n", (long)TimeOuts.to_ident);
3570			dprintf("  fileopen = %ld\n", (long)TimeOuts.to_fileopen);
3571			dprintf("  control = %ld\n", (long)TimeOuts.to_control);
3572		}
3573		return;
3574	}
3575
3576	for (;; val = p)
3577	{
3578		while (isascii(*val) && isspace(*val))
3579			val++;
3580		if (*val == '\0')
3581			break;
3582		for (p = val; *p != '\0' && *p != ','; p++)
3583			continue;
3584		if (*p != '\0')
3585			*p++ = '\0';
3586
3587		if (isascii(*val) && isdigit(*val))
3588		{
3589			/* old syntax -- set everything */
3590			TimeOuts.to_mail = convtime(val, 'm');
3591			TimeOuts.to_rcpt = TimeOuts.to_mail;
3592			TimeOuts.to_datainit = TimeOuts.to_mail;
3593			TimeOuts.to_datablock = TimeOuts.to_mail;
3594			TimeOuts.to_datafinal = TimeOuts.to_mail;
3595			TimeOuts.to_nextcommand = TimeOuts.to_mail;
3596			if (sticky)
3597			{
3598				setbitn(TO_MAIL, StickyTimeoutOpt);
3599				setbitn(TO_RCPT, StickyTimeoutOpt);
3600				setbitn(TO_DATAINIT, StickyTimeoutOpt);
3601				setbitn(TO_DATABLOCK, StickyTimeoutOpt);
3602				setbitn(TO_DATAFINAL, StickyTimeoutOpt);
3603				setbitn(TO_COMMAND, StickyTimeoutOpt);
3604			}
3605			continue;
3606		}
3607		else
3608		{
3609			register char *q = strchr(val, ':');
3610
3611			if (q == NULL && (q = strchr(val, '=')) == NULL)
3612			{
3613				/* syntax error */
3614				continue;
3615			}
3616			*q++ = '\0';
3617			settimeout(val, q, sticky);
3618		}
3619	}
3620}
3621