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