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