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