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