xref: /freebsd/contrib/sendmail/src/readcf.c (revision 5069714534cba67f1985e6dfe23b145178372b5f)
1 /*
2  * Copyright (c) 1998-2000 Sendmail, Inc. and its suppliers.
3  *	All rights reserved.
4  * Copyright (c) 1983, 1995-1997 Eric P. Allman.  All rights reserved.
5  * Copyright (c) 1988, 1993
6  *	The Regents of the University of California.  All rights reserved.
7  *
8  * By using this file, you agree to the terms and conditions set
9  * forth in the LICENSE file which can be found at the top level of
10  * the sendmail distribution.
11  *
12  */
13 
14 #ifndef lint
15 static char id[] = "@(#)$Id: readcf.c,v 8.382.4.31 2000/12/18 18:00:43 ca 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 
30 static void	fileclass __P((int, char *, char *, bool, bool));
31 static char	**makeargv __P((char *));
32 static void	settimeout __P((char *, char *, bool));
33 static 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 
82 void
83 readcf(cfname, safe, e)
84 	char *cfname;
85 	bool safe;
86 	register ENVELOPE *e;
87 {
88 	FILE *cf;
89 	int ruleset = -1;
90 	char *q;
91 	struct rewrite *rwp = NULL;
92 	char *bp;
93 	auto char *ep;
94 	int nfuzzy;
95 	char *file;
96 	bool optional;
97 	int mid;
98 	register char *p;
99 	long sff = SFF_OPENASROOT;
100 	struct stat statb;
101 	char buf[MAXLINE];
102 	char exbuf[MAXLINE];
103 	char pvpbuf[MAXLINE + MAXATOM];
104 	static char *null_list[1] = { NULL };
105 	extern u_char TokTypeNoC[];
106 
107 	FileName = cfname;
108 	LineNumber = 0;
109 
110 	if (DontLockReadFiles)
111 		sff |= SFF_NOLOCK;
112 	cf = safefopen(cfname, O_RDONLY, 0444, sff);
113 	if (cf == NULL)
114 	{
115 		syserr("cannot open");
116 		finis(FALSE, EX_OSFILE);
117 	}
118 
119 	if (fstat(fileno(cf), &statb) < 0)
120 	{
121 		syserr("cannot fstat");
122 		finis(FALSE, EX_OSFILE);
123 	}
124 
125 	if (!S_ISREG(statb.st_mode))
126 	{
127 		syserr("not a plain file");
128 		finis(FALSE, EX_OSFILE);
129 	}
130 
131 	if (OpMode != MD_TEST && bitset(S_IWGRP|S_IWOTH, statb.st_mode))
132 	{
133 		if (OpMode == MD_DAEMON || OpMode == MD_INITALIAS)
134 			fprintf(stderr, "%s: WARNING: dangerous write permissions\n",
135 				FileName);
136 		if (LogLevel > 0)
137 			sm_syslog(LOG_CRIT, NOQID,
138 				  "%s: WARNING: dangerous write permissions",
139 				  FileName);
140 	}
141 
142 #ifdef XLA
143 	xla_zero();
144 #endif /* XLA */
145 
146 	while ((bp = fgetfolded(buf, sizeof buf, cf)) != NULL)
147 	{
148 		if (bp[0] == '#')
149 		{
150 			if (bp != buf)
151 				free(bp);
152 			continue;
153 		}
154 
155 		/* do macro expansion mappings */
156 		translate_dollars(bp);
157 
158 		/* interpret this line */
159 		errno = 0;
160 		switch (bp[0])
161 		{
162 		  case '\0':
163 		  case '#':		/* comment */
164 			break;
165 
166 		  case 'R':		/* rewriting rule */
167 			if (ruleset < 0)
168 			{
169 				syserr("missing valid ruleset for \"%s\"", bp);
170 				break;
171 			}
172 			for (p = &bp[1]; *p != '\0' && *p != '\t'; p++)
173 				continue;
174 
175 			if (*p == '\0')
176 			{
177 				syserr("invalid rewrite line \"%s\" (tab expected)", bp);
178 				break;
179 			}
180 
181 			/* allocate space for the rule header */
182 			if (rwp == NULL)
183 			{
184 				RewriteRules[ruleset] = rwp =
185 					(struct rewrite *) xalloc(sizeof *rwp);
186 			}
187 			else
188 			{
189 				rwp->r_next = (struct rewrite *) xalloc(sizeof *rwp);
190 				rwp = rwp->r_next;
191 			}
192 			rwp->r_next = NULL;
193 
194 			/* expand and save the LHS */
195 			*p = '\0';
196 			expand(&bp[1], exbuf, sizeof exbuf, e);
197 			rwp->r_lhs = prescan(exbuf, '\t', pvpbuf,
198 					     sizeof pvpbuf, NULL,
199 					     ConfigLevel >= 9 ? TokTypeNoC : NULL);
200 			nfuzzy = 0;
201 			if (rwp->r_lhs != NULL)
202 			{
203 				register char **ap;
204 
205 				rwp->r_lhs = copyplist(rwp->r_lhs, TRUE);
206 
207 				/* count the number of fuzzy matches in LHS */
208 				for (ap = rwp->r_lhs; *ap != NULL; ap++)
209 				{
210 					char *botch;
211 
212 					botch = NULL;
213 					switch (**ap & 0377)
214 					{
215 					  case MATCHZANY:
216 					  case MATCHANY:
217 					  case MATCHONE:
218 					  case MATCHCLASS:
219 					  case MATCHNCLASS:
220 						nfuzzy++;
221 						break;
222 
223 					  case MATCHREPL:
224 						botch = "$0-$9";
225 						break;
226 
227 					  case CANONUSER:
228 						botch = "$:";
229 						break;
230 
231 					  case CALLSUBR:
232 						botch = "$>";
233 						break;
234 
235 					  case CONDIF:
236 						botch = "$?";
237 						break;
238 
239 					  case CONDFI:
240 						botch = "$.";
241 						break;
242 
243 					  case HOSTBEGIN:
244 						botch = "$[";
245 						break;
246 
247 					  case HOSTEND:
248 						botch = "$]";
249 						break;
250 
251 					  case LOOKUPBEGIN:
252 						botch = "$(";
253 						break;
254 
255 					  case LOOKUPEND:
256 						botch = "$)";
257 						break;
258 					}
259 					if (botch != NULL)
260 						syserr("Inappropriate use of %s on LHS",
261 							botch);
262 				}
263 				rwp->r_line = LineNumber;
264 			}
265 			else
266 			{
267 				syserr("R line: null LHS");
268 				rwp->r_lhs = null_list;
269 			}
270 
271 			/* expand and save the RHS */
272 			while (*++p == '\t')
273 				continue;
274 			q = p;
275 			while (*p != '\0' && *p != '\t')
276 				p++;
277 			*p = '\0';
278 			expand(q, exbuf, sizeof exbuf, e);
279 			rwp->r_rhs = prescan(exbuf, '\t', pvpbuf,
280 					     sizeof pvpbuf, NULL,
281 					     ConfigLevel >= 9 ? TokTypeNoC : NULL);
282 			if (rwp->r_rhs != NULL)
283 			{
284 				register char **ap;
285 
286 				rwp->r_rhs = copyplist(rwp->r_rhs, TRUE);
287 
288 				/* check no out-of-bounds replacements */
289 				nfuzzy += '0';
290 				for (ap = rwp->r_rhs; *ap != NULL; ap++)
291 				{
292 					char *botch;
293 
294 					botch = NULL;
295 					switch (**ap & 0377)
296 					{
297 					  case MATCHREPL:
298 						if ((*ap)[1] <= '0' || (*ap)[1] > nfuzzy)
299 						{
300 							syserr("replacement $%c out of bounds",
301 								(*ap)[1]);
302 						}
303 						break;
304 
305 					  case MATCHZANY:
306 						botch = "$*";
307 						break;
308 
309 					  case MATCHANY:
310 						botch = "$+";
311 						break;
312 
313 					  case MATCHONE:
314 						botch = "$-";
315 						break;
316 
317 					  case MATCHCLASS:
318 						botch = "$=";
319 						break;
320 
321 					  case MATCHNCLASS:
322 						botch = "$~";
323 						break;
324 					}
325 					if (botch != NULL)
326 						syserr("Inappropriate use of %s on RHS",
327 							botch);
328 				}
329 			}
330 			else
331 			{
332 				syserr("R line: null RHS");
333 				rwp->r_rhs = null_list;
334 			}
335 			break;
336 
337 		  case 'S':		/* select rewriting set */
338 			expand(&bp[1], exbuf, sizeof exbuf, e);
339 			ruleset = strtorwset(exbuf, NULL, ST_ENTER);
340 			if (ruleset < 0)
341 				break;
342 
343 			rwp = RewriteRules[ruleset];
344 			if (rwp != NULL)
345 			{
346 				if (OpMode == MD_TEST)
347 					printf("WARNING: Ruleset %s has multiple definitions\n",
348 					       &bp[1]);
349 				if (tTd(37, 1))
350 					dprintf("WARNING: Ruleset %s has multiple definitions\n",
351 						&bp[1]);
352 				while (rwp->r_next != NULL)
353 					rwp = rwp->r_next;
354 			}
355 			break;
356 
357 		  case 'D':		/* macro definition */
358 			mid = macid(&bp[1], &ep);
359 			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 			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 
614 void
615 translate_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 
695 static void
696 toomany(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 
722 static void
723 fileclass(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 **			The first word is the canonical name of the mailer.
848 **
849 **	Returns:
850 **		none.
851 **
852 **	Side Effects:
853 **		enters the mailer into the mailer table.
854 */
855 
856 void
857 makemailer(line)
858 	char *line;
859 {
860 	register char *p;
861 	register struct mailer *m;
862 	register STAB *s;
863 	int i;
864 	char fcode;
865 	auto char *endp;
866 	extern int NextMailer;
867 
868 	/* allocate a mailer and set up defaults */
869 	m = (struct mailer *) xalloc(sizeof *m);
870 	memset((char *) m, '\0', sizeof *m);
871 
872 	/* collect the mailer name */
873 	for (p = line; *p != '\0' && *p != ',' && !(isascii(*p) && isspace(*p)); p++)
874 		continue;
875 	if (*p != '\0')
876 		*p++ = '\0';
877 	if (line[0] == '\0')
878 	{
879 		syserr("name required for mailer");
880 		return;
881 	}
882 	m->m_name = newstr(line);
883 
884 	/* now scan through and assign info from the fields */
885 	while (*p != '\0')
886 	{
887 		auto char *delimptr;
888 
889 		while (*p != '\0' && (*p == ',' || (isascii(*p) && isspace(*p))))
890 			p++;
891 
892 		/* p now points to field code */
893 		fcode = *p;
894 		while (*p != '\0' && *p != '=' && *p != ',')
895 			p++;
896 		if (*p++ != '=')
897 		{
898 			syserr("mailer %s: `=' expected", m->m_name);
899 			return;
900 		}
901 		while (isascii(*p) && isspace(*p))
902 			p++;
903 
904 		/* p now points to the field body */
905 		p = munchstring(p, &delimptr, ',');
906 
907 		/* install the field into the mailer struct */
908 		switch (fcode)
909 		{
910 		  case 'P':		/* pathname */
911 			if (*p == '\0')
912 				syserr("mailer %s: empty path name", m->m_name);
913 			else
914 				m->m_mailer = newstr(p);
915 			break;
916 
917 		  case 'F':		/* flags */
918 			for (; *p != '\0'; p++)
919 				if (!(isascii(*p) && isspace(*p)))
920 					setbitn(bitidx(*p), m->m_flags);
921 			break;
922 
923 		  case 'S':		/* sender rewriting ruleset */
924 		  case 'R':		/* recipient rewriting ruleset */
925 			i = strtorwset(p, &endp, ST_ENTER);
926 			if (i < 0)
927 				return;
928 			if (fcode == 'S')
929 				m->m_sh_rwset = m->m_se_rwset = i;
930 			else
931 				m->m_rh_rwset = m->m_re_rwset = i;
932 
933 			p = endp;
934 			if (*p++ == '/')
935 			{
936 				i = strtorwset(p, NULL, ST_ENTER);
937 				if (i < 0)
938 					return;
939 				if (fcode == 'S')
940 					m->m_sh_rwset = i;
941 				else
942 					m->m_rh_rwset = i;
943 			}
944 			break;
945 
946 		  case 'E':		/* end of line string */
947 			if (*p == '\0')
948 				syserr("mailer %s: null end-of-line string",
949 					m->m_name);
950 			else
951 				m->m_eol = newstr(p);
952 			break;
953 
954 		  case 'A':		/* argument vector */
955 			if (*p == '\0')
956 				syserr("mailer %s: null argument vector",
957 					m->m_name);
958 			else
959 				m->m_argv = makeargv(p);
960 			break;
961 
962 		  case 'M':		/* maximum message size */
963 			m->m_maxsize = atol(p);
964 			break;
965 
966 		  case 'm':		/* maximum messages per connection */
967 			m->m_maxdeliveries = atoi(p);
968 			break;
969 
970 #if _FFR_DYNAMIC_TOBUF
971 		  case 'r':		/* max recipient per envelope */
972 			m->m_maxrcpt = atoi(p);
973 			break;
974 #endif /* _FFR_DYNAMIC_TOBUF */
975 
976 		  case 'L':		/* maximum line length */
977 			m->m_linelimit = atoi(p);
978 			if (m->m_linelimit < 0)
979 				m->m_linelimit = 0;
980 			break;
981 
982 		  case 'N':		/* run niceness */
983 			m->m_nice = atoi(p);
984 			break;
985 
986 		  case 'D':		/* working directory */
987 			if (*p == '\0')
988 				syserr("mailer %s: null working directory",
989 					m->m_name);
990 			else
991 				m->m_execdir = newstr(p);
992 			break;
993 
994 		  case 'C':		/* default charset */
995 			if (*p == '\0')
996 				syserr("mailer %s: null charset", m->m_name);
997 			else
998 				m->m_defcharset = newstr(p);
999 			break;
1000 
1001 		  case 'T':		/* MTA-Name/Address/Diagnostic types */
1002 			/* extract MTA name type; default to "dns" */
1003 			m->m_mtatype = newstr(p);
1004 			p = strchr(m->m_mtatype, '/');
1005 			if (p != NULL)
1006 			{
1007 				*p++ = '\0';
1008 				if (*p == '\0')
1009 					p = NULL;
1010 			}
1011 			if (*m->m_mtatype == '\0')
1012 				m->m_mtatype = "dns";
1013 
1014 			/* extract address type; default to "rfc822" */
1015 			m->m_addrtype = p;
1016 			if (p != NULL)
1017 				p = strchr(p, '/');
1018 			if (p != NULL)
1019 			{
1020 				*p++ = '\0';
1021 				if (*p == '\0')
1022 					p = NULL;
1023 			}
1024 			if (m->m_addrtype == NULL || *m->m_addrtype == '\0')
1025 				m->m_addrtype = "rfc822";
1026 
1027 			/* extract diagnostic type; default to "smtp" */
1028 			m->m_diagtype = p;
1029 			if (m->m_diagtype == NULL || *m->m_diagtype == '\0')
1030 				m->m_diagtype = "smtp";
1031 			break;
1032 
1033 		  case 'U':		/* user id */
1034 			if (isascii(*p) && !isdigit(*p))
1035 			{
1036 				char *q = p;
1037 				struct passwd *pw;
1038 
1039 				while (*p != '\0' && isascii(*p) &&
1040 				       (isalnum(*p) || strchr("-_", *p) != NULL))
1041 					p++;
1042 				while (isascii(*p) && isspace(*p))
1043 					*p++ = '\0';
1044 				if (*p != '\0')
1045 					*p++ = '\0';
1046 				if (*q == '\0')
1047 				{
1048 					syserr("mailer %s: null user name",
1049 						m->m_name);
1050 					break;
1051 				}
1052 				pw = sm_getpwnam(q);
1053 				if (pw == NULL)
1054 				{
1055 					syserr("readcf: mailer U= flag: unknown user %s", q);
1056 					break;
1057 				}
1058 				else
1059 				{
1060 					m->m_uid = pw->pw_uid;
1061 					m->m_gid = pw->pw_gid;
1062 				}
1063 			}
1064 			else
1065 			{
1066 				auto char *q;
1067 
1068 				m->m_uid = strtol(p, &q, 0);
1069 				p = q;
1070 				while (isascii(*p) && isspace(*p))
1071 					p++;
1072 				if (*p != '\0')
1073 					p++;
1074 			}
1075 			while (isascii(*p) && isspace(*p))
1076 				p++;
1077 			if (*p == '\0')
1078 				break;
1079 			if (isascii(*p) && !isdigit(*p))
1080 			{
1081 				char *q = p;
1082 				struct group *gr;
1083 
1084 				while (isascii(*p) && isalnum(*p))
1085 					p++;
1086 				*p++ = '\0';
1087 				if (*q == '\0')
1088 				{
1089 					syserr("mailer %s: null group name",
1090 						m->m_name);
1091 					break;
1092 				}
1093 				gr = getgrnam(q);
1094 				if (gr == NULL)
1095 				{
1096 					syserr("readcf: mailer U= flag: unknown group %s", q);
1097 					break;
1098 				}
1099 				else
1100 					m->m_gid = gr->gr_gid;
1101 			}
1102 			else
1103 			{
1104 				m->m_gid = strtol(p, NULL, 0);
1105 			}
1106 			break;
1107 
1108 		  case 'W':		/* wait timeout */
1109 			m->m_wait = convtime(p, 's');
1110 			break;
1111 
1112 		  case '/':		/* new root directory */
1113 			if (*p == '\0')
1114 				syserr("mailer %s: null root directory",
1115 					m->m_name);
1116 			else
1117 				m->m_rootdir = newstr(p);
1118 			break;
1119 
1120 		  default:
1121 			syserr("M%s: unknown mailer equate %c=",
1122 			       m->m_name, fcode);
1123 			break;
1124 		}
1125 
1126 		p = delimptr;
1127 	}
1128 
1129 	/* do some rationality checking */
1130 	if (m->m_argv == NULL)
1131 	{
1132 		syserr("M%s: A= argument required", m->m_name);
1133 		return;
1134 	}
1135 	if (m->m_mailer == NULL)
1136 	{
1137 		syserr("M%s: P= argument required", m->m_name);
1138 		return;
1139 	}
1140 
1141 	if (NextMailer >= MAXMAILERS)
1142 	{
1143 		syserr("too many mailers defined (%d max)", MAXMAILERS);
1144 		return;
1145 	}
1146 
1147 #if _FFR_DYNAMIC_TOBUF
1148 	if (m->m_maxrcpt <= 0)
1149 		m->m_maxrcpt = DEFAULT_MAX_RCPT;
1150 #endif /* _FFR_DYNAMIC_TOBUF */
1151 
1152 	/* do some heuristic cleanup for back compatibility */
1153 	if (bitnset(M_LIMITS, m->m_flags))
1154 	{
1155 		if (m->m_linelimit == 0)
1156 			m->m_linelimit = SMTPLINELIM;
1157 		if (ConfigLevel < 2)
1158 			setbitn(M_7BITS, m->m_flags);
1159 	}
1160 
1161 	if (strcmp(m->m_mailer, "[TCP]") == 0)
1162 	{
1163 #if _FFR_REMOVE_TCP_MAILER_PATH
1164 		syserr("M%s: P=[TCP] is deprecated, use P=[IPC] instead\n",
1165 		       m->m_name);
1166 		return;
1167 #else /* _FFR_REMOVE_TCP_MAILER_PATH */
1168 		printf("M%s: Warning: P=[TCP] is deprecated, use P=[IPC] instead\n",
1169 		       m->m_name);
1170 #endif /* _FFR_REMOVE_TCP_MAILER_PATH */
1171 	}
1172 
1173 	if (strcmp(m->m_mailer, "[IPC]") == 0
1174 #if !_FFR_REMOVE_TCP_MAILER_PATH
1175 	    || strcmp(m->m_mailer, "[TCP]") == 0
1176 #endif /* !_FFR_REMOVE_TCP_MAILER_PATH */
1177 	    )
1178 	{
1179 		/* Use the second argument for host or path to socket */
1180 		if (m->m_argv[0] == NULL || m->m_argv[1] == NULL ||
1181 		    m->m_argv[1][0] == '\0')
1182 		{
1183 			syserr("M%s: too few parameters for %s mailer",
1184 			       m->m_name, m->m_mailer);
1185 			return;
1186 		}
1187 		if (strcmp(m->m_argv[0], "TCP") != 0
1188 #if NETUNIX
1189 		    && strcmp(m->m_argv[0], "FILE") != 0
1190 #endif /* NETUNIX */
1191 #if !_FFR_DEPRECATE_IPC_MAILER_ARG
1192 		    && strcmp(m->m_argv[0], "IPC") != 0
1193 #endif /* !_FFR_DEPRECATE_IPC_MAILER_ARG */
1194 		    )
1195 		{
1196 			printf("M%s: Warning: first argument in %s mailer must be %s\n",
1197 			       m->m_name, m->m_mailer,
1198 #if NETUNIX
1199 			       "TCP or FILE"
1200 #else /* NETUNIX */
1201 			       "TCP"
1202 #endif /* NETUNIX */
1203 			       );
1204 		}
1205 
1206 	}
1207 	else if (strcmp(m->m_mailer, "[FILE]") == 0)
1208 	{
1209 		/* Use the second argument for filename */
1210 		if (m->m_argv[0] == NULL || m->m_argv[1] == NULL ||
1211 		    m->m_argv[2] != NULL)
1212 		{
1213 			syserr("M%s: too %s parameters for [FILE] mailer",
1214 			       m->m_name,
1215 			       (m->m_argv[0] == NULL ||
1216 				m->m_argv[1] == NULL) ? "few" : "many");
1217 			return;
1218 		}
1219 		else if (strcmp(m->m_argv[0], "FILE") != 0)
1220 		{
1221 			syserr("M%s: first argument in [FILE] mailer must be FILE",
1222 			       m->m_name);
1223 			return;
1224 		}
1225 	}
1226 
1227 	if (strcmp(m->m_mailer, "[IPC]") == 0 ||
1228 	    strcmp(m->m_mailer, "[TCP]") == 0)
1229 	{
1230 		if (m->m_mtatype == NULL)
1231 			m->m_mtatype = "dns";
1232 		if (m->m_addrtype == NULL)
1233 			m->m_addrtype = "rfc822";
1234 		if (m->m_diagtype == NULL)
1235 		{
1236 			if (m->m_argv[0] != NULL &&
1237 			    strcmp(m->m_argv[0], "FILE") == 0)
1238 				m->m_diagtype = "x-unix";
1239 			else
1240 				m->m_diagtype = "smtp";
1241 		}
1242 	}
1243 
1244 	if (m->m_eol == NULL)
1245 	{
1246 		char **pp;
1247 
1248 		/* default for SMTP is \r\n; use \n for local delivery */
1249 		for (pp = m->m_argv; *pp != NULL; pp++)
1250 		{
1251 			for (p = *pp; *p != '\0'; )
1252 			{
1253 				if ((*p++ & 0377) == MACROEXPAND && *p == 'u')
1254 					break;
1255 			}
1256 			if (*p != '\0')
1257 				break;
1258 		}
1259 		if (*pp == NULL)
1260 			m->m_eol = "\r\n";
1261 		else
1262 			m->m_eol = "\n";
1263 	}
1264 
1265 	/* enter the mailer into the symbol table */
1266 	s = stab(m->m_name, ST_MAILER, ST_ENTER);
1267 	if (s->s_mailer != NULL)
1268 	{
1269 		i = s->s_mailer->m_mno;
1270 		free(s->s_mailer);
1271 	}
1272 	else
1273 	{
1274 		i = NextMailer++;
1275 	}
1276 	Mailer[i] = s->s_mailer = m;
1277 	m->m_mno = i;
1278 }
1279 /*
1280 **  MUNCHSTRING -- translate a string into internal form.
1281 **
1282 **	Parameters:
1283 **		p -- the string to munch.
1284 **		delimptr -- if non-NULL, set to the pointer of the
1285 **			field delimiter character.
1286 **		delim -- the delimiter for the field.
1287 **
1288 **	Returns:
1289 **		the munched string.
1290 **
1291 **	Side Effects:
1292 **		the munched string is a local static buffer.
1293 **		it must be copied before the function is called again.
1294 */
1295 
1296 char *
1297 munchstring(p, delimptr, delim)
1298 	register char *p;
1299 	char **delimptr;
1300 	int delim;
1301 {
1302 	register char *q;
1303 	bool backslash = FALSE;
1304 	bool quotemode = FALSE;
1305 	static char buf[MAXLINE];
1306 
1307 	for (q = buf; *p != '\0' && q < &buf[sizeof buf - 1]; p++)
1308 	{
1309 		if (backslash)
1310 		{
1311 			/* everything is roughly literal */
1312 			backslash = FALSE;
1313 			switch (*p)
1314 			{
1315 			  case 'r':		/* carriage return */
1316 				*q++ = '\r';
1317 				continue;
1318 
1319 			  case 'n':		/* newline */
1320 				*q++ = '\n';
1321 				continue;
1322 
1323 			  case 'f':		/* form feed */
1324 				*q++ = '\f';
1325 				continue;
1326 
1327 			  case 'b':		/* backspace */
1328 				*q++ = '\b';
1329 				continue;
1330 			}
1331 			*q++ = *p;
1332 		}
1333 		else
1334 		{
1335 			if (*p == '\\')
1336 				backslash = TRUE;
1337 			else if (*p == '"')
1338 				quotemode = !quotemode;
1339 			else if (quotemode || *p != delim)
1340 				*q++ = *p;
1341 			else
1342 				break;
1343 		}
1344 	}
1345 
1346 	if (delimptr != NULL)
1347 		*delimptr = p;
1348 	*q++ = '\0';
1349 	return buf;
1350 }
1351 /*
1352 **  MAKEARGV -- break up a string into words
1353 **
1354 **	Parameters:
1355 **		p -- the string to break up.
1356 **
1357 **	Returns:
1358 **		a char **argv (dynamically allocated)
1359 **
1360 **	Side Effects:
1361 **		munges p.
1362 */
1363 
1364 static char **
1365 makeargv(p)
1366 	register char *p;
1367 {
1368 	char *q;
1369 	int i;
1370 	char **avp;
1371 	char *argv[MAXPV + 1];
1372 
1373 	/* take apart the words */
1374 	i = 0;
1375 	while (*p != '\0' && i < MAXPV)
1376 	{
1377 		q = p;
1378 		while (*p != '\0' && !(isascii(*p) && isspace(*p)))
1379 			p++;
1380 		while (isascii(*p) && isspace(*p))
1381 			*p++ = '\0';
1382 		argv[i++] = newstr(q);
1383 	}
1384 	argv[i++] = NULL;
1385 
1386 	/* now make a copy of the argv */
1387 	avp = (char **) xalloc(sizeof *avp * i);
1388 	memmove((char *) avp, (char *) argv, sizeof *avp * i);
1389 
1390 	return avp;
1391 }
1392 /*
1393 **  PRINTRULES -- print rewrite rules (for debugging)
1394 **
1395 **	Parameters:
1396 **		none.
1397 **
1398 **	Returns:
1399 **		none.
1400 **
1401 **	Side Effects:
1402 **		prints rewrite rules.
1403 */
1404 
1405 void
1406 printrules()
1407 {
1408 	register struct rewrite *rwp;
1409 	register int ruleset;
1410 
1411 	for (ruleset = 0; ruleset < 10; ruleset++)
1412 	{
1413 		if (RewriteRules[ruleset] == NULL)
1414 			continue;
1415 		printf("\n----Rule Set %d:", ruleset);
1416 
1417 		for (rwp = RewriteRules[ruleset]; rwp != NULL; rwp = rwp->r_next)
1418 		{
1419 			printf("\nLHS:");
1420 			printav(rwp->r_lhs);
1421 			printf("RHS:");
1422 			printav(rwp->r_rhs);
1423 		}
1424 	}
1425 }
1426 /*
1427 **  PRINTMAILER -- print mailer structure (for debugging)
1428 **
1429 **	Parameters:
1430 **		m -- the mailer to print
1431 **
1432 **	Returns:
1433 **		none.
1434 */
1435 
1436 void
1437 printmailer(m)
1438 	register MAILER *m;
1439 {
1440 	int j;
1441 
1442 	printf("mailer %d (%s): P=%s S=", m->m_mno, m->m_name, m->m_mailer);
1443 	if (RuleSetNames[m->m_se_rwset] == NULL)
1444 		printf("%d/", m->m_se_rwset);
1445 	else
1446 		printf("%s/", RuleSetNames[m->m_se_rwset]);
1447 	if (RuleSetNames[m->m_sh_rwset] == NULL)
1448 		printf("%d R=", m->m_sh_rwset);
1449 	else
1450 		printf("%s R=", RuleSetNames[m->m_sh_rwset]);
1451 	if (RuleSetNames[m->m_re_rwset] == NULL)
1452 		printf("%d/", m->m_re_rwset);
1453 	else
1454 		printf("%s/", RuleSetNames[m->m_re_rwset]);
1455 	if (RuleSetNames[m->m_rh_rwset] == NULL)
1456 		printf("%d ", m->m_rh_rwset);
1457 	else
1458 		printf("%s ", RuleSetNames[m->m_rh_rwset]);
1459 	printf("M=%ld U=%d:%d F=", m->m_maxsize,
1460 	       (int) m->m_uid, (int) m->m_gid);
1461 	for (j = '\0'; j <= '\177'; j++)
1462 		if (bitnset(j, m->m_flags))
1463 			(void) putchar(j);
1464 	printf(" L=%d E=", m->m_linelimit);
1465 	xputs(m->m_eol);
1466 	if (m->m_defcharset != NULL)
1467 		printf(" C=%s", m->m_defcharset);
1468 	printf(" T=%s/%s/%s",
1469 		m->m_mtatype == NULL ? "<undefined>" : m->m_mtatype,
1470 		m->m_addrtype == NULL ? "<undefined>" : m->m_addrtype,
1471 		m->m_diagtype == NULL ? "<undefined>" : m->m_diagtype);
1472 #if _FFR_DYNAMIC_TOBUF
1473 	printf(" r=%d", m->m_maxrcpt);
1474 #endif /* _FFR_DYNAMIC_TOBUF */
1475 	if (m->m_argv != NULL)
1476 	{
1477 		char **a = m->m_argv;
1478 
1479 		printf(" A=");
1480 		while (*a != NULL)
1481 		{
1482 			if (a != m->m_argv)
1483 				printf(" ");
1484 			xputs(*a++);
1485 		}
1486 	}
1487 	printf("\n");
1488 }
1489 /*
1490 **  SETOPTION -- set global processing option
1491 **
1492 **	Parameters:
1493 **		opt -- option name.
1494 **		val -- option value (as a text string).
1495 **		safe -- set if this came from a configuration file.
1496 **			Some options (if set from the command line) will
1497 **			reset the user id to avoid security problems.
1498 **		sticky -- if set, don't let other setoptions override
1499 **			this value.
1500 **		e -- the main envelope.
1501 **
1502 **	Returns:
1503 **		none.
1504 **
1505 **	Side Effects:
1506 **		Sets options as implied by the arguments.
1507 */
1508 
1509 static BITMAP256	StickyOpt;		/* set if option is stuck */
1510 
1511 #if NAMED_BIND
1512 
1513 static struct resolverflags
1514 {
1515 	char	*rf_name;	/* name of the flag */
1516 	long	rf_bits;	/* bits to set/clear */
1517 } ResolverFlags[] =
1518 {
1519 	{ "debug",	RES_DEBUG	},
1520 	{ "aaonly",	RES_AAONLY	},
1521 	{ "usevc",	RES_USEVC	},
1522 	{ "primary",	RES_PRIMARY	},
1523 	{ "igntc",	RES_IGNTC	},
1524 	{ "recurse",	RES_RECURSE	},
1525 	{ "defnames",	RES_DEFNAMES	},
1526 	{ "stayopen",	RES_STAYOPEN	},
1527 	{ "dnsrch",	RES_DNSRCH	},
1528 	{ "true",	0		},	/* avoid error on old syntax */
1529 	{ NULL,		0		}
1530 };
1531 
1532 #endif /* NAMED_BIND */
1533 
1534 #define OI_NONE		0	/* no special treatment */
1535 #define OI_SAFE		0x0001	/* safe for random people to use */
1536 #define OI_SUBOPT	0x0002	/* option has suboptions */
1537 
1538 static struct optioninfo
1539 {
1540 	char	*o_name;	/* long name of option */
1541 	u_char	o_code;		/* short name of option */
1542 	u_short	o_flags;	/* option flags */
1543 } OptionTab[] =
1544 {
1545 #if defined(SUN_EXTENSIONS) && defined(REMOTE_MODE)
1546 	{ "RemoteMode",			'>',		OI_NONE	},
1547 #endif /* defined(SUN_EXTENSIONS) && defined(REMOTE_MODE) */
1548 	{ "SevenBitInput",		'7',		OI_SAFE	},
1549 #if MIME8TO7
1550 	{ "EightBitMode",		'8',		OI_SAFE	},
1551 #endif /* MIME8TO7 */
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 
1745 void
1746 setoption(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 #if MIME8TO7
1900 	  case '8':		/* handling of 8-bit input */
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 		break;
1938 #endif /* MIME8TO7 */
1939 
1940 	  case 'A':		/* set default alias file */
1941 		if (val[0] == '\0')
1942 			setalias("aliases");
1943 		else
1944 			setalias(val);
1945 		break;
1946 
1947 	  case 'a':		/* look N minutes for "@:@" in alias file */
1948 		if (val[0] == '\0')
1949 			SafeAlias = 5 * 60;		/* five minutes */
1950 		else
1951 			SafeAlias = convtime(val, 'm');
1952 		break;
1953 
1954 	  case 'B':		/* substitution for blank character */
1955 		SpaceSub = val[0];
1956 		if (SpaceSub == '\0')
1957 			SpaceSub = ' ';
1958 		break;
1959 
1960 	  case 'b':		/* min blocks free on queue fs/max msg size */
1961 		p = strchr(val, '/');
1962 		if (p != NULL)
1963 		{
1964 			*p++ = '\0';
1965 			MaxMessageSize = atol(p);
1966 		}
1967 		MinBlocksFree = atol(val);
1968 		break;
1969 
1970 	  case 'c':		/* don't connect to "expensive" mailers */
1971 		NoConnect = atobool(val);
1972 		break;
1973 
1974 	  case 'C':		/* checkpoint every N addresses */
1975 		CheckpointInterval = atoi(val);
1976 		break;
1977 
1978 	  case 'd':		/* delivery mode */
1979 		switch (*val)
1980 		{
1981 		  case '\0':
1982 			set_delivery_mode(SM_DELIVER, e);
1983 			break;
1984 
1985 		  case SM_QUEUE:	/* queue only */
1986 		  case SM_DEFER:	/* queue only and defer map lookups */
1987 #if !QUEUE
1988 			syserr("need QUEUE to set -odqueue or -oddefer");
1989 			break;
1990 #endif /* !QUEUE */
1991 			/* FALLTHROUGH */
1992 
1993 		  case SM_DELIVER:	/* do everything */
1994 		  case SM_FORK:		/* fork after verification */
1995 			set_delivery_mode(*val, e);
1996 			break;
1997 
1998 		  default:
1999 			syserr("Unknown delivery mode %c", *val);
2000 			finis(FALSE, EX_USAGE);
2001 		}
2002 		break;
2003 
2004 #if !_FFR_REMOVE_AUTOREBUILD
2005 	  case 'D':		/* rebuild alias database as needed */
2006 		AutoRebuild = atobool(val);
2007 		break;
2008 #endif /* !_FFR_REMOVE_AUTOREBUILD */
2009 
2010 	  case 'E':		/* error message header/header file */
2011 		if (*val != '\0')
2012 			ErrMsgFile = newstr(val);
2013 		break;
2014 
2015 	  case 'e':		/* set error processing mode */
2016 		switch (*val)
2017 		{
2018 		  case EM_QUIET:	/* be silent about it */
2019 		  case EM_MAIL:		/* mail back */
2020 		  case EM_BERKNET:	/* do berknet error processing */
2021 		  case EM_WRITE:	/* write back (or mail) */
2022 		  case EM_PRINT:	/* print errors normally (default) */
2023 			e->e_errormode = *val;
2024 			break;
2025 		}
2026 		break;
2027 
2028 	  case 'F':		/* file mode */
2029 		FileMode = atooct(val) & 0777;
2030 		break;
2031 
2032 	  case 'f':		/* save Unix-style From lines on front */
2033 		SaveFrom = atobool(val);
2034 		break;
2035 
2036 	  case 'G':		/* match recipients against GECOS field */
2037 		MatchGecos = atobool(val);
2038 		break;
2039 
2040 	  case 'g':		/* default gid */
2041   g_opt:
2042 		if (isascii(*val) && isdigit(*val))
2043 			DefGid = atoi(val);
2044 		else
2045 		{
2046 			register struct group *gr;
2047 
2048 			DefGid = -1;
2049 			gr = getgrnam(val);
2050 			if (gr == NULL)
2051 				syserr("readcf: option %c: unknown group %s",
2052 					opt, val);
2053 			else
2054 				DefGid = gr->gr_gid;
2055 		}
2056 		break;
2057 
2058 	  case 'H':		/* help file */
2059 		if (val[0] == '\0')
2060 			HelpFile = "helpfile";
2061 		else
2062 			HelpFile = newstr(val);
2063 		break;
2064 
2065 	  case 'h':		/* maximum hop count */
2066 		MaxHopCount = atoi(val);
2067 		break;
2068 
2069 	  case 'I':		/* use internet domain name server */
2070 #if NAMED_BIND
2071 		for (p = val; *p != 0; )
2072 		{
2073 			bool clearmode;
2074 			char *q;
2075 			struct resolverflags *rfp;
2076 
2077 			while (*p == ' ')
2078 				p++;
2079 			if (*p == '\0')
2080 				break;
2081 			clearmode = FALSE;
2082 			if (*p == '-')
2083 				clearmode = TRUE;
2084 			else if (*p != '+')
2085 				p--;
2086 			p++;
2087 			q = p;
2088 			while (*p != '\0' && !(isascii(*p) && isspace(*p)))
2089 				p++;
2090 			if (*p != '\0')
2091 				*p++ = '\0';
2092 			if (strcasecmp(q, "HasWildcardMX") == 0)
2093 			{
2094 				HasWildcardMX = !clearmode;
2095 				continue;
2096 			}
2097 			for (rfp = ResolverFlags; rfp->rf_name != NULL; rfp++)
2098 			{
2099 				if (strcasecmp(q, rfp->rf_name) == 0)
2100 					break;
2101 			}
2102 			if (rfp->rf_name == NULL)
2103 				syserr("readcf: I option value %s unrecognized", q);
2104 			else if (clearmode)
2105 				_res.options &= ~rfp->rf_bits;
2106 			else
2107 				_res.options |= rfp->rf_bits;
2108 		}
2109 		if (tTd(8, 2))
2110 			dprintf("_res.options = %x, HasWildcardMX = %d\n",
2111 				(u_int) _res.options, HasWildcardMX);
2112 #else /* NAMED_BIND */
2113 		usrerr("name server (I option) specified but BIND not compiled in");
2114 #endif /* NAMED_BIND */
2115 		break;
2116 
2117 	  case 'i':		/* ignore dot lines in message */
2118 		IgnrDot = atobool(val);
2119 		break;
2120 
2121 	  case 'j':		/* send errors in MIME (RFC 1341) format */
2122 		SendMIMEErrors = atobool(val);
2123 		break;
2124 
2125 	  case 'J':		/* .forward search path */
2126 		ForwardPath = newstr(val);
2127 		break;
2128 
2129 	  case 'k':		/* connection cache size */
2130 		MaxMciCache = atoi(val);
2131 		if (MaxMciCache < 0)
2132 			MaxMciCache = 0;
2133 		break;
2134 
2135 	  case 'K':		/* connection cache timeout */
2136 		MciCacheTimeout = convtime(val, 'm');
2137 		break;
2138 
2139 	  case 'l':		/* use Errors-To: header */
2140 		UseErrorsTo = atobool(val);
2141 		break;
2142 
2143 	  case 'L':		/* log level */
2144 		if (safe || LogLevel < atoi(val))
2145 			LogLevel = atoi(val);
2146 		break;
2147 
2148 	  case 'M':		/* define macro */
2149 		sticky = FALSE;
2150 		mid = macid(val, &ep);
2151 		if (mid == 0)
2152 			break;
2153 		p = newstr(ep);
2154 		if (!safe)
2155 			cleanstrcpy(p, p, MAXNAME);
2156 		define(mid, p, CurEnv);
2157 		break;
2158 
2159 	  case 'm':		/* send to me too */
2160 		MeToo = atobool(val);
2161 		break;
2162 
2163 	  case 'n':		/* validate RHS in newaliases */
2164 		CheckAliases = atobool(val);
2165 		break;
2166 
2167 	    /* 'N' available -- was "net name" */
2168 
2169 	  case 'O':		/* daemon options */
2170 #if DAEMON
2171 		if (!setdaemonoptions(val))
2172 			syserr("too many daemons defined (%d max)", MAXDAEMONS);
2173 #else /* DAEMON */
2174 		syserr("DaemonPortOptions (O option) set but DAEMON not compiled in");
2175 #endif /* DAEMON */
2176 		break;
2177 
2178 	  case 'o':		/* assume old style headers */
2179 		if (atobool(val))
2180 			CurEnv->e_flags |= EF_OLDSTYLE;
2181 		else
2182 			CurEnv->e_flags &= ~EF_OLDSTYLE;
2183 		break;
2184 
2185 	  case 'p':		/* select privacy level */
2186 		p = val;
2187 		for (;;)
2188 		{
2189 			register struct prival *pv;
2190 			extern struct prival PrivacyValues[];
2191 
2192 			while (isascii(*p) && (isspace(*p) || ispunct(*p)))
2193 				p++;
2194 			if (*p == '\0')
2195 				break;
2196 			val = p;
2197 			while (isascii(*p) && isalnum(*p))
2198 				p++;
2199 			if (*p != '\0')
2200 				*p++ = '\0';
2201 
2202 			for (pv = PrivacyValues; pv->pv_name != NULL; pv++)
2203 			{
2204 				if (strcasecmp(val, pv->pv_name) == 0)
2205 					break;
2206 			}
2207 			if (pv->pv_name == NULL)
2208 				syserr("readcf: Op line: %s unrecognized", val);
2209 			else
2210 				PrivacyFlags |= pv->pv_flag;
2211 		}
2212 		sticky = FALSE;
2213 		break;
2214 
2215 	  case 'P':		/* postmaster copy address for returned mail */
2216 		PostMasterCopy = newstr(val);
2217 		break;
2218 
2219 	  case 'q':		/* slope of queue only function */
2220 		QueueFactor = atoi(val);
2221 		break;
2222 
2223 	  case 'Q':		/* queue directory */
2224 		if (val[0] == '\0')
2225 		{
2226 			QueueDir = "mqueue";
2227 		}
2228 		else
2229 		{
2230 			QueueDir = newstr(val);
2231 		}
2232 		if (RealUid != 0 && !safe)
2233 			Warn_Q_option = TRUE;
2234 		break;
2235 
2236 	  case 'R':		/* don't prune routes */
2237 		DontPruneRoutes = atobool(val);
2238 		break;
2239 
2240 	  case 'r':		/* read timeout */
2241 		if (subopt == NULL)
2242 			inittimeouts(val, sticky);
2243 		else
2244 			settimeout(subopt, val, sticky);
2245 		break;
2246 
2247 	  case 'S':		/* status file */
2248 		if (val[0] == '\0')
2249 			StatFile = "statistics";
2250 		else
2251 			StatFile = newstr(val);
2252 		break;
2253 
2254 	  case 's':		/* be super safe, even if expensive */
2255 		SuperSafe = atobool(val);
2256 		break;
2257 
2258 	  case 'T':		/* queue timeout */
2259 		p = strchr(val, '/');
2260 		if (p != NULL)
2261 		{
2262 			*p++ = '\0';
2263 			settimeout("queuewarn", p, sticky);
2264 		}
2265 		settimeout("queuereturn", val, sticky);
2266 		break;
2267 
2268 	  case 't':		/* time zone name */
2269 		TimeZoneSpec = newstr(val);
2270 		break;
2271 
2272 	  case 'U':		/* location of user database */
2273 		UdbSpec = newstr(val);
2274 		break;
2275 
2276 	  case 'u':		/* set default uid */
2277 		for (p = val; *p != '\0'; p++)
2278 		{
2279 			if (*p == '.' || *p == '/' || *p == ':')
2280 			{
2281 				*p++ = '\0';
2282 				break;
2283 			}
2284 		}
2285 		if (isascii(*val) && isdigit(*val))
2286 		{
2287 			DefUid = atoi(val);
2288 			setdefuser();
2289 		}
2290 		else
2291 		{
2292 			register struct passwd *pw;
2293 
2294 			DefUid = -1;
2295 			pw = sm_getpwnam(val);
2296 			if (pw == NULL)
2297 			{
2298 				syserr("readcf: option u: unknown user %s", val);
2299 				break;
2300 			}
2301 			else
2302 			{
2303 				DefUid = pw->pw_uid;
2304 				DefGid = pw->pw_gid;
2305 				DefUser = newstr(pw->pw_name);
2306 			}
2307 		}
2308 
2309 #ifdef UID_MAX
2310 		if (DefUid > UID_MAX)
2311 		{
2312 			syserr("readcf: option u: uid value (%ld) > UID_MAX (%ld); ignored",
2313 				(long) DefUid, (long) UID_MAX);
2314 			break;
2315 		}
2316 #endif /* UID_MAX */
2317 
2318 		/* handle the group if it is there */
2319 		if (*p == '\0')
2320 			break;
2321 		val = p;
2322 		goto g_opt;
2323 
2324 	  case 'V':		/* fallback MX host */
2325 		if (val[0] != '\0')
2326 			FallBackMX = newstr(val);
2327 		break;
2328 
2329 	  case 'v':		/* run in verbose mode */
2330 		Verbose = atobool(val) ? 1 : 0;
2331 		break;
2332 
2333 	  case 'w':		/* if we are best MX, try host directly */
2334 		TryNullMXList = atobool(val);
2335 		break;
2336 
2337 	    /* 'W' available -- was wizard password */
2338 
2339 	  case 'x':		/* load avg at which to auto-queue msgs */
2340 		QueueLA = atoi(val);
2341 		break;
2342 
2343 	  case 'X':		/* load avg at which to auto-reject connections */
2344 		RefuseLA = atoi(val);
2345 		break;
2346 
2347 	  case 'y':		/* work recipient factor */
2348 		WkRecipFact = atoi(val);
2349 		break;
2350 
2351 	  case 'Y':		/* fork jobs during queue runs */
2352 		ForkQueueRuns = atobool(val);
2353 		break;
2354 
2355 	  case 'z':		/* work message class factor */
2356 		WkClassFact = atoi(val);
2357 		break;
2358 
2359 	  case 'Z':		/* work time factor */
2360 		WkTimeFact = atoi(val);
2361 		break;
2362 
2363 
2364 	  case O_QUEUESORTORD:	/* queue sorting order */
2365 		switch (*val)
2366 		{
2367 		  case 'h':	/* Host first */
2368 		  case 'H':
2369 			QueueSortOrder = QSO_BYHOST;
2370 			break;
2371 
2372 		  case 'p':	/* Priority order */
2373 		  case 'P':
2374 			QueueSortOrder = QSO_BYPRIORITY;
2375 			break;
2376 
2377 		  case 't':	/* Submission time */
2378 		  case 'T':
2379 			QueueSortOrder = QSO_BYTIME;
2380 			break;
2381 
2382 		  case 'f':	/* File Name */
2383 		  case 'F':
2384 			QueueSortOrder = QSO_BYFILENAME;
2385 			break;
2386 
2387 		  default:
2388 			syserr("Invalid queue sort order \"%s\"", val);
2389 		}
2390 		break;
2391 
2392 #if _FFR_QUEUEDELAY
2393 	  case O_QUEUEDELAY:	/* queue delay algorithm */
2394 		switch (*val)
2395 		{
2396 		  case 'e':	/* exponential */
2397 		  case 'E':
2398 			QueueAlg = QD_EXP;
2399 			QueueInitDelay = 10 MINUTES;
2400 			QueueMaxDelay = 2 HOURS;
2401 			p = strchr(val, '/');
2402 			if (p != NULL)
2403 			{
2404 				char *q;
2405 
2406 				*p++ = '\0';
2407 				q = strchr(p, '/');
2408 				if (q != NULL)
2409 					*q++ = '\0';
2410 				QueueInitDelay = convtime(p, 's');
2411 				if (q != NULL)
2412 				{
2413 					QueueMaxDelay = convtime(q, 's');
2414 				}
2415 			}
2416 			break;
2417 
2418 		  case 'l':	/* linear */
2419 		  case 'L':
2420 			QueueAlg = QD_LINEAR;
2421 			break;
2422 
2423 		  default:
2424 			syserr("Invalid queue delay algorithm \"%s\"", val);
2425 		}
2426 		break;
2427 #endif /* _FFR_QUEUEDELAY */
2428 
2429 	  case O_HOSTSFILE:	/* pathname of /etc/hosts file */
2430 		HostsFile = newstr(val);
2431 		break;
2432 
2433 	  case O_MQA:		/* minimum queue age between deliveries */
2434 		MinQueueAge = convtime(val, 'm');
2435 		break;
2436 
2437 	  case O_DEFCHARSET:	/* default character set for mimefying */
2438 		DefaultCharSet = newstr(denlstring(val, TRUE, TRUE));
2439 		break;
2440 
2441 	  case O_SSFILE:	/* service switch file */
2442 		ServiceSwitchFile = newstr(val);
2443 		break;
2444 
2445 	  case O_DIALDELAY:	/* delay for dial-on-demand operation */
2446 		DialDelay = convtime(val, 's');
2447 		break;
2448 
2449 	  case O_NORCPTACTION:	/* what to do if no recipient */
2450 		if (strcasecmp(val, "none") == 0)
2451 			NoRecipientAction = NRA_NO_ACTION;
2452 		else if (strcasecmp(val, "add-to") == 0)
2453 			NoRecipientAction = NRA_ADD_TO;
2454 		else if (strcasecmp(val, "add-apparently-to") == 0)
2455 			NoRecipientAction = NRA_ADD_APPARENTLY_TO;
2456 		else if (strcasecmp(val, "add-bcc") == 0)
2457 			NoRecipientAction = NRA_ADD_BCC;
2458 		else if (strcasecmp(val, "add-to-undisclosed") == 0)
2459 			NoRecipientAction = NRA_ADD_TO_UNDISCLOSED;
2460 		else
2461 			syserr("Invalid NoRecipientAction: %s", val);
2462 		break;
2463 
2464 	  case O_SAFEFILEENV:	/* chroot() environ for writing to files */
2465 		SafeFileEnv = newstr(val);
2466 		break;
2467 
2468 	  case O_MAXMSGSIZE:	/* maximum message size */
2469 		MaxMessageSize = atol(val);
2470 		break;
2471 
2472 	  case O_COLONOKINADDR:	/* old style handling of colon addresses */
2473 		ColonOkInAddr = atobool(val);
2474 		break;
2475 
2476 	  case O_MAXQUEUERUN:	/* max # of jobs in a single queue run */
2477 		MaxQueueRun = atol(val);
2478 		break;
2479 
2480 	  case O_MAXCHILDREN:	/* max # of children of daemon */
2481 		MaxChildren = atoi(val);
2482 		break;
2483 
2484 #if _FFR_MAX_FORWARD_ENTRIES
2485 	  case O_MAXFORWARD:	/* max # of forward entries */
2486 		MaxForwardEntries = atoi(val);
2487 		break;
2488 #endif /* _FFR_MAX_FORWARD_ENTRIES */
2489 
2490 	  case O_KEEPCNAMES:	/* don't expand CNAME records */
2491 		DontExpandCnames = atobool(val);
2492 		break;
2493 
2494 	  case O_MUSTQUOTE:	/* must quote these characters in phrases */
2495 		(void) strlcpy(buf, "@,;:\\()[]", sizeof buf);
2496 		if (strlen(val) < (SIZE_T) sizeof buf - 10)
2497 			(void) strlcat(buf, val, sizeof buf);
2498 		else
2499 			printf("Warning: MustQuoteChars too long, ignored.\n");
2500 		MustQuoteChars = newstr(buf);
2501 		break;
2502 
2503 	  case O_SMTPGREETING:	/* SMTP greeting message (old $e macro) */
2504 		SmtpGreeting = newstr(munchstring(val, NULL, '\0'));
2505 		break;
2506 
2507 	  case O_UNIXFROM:	/* UNIX From_ line (old $l macro) */
2508 		UnixFromLine = newstr(munchstring(val, NULL, '\0'));
2509 		break;
2510 
2511 	  case O_OPCHARS:	/* operator characters (old $o macro) */
2512 		if (OperatorChars != NULL)
2513 			printf("Warning: OperatorChars is being redefined.\n         It should only be set before ruleset definitions.\n");
2514 		OperatorChars = newstr(munchstring(val, NULL, '\0'));
2515 		break;
2516 
2517 	  case O_DONTINITGRPS:	/* don't call initgroups(3) */
2518 		DontInitGroups = atobool(val);
2519 		break;
2520 
2521 	  case O_SLFH:		/* make sure from fits on one line */
2522 		SingleLineFromHeader = atobool(val);
2523 		break;
2524 
2525 	  case O_ABH:		/* allow HELO commands with syntax errors */
2526 		AllowBogusHELO = atobool(val);
2527 		break;
2528 
2529 	  case O_CONNTHROT:	/* connection rate throttle */
2530 		ConnRateThrottle = atoi(val);
2531 		break;
2532 
2533 	  case O_UGW:		/* group writable files are unsafe */
2534 		if (!atobool(val))
2535 		{
2536 			setbitn(DBS_GROUPWRITABLEFORWARDFILESAFE,
2537 				DontBlameSendmail);
2538 			setbitn(DBS_GROUPWRITABLEINCLUDEFILESAFE,
2539 				DontBlameSendmail);
2540 		}
2541 		break;
2542 
2543 	  case O_DBLBOUNCE:	/* address to which to send double bounces */
2544 		if (val[0] != '\0')
2545 			DoubleBounceAddr = newstr(val);
2546 		else
2547 			syserr("readcf: option DoubleBounceAddress: value required");
2548 		break;
2549 
2550 	  case O_HSDIR:		/* persistent host status directory */
2551 		if (val[0] != '\0')
2552 			HostStatDir = newstr(val);
2553 		break;
2554 
2555 	  case O_SINGTHREAD:	/* single thread deliveries (requires hsdir) */
2556 		SingleThreadDelivery = atobool(val);
2557 		break;
2558 
2559 	  case O_RUNASUSER:	/* run bulk of code as this user */
2560 		for (p = val; *p != '\0'; p++)
2561 		{
2562 			if (*p == '.' || *p == '/' || *p == ':')
2563 			{
2564 				*p++ = '\0';
2565 				break;
2566 			}
2567 		}
2568 		if (isascii(*val) && isdigit(*val))
2569 		{
2570 			if (can_setuid)
2571 				RunAsUid = atoi(val);
2572 		}
2573 		else
2574 		{
2575 			register struct passwd *pw;
2576 
2577 			pw = sm_getpwnam(val);
2578 			if (pw == NULL)
2579 			{
2580 				syserr("readcf: option RunAsUser: unknown user %s", val);
2581 				break;
2582 			}
2583 			else if (can_setuid)
2584 			{
2585 				if (*p == '\0')
2586 					RunAsUserName = newstr(val);
2587 				RunAsUid = pw->pw_uid;
2588 				RunAsGid = pw->pw_gid;
2589 			}
2590 		}
2591 #ifdef UID_MAX
2592 		if (RunAsUid > UID_MAX)
2593 		{
2594 			syserr("readcf: option RunAsUser: uid value (%ld) > UID_MAX (%ld); ignored",
2595 				(long) RunAsUid, (long) UID_MAX);
2596 			break;
2597 		}
2598 #endif /* UID_MAX */
2599 		if (*p != '\0')
2600 		{
2601 			if (isascii(*p) && isdigit(*p))
2602 			{
2603 				if (can_setuid)
2604 					RunAsGid = atoi(p);
2605 			}
2606 			else
2607 			{
2608 				register struct group *gr;
2609 
2610 				gr = getgrnam(p);
2611 				if (gr == NULL)
2612 					syserr("readcf: option RunAsUser: unknown group %s",
2613 						p);
2614 				else if (can_setuid)
2615 					RunAsGid = gr->gr_gid;
2616 			}
2617 		}
2618 		if (tTd(47, 5))
2619 			dprintf("readcf: RunAsUser = %d:%d\n",
2620 				(int)RunAsUid, (int)RunAsGid);
2621 		break;
2622 
2623 	  case O_DSN_RRT:
2624 		RrtImpliesDsn = atobool(val);
2625 		break;
2626 
2627 	  case O_PIDFILE:
2628 		if (PidFile != NULL)
2629 			free(PidFile);
2630 		PidFile = newstr(val);
2631 		break;
2632 
2633 	case O_DONTBLAMESENDMAIL:
2634 		p = val;
2635 		for (;;)
2636 		{
2637 			register struct dbsval *dbs;
2638 			extern struct dbsval DontBlameSendmailValues[];
2639 
2640 			while (isascii(*p) && (isspace(*p) || ispunct(*p)))
2641 				p++;
2642 			if (*p == '\0')
2643 				break;
2644 			val = p;
2645 			while (isascii(*p) && isalnum(*p))
2646 				p++;
2647 			if (*p != '\0')
2648 				*p++ = '\0';
2649 
2650 			for (dbs = DontBlameSendmailValues;
2651 			     dbs->dbs_name != NULL; dbs++)
2652 			{
2653 				if (strcasecmp(val, dbs->dbs_name) == 0)
2654 					break;
2655 			}
2656 			if (dbs->dbs_name == NULL)
2657 				syserr("readcf: DontBlameSendmail option: %s unrecognized", val);
2658 			else if (dbs->dbs_flag == DBS_SAFE)
2659 				clrbitmap(DontBlameSendmail);
2660 			else
2661 				setbitn(dbs->dbs_flag, DontBlameSendmail);
2662 		}
2663 		sticky = FALSE;
2664 		break;
2665 
2666 	  case O_DPI:
2667 		DontProbeInterfaces = atobool(val);
2668 		break;
2669 
2670 	  case O_MAXRCPT:
2671 		MaxRcptPerMsg = atoi(val);
2672 		break;
2673 
2674 	  case O_DEADLETTER:
2675 		if (DeadLetterDrop != NULL)
2676 			free(DeadLetterDrop);
2677 		DeadLetterDrop = newstr(val);
2678 		break;
2679 
2680 #if _FFR_DONTLOCKFILESFORREAD_OPTION
2681 	  case O_DONTLOCK:
2682 		DontLockReadFiles = atobool(val);
2683 		break;
2684 #endif /* _FFR_DONTLOCKFILESFORREAD_OPTION */
2685 
2686 	  case O_MAXALIASRCSN:
2687 		MaxAliasRecursion = atoi(val);
2688 		break;
2689 
2690 	  case O_CNCTONLYTO:
2691 		/* XXX should probably use gethostbyname */
2692 #if NETINET || NETINET6
2693 # if NETINET6
2694 		if (inet_addr(val) == INADDR_NONE)
2695 		{
2696 			ConnectOnlyTo.sa.sa_family = AF_INET6;
2697 			if (inet_pton(AF_INET6, val,
2698 				      &ConnectOnlyTo.sin6.sin6_addr) != 1)
2699 				syserr("readcf: option ConnectOnlyTo: invalid IP address %s",
2700 				       val);
2701 		}
2702 		else
2703 # endif /* NETINET6 */
2704 		{
2705 			ConnectOnlyTo.sa.sa_family = AF_INET;
2706 			ConnectOnlyTo.sin.sin_addr.s_addr = inet_addr(val);
2707 		}
2708 #endif /* NETINET || NETINET6 */
2709 		break;
2710 
2711 	  case O_TRUSTUSER:
2712 #if HASFCHOWN
2713 		if (isascii(*val) && isdigit(*val))
2714 			TrustedUid = atoi(val);
2715 		else
2716 		{
2717 			register struct passwd *pw;
2718 
2719 			TrustedUid = 0;
2720 			pw = sm_getpwnam(val);
2721 			if (pw == NULL)
2722 			{
2723 				syserr("readcf: option TrustedUser: unknown user %s", val);
2724 				break;
2725 			}
2726 			else
2727 				TrustedUid = pw->pw_uid;
2728 		}
2729 
2730 # ifdef UID_MAX
2731 		if (TrustedUid > UID_MAX)
2732 		{
2733 			syserr("readcf: option TrustedUser: uid value (%ld) > UID_MAX (%ld)",
2734 				(long) TrustedUid, (long) UID_MAX);
2735 			TrustedUid = 0;
2736 		}
2737 # endif /* UID_MAX */
2738 #else /* HASFCHOWN */
2739 		syserr("readcf: option TrustedUser: can not be used on systems which do not support fchown()");
2740 #endif /* HASFCHOWN */
2741 		break;
2742 
2743 	  case O_MAXMIMEHDRLEN:
2744 		p = strchr(val, '/');
2745 		if (p != NULL)
2746 			*p++ = '\0';
2747 		MaxMimeHeaderLength = atoi(val);
2748 		if (p != NULL && *p != '\0')
2749 			MaxMimeFieldLength = atoi(p);
2750 		else
2751 			MaxMimeFieldLength = MaxMimeHeaderLength / 2;
2752 
2753 		if (MaxMimeHeaderLength < 0)
2754 			MaxMimeHeaderLength = 0;
2755 		else if (MaxMimeHeaderLength < 128)
2756 			printf("Warning: MaxMimeHeaderLength: header length limit set lower than 128\n");
2757 
2758 		if (MaxMimeFieldLength < 0)
2759 			MaxMimeFieldLength = 0;
2760 		else if (MaxMimeFieldLength < 40)
2761 			printf("Warning: MaxMimeHeaderLength: field length limit set lower than 40\n");
2762 		break;
2763 
2764 	  case O_CONTROLSOCKET:
2765 		if (ControlSocketName != NULL)
2766 			free(ControlSocketName);
2767 		ControlSocketName = newstr(val);
2768 		break;
2769 
2770 	  case O_MAXHDRSLEN:
2771 		MaxHeadersLength = atoi(val);
2772 
2773 		if (MaxHeadersLength > 0 &&
2774 		    MaxHeadersLength < (MAXHDRSLEN / 2))
2775 			printf("Warning: MaxHeadersLength: headers length limit set lower than %d\n", (MAXHDRSLEN / 2));
2776 		break;
2777 
2778 	  case O_PROCTITLEPREFIX:
2779 		if (ProcTitlePrefix != NULL)
2780 			free(ProcTitlePrefix);
2781 		ProcTitlePrefix = newstr(val);
2782 		break;
2783 
2784 #if SASL
2785 	  case O_SASLINFO:
2786 #if _FFR_ALLOW_SASLINFO
2787 		/*
2788 		**  Allow users to select their own authinfo file.
2789 		**  However, this is not a "perfect" solution.
2790 		**  If mail is queued, the authentication info
2791 		**  will not be used in subsequent delivery attempts.
2792 		**  If we really want to support this, then it has
2793 		**  to be stored in the queue file.
2794 		*/
2795 		if (!bitset(SUBMIT_MSA, SubmitMode) && RealUid != 0 &&
2796 		    RunAsUid != RealUid)
2797 		{
2798 			errno = 0;
2799 			syserr("Error: %s only allowed with -U\n",
2800 				o->o_name == NULL ? "<unknown>" : o->o_name);
2801 			ExitStat = EX_USAGE;
2802 			break;
2803 		}
2804 #endif /* _FFR_ALLOW_SASLINFO */
2805 		if (SASLInfo != NULL)
2806 			free(SASLInfo);
2807 		SASLInfo = newstr(val);
2808 		break;
2809 
2810 	  case O_SASLMECH:
2811 		if (AuthMechanisms != NULL)
2812 			free(AuthMechanisms);
2813 		if (*val != '\0')
2814 			AuthMechanisms = newstr(val);
2815 		else
2816 			AuthMechanisms = NULL;
2817 		break;
2818 
2819 	  case O_SASLOPTS:
2820 		while (val != NULL && *val != '\0')
2821 		{
2822 			switch(*val)
2823 			{
2824 			  case 'A':
2825 				SASLOpts |= SASL_AUTH_AUTH;
2826 				break;
2827 # if _FFR_SASL_OPTS
2828 			  case 'a':
2829 				SASLOpts |= SASL_SEC_NOACTIVE;
2830 				break;
2831 			  case 'c':
2832 				SASLOpts |= SASL_SEC_PASS_CREDENTIALS;
2833 				break;
2834 			  case 'd':
2835 				SASLOpts |= SASL_SEC_NODICTIONARY;
2836 				break;
2837 			  case 'f':
2838 				SASLOpts |= SASL_SEC_FORWARD_SECRECY;
2839 				break;
2840 			  case 'p':
2841 				SASLOpts |= SASL_SEC_NOPLAINTEXT;
2842 				break;
2843 			  case 'y':
2844 				SASLOpts |= SASL_SEC_NOANONYMOUS;
2845 				break;
2846 # endif /* _FFR_SASL_OPTS */
2847 			  default:
2848 				printf("Warning: Option: %s unknown parameter '%c'\n",
2849 					o->o_name == NULL ? "<unknown>"
2850 							  : o->o_name,
2851 					(isascii(*val) && isprint(*val)) ? *val
2852 									 : '?');
2853 				break;
2854 			}
2855 			++val;
2856 			val = strpbrk(val, ", \t");
2857 			if (val != NULL)
2858 				++val;
2859 		}
2860 		break;
2861 
2862 #else /* SASL */
2863 	  case O_SASLINFO:
2864 	  case O_SASLMECH:
2865 	  case O_SASLOPTS:
2866 		printf("Warning: Option: %s requires SASL support (-DSASL)\n",
2867 			o->o_name == NULL ? "<unknown>" : o->o_name);
2868 		break;
2869 #endif /* SASL */
2870 
2871 #if STARTTLS
2872 	  case O_SRVCERTFILE:
2873 		if (SrvCERTfile != NULL)
2874 			free(SrvCERTfile);
2875 		SrvCERTfile = newstr(val);
2876 		break;
2877 
2878 	  case O_SRVKEYFILE:
2879 		if (Srvkeyfile != NULL)
2880 			free(Srvkeyfile);
2881 		Srvkeyfile = newstr(val);
2882 		break;
2883 
2884 	  case O_CLTCERTFILE:
2885 		if (CltCERTfile != NULL)
2886 			free(CltCERTfile);
2887 		CltCERTfile = newstr(val);
2888 		break;
2889 
2890 	  case O_CLTKEYFILE:
2891 		if (Cltkeyfile != NULL)
2892 			free(Cltkeyfile);
2893 		Cltkeyfile = newstr(val);
2894 		break;
2895 
2896 	  case O_CACERTFILE:
2897 		if (CACERTfile != NULL)
2898 			free(CACERTfile);
2899 		CACERTfile = newstr(val);
2900 		break;
2901 
2902 	  case O_CACERTPATH:
2903 		if (CACERTpath != NULL)
2904 			free(CACERTpath);
2905 		CACERTpath = newstr(val);
2906 		break;
2907 
2908 	  case O_DHPARAMS:
2909 		if (DHParams != NULL)
2910 			free(DHParams);
2911 		DHParams = newstr(val);
2912 		break;
2913 
2914 #  if _FFR_TLS_1
2915 	  case O_DHPARAMS5:
2916 		if (DHParams5 != NULL)
2917 			free(DHParams5);
2918 		DHParams5 = newstr(val);
2919 		break;
2920 
2921 	  case O_CIPHERLIST:
2922 		if (CipherList != NULL)
2923 			free(CipherList);
2924 		CipherList = newstr(val);
2925 		break;
2926 #  endif /* _FFR_TLS_1 */
2927 
2928 	  case O_RANDFILE:
2929 		if (RandFile != NULL)
2930 			free(RandFile);
2931 		RandFile= newstr(val);
2932 		break;
2933 
2934 # else /* STARTTLS */
2935 	  case O_SRVCERTFILE:
2936 	  case O_SRVKEYFILE:
2937 	  case O_CLTCERTFILE:
2938 	  case O_CLTKEYFILE:
2939 	  case O_CACERTFILE:
2940 	  case O_CACERTPATH:
2941 	  case O_DHPARAMS:
2942 #  if _FFR_TLS_1
2943 	  case O_DHPARAMS5:
2944 	  case O_CIPHERLIST:
2945 #  endif /* _FFR_TLS_1 */
2946 	  case O_RANDFILE:
2947 		printf("Warning: Option: %s requires TLS support\n",
2948 			o->o_name == NULL ? "<unknown>" : o->o_name);
2949 		break;
2950 
2951 # endif /* STARTTLS */
2952 
2953 	  case O_CLIENTPORT:
2954 #if DAEMON
2955 		setclientoptions(val);
2956 #else /* DAEMON */
2957 		syserr("ClientPortOptions (O option) set but DAEMON not compiled in");
2958 #endif /* DAEMON */
2959 		break;
2960 
2961 	  case O_DF_BUFSIZE:
2962 		DataFileBufferSize = atoi(val);
2963 		break;
2964 
2965 	  case O_XF_BUFSIZE:
2966 		XscriptFileBufferSize = atoi(val);
2967 		break;
2968 
2969 	  case O_LDAPDEFAULTSPEC:
2970 #ifdef LDAPMAP
2971 		ldapmap_set_defaults(val);
2972 #else /* LDAPMAP */
2973 		printf("Warning: Option: %s requires LDAP support (-DLDAPMAP)\n",
2974 			o->o_name == NULL ? "<unknown>" : o->o_name);
2975 #endif /* LDAPMAP */
2976 		break;
2977 
2978 #if _FFR_MILTER
2979 	  case O_INPUTMILTER:
2980 		InputFilterList = newstr(val);
2981 		break;
2982 
2983 	  case O_MILTER:
2984 		milter_set_option(subopt, val, sticky);
2985 		break;
2986 #endif /* _FFR_MILTER */
2987 
2988 #if _FFR_QUEUE_FILE_MODE
2989 	  case O_QUEUE_FILE_MODE:	/* queue file mode */
2990 		QueueFileMode = atooct(val) & 0777;
2991 		break;
2992 #endif /* _FFR_QUEUE_FILE_MODE */
2993 
2994 	  default:
2995 		if (tTd(37, 1))
2996 		{
2997 			if (isascii(opt) && isprint(opt))
2998 				dprintf("Warning: option %c unknown\n", opt);
2999 			else
3000 				dprintf("Warning: option 0x%x unknown\n", opt);
3001 		}
3002 		break;
3003 	}
3004 
3005 	/*
3006 	**  Options with suboptions are responsible for taking care
3007 	**  of sticky-ness (e.g., that a command line setting is kept
3008 	**  when reading in the sendmail.cf file).  This has to be done
3009 	**  when the suboptions are parsed since each suboption must be
3010 	**  sticky, not the root option.
3011 	*/
3012 
3013 	if (sticky && !bitset(OI_SUBOPT, o->o_flags))
3014 		setbitn(opt, StickyOpt);
3015 }
3016 /*
3017 **  SETCLASS -- set a string into a class
3018 **
3019 **	Parameters:
3020 **		class -- the class to put the string in.
3021 **		str -- the string to enter
3022 **
3023 **	Returns:
3024 **		none.
3025 **
3026 **	Side Effects:
3027 **		puts the word into the symbol table.
3028 */
3029 
3030 void
3031 setclass(class, str)
3032 	int class;
3033 	char *str;
3034 {
3035 	register STAB *s;
3036 
3037 	if ((*str & 0377) == MATCHCLASS)
3038 	{
3039 		int mid;
3040 
3041 		str++;
3042 		mid = macid(str, NULL);
3043 		if (mid == 0)
3044 			return;
3045 
3046 		if (tTd(37, 8))
3047 			dprintf("setclass(%s, $=%s)\n",
3048 				macname(class), macname(mid));
3049 		copy_class(mid, class);
3050 	}
3051 	else
3052 	{
3053 		if (tTd(37, 8))
3054 			dprintf("setclass(%s, %s)\n", macname(class), str);
3055 
3056 		s = stab(str, ST_CLASS, ST_ENTER);
3057 		setbitn(bitidx(class), s->s_class);
3058 	}
3059 }
3060 /*
3061 **  MAKEMAPENTRY -- create a map entry
3062 **
3063 **	Parameters:
3064 **		line -- the config file line
3065 **
3066 **	Returns:
3067 **		A pointer to the map that has been created.
3068 **		NULL if there was a syntax error.
3069 **
3070 **	Side Effects:
3071 **		Enters the map into the dictionary.
3072 */
3073 
3074 MAP *
3075 makemapentry(line)
3076 	char *line;
3077 {
3078 	register char *p;
3079 	char *mapname;
3080 	char *classname;
3081 	register STAB *s;
3082 	STAB *class;
3083 
3084 	for (p = line; isascii(*p) && isspace(*p); p++)
3085 		continue;
3086 	if (!(isascii(*p) && isalnum(*p)))
3087 	{
3088 		syserr("readcf: config K line: no map name");
3089 		return NULL;
3090 	}
3091 
3092 	mapname = p;
3093 	while ((isascii(*++p) && isalnum(*p)) || *p == '_' || *p == '.')
3094 		continue;
3095 	if (*p != '\0')
3096 		*p++ = '\0';
3097 	while (isascii(*p) && isspace(*p))
3098 		p++;
3099 	if (!(isascii(*p) && isalnum(*p)))
3100 	{
3101 		syserr("readcf: config K line, map %s: no map class", mapname);
3102 		return NULL;
3103 	}
3104 	classname = p;
3105 	while (isascii(*++p) && isalnum(*p))
3106 		continue;
3107 	if (*p != '\0')
3108 		*p++ = '\0';
3109 	while (isascii(*p) && isspace(*p))
3110 		p++;
3111 
3112 	/* look up the class */
3113 	class = stab(classname, ST_MAPCLASS, ST_FIND);
3114 	if (class == NULL)
3115 	{
3116 		syserr("readcf: map %s: class %s not available", mapname, classname);
3117 		return NULL;
3118 	}
3119 
3120 	/* enter the map */
3121 	s = stab(mapname, ST_MAP, ST_ENTER);
3122 	s->s_map.map_class = &class->s_mapclass;
3123 	s->s_map.map_mname = newstr(mapname);
3124 
3125 	if (class->s_mapclass.map_parse(&s->s_map, p))
3126 		s->s_map.map_mflags |= MF_VALID;
3127 
3128 	if (tTd(37, 5))
3129 	{
3130 		dprintf("map %s, class %s, flags %lx, file %s,\n",
3131 			s->s_map.map_mname, s->s_map.map_class->map_cname,
3132 			s->s_map.map_mflags,
3133 			s->s_map.map_file == NULL ? "(null)" : s->s_map.map_file);
3134 		dprintf("\tapp %s, domain %s, rebuild %s\n",
3135 			s->s_map.map_app == NULL ? "(null)" : s->s_map.map_app,
3136 			s->s_map.map_domain == NULL ? "(null)" : s->s_map.map_domain,
3137 			s->s_map.map_rebuild == NULL ? "(null)" : s->s_map.map_rebuild);
3138 	}
3139 
3140 	return &s->s_map;
3141 }
3142 /*
3143 **  STRTORWSET -- convert string to rewriting set number
3144 **
3145 **	Parameters:
3146 **		p -- the pointer to the string to decode.
3147 **		endp -- if set, store the trailing delimiter here.
3148 **		stabmode -- ST_ENTER to create this entry, ST_FIND if
3149 **			it must already exist.
3150 **
3151 **	Returns:
3152 **		The appropriate ruleset number.
3153 **		-1 if it is not valid (error already printed)
3154 */
3155 
3156 int
3157 strtorwset(p, endp, stabmode)
3158 	char *p;
3159 	char **endp;
3160 	int stabmode;
3161 {
3162 	int ruleset;
3163 	static int nextruleset = MAXRWSETS;
3164 
3165 	while (isascii(*p) && isspace(*p))
3166 		p++;
3167 	if (!isascii(*p))
3168 	{
3169 		syserr("invalid ruleset name: \"%.20s\"", p);
3170 		return -1;
3171 	}
3172 	if (isdigit(*p))
3173 	{
3174 		ruleset = strtol(p, endp, 10);
3175 		if (ruleset >= MAXRWSETS / 2 || ruleset < 0)
3176 		{
3177 			syserr("bad ruleset %d (%d max)",
3178 				ruleset, MAXRWSETS / 2);
3179 			ruleset = -1;
3180 		}
3181 	}
3182 	else
3183 	{
3184 		STAB *s;
3185 		char delim;
3186 		char *q = NULL;
3187 
3188 		q = p;
3189 		while (*p != '\0' && isascii(*p) &&
3190 		       (isalnum(*p) || *p == '_'))
3191 			p++;
3192 		if (q == p || !(isascii(*q) && isalpha(*q)))
3193 		{
3194 			/* no valid characters */
3195 			syserr("invalid ruleset name: \"%.20s\"", q);
3196 			return -1;
3197 		}
3198 		while (isascii(*p) && isspace(*p))
3199 			*p++ = '\0';
3200 		delim = *p;
3201 		if (delim != '\0')
3202 			*p = '\0';
3203 		s = stab(q, ST_RULESET, stabmode);
3204 		if (delim != '\0')
3205 			*p = delim;
3206 
3207 		if (s == NULL)
3208 			return -1;
3209 
3210 		if (stabmode == ST_ENTER && delim == '=')
3211 		{
3212 			while (isascii(*++p) && isspace(*p))
3213 				continue;
3214 			if (!(isascii(*p) && isdigit(*p)))
3215 			{
3216 				syserr("bad ruleset definition \"%s\" (number required after `=')", q);
3217 				ruleset = -1;
3218 			}
3219 			else
3220 			{
3221 				ruleset = strtol(p, endp, 10);
3222 				if (ruleset >= MAXRWSETS / 2 || ruleset < 0)
3223 				{
3224 					syserr("bad ruleset number %d in \"%s\" (%d max)",
3225 						ruleset, q, MAXRWSETS / 2);
3226 					ruleset = -1;
3227 				}
3228 			}
3229 		}
3230 		else
3231 		{
3232 			if (endp != NULL)
3233 				*endp = p;
3234 			if (s->s_ruleset >= 0)
3235 				ruleset = s->s_ruleset;
3236 			else if ((ruleset = --nextruleset) < MAXRWSETS / 2)
3237 			{
3238 				syserr("%s: too many named rulesets (%d max)",
3239 					q, MAXRWSETS / 2);
3240 				ruleset = -1;
3241 			}
3242 		}
3243 		if (s->s_ruleset >= 0 &&
3244 		    ruleset >= 0 &&
3245 		    ruleset != s->s_ruleset)
3246 		{
3247 			syserr("%s: ruleset changed value (old %d, new %d)",
3248 				q, s->s_ruleset, ruleset);
3249 			ruleset = s->s_ruleset;
3250 		}
3251 		else if (ruleset >= 0)
3252 		{
3253 			s->s_ruleset = ruleset;
3254 		}
3255 		if (stabmode == ST_ENTER && ruleset >= 0)
3256 		{
3257 			char *h = NULL;
3258 
3259 			if (RuleSetNames[ruleset] != NULL)
3260 				free(RuleSetNames[ruleset]);
3261 			if (delim != '\0' && (h = strchr(q, delim)) != NULL)
3262 				*h = '\0';
3263 			RuleSetNames[ruleset] = newstr(q);
3264 			if (delim == '/' && h != NULL)
3265 				*h = delim;	/* put back delim */
3266 		}
3267 	}
3268 	return ruleset;
3269 }
3270 /*
3271 **  SETTIMEOUT -- set an individual timeout
3272 **
3273 **	Parameters:
3274 **		name -- the name of the timeout.
3275 **		val -- the value of the timeout.
3276 **		sticky -- if set, don't let other setoptions override
3277 **			this value.
3278 **
3279 **	Returns:
3280 **		none.
3281 */
3282 
3283 /* set if Timeout sub-option is stuck */
3284 static BITMAP256	StickyTimeoutOpt;
3285 
3286 static struct timeoutinfo
3287 {
3288 	char	*to_name;	/* long name of timeout */
3289 	u_char	to_code;	/* code for option */
3290 } TimeOutTab[] =
3291 {
3292 #define TO_INITIAL			0x01
3293 	{ "initial",			TO_INITIAL			},
3294 #define TO_MAIL				0x02
3295 	{ "mail",			TO_MAIL				},
3296 #define TO_RCPT				0x03
3297 	{ "rcpt",			TO_RCPT				},
3298 #define TO_DATAINIT			0x04
3299 	{ "datainit",			TO_DATAINIT			},
3300 #define TO_DATABLOCK			0x05
3301 	{ "datablock",			TO_DATABLOCK			},
3302 #define TO_DATAFINAL			0x06
3303 	{ "datafinal",			TO_DATAFINAL			},
3304 #define TO_COMMAND			0x07
3305 	{ "command",			TO_COMMAND			},
3306 #define TO_RSET				0x08
3307 	{ "rset",			TO_RSET				},
3308 #define TO_HELO				0x09
3309 	{ "helo",			TO_HELO				},
3310 #define TO_QUIT				0x0A
3311 	{ "quit",			TO_QUIT				},
3312 #define TO_MISC				0x0B
3313 	{ "misc",			TO_MISC				},
3314 #define TO_IDENT			0x0C
3315 	{ "ident",			TO_IDENT			},
3316 #define TO_FILEOPEN			0x0D
3317 	{ "fileopen",			TO_FILEOPEN			},
3318 #define TO_CONNECT			0x0E
3319 	{ "connect",			TO_CONNECT			},
3320 #define TO_ICONNECT			0x0F
3321 	{ "iconnect",			TO_ICONNECT			},
3322 #define TO_QUEUEWARN			0x10
3323 	{ "queuewarn",			TO_QUEUEWARN			},
3324 	{ "queuewarn.*",		TO_QUEUEWARN			},
3325 #define TO_QUEUEWARN_NORMAL		0x11
3326 	{ "queuewarn.normal",		TO_QUEUEWARN_NORMAL		},
3327 #define TO_QUEUEWARN_URGENT		0x12
3328 	{ "queuewarn.urgent",		TO_QUEUEWARN_URGENT		},
3329 #define TO_QUEUEWARN_NON_URGENT		0x13
3330 	{ "queuewarn.non-urgent",	TO_QUEUEWARN_NON_URGENT		},
3331 #define TO_QUEUERETURN			0x14
3332 	{ "queuereturn",		TO_QUEUERETURN			},
3333 	{ "queuereturn.*",		TO_QUEUERETURN			},
3334 #define TO_QUEUERETURN_NORMAL		0x15
3335 	{ "queuereturn.normal",		TO_QUEUERETURN_NORMAL		},
3336 #define TO_QUEUERETURN_URGENT		0x16
3337 	{ "queuereturn.urgent",		TO_QUEUERETURN_URGENT		},
3338 #define TO_QUEUERETURN_NON_URGENT	0x17
3339 	{ "queuereturn.non-urgent",	TO_QUEUERETURN_NON_URGENT	},
3340 #define TO_HOSTSTATUS			0x18
3341 	{ "hoststatus",			TO_HOSTSTATUS			},
3342 #define TO_RESOLVER_RETRANS		0x19
3343 	{ "resolver.retrans",		TO_RESOLVER_RETRANS		},
3344 #define TO_RESOLVER_RETRANS_NORMAL	0x1A
3345 	{ "resolver.retrans.normal",	TO_RESOLVER_RETRANS_NORMAL	},
3346 #define TO_RESOLVER_RETRANS_FIRST	0x1B
3347 	{ "resolver.retrans.first",	TO_RESOLVER_RETRANS_FIRST	},
3348 #define TO_RESOLVER_RETRY		0x1C
3349 	{ "resolver.retry",		TO_RESOLVER_RETRY		},
3350 #define TO_RESOLVER_RETRY_NORMAL	0x1D
3351 	{ "resolver.retry.normal",	TO_RESOLVER_RETRY_NORMAL	},
3352 #define TO_RESOLVER_RETRY_FIRST		0x1E
3353 	{ "resolver.retry.first",	TO_RESOLVER_RETRY_FIRST		},
3354 #define TO_CONTROL			0x1F
3355 	{ "control",			TO_CONTROL			},
3356 	{ NULL,				0				},
3357 };
3358 
3359 
3360 static void
3361 settimeout(name, val, sticky)
3362 	char *name;
3363 	char *val;
3364 	bool sticky;
3365 {
3366 	register struct timeoutinfo *to;
3367 	int i;
3368 	time_t toval;
3369 
3370 	if (tTd(37, 2))
3371 		dprintf("settimeout(%s = %s)", name, val);
3372 
3373 	for (to = TimeOutTab; to->to_name != NULL; to++)
3374 	{
3375 		if (strcasecmp(to->to_name, name) == 0)
3376 			break;
3377 	}
3378 
3379 	if (to->to_name == NULL)
3380 	{
3381 		errno = 0; /* avoid bogus error text */
3382 		syserr("settimeout: invalid timeout %s", name);
3383 		return;
3384 	}
3385 
3386 	/*
3387 	**  See if this option is preset for us.
3388 	*/
3389 
3390 	if (!sticky && bitnset(to->to_code, StickyTimeoutOpt))
3391 	{
3392 		if (tTd(37, 2))
3393 			dprintf(" (ignored)\n");
3394 		return;
3395 	}
3396 
3397 	if (tTd(37, 2))
3398 		dprintf("\n");
3399 
3400 	toval = convtime(val, 'm');
3401 
3402 	switch (to->to_code)
3403 	{
3404 	  case TO_INITIAL:
3405 		TimeOuts.to_initial = toval;
3406 		break;
3407 
3408 	  case TO_MAIL:
3409 		TimeOuts.to_mail = toval;
3410 		break;
3411 
3412 	  case TO_RCPT:
3413 		TimeOuts.to_rcpt = toval;
3414 		break;
3415 
3416 	  case TO_DATAINIT:
3417 		TimeOuts.to_datainit = toval;
3418 		break;
3419 
3420 	  case TO_DATABLOCK:
3421 		TimeOuts.to_datablock = toval;
3422 		break;
3423 
3424 	  case TO_DATAFINAL:
3425 		TimeOuts.to_datafinal = toval;
3426 		break;
3427 
3428 	  case TO_COMMAND:
3429 		TimeOuts.to_nextcommand = toval;
3430 		break;
3431 
3432 	  case TO_RSET:
3433 		TimeOuts.to_rset = toval;
3434 		break;
3435 
3436 	  case TO_HELO:
3437 		TimeOuts.to_helo = toval;
3438 		break;
3439 
3440 	  case TO_QUIT:
3441 		TimeOuts.to_quit = toval;
3442 		break;
3443 
3444 	  case TO_MISC:
3445 		TimeOuts.to_miscshort = toval;
3446 		break;
3447 
3448 	  case TO_IDENT:
3449 		TimeOuts.to_ident = toval;
3450 		break;
3451 
3452 	  case TO_FILEOPEN:
3453 		TimeOuts.to_fileopen = toval;
3454 		break;
3455 
3456 	  case TO_CONNECT:
3457 		TimeOuts.to_connect = toval;
3458 		break;
3459 
3460 	  case TO_ICONNECT:
3461 		TimeOuts.to_iconnect = toval;
3462 		break;
3463 
3464 	  case TO_QUEUEWARN:
3465 		toval = convtime(val, 'h');
3466 		TimeOuts.to_q_warning[TOC_NORMAL] = toval;
3467 		TimeOuts.to_q_warning[TOC_URGENT] = toval;
3468 		TimeOuts.to_q_warning[TOC_NONURGENT] = toval;
3469 		break;
3470 
3471 	  case TO_QUEUEWARN_NORMAL:
3472 		toval = convtime(val, 'h');
3473 		TimeOuts.to_q_warning[TOC_NORMAL] = toval;
3474 		break;
3475 
3476 	  case TO_QUEUEWARN_URGENT:
3477 		toval = convtime(val, 'h');
3478 		TimeOuts.to_q_warning[TOC_URGENT] = toval;
3479 		break;
3480 
3481 	  case TO_QUEUEWARN_NON_URGENT:
3482 		toval = convtime(val, 'h');
3483 		TimeOuts.to_q_warning[TOC_NONURGENT] = toval;
3484 		break;
3485 
3486 	  case TO_QUEUERETURN:
3487 		toval = convtime(val, 'd');
3488 		TimeOuts.to_q_return[TOC_NORMAL] = toval;
3489 		TimeOuts.to_q_return[TOC_URGENT] = toval;
3490 		TimeOuts.to_q_return[TOC_NONURGENT] = toval;
3491 		break;
3492 
3493 	  case TO_QUEUERETURN_NORMAL:
3494 		toval = convtime(val, 'd');
3495 		TimeOuts.to_q_return[TOC_NORMAL] = toval;
3496 		break;
3497 
3498 	  case TO_QUEUERETURN_URGENT:
3499 		toval = convtime(val, 'd');
3500 		TimeOuts.to_q_return[TOC_URGENT] = toval;
3501 		break;
3502 
3503 	  case TO_QUEUERETURN_NON_URGENT:
3504 		toval = convtime(val, 'd');
3505 		TimeOuts.to_q_return[TOC_NONURGENT] = toval;
3506 		break;
3507 
3508 
3509 	  case TO_HOSTSTATUS:
3510 		MciInfoTimeout = toval;
3511 		break;
3512 
3513 	  case TO_RESOLVER_RETRANS:
3514 		toval = convtime(val, 's');
3515 		TimeOuts.res_retrans[RES_TO_DEFAULT] = toval;
3516 		TimeOuts.res_retrans[RES_TO_FIRST] = toval;
3517 		TimeOuts.res_retrans[RES_TO_NORMAL] = toval;
3518 		break;
3519 
3520 	  case TO_RESOLVER_RETRY:
3521 		i = atoi(val);
3522 		TimeOuts.res_retry[RES_TO_DEFAULT] = i;
3523 		TimeOuts.res_retry[RES_TO_FIRST] = i;
3524 		TimeOuts.res_retry[RES_TO_NORMAL] = i;
3525 		break;
3526 
3527 	  case TO_RESOLVER_RETRANS_NORMAL:
3528 		TimeOuts.res_retrans[RES_TO_NORMAL] = convtime(val, 's');
3529 		break;
3530 
3531 	  case TO_RESOLVER_RETRY_NORMAL:
3532 		TimeOuts.res_retry[RES_TO_NORMAL] = atoi(val);
3533 		break;
3534 
3535 	  case TO_RESOLVER_RETRANS_FIRST:
3536 		TimeOuts.res_retrans[RES_TO_FIRST] = convtime(val, 's');
3537 		break;
3538 
3539 	  case TO_RESOLVER_RETRY_FIRST:
3540 		TimeOuts.res_retry[RES_TO_FIRST] = atoi(val);
3541 		break;
3542 
3543 	  case TO_CONTROL:
3544 		TimeOuts.to_control = toval;
3545 		break;
3546 
3547 	  default:
3548 		syserr("settimeout: invalid timeout %s", name);
3549 		break;
3550 	}
3551 
3552 	if (sticky)
3553 		setbitn(to->to_code, StickyTimeoutOpt);
3554 }
3555 /*
3556 **  INITTIMEOUTS -- parse and set timeout values
3557 **
3558 **	Parameters:
3559 **		val -- a pointer to the values.  If NULL, do initial
3560 **			settings.
3561 **		sticky -- if set, don't let other setoptions override
3562 **			this suboption value.
3563 **
3564 **	Returns:
3565 **		none.
3566 **
3567 **	Side Effects:
3568 **		Initializes the TimeOuts structure
3569 */
3570 
3571 void
3572 inittimeouts(val, sticky)
3573 	register char *val;
3574 	bool sticky;
3575 {
3576 	register char *p;
3577 
3578 	if (tTd(37, 2))
3579 		dprintf("inittimeouts(%s)\n", val == NULL ? "<NULL>" : val);
3580 	if (val == NULL)
3581 	{
3582 		TimeOuts.to_connect = (time_t) 0 SECONDS;
3583 		TimeOuts.to_initial = (time_t) 5 MINUTES;
3584 		TimeOuts.to_helo = (time_t) 5 MINUTES;
3585 		TimeOuts.to_mail = (time_t) 10 MINUTES;
3586 		TimeOuts.to_rcpt = (time_t) 1 HOUR;
3587 		TimeOuts.to_datainit = (time_t) 5 MINUTES;
3588 		TimeOuts.to_datablock = (time_t) 1 HOUR;
3589 		TimeOuts.to_datafinal = (time_t) 1 HOUR;
3590 		TimeOuts.to_rset = (time_t) 5 MINUTES;
3591 		TimeOuts.to_quit = (time_t) 2 MINUTES;
3592 		TimeOuts.to_nextcommand = (time_t) 1 HOUR;
3593 		TimeOuts.to_miscshort = (time_t) 2 MINUTES;
3594 #if IDENTPROTO
3595 		TimeOuts.to_ident = (time_t) 5 SECONDS;
3596 #else /* IDENTPROTO */
3597 		TimeOuts.to_ident = (time_t) 0 SECONDS;
3598 #endif /* IDENTPROTO */
3599 		TimeOuts.to_fileopen = (time_t) 60 SECONDS;
3600 		TimeOuts.to_control = (time_t) 2 MINUTES;
3601 		if (tTd(37, 5))
3602 		{
3603 			dprintf("Timeouts:\n");
3604 			dprintf("  connect = %ld\n", (long)TimeOuts.to_connect);
3605 			dprintf("  initial = %ld\n", (long)TimeOuts.to_initial);
3606 			dprintf("  helo = %ld\n", (long)TimeOuts.to_helo);
3607 			dprintf("  mail = %ld\n", (long)TimeOuts.to_mail);
3608 			dprintf("  rcpt = %ld\n", (long)TimeOuts.to_rcpt);
3609 			dprintf("  datainit = %ld\n", (long)TimeOuts.to_datainit);
3610 			dprintf("  datablock = %ld\n", (long)TimeOuts.to_datablock);
3611 			dprintf("  datafinal = %ld\n", (long)TimeOuts.to_datafinal);
3612 			dprintf("  rset = %ld\n", (long)TimeOuts.to_rset);
3613 			dprintf("  quit = %ld\n", (long)TimeOuts.to_quit);
3614 			dprintf("  nextcommand = %ld\n", (long)TimeOuts.to_nextcommand);
3615 			dprintf("  miscshort = %ld\n", (long)TimeOuts.to_miscshort);
3616 			dprintf("  ident = %ld\n", (long)TimeOuts.to_ident);
3617 			dprintf("  fileopen = %ld\n", (long)TimeOuts.to_fileopen);
3618 			dprintf("  control = %ld\n", (long)TimeOuts.to_control);
3619 		}
3620 		return;
3621 	}
3622 
3623 	for (;; val = p)
3624 	{
3625 		while (isascii(*val) && isspace(*val))
3626 			val++;
3627 		if (*val == '\0')
3628 			break;
3629 		for (p = val; *p != '\0' && *p != ','; p++)
3630 			continue;
3631 		if (*p != '\0')
3632 			*p++ = '\0';
3633 
3634 		if (isascii(*val) && isdigit(*val))
3635 		{
3636 			/* old syntax -- set everything */
3637 			TimeOuts.to_mail = convtime(val, 'm');
3638 			TimeOuts.to_rcpt = TimeOuts.to_mail;
3639 			TimeOuts.to_datainit = TimeOuts.to_mail;
3640 			TimeOuts.to_datablock = TimeOuts.to_mail;
3641 			TimeOuts.to_datafinal = TimeOuts.to_mail;
3642 			TimeOuts.to_nextcommand = TimeOuts.to_mail;
3643 			if (sticky)
3644 			{
3645 				setbitn(TO_MAIL, StickyTimeoutOpt);
3646 				setbitn(TO_RCPT, StickyTimeoutOpt);
3647 				setbitn(TO_DATAINIT, StickyTimeoutOpt);
3648 				setbitn(TO_DATABLOCK, StickyTimeoutOpt);
3649 				setbitn(TO_DATAFINAL, StickyTimeoutOpt);
3650 				setbitn(TO_COMMAND, StickyTimeoutOpt);
3651 			}
3652 			continue;
3653 		}
3654 		else
3655 		{
3656 			register char *q = strchr(val, ':');
3657 
3658 			if (q == NULL && (q = strchr(val, '=')) == NULL)
3659 			{
3660 				/* syntax error */
3661 				continue;
3662 			}
3663 			*q++ = '\0';
3664 			settimeout(val, q, sticky);
3665 		}
3666 	}
3667 }
3668