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