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