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