xref: /csrg-svn/usr.sbin/sendmail/src/readcf.c (revision 69476)
1 /*
2  * Copyright (c) 1983, 1995 Eric P. Allman
3  * Copyright (c) 1988, 1993
4  *	The Regents of the University of California.  All rights reserved.
5  *
6  * %sccs.include.redist.c%
7  */
8 
9 #ifndef lint
10 static char sccsid[] = "@(#)readcf.c	8.90 (Berkeley) 05/15/95";
11 #endif /* not lint */
12 
13 # include "sendmail.h"
14 # include <grp.h>
15 #if NAMED_BIND
16 # include <resolv.h>
17 #endif
18 
19 /*
20 **  READCF -- read control file.
21 **
22 **	This routine reads the control file and builds the internal
23 **	form.
24 **
25 **	The file is formatted as a sequence of lines, each taken
26 **	atomically.  The first character of each line describes how
27 **	the line is to be interpreted.  The lines are:
28 **		Dxval		Define macro x to have value val.
29 **		Cxword		Put word into class x.
30 **		Fxfile [fmt]	Read file for lines to put into
31 **				class x.  Use scanf string 'fmt'
32 **				or "%s" if not present.  Fmt should
33 **				only produce one string-valued result.
34 **		Hname: value	Define header with field-name 'name'
35 **				and value as specified; this will be
36 **				macro expanded immediately before
37 **				use.
38 **		Sn		Use rewriting set n.
39 **		Rlhs rhs	Rewrite addresses that match lhs to
40 **				be rhs.
41 **		Mn arg=val...	Define mailer.  n is the internal name.
42 **				Args specify mailer parameters.
43 **		Oxvalue		Set option x to value.
44 **		Pname=value	Set precedence name to value.
45 **		Vversioncode[/vendorcode]
46 **				Version level/vendor name of
47 **				configuration syntax.
48 **		Kmapname mapclass arguments....
49 **				Define keyed lookup of a given class.
50 **				Arguments are class dependent.
51 **		Eenvar=value	Set the environment value to the given value.
52 **
53 **	Parameters:
54 **		cfname -- control file name.
55 **		safe -- TRUE if this is the system config file;
56 **			FALSE otherwise.
57 **		e -- the main envelope.
58 **
59 **	Returns:
60 **		none.
61 **
62 **	Side Effects:
63 **		Builds several internal tables.
64 */
65 
66 readcf(cfname, safe, e)
67 	char *cfname;
68 	bool safe;
69 	register ENVELOPE *e;
70 {
71 	FILE *cf;
72 	int ruleset = 0;
73 	int nextruleset = MAXRWSETS;
74 	char *q;
75 	struct rewrite *rwp = NULL;
76 	char *bp;
77 	auto char *ep;
78 	int nfuzzy;
79 	char *file;
80 	bool optional;
81 	int mid;
82 	char buf[MAXLINE];
83 	register char *p;
84 	extern char **copyplist();
85 	struct stat statb;
86 	char exbuf[MAXLINE];
87 	char pvpbuf[MAXLINE + MAXATOM];
88 	static char *null_list[1] = { NULL };
89 	extern char *munchstring();
90 	extern void makemapentry();
91 
92 	FileName = cfname;
93 	LineNumber = 0;
94 
95 	cf = fopen(cfname, "r");
96 	if (cf == NULL)
97 	{
98 		syserr("cannot open");
99 		exit(EX_OSFILE);
100 	}
101 
102 	if (fstat(fileno(cf), &statb) < 0)
103 	{
104 		syserr("cannot fstat");
105 		exit(EX_OSFILE);
106 	}
107 
108 	if (!S_ISREG(statb.st_mode))
109 	{
110 		syserr("not a plain file");
111 		exit(EX_OSFILE);
112 	}
113 
114 	if (OpMode != MD_TEST && bitset(S_IWGRP|S_IWOTH, statb.st_mode))
115 	{
116 		if (OpMode == MD_DAEMON || OpMode == MD_FREEZE)
117 			fprintf(stderr, "%s: WARNING: dangerous write permissions\n",
118 				FileName);
119 #ifdef LOG
120 		if (LogLevel > 0)
121 			syslog(LOG_CRIT, "%s: WARNING: dangerous write permissions",
122 				FileName);
123 #endif
124 	}
125 
126 #ifdef XLA
127 	xla_zero();
128 #endif
129 
130 	while ((bp = fgetfolded(buf, sizeof buf, cf)) != NULL)
131 	{
132 		if (bp[0] == '#')
133 		{
134 			if (bp != buf)
135 				free(bp);
136 			continue;
137 		}
138 
139 		/* do macro expansion mappings */
140 		for (p = bp; *p != '\0'; p++)
141 		{
142 			if (*p == '#' && p > bp && ConfigLevel >= 3)
143 			{
144 				/* this is an on-line comment */
145 				register char *e;
146 
147 				switch (*--p & 0377)
148 				{
149 				  case MACROEXPAND:
150 					/* it's from $# -- let it go through */
151 					p++;
152 					break;
153 
154 				  case '\\':
155 					/* it's backslash escaped */
156 					(void) strcpy(p, p + 1);
157 					break;
158 
159 				  default:
160 					/* delete preceeding white space */
161 					while (isascii(*p) && isspace(*p) && p > bp)
162 						p--;
163 					if ((e = strchr(++p, '\n')) != NULL)
164 						(void) strcpy(p, e);
165 					else
166 						p[0] = p[1] = '\0';
167 					break;
168 				}
169 				continue;
170 			}
171 
172 			if (*p != '$' || p[1] == '\0')
173 				continue;
174 
175 			if (p[1] == '$')
176 			{
177 				/* actual dollar sign.... */
178 				(void) strcpy(p, p + 1);
179 				continue;
180 			}
181 
182 			/* convert to macro expansion character */
183 			*p++ = MACROEXPAND;
184 
185 			/* convert macro name to code */
186 			*p = macid(p, &ep);
187 			if (ep != p)
188 				strcpy(p + 1, ep);
189 		}
190 
191 		/* interpret this line */
192 		errno = 0;
193 		switch (bp[0])
194 		{
195 		  case '\0':
196 		  case '#':		/* comment */
197 			break;
198 
199 		  case 'R':		/* rewriting rule */
200 			for (p = &bp[1]; *p != '\0' && *p != '\t'; p++)
201 				continue;
202 
203 			if (*p == '\0')
204 			{
205 				syserr("invalid rewrite line \"%s\" (tab expected)", bp);
206 				break;
207 			}
208 
209 			/* allocate space for the rule header */
210 			if (rwp == NULL)
211 			{
212 				RewriteRules[ruleset] = rwp =
213 					(struct rewrite *) xalloc(sizeof *rwp);
214 			}
215 			else
216 			{
217 				rwp->r_next = (struct rewrite *) xalloc(sizeof *rwp);
218 				rwp = rwp->r_next;
219 			}
220 			rwp->r_next = NULL;
221 
222 			/* expand and save the LHS */
223 			*p = '\0';
224 			expand(&bp[1], exbuf, sizeof exbuf, e);
225 			rwp->r_lhs = prescan(exbuf, '\t', pvpbuf,
226 					     sizeof pvpbuf, NULL, NULL);
227 			nfuzzy = 0;
228 			if (rwp->r_lhs != NULL)
229 			{
230 				register char **ap;
231 
232 				rwp->r_lhs = copyplist(rwp->r_lhs, TRUE);
233 
234 				/* count the number of fuzzy matches in LHS */
235 				for (ap = rwp->r_lhs; *ap != NULL; ap++)
236 				{
237 					char *botch;
238 
239 					botch = NULL;
240 					switch (**ap & 0377)
241 					{
242 					  case MATCHZANY:
243 					  case MATCHANY:
244 					  case MATCHONE:
245 					  case MATCHCLASS:
246 					  case MATCHNCLASS:
247 						nfuzzy++;
248 						break;
249 
250 					  case MATCHREPL:
251 						botch = "$0-$9";
252 						break;
253 
254 					  case CANONNET:
255 						botch = "$#";
256 						break;
257 
258 					  case CANONUSER:
259 						botch = "$:";
260 						break;
261 
262 					  case CALLSUBR:
263 						botch = "$>";
264 						break;
265 
266 					  case CONDIF:
267 						botch = "$?";
268 						break;
269 
270 					  case CONDELSE:
271 						botch = "$|";
272 						break;
273 
274 					  case CONDFI:
275 						botch = "$.";
276 						break;
277 
278 					  case HOSTBEGIN:
279 						botch = "$[";
280 						break;
281 
282 					  case HOSTEND:
283 						botch = "$]";
284 						break;
285 
286 					  case LOOKUPBEGIN:
287 						botch = "$(";
288 						break;
289 
290 					  case LOOKUPEND:
291 						botch = "$)";
292 						break;
293 					}
294 					if (botch != NULL)
295 						syserr("Inappropriate use of %s on LHS",
296 							botch);
297 				}
298 			}
299 			else
300 			{
301 				syserr("R line: null LHS");
302 				rwp->r_lhs = null_list;
303 			}
304 
305 			/* expand and save the RHS */
306 			while (*++p == '\t')
307 				continue;
308 			q = p;
309 			while (*p != '\0' && *p != '\t')
310 				p++;
311 			*p = '\0';
312 			expand(q, exbuf, sizeof exbuf, e);
313 			rwp->r_rhs = prescan(exbuf, '\t', pvpbuf,
314 					     sizeof pvpbuf, NULL, NULL);
315 			if (rwp->r_rhs != NULL)
316 			{
317 				register char **ap;
318 
319 				rwp->r_rhs = copyplist(rwp->r_rhs, TRUE);
320 
321 				/* check no out-of-bounds replacements */
322 				nfuzzy += '0';
323 				for (ap = rwp->r_rhs; *ap != NULL; ap++)
324 				{
325 					char *botch;
326 
327 					botch = NULL;
328 					switch (**ap & 0377)
329 					{
330 					  case MATCHREPL:
331 						if ((*ap)[1] <= '0' || (*ap)[1] > nfuzzy)
332 						{
333 							syserr("replacement $%c out of bounds",
334 								(*ap)[1]);
335 						}
336 						break;
337 
338 					  case MATCHZANY:
339 						botch = "$*";
340 						break;
341 
342 					  case MATCHANY:
343 						botch = "$+";
344 						break;
345 
346 					  case MATCHONE:
347 						botch = "$-";
348 						break;
349 
350 					  case MATCHCLASS:
351 						botch = "$=";
352 						break;
353 
354 					  case MATCHNCLASS:
355 						botch = "$~";
356 						break;
357 					}
358 					if (botch != NULL)
359 						syserr("Inappropriate use of %s on RHS",
360 							botch);
361 				}
362 			}
363 			else
364 			{
365 				syserr("R line: null RHS");
366 				rwp->r_rhs = null_list;
367 			}
368 			break;
369 
370 		  case 'S':		/* select rewriting set */
371 			for (p = &bp[1]; isascii(*p) && isspace(*p); p++)
372 				continue;
373 			if (!isascii(*p))
374 			{
375 				syserr("invalid argument to S line: \"%.20s\"",
376 					&bp[1]);
377 				break;
378 			}
379 			if (isdigit(*p))
380 			{
381 				ruleset = atoi(p);
382 				if (ruleset >= MAXRWSETS / 2 || ruleset < 0)
383 				{
384 					syserr("bad ruleset %d (%d max)",
385 						ruleset, MAXRWSETS / 2);
386 					ruleset = 0;
387 				}
388 			}
389 			else
390 			{
391 				STAB *s;
392 				char delim;
393 
394 				q = p;
395 				while (*p != '\0' && isascii(*p) &&
396 				       (isalnum(*p) || strchr("-_$", *p) != NULL))
397 					p++;
398 				while (isascii(*p) && isspace(*p))
399 					*p++ = '\0';
400 				delim = *p;
401 				if (delim != '\0')
402 					*p++ = '\0';
403 				s = stab(q, ST_RULESET, ST_ENTER);
404 				if (s->s_ruleset != 0)
405 					ruleset = s->s_ruleset;
406 				else if (delim == '=')
407 				{
408 					ruleset = atoi(p);
409 					if (ruleset >= MAXRWSETS / 2 || ruleset < 0)
410 					{
411 						syserr("bad ruleset %s = %d (%d max)",
412 							q, ruleset, MAXRWSETS / 2);
413 						ruleset = 0;
414 					}
415 				}
416 				else if ((ruleset = --nextruleset) < MAXRWSETS / 2)
417 				{
418 					syserr("%s: too many named rulesets (%d max)",
419 						q, MAXRWSETS / 2);
420 					ruleset = 0;
421 				}
422 				s->s_ruleset = ruleset;
423 			}
424 			rwp = NULL;
425 			break;
426 
427 		  case 'D':		/* macro definition */
428 			mid = macid(&bp[1], &ep);
429 			p = munchstring(ep, NULL);
430 			define(mid, newstr(p), e);
431 			break;
432 
433 		  case 'H':		/* required header line */
434 			(void) chompheader(&bp[1], TRUE, NULL, e);
435 			break;
436 
437 		  case 'C':		/* word class */
438 		  case 'T':		/* trusted user (set class `t') */
439 			if (bp[0] == 'C')
440 			{
441 				mid = macid(&bp[1], &ep);
442 				expand(ep, exbuf, sizeof exbuf, e);
443 				p = exbuf;
444 			}
445 			else
446 			{
447 				mid = 't';
448 				p = &bp[1];
449 			}
450 			while (*p != '\0')
451 			{
452 				register char *wd;
453 				char delim;
454 
455 				while (*p != '\0' && isascii(*p) && isspace(*p))
456 					p++;
457 				wd = p;
458 				while (*p != '\0' && !(isascii(*p) && isspace(*p)))
459 					p++;
460 				delim = *p;
461 				*p = '\0';
462 				if (wd[0] != '\0')
463 					setclass(mid, wd);
464 				*p = delim;
465 			}
466 			break;
467 
468 		  case 'F':		/* word class from file */
469 			mid = macid(&bp[1], &ep);
470 			for (p = ep; isascii(*p) && isspace(*p); )
471 				p++;
472 			if (p[0] == '-' && p[1] == 'o')
473 			{
474 				optional = TRUE;
475 				while (*p != '\0' && !(isascii(*p) && isspace(*p)))
476 					p++;
477 				while (isascii(*p) && isspace(*p))
478 					p++;
479 			}
480 			else
481 				optional = FALSE;
482 			file = p;
483 			while (*p != '\0' && !(isascii(*p) && isspace(*p)))
484 				p++;
485 			if (*p == '\0')
486 				p = "%s";
487 			else
488 			{
489 				*p = '\0';
490 				while (isascii(*++p) && isspace(*p))
491 					continue;
492 			}
493 			fileclass(bp[1], file, p, safe, optional);
494 			break;
495 
496 #ifdef XLA
497 		  case 'L':		/* extended load average description */
498 			xla_init(&bp[1]);
499 			break;
500 #endif
501 
502 		  case 'M':		/* define mailer */
503 			makemailer(&bp[1]);
504 			break;
505 
506 		  case 'O':		/* set option */
507 			setoption(bp[1], &bp[2], safe, FALSE, e);
508 			break;
509 
510 		  case 'P':		/* set precedence */
511 			if (NumPriorities >= MAXPRIORITIES)
512 			{
513 				toomany('P', MAXPRIORITIES);
514 				break;
515 			}
516 			for (p = &bp[1]; *p != '\0' && *p != '=' && *p != '\t'; p++)
517 				continue;
518 			if (*p == '\0')
519 				goto badline;
520 			*p = '\0';
521 			Priorities[NumPriorities].pri_name = newstr(&bp[1]);
522 			Priorities[NumPriorities].pri_val = atoi(++p);
523 			NumPriorities++;
524 			break;
525 
526 		  case 'V':		/* configuration syntax version */
527 			for (p = &bp[1]; isascii(*p) && isspace(*p); p++)
528 				continue;
529 			if (!isascii(*p) || !isdigit(*p))
530 			{
531 				syserr("invalid argument to V line: \"%.20s\"",
532 					&bp[1]);
533 				break;
534 			}
535 			ConfigLevel = strtol(p, &ep, 10);
536 
537 			/*
538 			**  Do heuristic tweaking for back compatibility.
539 			*/
540 
541 			if (ConfigLevel >= 5)
542 			{
543 				/* level 5 configs have short name in $w */
544 				p = macvalue('w', e);
545 				if (p != NULL && (p = strchr(p, '.')) != NULL)
546 					*p = '\0';
547 			}
548 			if (ConfigLevel >= 6)
549 			{
550 				ColonOkInAddr = FALSE;
551 			}
552 
553 			/*
554 			**  Look for vendor code.
555 			*/
556 
557 			if (*ep++ == '/')
558 			{
559 				/* extract vendor code */
560 				for (p = ep; isascii(*p) && isalpha(*p); )
561 					p++;
562 				*p = '\0';
563 
564 				if (!setvendor(ep))
565 					syserr("invalid V line vendor code: \"%s\"",
566 						ep);
567 			}
568 			break;
569 
570 		  case 'K':
571 			makemapentry(&bp[1]);
572 			break;
573 
574 		  case 'E':
575 			p = strchr(bp, '=');
576 			if (p != NULL)
577 				*p++ = '\0';
578 			setuserenv(&bp[1], p);
579 			break;
580 
581 		  default:
582 		  badline:
583 			syserr("unknown control line \"%s\"", bp);
584 		}
585 		if (bp != buf)
586 			free(bp);
587 	}
588 	if (ferror(cf))
589 	{
590 		syserr("I/O read error", cfname);
591 		exit(EX_OSFILE);
592 	}
593 	fclose(cf);
594 	FileName = NULL;
595 
596 	/* initialize host maps from local service tables */
597 	inithostmaps();
598 
599 	/* determine if we need to do special name-server frotz */
600 	{
601 		int nmaps;
602 		char *maptype[MAXMAPSTACK];
603 		short mapreturn[MAXMAPACTIONS];
604 
605 		nmaps = switch_map_find("hosts", maptype, mapreturn);
606 		UseNameServer = FALSE;
607 		if (nmaps > 0 && nmaps <= MAXMAPSTACK)
608 		{
609 			register int mapno;
610 
611 			for (mapno = 0; mapno < nmaps && !UseNameServer; mapno++)
612 			{
613 				if (strcmp(maptype[mapno], "dns") == 0)
614 					UseNameServer = TRUE;
615 			}
616 		}
617 
618 #ifdef HESIOD
619 		nmaps = switch_map_find("passwd", maptype, mapreturn);
620 		UseHesiod = FALSE;
621 		if (nmaps > 0 && nmaps <= MAXMAPSTACK)
622 		{
623 			register int mapno;
624 
625 			for (mapno = 0; mapno < nmaps && !UseHesiod; mapno++)
626 			{
627 				if (strcmp(maptype[mapno], "hesiod") == 0)
628 					UseHesiod = TRUE;
629 			}
630 		}
631 #endif
632 	}
633 }
634 /*
635 **  TOOMANY -- signal too many of some option
636 **
637 **	Parameters:
638 **		id -- the id of the error line
639 **		maxcnt -- the maximum possible values
640 **
641 **	Returns:
642 **		none.
643 **
644 **	Side Effects:
645 **		gives a syserr.
646 */
647 
648 toomany(id, maxcnt)
649 	char id;
650 	int maxcnt;
651 {
652 	syserr("too many %c lines, %d max", id, maxcnt);
653 }
654 /*
655 **  FILECLASS -- read members of a class from a file
656 **
657 **	Parameters:
658 **		class -- class to define.
659 **		filename -- name of file to read.
660 **		fmt -- scanf string to use for match.
661 **		safe -- if set, this is a safe read.
662 **		optional -- if set, it is not an error for the file to
663 **			not exist.
664 **
665 **	Returns:
666 **		none
667 **
668 **	Side Effects:
669 **
670 **		puts all lines in filename that match a scanf into
671 **			the named class.
672 */
673 
674 fileclass(class, filename, fmt, safe, optional)
675 	int class;
676 	char *filename;
677 	char *fmt;
678 	bool safe;
679 	bool optional;
680 {
681 	FILE *f;
682 	int sff;
683 	int pid;
684 	register char *p;
685 	char buf[MAXLINE];
686 
687 	if (tTd(37, 2))
688 		printf("fileclass(%s, fmt=%s)\n", filename, fmt);
689 
690 	if (filename[0] == '|')
691 	{
692 		auto int fd;
693 		int i;
694 		char *argv[MAXPV + 1];
695 
696 		i = 0;
697 		for (p = strtok(&filename[1], " \t"); p != NULL; p = strtok(NULL, " \t"))
698 		{
699 			if (i >= MAXPV)
700 				break;
701 			argv[i++] = p;
702 		}
703 		argv[i] = NULL;
704 		pid = prog_open(argv, &fd, CurEnv);
705 		if (pid < 0)
706 			f = NULL;
707 		else
708 			f = fdopen(fd, "r");
709 	}
710 	else
711 	{
712 		pid = -1;
713 		sff = SFF_REGONLY;
714 		if (safe)
715 			sff |= SFF_OPENASROOT;
716 		f = safefopen(filename, O_RDONLY, 0, sff);
717 	}
718 	if (f == NULL)
719 	{
720 		if (!optional)
721 			syserr("fileclass: cannot open %s", filename);
722 		return;
723 	}
724 
725 	while (fgets(buf, sizeof buf, f) != NULL)
726 	{
727 		register STAB *s;
728 		register char *p;
729 # ifdef SCANF
730 		char wordbuf[MAXNAME+1];
731 
732 		if (sscanf(buf, fmt, wordbuf) != 1)
733 			continue;
734 		p = wordbuf;
735 # else /* SCANF */
736 		p = buf;
737 # endif /* SCANF */
738 
739 		/*
740 		**  Break up the match into words.
741 		*/
742 
743 		while (*p != '\0')
744 		{
745 			register char *q;
746 
747 			/* strip leading spaces */
748 			while (isascii(*p) && isspace(*p))
749 				p++;
750 			if (*p == '\0')
751 				break;
752 
753 			/* find the end of the word */
754 			q = p;
755 			while (*p != '\0' && !(isascii(*p) && isspace(*p)))
756 				p++;
757 			if (*p != '\0')
758 				*p++ = '\0';
759 
760 			/* enter the word in the symbol table */
761 			setclass(class, q);
762 		}
763 	}
764 
765 	(void) fclose(f);
766 	if (pid > 0)
767 		(void) waitfor(pid);
768 }
769 /*
770 **  MAKEMAILER -- define a new mailer.
771 **
772 **	Parameters:
773 **		line -- description of mailer.  This is in labeled
774 **			fields.  The fields are:
775 **			   A -- the argv for this mailer
776 **			   C -- the character set for MIME conversions
777 **			   D -- the directory to run in
778 **			   E -- the eol string
779 **			   F -- the flags associated with the mailer
780 **			   L -- the maximum line length
781 **			   M -- the maximum message size
782 **			   N -- the niceness at which to run
783 **			   P -- the path to the mailer
784 **			   R -- the recipient rewriting set
785 **			   S -- the sender rewriting set
786 **			   T -- the mailer type (for DSNs)
787 **			   U -- the uid to run as
788 **			The first word is the canonical name of the mailer.
789 **
790 **	Returns:
791 **		none.
792 **
793 **	Side Effects:
794 **		enters the mailer into the mailer table.
795 */
796 
797 makemailer(line)
798 	char *line;
799 {
800 	register char *p;
801 	register struct mailer *m;
802 	register STAB *s;
803 	int i;
804 	char fcode;
805 	auto char *endp;
806 	extern int NextMailer;
807 	extern char **makeargv();
808 	extern char *munchstring();
809 
810 	/* allocate a mailer and set up defaults */
811 	m = (struct mailer *) xalloc(sizeof *m);
812 	bzero((char *) m, sizeof *m);
813 	m->m_eol = "\n";
814 	m->m_uid = m->m_gid = 0;
815 
816 	/* collect the mailer name */
817 	for (p = line; *p != '\0' && *p != ',' && !(isascii(*p) && isspace(*p)); p++)
818 		continue;
819 	if (*p != '\0')
820 		*p++ = '\0';
821 	m->m_name = newstr(line);
822 
823 	/* now scan through and assign info from the fields */
824 	while (*p != '\0')
825 	{
826 		auto char *delimptr;
827 
828 		while (*p != '\0' && (*p == ',' || (isascii(*p) && isspace(*p))))
829 			p++;
830 
831 		/* p now points to field code */
832 		fcode = *p;
833 		while (*p != '\0' && *p != '=' && *p != ',')
834 			p++;
835 		if (*p++ != '=')
836 		{
837 			syserr("mailer %s: `=' expected", m->m_name);
838 			return;
839 		}
840 		while (isascii(*p) && isspace(*p))
841 			p++;
842 
843 		/* p now points to the field body */
844 		p = munchstring(p, &delimptr);
845 
846 		/* install the field into the mailer struct */
847 		switch (fcode)
848 		{
849 		  case 'P':		/* pathname */
850 			m->m_mailer = newstr(p);
851 			break;
852 
853 		  case 'F':		/* flags */
854 			for (; *p != '\0'; p++)
855 				if (!(isascii(*p) && isspace(*p)))
856 					setbitn(*p, m->m_flags);
857 			break;
858 
859 		  case 'S':		/* sender rewriting ruleset */
860 		  case 'R':		/* recipient rewriting ruleset */
861 			i = strtol(p, &endp, 10);
862 			if (i < 0 || i >= MAXRWSETS)
863 			{
864 				syserr("invalid rewrite set, %d max", MAXRWSETS);
865 				return;
866 			}
867 			if (fcode == 'S')
868 				m->m_sh_rwset = m->m_se_rwset = i;
869 			else
870 				m->m_rh_rwset = m->m_re_rwset = i;
871 
872 			p = endp;
873 			if (*p++ == '/')
874 			{
875 				i = strtol(p, NULL, 10);
876 				if (i < 0 || i >= MAXRWSETS)
877 				{
878 					syserr("invalid rewrite set, %d max",
879 						MAXRWSETS);
880 					return;
881 				}
882 				if (fcode == 'S')
883 					m->m_sh_rwset = i;
884 				else
885 					m->m_rh_rwset = i;
886 			}
887 			break;
888 
889 		  case 'E':		/* end of line string */
890 			m->m_eol = newstr(p);
891 			break;
892 
893 		  case 'A':		/* argument vector */
894 			m->m_argv = makeargv(p);
895 			break;
896 
897 		  case 'M':		/* maximum message size */
898 			m->m_maxsize = atol(p);
899 			break;
900 
901 		  case 'L':		/* maximum line length */
902 			m->m_linelimit = atoi(p);
903 			break;
904 
905 		  case 'N':		/* run niceness */
906 			m->m_nice = atoi(p);
907 			break;
908 
909 		  case 'D':		/* working directory */
910 			m->m_execdir = newstr(p);
911 			break;
912 
913 		  case 'C':		/* default charset */
914 			m->m_defcharset = newstr(p);
915 			break;
916 
917 		  case 'T':		/* MTA Type */
918 			m->m_mtatype = newstr(p);
919 			p = strchr(m->m_mtatype, '/');
920 			if (p != NULL)
921 			{
922 				*p++ = '\0';
923 				if (*p == '\0')
924 					p = NULL;
925 			}
926 			if (p == NULL)
927 				m->m_addrtype = m->m_mtatype;
928 			else
929 			{
930 				m->m_addrtype = p;
931 				p = strchr(p, '/');
932 			}
933 			if (p != NULL)
934 			{
935 				*p++ = '\0';
936 				if (*p == '\0')
937 					p = NULL;
938 			}
939 			if (p == NULL)
940 				m->m_diagtype = m->m_mtatype;
941 			else
942 				m->m_diagtype = p;
943 			break;
944 
945 		  case 'U':		/* user id */
946 			if (isascii(*p) && !isdigit(*p))
947 			{
948 				char *q = p;
949 				struct passwd *pw;
950 
951 				while (isascii(*p) && isalnum(*p))
952 					p++;
953 				while (isascii(*p) && isspace(*p))
954 					*p++ = '\0';
955 				if (*p != '\0')
956 					*p++ = '\0';
957 				pw = sm_getpwnam(q);
958 				if (pw == NULL)
959 					syserr("readcf: mailer U= flag: unknown user %s", q);
960 				else
961 				{
962 					m->m_uid = pw->pw_uid;
963 					m->m_gid = pw->pw_gid;
964 				}
965 			}
966 			else
967 			{
968 				auto char *q;
969 
970 				m->m_uid = strtol(p, &q, 0);
971 				p = q;
972 			}
973 			while (isascii(*p) && isspace(*p))
974 				p++;
975 			if (*p == '\0')
976 				break;
977 			if (isascii(*p) && !isdigit(*p))
978 			{
979 				char *q = p;
980 				struct group *gr;
981 
982 				while (isascii(*p) && isalnum(*p))
983 					p++;
984 				*p++ = '\0';
985 				gr = getgrnam(q);
986 				if (gr == NULL)
987 					syserr("readcf: mailer U= flag: unknown group %s", q);
988 				else
989 					m->m_gid = gr->gr_gid;
990 			}
991 			else
992 			{
993 				m->m_gid = strtol(p, NULL, 0);
994 			}
995 			break;
996 		}
997 
998 		p = delimptr;
999 	}
1000 
1001 	/* do some rationality checking */
1002 	if (m->m_argv == NULL)
1003 	{
1004 		syserr("M%s: A= argument required", m->m_name);
1005 		return;
1006 	}
1007 	if (m->m_mailer == NULL)
1008 	{
1009 		syserr("M%s: P= argument required", m->m_name);
1010 		return;
1011 	}
1012 
1013 	if (NextMailer >= MAXMAILERS)
1014 	{
1015 		syserr("too many mailers defined (%d max)", MAXMAILERS);
1016 		return;
1017 	}
1018 
1019 	/* do some heuristic cleanup for back compatibility */
1020 	if (bitnset(M_LIMITS, m->m_flags))
1021 	{
1022 		if (m->m_linelimit == 0)
1023 			m->m_linelimit = SMTPLINELIM;
1024 		if (ConfigLevel < 2)
1025 			setbitn(M_7BITS, m->m_flags);
1026 	}
1027 
1028 	if (ConfigLevel < 6 &&
1029 	    (strcmp(m->m_mailer, "[IPC]") == 0 ||
1030 	     strcmp(m->m_mailer, "[TCP]") == 0))
1031 	{
1032 		if (m->m_mtatype == NULL)
1033 			m->m_mtatype = "dns";
1034 		if (m->m_addrtype == NULL)
1035 			m->m_addrtype = "rfc822";
1036 		if (m->m_diagtype == NULL)
1037 			m->m_diagtype = "smtp";
1038 	}
1039 
1040 	/* enter the mailer into the symbol table */
1041 	s = stab(m->m_name, ST_MAILER, ST_ENTER);
1042 	if (s->s_mailer != NULL)
1043 	{
1044 		i = s->s_mailer->m_mno;
1045 		free(s->s_mailer);
1046 	}
1047 	else
1048 	{
1049 		i = NextMailer++;
1050 	}
1051 	Mailer[i] = s->s_mailer = m;
1052 	m->m_mno = i;
1053 }
1054 /*
1055 **  MUNCHSTRING -- translate a string into internal form.
1056 **
1057 **	Parameters:
1058 **		p -- the string to munch.
1059 **		delimptr -- if non-NULL, set to the pointer of the
1060 **			field delimiter character.
1061 **
1062 **	Returns:
1063 **		the munched string.
1064 */
1065 
1066 char *
1067 munchstring(p, delimptr)
1068 	register char *p;
1069 	char **delimptr;
1070 {
1071 	register char *q;
1072 	bool backslash = FALSE;
1073 	bool quotemode = FALSE;
1074 	static char buf[MAXLINE];
1075 
1076 	for (q = buf; *p != '\0'; p++)
1077 	{
1078 		if (backslash)
1079 		{
1080 			/* everything is roughly literal */
1081 			backslash = FALSE;
1082 			switch (*p)
1083 			{
1084 			  case 'r':		/* carriage return */
1085 				*q++ = '\r';
1086 				continue;
1087 
1088 			  case 'n':		/* newline */
1089 				*q++ = '\n';
1090 				continue;
1091 
1092 			  case 'f':		/* form feed */
1093 				*q++ = '\f';
1094 				continue;
1095 
1096 			  case 'b':		/* backspace */
1097 				*q++ = '\b';
1098 				continue;
1099 			}
1100 			*q++ = *p;
1101 		}
1102 		else
1103 		{
1104 			if (*p == '\\')
1105 				backslash = TRUE;
1106 			else if (*p == '"')
1107 				quotemode = !quotemode;
1108 			else if (quotemode || *p != ',')
1109 				*q++ = *p;
1110 			else
1111 				break;
1112 		}
1113 	}
1114 
1115 	if (delimptr != NULL)
1116 		*delimptr = p;
1117 	*q++ = '\0';
1118 	return (buf);
1119 }
1120 /*
1121 **  MAKEARGV -- break up a string into words
1122 **
1123 **	Parameters:
1124 **		p -- the string to break up.
1125 **
1126 **	Returns:
1127 **		a char **argv (dynamically allocated)
1128 **
1129 **	Side Effects:
1130 **		munges p.
1131 */
1132 
1133 char **
1134 makeargv(p)
1135 	register char *p;
1136 {
1137 	char *q;
1138 	int i;
1139 	char **avp;
1140 	char *argv[MAXPV + 1];
1141 
1142 	/* take apart the words */
1143 	i = 0;
1144 	while (*p != '\0' && i < MAXPV)
1145 	{
1146 		q = p;
1147 		while (*p != '\0' && !(isascii(*p) && isspace(*p)))
1148 			p++;
1149 		while (isascii(*p) && isspace(*p))
1150 			*p++ = '\0';
1151 		argv[i++] = newstr(q);
1152 	}
1153 	argv[i++] = NULL;
1154 
1155 	/* now make a copy of the argv */
1156 	avp = (char **) xalloc(sizeof *avp * i);
1157 	bcopy((char *) argv, (char *) avp, sizeof *avp * i);
1158 
1159 	return (avp);
1160 }
1161 /*
1162 **  PRINTRULES -- print rewrite rules (for debugging)
1163 **
1164 **	Parameters:
1165 **		none.
1166 **
1167 **	Returns:
1168 **		none.
1169 **
1170 **	Side Effects:
1171 **		prints rewrite rules.
1172 */
1173 
1174 printrules()
1175 {
1176 	register struct rewrite *rwp;
1177 	register int ruleset;
1178 
1179 	for (ruleset = 0; ruleset < 10; ruleset++)
1180 	{
1181 		if (RewriteRules[ruleset] == NULL)
1182 			continue;
1183 		printf("\n----Rule Set %d:", ruleset);
1184 
1185 		for (rwp = RewriteRules[ruleset]; rwp != NULL; rwp = rwp->r_next)
1186 		{
1187 			printf("\nLHS:");
1188 			printav(rwp->r_lhs);
1189 			printf("RHS:");
1190 			printav(rwp->r_rhs);
1191 		}
1192 	}
1193 }
1194 /*
1195 **  PRINTMAILER -- print mailer structure (for debugging)
1196 **
1197 **	Parameters:
1198 **		m -- the mailer to print
1199 **
1200 **	Returns:
1201 **		none.
1202 */
1203 
1204 printmailer(m)
1205 	register MAILER *m;
1206 {
1207 	int j;
1208 
1209 	printf("mailer %d (%s): P=%s S=%d/%d R=%d/%d M=%ld U=%d:%d F=",
1210 		m->m_mno, m->m_name,
1211 		m->m_mailer, m->m_se_rwset, m->m_sh_rwset,
1212 		m->m_re_rwset, m->m_rh_rwset, m->m_maxsize,
1213 		m->m_uid, m->m_gid);
1214 	for (j = '\0'; j <= '\177'; j++)
1215 		if (bitnset(j, m->m_flags))
1216 			(void) putchar(j);
1217 	printf(" L=%d E=", m->m_linelimit);
1218 	xputs(m->m_eol);
1219 	if (m->m_defcharset != NULL)
1220 		printf(" C=%s", m->m_defcharset);
1221 	printf(" T=%s/%s/%s",
1222 		m->m_mtatype == NULL ? "<undefined>" : m->m_mtatype,
1223 		m->m_addrtype == NULL ? "<undefined>" : m->m_addrtype,
1224 		m->m_diagtype == NULL ? "<undefined>" : m->m_diagtype);
1225 	if (m->m_argv != NULL)
1226 	{
1227 		char **a = m->m_argv;
1228 
1229 		printf(" A=");
1230 		while (*a != NULL)
1231 		{
1232 			if (a != m->m_argv)
1233 				printf(" ");
1234 			xputs(*a++);
1235 		}
1236 	}
1237 	printf("\n");
1238 }
1239 /*
1240 **  SETOPTION -- set global processing option
1241 **
1242 **	Parameters:
1243 **		opt -- option name.
1244 **		val -- option value (as a text string).
1245 **		safe -- set if this came from a configuration file.
1246 **			Some options (if set from the command line) will
1247 **			reset the user id to avoid security problems.
1248 **		sticky -- if set, don't let other setoptions override
1249 **			this value.
1250 **		e -- the main envelope.
1251 **
1252 **	Returns:
1253 **		none.
1254 **
1255 **	Side Effects:
1256 **		Sets options as implied by the arguments.
1257 */
1258 
1259 static BITMAP	StickyOpt;		/* set if option is stuck */
1260 
1261 
1262 #if NAMED_BIND
1263 
1264 struct resolverflags
1265 {
1266 	char	*rf_name;	/* name of the flag */
1267 	long	rf_bits;	/* bits to set/clear */
1268 } ResolverFlags[] =
1269 {
1270 	"debug",	RES_DEBUG,
1271 	"aaonly",	RES_AAONLY,
1272 	"usevc",	RES_USEVC,
1273 	"primary",	RES_PRIMARY,
1274 	"igntc",	RES_IGNTC,
1275 	"recurse",	RES_RECURSE,
1276 	"defnames",	RES_DEFNAMES,
1277 	"stayopen",	RES_STAYOPEN,
1278 	"dnsrch",	RES_DNSRCH,
1279 	"true",		0,		/* to avoid error on old syntax */
1280 	NULL,		0
1281 };
1282 
1283 #endif
1284 
1285 struct optioninfo
1286 {
1287 	char	*o_name;	/* long name of option */
1288 	u_char	o_code;		/* short name of option */
1289 	bool	o_safe;		/* safe for random people to use */
1290 } OptionTab[] =
1291 {
1292 	"SevenBitInput",	'7',		TRUE,
1293 	"EightBitMode",		'8',		TRUE,
1294 	"AliasFile",		'A',		FALSE,
1295 	"AliasWait",		'a',		FALSE,
1296 	"BlankSub",		'B',		FALSE,
1297 	"MinFreeBlocks",	'b',		TRUE,
1298 	"CheckpointInterval",	'C',		TRUE,
1299 	"HoldExpensive",	'c',		FALSE,
1300 	"AutoRebuildAliases",	'D',		FALSE,
1301 	"DeliveryMode",		'd',		TRUE,
1302 	"ErrorHeader",		'E',		FALSE,
1303 	"ErrorMode",		'e',		TRUE,
1304 	"TempFileMode",		'F',		FALSE,
1305 	"SaveFromLine",		'f',		FALSE,
1306 	"MatchGECOS",		'G',		FALSE,
1307 	"HelpFile",		'H',		FALSE,
1308 	"MaxHopCount",		'h',		FALSE,
1309 	"ResolverOptions",	'I',		FALSE,
1310 	"IgnoreDots",		'i',		TRUE,
1311 	"ForwardPath",		'J',		FALSE,
1312 	"SendMimeErrors",	'j',		TRUE,
1313 	"ConnectionCacheSize",	'k',		FALSE,
1314 	"ConnectionCacheTimeout", 'K',		FALSE,
1315 	"UseErrorsTo",		'l',		FALSE,
1316 	"LogLevel",		'L',		FALSE,
1317 	"MeToo",		'm',		TRUE,
1318 	"CheckAliases",		'n',		FALSE,
1319 	"OldStyleHeaders",	'o',		TRUE,
1320 	"DaemonPortOptions",	'O',		FALSE,
1321 	"PrivacyOptions",	'p',		TRUE,
1322 	"PostmasterCopy",	'P',		FALSE,
1323 	"QueueFactor",		'q',		FALSE,
1324 	"QueueDirectory",	'Q',		FALSE,
1325 	"DontPruneRoutes",	'R',		FALSE,
1326 	"Timeout",		'r',		TRUE,
1327 	"StatusFile",		'S',		FALSE,
1328 	"SuperSafe",		's',		TRUE,
1329 	"QueueTimeout",		'T',		FALSE,
1330 	"TimeZoneSpec",		't',		FALSE,
1331 	"UserDatabaseSpec",	'U',		FALSE,
1332 	"DefaultUser",		'u',		FALSE,
1333 	"FallbackMXhost",	'V',		FALSE,
1334 	"Verbose",		'v',		TRUE,
1335 	"TryNullMXList",	'w',		TRUE,
1336 	"QueueLA",		'x',		FALSE,
1337 	"RefuseLA",		'X',		FALSE,
1338 	"RecipientFactor",	'y',		FALSE,
1339 	"ForkEachJob",		'Y',		FALSE,
1340 	"ClassFactor",		'z',		FALSE,
1341 	"RetryFactor",		'Z',		FALSE,
1342 #define O_QUEUESORTORD	0x81
1343 	"QueueSortOrder",	O_QUEUESORTORD,	TRUE,
1344 #define O_HOSTSFILE	0x82
1345 	"HostsFile",		O_HOSTSFILE,	FALSE,
1346 #define O_MQA		0x83
1347 	"MinQueueAge",		O_MQA,		TRUE,
1348 #define O_MHSA		0x84
1349 /*
1350 	"MaxHostStatAge",	O_MHSA,		TRUE,
1351 */
1352 #define O_DEFCHARSET	0x85
1353 	"DefaultCharSet",	O_DEFCHARSET,	TRUE,
1354 #define O_SSFILE	0x86
1355 	"ServiceSwitchFile",	O_SSFILE,	FALSE,
1356 #define O_DIALDELAY	0x87
1357 	"DialDelay",		O_DIALDELAY,	TRUE,
1358 #define O_NORCPTACTION	0x88
1359 	"NoRecipientAction",	O_NORCPTACTION,	TRUE,
1360 #define O_SAFEFILEENV	0x89
1361 	"SafeFileEnvironment",	O_SAFEFILEENV,	FALSE,
1362 #define O_MAXMSGSIZE	0x8a
1363 	"MaxMessageSize",	O_MAXMSGSIZE,	FALSE,
1364 #define O_COLONOKINADDR	0x8b
1365 	"ColonOkInAddr",	O_COLONOKINADDR, TRUE,
1366 
1367 	NULL,			'\0',		FALSE,
1368 };
1369 
1370 
1371 
1372 setoption(opt, val, safe, sticky, e)
1373 	u_char opt;
1374 	char *val;
1375 	bool safe;
1376 	bool sticky;
1377 	register ENVELOPE *e;
1378 {
1379 	register char *p;
1380 	register struct optioninfo *o;
1381 	char *subopt;
1382 	extern bool atobool();
1383 	extern time_t convtime();
1384 	extern int QueueLA;
1385 	extern int RefuseLA;
1386 	extern bool Warn_Q_option;
1387 
1388 	errno = 0;
1389 	if (opt == ' ')
1390 	{
1391 		/* full word options */
1392 		struct optioninfo *sel;
1393 
1394 		p = strchr(val, '=');
1395 		if (p == NULL)
1396 			p = &val[strlen(val)];
1397 		while (*--p == ' ')
1398 			continue;
1399 		while (*++p == ' ')
1400 			*p = '\0';
1401 		if (p == val)
1402 		{
1403 			syserr("readcf: null option name");
1404 			return;
1405 		}
1406 		if (*p == '=')
1407 			*p++ = '\0';
1408 		while (*p == ' ')
1409 			p++;
1410 		subopt = strchr(val, '.');
1411 		if (subopt != NULL)
1412 			*subopt++ = '\0';
1413 		sel = NULL;
1414 		for (o = OptionTab; o->o_name != NULL; o++)
1415 		{
1416 			if (strncasecmp(o->o_name, val, strlen(val)) != 0)
1417 				continue;
1418 			if (strlen(o->o_name) == strlen(val))
1419 			{
1420 				/* completely specified -- this must be it */
1421 				sel = NULL;
1422 				break;
1423 			}
1424 			if (sel != NULL)
1425 				break;
1426 			sel = o;
1427 		}
1428 		if (sel != NULL && o->o_name == NULL)
1429 			o = sel;
1430 		else if (o->o_name == NULL)
1431 		{
1432 			syserr("readcf: unknown option name %s", val);
1433 			return;
1434 		}
1435 		else if (sel != NULL)
1436 		{
1437 			syserr("readcf: ambiguous option name %s (matches %s and %s)",
1438 				val, sel->o_name, o->o_name);
1439 			return;
1440 		}
1441 		if (strlen(val) != strlen(o->o_name))
1442 		{
1443 			bool oldVerbose = Verbose;
1444 
1445 			Verbose = TRUE;
1446 			message("Option %s used as abbreviation for %s",
1447 				val, o->o_name);
1448 			Verbose = oldVerbose;
1449 		}
1450 		opt = o->o_code;
1451 		val = p;
1452 	}
1453 	else
1454 	{
1455 		for (o = OptionTab; o->o_name != NULL; o++)
1456 		{
1457 			if (o->o_code == opt)
1458 				break;
1459 		}
1460 		subopt = NULL;
1461 	}
1462 
1463 	if (tTd(37, 1))
1464 	{
1465 		printf(isascii(opt) && isprint(opt) ?
1466 			    "setoption %s (%c).%s=%s" :
1467 			    "setoption %s (0x%x).%s=%s",
1468 			o->o_name == NULL ? "<unknown>" : o->o_name,
1469 			opt,
1470 			subopt == NULL ? "" : subopt,
1471 			val);
1472 	}
1473 
1474 	/*
1475 	**  See if this option is preset for us.
1476 	*/
1477 
1478 	if (!sticky && bitnset(opt, StickyOpt))
1479 	{
1480 		if (tTd(37, 1))
1481 			printf(" (ignored)\n");
1482 		return;
1483 	}
1484 
1485 	/*
1486 	**  Check to see if this option can be specified by this user.
1487 	*/
1488 
1489 	if (!safe && RealUid == 0)
1490 		safe = TRUE;
1491 	if (!safe && !o->o_safe)
1492 	{
1493 		if (opt != 'M' || (val[0] != 'r' && val[0] != 's'))
1494 		{
1495 			if (tTd(37, 1))
1496 				printf(" (unsafe)");
1497 			if (RealUid != geteuid())
1498 			{
1499 				if (tTd(37, 1))
1500 					printf("(Resetting uid)");
1501 				(void) setgid(RealGid);
1502 				(void) setuid(RealUid);
1503 			}
1504 		}
1505 	}
1506 	if (tTd(37, 1))
1507 		printf("\n");
1508 
1509 	switch (opt & 0xff)
1510 	{
1511 	  case '7':		/* force seven-bit input */
1512 		SevenBitInput = atobool(val);
1513 		break;
1514 
1515 	  case '8':		/* handling of 8-bit input */
1516 		switch (*val)
1517 		{
1518 		  case 'm':		/* convert 8-bit, convert MIME */
1519 			MimeMode = MM_CVTMIME|MM_MIME8BIT;
1520 			break;
1521 
1522 		  case 'p':		/* pass 8 bit, convert MIME */
1523 			MimeMode = MM_CVTMIME|MM_PASS8BIT;
1524 			break;
1525 
1526 		  case 's':		/* strict adherence */
1527 			MimeMode = MM_CVTMIME;
1528 			break;
1529 
1530 #if 0
1531 		  case 'r':		/* reject 8-bit, don't convert MIME */
1532 			MimeMode = 0;
1533 			break;
1534 
1535 		  case 'j':		/* "just send 8" */
1536 			MimeMode = MM_PASS8BIT;
1537 			break;
1538 
1539 		  case 'a':		/* encode 8 bit if available */
1540 			MimeMode = MM_MIME8BIT|MM_PASS8BIT|MM_CVTMIME;
1541 			break;
1542 
1543 		  case 'c':		/* convert 8 bit to MIME, never 7 bit */
1544 			MimeMode = MM_MIME8BIT;
1545 			break;
1546 #endif
1547 
1548 		  default:
1549 			syserr("Unknown 8-bit mode %c", *val);
1550 			exit(EX_USAGE);
1551 		}
1552 		break;
1553 
1554 	  case 'A':		/* set default alias file */
1555 		if (val[0] == '\0')
1556 			setalias("aliases");
1557 		else
1558 			setalias(val);
1559 		break;
1560 
1561 	  case 'a':		/* look N minutes for "@:@" in alias file */
1562 		if (val[0] == '\0')
1563 			SafeAlias = 5 * 60;		/* five minutes */
1564 		else
1565 			SafeAlias = convtime(val, 'm');
1566 		break;
1567 
1568 	  case 'B':		/* substitution for blank character */
1569 		SpaceSub = val[0];
1570 		if (SpaceSub == '\0')
1571 			SpaceSub = ' ';
1572 		break;
1573 
1574 	  case 'b':		/* min blocks free on queue fs/max msg size */
1575 		p = strchr(val, '/');
1576 		if (p != NULL)
1577 		{
1578 			*p++ = '\0';
1579 			MaxMessageSize = atol(p);
1580 		}
1581 		MinBlocksFree = atol(val);
1582 		break;
1583 
1584 	  case 'c':		/* don't connect to "expensive" mailers */
1585 		NoConnect = atobool(val);
1586 		break;
1587 
1588 	  case 'C':		/* checkpoint every N addresses */
1589 		CheckpointInterval = atoi(val);
1590 		break;
1591 
1592 	  case 'd':		/* delivery mode */
1593 		switch (*val)
1594 		{
1595 		  case '\0':
1596 			e->e_sendmode = SM_DELIVER;
1597 			break;
1598 
1599 		  case SM_QUEUE:	/* queue only */
1600 #ifndef QUEUE
1601 			syserr("need QUEUE to set -odqueue");
1602 #endif /* QUEUE */
1603 			/* fall through..... */
1604 
1605 		  case SM_DELIVER:	/* do everything */
1606 		  case SM_FORK:		/* fork after verification */
1607 			e->e_sendmode = *val;
1608 			break;
1609 
1610 		  default:
1611 			syserr("Unknown delivery mode %c", *val);
1612 			exit(EX_USAGE);
1613 		}
1614 		break;
1615 
1616 	  case 'D':		/* rebuild alias database as needed */
1617 		AutoRebuild = atobool(val);
1618 		break;
1619 
1620 	  case 'E':		/* error message header/header file */
1621 		if (*val != '\0')
1622 			ErrMsgFile = newstr(val);
1623 		break;
1624 
1625 	  case 'e':		/* set error processing mode */
1626 		switch (*val)
1627 		{
1628 		  case EM_QUIET:	/* be silent about it */
1629 		  case EM_MAIL:		/* mail back */
1630 		  case EM_BERKNET:	/* do berknet error processing */
1631 		  case EM_WRITE:	/* write back (or mail) */
1632 		  case EM_PRINT:	/* print errors normally (default) */
1633 			e->e_errormode = *val;
1634 			break;
1635 		}
1636 		break;
1637 
1638 	  case 'F':		/* file mode */
1639 		FileMode = atooct(val) & 0777;
1640 		break;
1641 
1642 	  case 'f':		/* save Unix-style From lines on front */
1643 		SaveFrom = atobool(val);
1644 		break;
1645 
1646 	  case 'G':		/* match recipients against GECOS field */
1647 		MatchGecos = atobool(val);
1648 		break;
1649 
1650 	  case 'g':		/* default gid */
1651   g_opt:
1652 		if (isascii(*val) && isdigit(*val))
1653 			DefGid = atoi(val);
1654 		else
1655 		{
1656 			register struct group *gr;
1657 
1658 			DefGid = -1;
1659 			gr = getgrnam(val);
1660 			if (gr == NULL)
1661 				syserr("readcf: option %c: unknown group %s",
1662 					opt, val);
1663 			else
1664 				DefGid = gr->gr_gid;
1665 		}
1666 		break;
1667 
1668 	  case 'H':		/* help file */
1669 		if (val[0] == '\0')
1670 			HelpFile = "sendmail.hf";
1671 		else
1672 			HelpFile = newstr(val);
1673 		break;
1674 
1675 	  case 'h':		/* maximum hop count */
1676 		MaxHopCount = atoi(val);
1677 		break;
1678 
1679 	  case 'I':		/* use internet domain name server */
1680 #if NAMED_BIND
1681 		for (p = val; *p != 0; )
1682 		{
1683 			bool clearmode;
1684 			char *q;
1685 			struct resolverflags *rfp;
1686 
1687 			while (*p == ' ')
1688 				p++;
1689 			if (*p == '\0')
1690 				break;
1691 			clearmode = FALSE;
1692 			if (*p == '-')
1693 				clearmode = TRUE;
1694 			else if (*p != '+')
1695 				p--;
1696 			p++;
1697 			q = p;
1698 			while (*p != '\0' && !(isascii(*p) && isspace(*p)))
1699 				p++;
1700 			if (*p != '\0')
1701 				*p++ = '\0';
1702 			if (strcasecmp(q, "HasWildcardMX") == 0)
1703 			{
1704 				NoMXforCanon = !clearmode;
1705 				continue;
1706 			}
1707 			for (rfp = ResolverFlags; rfp->rf_name != NULL; rfp++)
1708 			{
1709 				if (strcasecmp(q, rfp->rf_name) == 0)
1710 					break;
1711 			}
1712 			if (rfp->rf_name == NULL)
1713 				syserr("readcf: I option value %s unrecognized", q);
1714 			else if (clearmode)
1715 				_res.options &= ~rfp->rf_bits;
1716 			else
1717 				_res.options |= rfp->rf_bits;
1718 		}
1719 		if (tTd(8, 2))
1720 			printf("_res.options = %x, HasWildcardMX = %d\n",
1721 				_res.options, !NoMXforCanon);
1722 #else
1723 		usrerr("name server (I option) specified but BIND not compiled in");
1724 #endif
1725 		break;
1726 
1727 	  case 'i':		/* ignore dot lines in message */
1728 		IgnrDot = atobool(val);
1729 		break;
1730 
1731 	  case 'j':		/* send errors in MIME (RFC 1341) format */
1732 		SendMIMEErrors = atobool(val);
1733 		break;
1734 
1735 	  case 'J':		/* .forward search path */
1736 		ForwardPath = newstr(val);
1737 		break;
1738 
1739 	  case 'k':		/* connection cache size */
1740 		MaxMciCache = atoi(val);
1741 		if (MaxMciCache < 0)
1742 			MaxMciCache = 0;
1743 		break;
1744 
1745 	  case 'K':		/* connection cache timeout */
1746 		MciCacheTimeout = convtime(val, 'm');
1747 		break;
1748 
1749 	  case 'l':		/* use Errors-To: header */
1750 		UseErrorsTo = atobool(val);
1751 		break;
1752 
1753 	  case 'L':		/* log level */
1754 		if (safe || LogLevel < atoi(val))
1755 			LogLevel = atoi(val);
1756 		break;
1757 
1758 	  case 'M':		/* define macro */
1759 		p = newstr(&val[1]);
1760 		if (!safe)
1761 			cleanstrcpy(p, p, MAXNAME);
1762 		define(val[0], p, CurEnv);
1763 		sticky = FALSE;
1764 		break;
1765 
1766 	  case 'm':		/* send to me too */
1767 		MeToo = atobool(val);
1768 		break;
1769 
1770 	  case 'n':		/* validate RHS in newaliases */
1771 		CheckAliases = atobool(val);
1772 		break;
1773 
1774 	    /* 'N' available -- was "net name" */
1775 
1776 	  case 'O':		/* daemon options */
1777 		setdaemonoptions(val);
1778 		break;
1779 
1780 	  case 'o':		/* assume old style headers */
1781 		if (atobool(val))
1782 			CurEnv->e_flags |= EF_OLDSTYLE;
1783 		else
1784 			CurEnv->e_flags &= ~EF_OLDSTYLE;
1785 		break;
1786 
1787 	  case 'p':		/* select privacy level */
1788 		p = val;
1789 		for (;;)
1790 		{
1791 			register struct prival *pv;
1792 			extern struct prival PrivacyValues[];
1793 
1794 			while (isascii(*p) && (isspace(*p) || ispunct(*p)))
1795 				p++;
1796 			if (*p == '\0')
1797 				break;
1798 			val = p;
1799 			while (isascii(*p) && isalnum(*p))
1800 				p++;
1801 			if (*p != '\0')
1802 				*p++ = '\0';
1803 
1804 			for (pv = PrivacyValues; pv->pv_name != NULL; pv++)
1805 			{
1806 				if (strcasecmp(val, pv->pv_name) == 0)
1807 					break;
1808 			}
1809 			if (pv->pv_name == NULL)
1810 				syserr("readcf: Op line: %s unrecognized", val);
1811 			PrivacyFlags |= pv->pv_flag;
1812 		}
1813 		sticky = FALSE;
1814 		break;
1815 
1816 	  case 'P':		/* postmaster copy address for returned mail */
1817 		PostMasterCopy = newstr(val);
1818 		break;
1819 
1820 	  case 'q':		/* slope of queue only function */
1821 		QueueFactor = atoi(val);
1822 		break;
1823 
1824 	  case 'Q':		/* queue directory */
1825 		if (val[0] == '\0')
1826 			QueueDir = "mqueue";
1827 		else
1828 			QueueDir = newstr(val);
1829 		if (RealUid != 0 && !safe)
1830 			Warn_Q_option = TRUE;
1831 		break;
1832 
1833 	  case 'R':		/* don't prune routes */
1834 		DontPruneRoutes = atobool(val);
1835 		break;
1836 
1837 	  case 'r':		/* read timeout */
1838 		if (subopt == NULL)
1839 			inittimeouts(val);
1840 		else
1841 			settimeout(subopt, val);
1842 		break;
1843 
1844 	  case 'S':		/* status file */
1845 		if (val[0] == '\0')
1846 			StatFile = "sendmail.st";
1847 		else
1848 			StatFile = newstr(val);
1849 		break;
1850 
1851 	  case 's':		/* be super safe, even if expensive */
1852 		SuperSafe = atobool(val);
1853 		break;
1854 
1855 	  case 'T':		/* queue timeout */
1856 		p = strchr(val, '/');
1857 		if (p != NULL)
1858 		{
1859 			*p++ = '\0';
1860 			settimeout("queuewarn", p);
1861 		}
1862 		settimeout("queuereturn", val);
1863 		break;
1864 
1865 	  case 't':		/* time zone name */
1866 		TimeZoneSpec = newstr(val);
1867 		break;
1868 
1869 	  case 'U':		/* location of user database */
1870 		UdbSpec = newstr(val);
1871 		break;
1872 
1873 	  case 'u':		/* set default uid */
1874 		for (p = val; *p != '\0'; p++)
1875 		{
1876 			if (*p == '.' || *p == '/' || *p == ':')
1877 			{
1878 				*p++ = '\0';
1879 				break;
1880 			}
1881 		}
1882 		if (isascii(*val) && isdigit(*val))
1883 			DefUid = atoi(val);
1884 		else
1885 		{
1886 			register struct passwd *pw;
1887 
1888 			DefUid = -1;
1889 			pw = sm_getpwnam(val);
1890 			if (pw == NULL)
1891 				syserr("readcf: option u: unknown user %s", val);
1892 			else
1893 			{
1894 				DefUid = pw->pw_uid;
1895 				DefGid = pw->pw_gid;
1896 			}
1897 		}
1898 		setdefuser();
1899 
1900 		/* handle the group if it is there */
1901 		if (*p == '\0')
1902 			break;
1903 		val = p;
1904 		goto g_opt;
1905 
1906 	  case 'V':		/* fallback MX host */
1907 		FallBackMX = newstr(val);
1908 		break;
1909 
1910 	  case 'v':		/* run in verbose mode */
1911 		Verbose = atobool(val);
1912 		break;
1913 
1914 	  case 'w':		/* if we are best MX, try host directly */
1915 		TryNullMXList = atobool(val);
1916 		break;
1917 
1918 	    /* 'W' available -- was wizard password */
1919 
1920 	  case 'x':		/* load avg at which to auto-queue msgs */
1921 		QueueLA = atoi(val);
1922 		break;
1923 
1924 	  case 'X':		/* load avg at which to auto-reject connections */
1925 		RefuseLA = atoi(val);
1926 		break;
1927 
1928 	  case 'y':		/* work recipient factor */
1929 		WkRecipFact = atoi(val);
1930 		break;
1931 
1932 	  case 'Y':		/* fork jobs during queue runs */
1933 		ForkQueueRuns = atobool(val);
1934 		break;
1935 
1936 	  case 'z':		/* work message class factor */
1937 		WkClassFact = atoi(val);
1938 		break;
1939 
1940 	  case 'Z':		/* work time factor */
1941 		WkTimeFact = atoi(val);
1942 		break;
1943 
1944 	  case O_QUEUESORTORD:	/* queue sorting order */
1945 		switch (*val)
1946 		{
1947 		  case 'h':	/* Host first */
1948 		  case 'H':
1949 			QueueSortOrder = QS_BYHOST;
1950 			break;
1951 
1952 		  case 'p':	/* Priority order */
1953 		  case 'P':
1954 			QueueSortOrder = QS_BYPRIORITY;
1955 			break;
1956 
1957 		  default:
1958 			syserr("Invalid queue sort order \"%s\"", val);
1959 		}
1960 		break;
1961 
1962 	  case O_HOSTSFILE:	/* pathname of /etc/hosts file */
1963 		HostsFile = newstr(val);
1964 		break;
1965 
1966 	  case O_MQA:		/* minimum queue age between deliveries */
1967 		MinQueueAge = convtime(val, 'm');
1968 		break;
1969 
1970 	  case O_MHSA:		/* maximum age of cached host status */
1971 		MaxHostStatAge = convtime(val, 'm');
1972 		break;
1973 
1974 	  case O_DEFCHARSET:	/* default character set for mimefying */
1975 		DefaultCharSet = newstr(denlstring(val, TRUE, TRUE));
1976 		break;
1977 
1978 	  case O_SSFILE:	/* service switch file */
1979 		ServiceSwitchFile = newstr(val);
1980 		break;
1981 
1982 	  case O_DIALDELAY:	/* delay for dial-on-demand operation */
1983 		DialDelay = convtime(val, 's');
1984 		break;
1985 
1986 	  case O_NORCPTACTION:	/* what to do if no recipient */
1987 		if (strcasecmp(val, "none") == 0)
1988 			NoRecipientAction = NRA_NO_ACTION;
1989 		else if (strcasecmp(val, "add-to") == 0)
1990 			NoRecipientAction = NRA_ADD_TO;
1991 		else if (strcasecmp(val, "add-apparently-to") == 0)
1992 			NoRecipientAction = NRA_ADD_APPARENTLY_TO;
1993 		else if (strcasecmp(val, "add-bcc") == 0)
1994 			NoRecipientAction = NRA_ADD_BCC;
1995 		else if (strcasecmp(val, "add-to-undisclosed") == 0)
1996 			NoRecipientAction = NRA_ADD_TO_UNDISCLOSED;
1997 		else
1998 			syserr("Invalid NoRecipientAction: %s", val);
1999 
2000 	  case O_SAFEFILEENV:	/* chroot() environ for writing to files */
2001 		SafeFileEnv = newstr(val);
2002 		break;
2003 
2004 	  case O_MAXMSGSIZE:	/* maximum message size */
2005 		MaxMessageSize = atol(p);
2006 		break;
2007 
2008 	  case O_COLONOKINADDR:	/* old style handling of colon addresses */
2009 		ColonOkInAddr = atobool(p);
2010 		break;
2011 
2012 	  default:
2013 		if (tTd(37, 1))
2014 		{
2015 			if (isascii(opt) && isprint(opt))
2016 				printf("Warning: option %c unknown\n", opt);
2017 			else
2018 				printf("Warning: option 0x%x unknown\n", opt);
2019 		}
2020 		break;
2021 	}
2022 	if (sticky)
2023 		setbitn(opt, StickyOpt);
2024 	return;
2025 }
2026 /*
2027 **  SETCLASS -- set a string into a class
2028 **
2029 **	Parameters:
2030 **		class -- the class to put the string in.
2031 **		str -- the string to enter
2032 **
2033 **	Returns:
2034 **		none.
2035 **
2036 **	Side Effects:
2037 **		puts the word into the symbol table.
2038 */
2039 
2040 setclass(class, str)
2041 	int class;
2042 	char *str;
2043 {
2044 	register STAB *s;
2045 
2046 	if (tTd(37, 8))
2047 		printf("setclass(%c, %s)\n", class, str);
2048 	s = stab(str, ST_CLASS, ST_ENTER);
2049 	setbitn(class, s->s_class);
2050 }
2051 /*
2052 **  MAKEMAPENTRY -- create a map entry
2053 **
2054 **	Parameters:
2055 **		line -- the config file line
2056 **
2057 **	Returns:
2058 **		TRUE if it successfully entered the map entry.
2059 **		FALSE otherwise (usually syntax error).
2060 **
2061 **	Side Effects:
2062 **		Enters the map into the dictionary.
2063 */
2064 
2065 void
2066 makemapentry(line)
2067 	char *line;
2068 {
2069 	register char *p;
2070 	char *mapname;
2071 	char *classname;
2072 	register STAB *s;
2073 	STAB *class;
2074 
2075 	for (p = line; isascii(*p) && isspace(*p); p++)
2076 		continue;
2077 	if (!(isascii(*p) && isalnum(*p)))
2078 	{
2079 		syserr("readcf: config K line: no map name");
2080 		return;
2081 	}
2082 
2083 	mapname = p;
2084 	while ((isascii(*++p) && isalnum(*p)) || *p == '.')
2085 		continue;
2086 	if (*p != '\0')
2087 		*p++ = '\0';
2088 	while (isascii(*p) && isspace(*p))
2089 		p++;
2090 	if (!(isascii(*p) && isalnum(*p)))
2091 	{
2092 		syserr("readcf: config K line, map %s: no map class", mapname);
2093 		return;
2094 	}
2095 	classname = p;
2096 	while (isascii(*++p) && isalnum(*p))
2097 		continue;
2098 	if (*p != '\0')
2099 		*p++ = '\0';
2100 	while (isascii(*p) && isspace(*p))
2101 		p++;
2102 
2103 	/* look up the class */
2104 	class = stab(classname, ST_MAPCLASS, ST_FIND);
2105 	if (class == NULL)
2106 	{
2107 		syserr("readcf: map %s: class %s not available", mapname, classname);
2108 		return;
2109 	}
2110 
2111 	/* enter the map */
2112 	s = stab(mapname, ST_MAP, ST_ENTER);
2113 	s->s_map.map_class = &class->s_mapclass;
2114 	s->s_map.map_mname = newstr(mapname);
2115 
2116 	if (class->s_mapclass.map_parse(&s->s_map, p))
2117 		s->s_map.map_mflags |= MF_VALID;
2118 
2119 	if (tTd(37, 5))
2120 	{
2121 		printf("map %s, class %s, flags %x, file %s,\n",
2122 			s->s_map.map_mname, s->s_map.map_class->map_cname,
2123 			s->s_map.map_mflags,
2124 			s->s_map.map_file == NULL ? "(null)" : s->s_map.map_file);
2125 		printf("\tapp %s, domain %s, rebuild %s\n",
2126 			s->s_map.map_app == NULL ? "(null)" : s->s_map.map_app,
2127 			s->s_map.map_domain == NULL ? "(null)" : s->s_map.map_domain,
2128 			s->s_map.map_rebuild == NULL ? "(null)" : s->s_map.map_rebuild);
2129 	}
2130 }
2131 /*
2132 **  INITTIMEOUTS -- parse and set timeout values
2133 **
2134 **	Parameters:
2135 **		val -- a pointer to the values.  If NULL, do initial
2136 **			settings.
2137 **
2138 **	Returns:
2139 **		none.
2140 **
2141 **	Side Effects:
2142 **		Initializes the TimeOuts structure
2143 */
2144 
2145 #define SECONDS
2146 #define MINUTES	* 60
2147 #define HOUR	* 3600
2148 
2149 inittimeouts(val)
2150 	register char *val;
2151 {
2152 	register char *p;
2153 	extern time_t convtime();
2154 
2155 	if (val == NULL)
2156 	{
2157 		TimeOuts.to_initial = (time_t) 5 MINUTES;
2158 		TimeOuts.to_helo = (time_t) 5 MINUTES;
2159 		TimeOuts.to_mail = (time_t) 10 MINUTES;
2160 		TimeOuts.to_rcpt = (time_t) 1 HOUR;
2161 		TimeOuts.to_datainit = (time_t) 5 MINUTES;
2162 		TimeOuts.to_datablock = (time_t) 1 HOUR;
2163 		TimeOuts.to_datafinal = (time_t) 1 HOUR;
2164 		TimeOuts.to_rset = (time_t) 5 MINUTES;
2165 		TimeOuts.to_quit = (time_t) 2 MINUTES;
2166 		TimeOuts.to_nextcommand = (time_t) 1 HOUR;
2167 		TimeOuts.to_miscshort = (time_t) 2 MINUTES;
2168 #if IDENTPROTO
2169 		TimeOuts.to_ident = (time_t) 30 SECONDS;
2170 #else
2171 		TimeOuts.to_ident = (time_t) 0 SECONDS;
2172 #endif
2173 		TimeOuts.to_fileopen = (time_t) 60 SECONDS;
2174 		return;
2175 	}
2176 
2177 	for (;; val = p)
2178 	{
2179 		while (isascii(*val) && isspace(*val))
2180 			val++;
2181 		if (*val == '\0')
2182 			break;
2183 		for (p = val; *p != '\0' && *p != ','; p++)
2184 			continue;
2185 		if (*p != '\0')
2186 			*p++ = '\0';
2187 
2188 		if (isascii(*val) && isdigit(*val))
2189 		{
2190 			/* old syntax -- set everything */
2191 			TimeOuts.to_mail = convtime(val, 'm');
2192 			TimeOuts.to_rcpt = TimeOuts.to_mail;
2193 			TimeOuts.to_datainit = TimeOuts.to_mail;
2194 			TimeOuts.to_datablock = TimeOuts.to_mail;
2195 			TimeOuts.to_datafinal = TimeOuts.to_mail;
2196 			TimeOuts.to_nextcommand = TimeOuts.to_mail;
2197 			continue;
2198 		}
2199 		else
2200 		{
2201 			register char *q = strchr(val, ':');
2202 
2203 			if (q == NULL && (q = strchr(val, '=')) == NULL)
2204 			{
2205 				/* syntax error */
2206 				continue;
2207 			}
2208 			*q++ = '\0';
2209 			settimeout(val, q);
2210 		}
2211 	}
2212 }
2213 /*
2214 **  SETTIMEOUT -- set an individual timeout
2215 **
2216 **	Parameters:
2217 **		name -- the name of the timeout.
2218 **		val -- the value of the timeout.
2219 **
2220 **	Returns:
2221 **		none.
2222 */
2223 
2224 settimeout(name, val)
2225 	char *name;
2226 	char *val;
2227 {
2228 	register char *p;
2229 	time_t to;
2230 	extern time_t convtime();
2231 
2232 	to = convtime(val, 'm');
2233 	p = strchr(name, '.');
2234 	if (p != NULL)
2235 		*p++ = '\0';
2236 
2237 	if (strcasecmp(name, "initial") == 0)
2238 		TimeOuts.to_initial = to;
2239 	else if (strcasecmp(name, "mail") == 0)
2240 		TimeOuts.to_mail = to;
2241 	else if (strcasecmp(name, "rcpt") == 0)
2242 		TimeOuts.to_rcpt = to;
2243 	else if (strcasecmp(name, "datainit") == 0)
2244 		TimeOuts.to_datainit = to;
2245 	else if (strcasecmp(name, "datablock") == 0)
2246 		TimeOuts.to_datablock = to;
2247 	else if (strcasecmp(name, "datafinal") == 0)
2248 		TimeOuts.to_datafinal = to;
2249 	else if (strcasecmp(name, "command") == 0)
2250 		TimeOuts.to_nextcommand = to;
2251 	else if (strcasecmp(name, "rset") == 0)
2252 		TimeOuts.to_rset = to;
2253 	else if (strcasecmp(name, "helo") == 0)
2254 		TimeOuts.to_helo = to;
2255 	else if (strcasecmp(name, "quit") == 0)
2256 		TimeOuts.to_quit = to;
2257 	else if (strcasecmp(name, "misc") == 0)
2258 		TimeOuts.to_miscshort = to;
2259 	else if (strcasecmp(name, "ident") == 0)
2260 		TimeOuts.to_ident = to;
2261 	else if (strcasecmp(name, "fileopen") == 0)
2262 		TimeOuts.to_fileopen = to;
2263 	else if (strcasecmp(name, "queuewarn") == 0)
2264 	{
2265 		to = convtime(val, 'h');
2266 		if (p == NULL || strcmp(p, "*") == 0)
2267 		{
2268 			TimeOuts.to_q_warning[TOC_NORMAL] = to;
2269 			TimeOuts.to_q_warning[TOC_URGENT] = to;
2270 			TimeOuts.to_q_warning[TOC_NONURGENT] = to;
2271 		}
2272 		else if (strcasecmp(p, "normal") == 0)
2273 			TimeOuts.to_q_warning[TOC_NORMAL] = to;
2274 		else if (strcasecmp(p, "urgent") == 0)
2275 			TimeOuts.to_q_warning[TOC_URGENT] = to;
2276 		else if (strcasecmp(p, "non-urgent") == 0)
2277 			TimeOuts.to_q_warning[TOC_NONURGENT] = to;
2278 		else
2279 			syserr("settimeout: invalid queuewarn subtimeout %s", p);
2280 	}
2281 	else if (strcasecmp(name, "queuereturn") == 0)
2282 	{
2283 		to = convtime(val, 'd');
2284 		if (p == NULL || strcmp(p, "*") == 0)
2285 		{
2286 			TimeOuts.to_q_return[TOC_NORMAL] = to;
2287 			TimeOuts.to_q_return[TOC_URGENT] = to;
2288 			TimeOuts.to_q_return[TOC_NONURGENT] = to;
2289 		}
2290 		else if (strcasecmp(p, "normal") == 0)
2291 			TimeOuts.to_q_return[TOC_NORMAL] = to;
2292 		else if (strcasecmp(p, "urgent") == 0)
2293 			TimeOuts.to_q_return[TOC_URGENT] = to;
2294 		else if (strcasecmp(p, "non-urgent") == 0)
2295 			TimeOuts.to_q_return[TOC_NONURGENT] = to;
2296 		else
2297 			syserr("settimeout: invalid queuereturn subtimeout %s", p);
2298 	}
2299 	else
2300 		syserr("settimeout: invalid timeout %s", name);
2301 }
2302