xref: /csrg-svn/usr.sbin/sendmail/src/readcf.c (revision 68756)
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.80 (Berkeley) 04/09/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 			if (ConfigLevel >= 5)
536 			{
537 				/* level 5 configs have short name in $w */
538 				p = macvalue('w', e);
539 				if (p != NULL && (p = strchr(p, '.')) != NULL)
540 					*p = '\0';
541 			}
542 			if (*ep++ == '/')
543 			{
544 				/* extract vendor code */
545 				for (p = ep; isascii(*p) && isalpha(*p); )
546 					p++;
547 				*p = '\0';
548 
549 				if (!setvendor(ep))
550 					syserr("invalid V line vendor code: \"%s\"",
551 						ep);
552 			}
553 			break;
554 
555 		  case 'K':
556 			makemapentry(&bp[1]);
557 			break;
558 
559 		  default:
560 		  badline:
561 			syserr("unknown control line \"%s\"", bp);
562 		}
563 		if (bp != buf)
564 			free(bp);
565 	}
566 	if (ferror(cf))
567 	{
568 		syserr("I/O read error", cfname);
569 		exit(EX_OSFILE);
570 	}
571 	fclose(cf);
572 	FileName = NULL;
573 
574 	/* initialize host maps from local service tables */
575 	inithostmaps();
576 
577 	/* determine if we need to do special name-server frotz */
578 	{
579 		int nmaps;
580 		char *maptype[MAXMAPSTACK];
581 		short mapreturn[MAXMAPACTIONS];
582 
583 		nmaps = switch_map_find("hosts", maptype, mapreturn);
584 		UseNameServer = FALSE;
585 		if (nmaps > 0 && nmaps <= MAXMAPSTACK)
586 		{
587 			register int mapno;
588 
589 			for (mapno = 0; mapno < nmaps && !UseNameServer; mapno++)
590 			{
591 				if (strcmp(maptype[mapno], "dns") == 0)
592 					UseNameServer = TRUE;
593 			}
594 		}
595 
596 #ifdef HESIOD
597 		nmaps = switch_map_find("passwd", maptype, mapreturn);
598 		UseHesiod = FALSE;
599 		if (nmaps > 0 && nmaps <= MAXMAPSTACK)
600 		{
601 			register int mapno;
602 
603 			for (mapno = 0; mapno < nmaps && !UseHesiod; mapno++)
604 			{
605 				if (strcmp(maptype[mapno], "hesiod") == 0)
606 					UseHesiod = TRUE;
607 			}
608 		}
609 #endif
610 	}
611 }
612 /*
613 **  TOOMANY -- signal too many of some option
614 **
615 **	Parameters:
616 **		id -- the id of the error line
617 **		maxcnt -- the maximum possible values
618 **
619 **	Returns:
620 **		none.
621 **
622 **	Side Effects:
623 **		gives a syserr.
624 */
625 
626 toomany(id, maxcnt)
627 	char id;
628 	int maxcnt;
629 {
630 	syserr("too many %c lines, %d max", id, maxcnt);
631 }
632 /*
633 **  FILECLASS -- read members of a class from a file
634 **
635 **	Parameters:
636 **		class -- class to define.
637 **		filename -- name of file to read.
638 **		fmt -- scanf string to use for match.
639 **		safe -- if set, this is a safe read.
640 **		optional -- if set, it is not an error for the file to
641 **			not exist.
642 **
643 **	Returns:
644 **		none
645 **
646 **	Side Effects:
647 **
648 **		puts all lines in filename that match a scanf into
649 **			the named class.
650 */
651 
652 fileclass(class, filename, fmt, safe, optional)
653 	int class;
654 	char *filename;
655 	char *fmt;
656 	bool safe;
657 	bool optional;
658 {
659 	FILE *f;
660 	int sff;
661 	char buf[MAXLINE];
662 
663 	if (tTd(37, 2))
664 		printf("fileclass(%s, fmt=%s)\n", filename, fmt);
665 
666 	if (filename[0] == '|')
667 	{
668 		syserr("fileclass: pipes (F%c%s) not supported due to security problems",
669 			class, filename);
670 		return;
671 	}
672 	sff = SFF_REGONLY;
673 	if (safe)
674 		sff |= SFF_OPENASROOT;
675 	f = safefopen(filename, O_RDONLY, 0, sff);
676 	if (f == NULL)
677 	{
678 		if (!optional)
679 			syserr("fileclass: cannot open %s", filename);
680 		return;
681 	}
682 
683 	while (fgets(buf, sizeof buf, f) != NULL)
684 	{
685 		register STAB *s;
686 		register char *p;
687 # ifdef SCANF
688 		char wordbuf[MAXNAME+1];
689 
690 		if (sscanf(buf, fmt, wordbuf) != 1)
691 			continue;
692 		p = wordbuf;
693 # else /* SCANF */
694 		p = buf;
695 # endif /* SCANF */
696 
697 		/*
698 		**  Break up the match into words.
699 		*/
700 
701 		while (*p != '\0')
702 		{
703 			register char *q;
704 
705 			/* strip leading spaces */
706 			while (isascii(*p) && isspace(*p))
707 				p++;
708 			if (*p == '\0')
709 				break;
710 
711 			/* find the end of the word */
712 			q = p;
713 			while (*p != '\0' && !(isascii(*p) && isspace(*p)))
714 				p++;
715 			if (*p != '\0')
716 				*p++ = '\0';
717 
718 			/* enter the word in the symbol table */
719 			setclass(class, q);
720 		}
721 	}
722 
723 	(void) fclose(f);
724 }
725 /*
726 **  MAKEMAILER -- define a new mailer.
727 **
728 **	Parameters:
729 **		line -- description of mailer.  This is in labeled
730 **			fields.  The fields are:
731 **			   A -- the argv for this mailer
732 **			   C -- the character set for MIME conversions
733 **			   D -- the directory to run in
734 **			   E -- the eol string
735 **			   F -- the flags associated with the mailer
736 **			   L -- the maximum line length
737 **			   M -- the maximum message size
738 **			   P -- the path to the mailer
739 **			   R -- the recipient rewriting set
740 **			   S -- the sender rewriting set
741 **			   T -- the mailer type (for DSNs)
742 **			   U -- the uid to run as
743 **			The first word is the canonical name of the mailer.
744 **
745 **	Returns:
746 **		none.
747 **
748 **	Side Effects:
749 **		enters the mailer into the mailer table.
750 */
751 
752 makemailer(line)
753 	char *line;
754 {
755 	register char *p;
756 	register struct mailer *m;
757 	register STAB *s;
758 	int i;
759 	char fcode;
760 	auto char *endp;
761 	extern int NextMailer;
762 	extern char **makeargv();
763 	extern char *munchstring();
764 	extern long atol();
765 
766 	/* allocate a mailer and set up defaults */
767 	m = (struct mailer *) xalloc(sizeof *m);
768 	bzero((char *) m, sizeof *m);
769 	m->m_eol = "\n";
770 	m->m_uid = m->m_gid = 0;
771 
772 	/* collect the mailer name */
773 	for (p = line; *p != '\0' && *p != ',' && !(isascii(*p) && isspace(*p)); p++)
774 		continue;
775 	if (*p != '\0')
776 		*p++ = '\0';
777 	m->m_name = newstr(line);
778 
779 	/* now scan through and assign info from the fields */
780 	while (*p != '\0')
781 	{
782 		auto char *delimptr;
783 
784 		while (*p != '\0' && (*p == ',' || (isascii(*p) && isspace(*p))))
785 			p++;
786 
787 		/* p now points to field code */
788 		fcode = *p;
789 		while (*p != '\0' && *p != '=' && *p != ',')
790 			p++;
791 		if (*p++ != '=')
792 		{
793 			syserr("mailer %s: `=' expected", m->m_name);
794 			return;
795 		}
796 		while (isascii(*p) && isspace(*p))
797 			p++;
798 
799 		/* p now points to the field body */
800 		p = munchstring(p, &delimptr);
801 
802 		/* install the field into the mailer struct */
803 		switch (fcode)
804 		{
805 		  case 'P':		/* pathname */
806 			m->m_mailer = newstr(p);
807 			break;
808 
809 		  case 'F':		/* flags */
810 			for (; *p != '\0'; p++)
811 				if (!(isascii(*p) && isspace(*p)))
812 					setbitn(*p, m->m_flags);
813 			break;
814 
815 		  case 'S':		/* sender rewriting ruleset */
816 		  case 'R':		/* recipient rewriting ruleset */
817 			i = strtol(p, &endp, 10);
818 			if (i < 0 || i >= MAXRWSETS)
819 			{
820 				syserr("invalid rewrite set, %d max", MAXRWSETS);
821 				return;
822 			}
823 			if (fcode == 'S')
824 				m->m_sh_rwset = m->m_se_rwset = i;
825 			else
826 				m->m_rh_rwset = m->m_re_rwset = i;
827 
828 			p = endp;
829 			if (*p++ == '/')
830 			{
831 				i = strtol(p, NULL, 10);
832 				if (i < 0 || i >= MAXRWSETS)
833 				{
834 					syserr("invalid rewrite set, %d max",
835 						MAXRWSETS);
836 					return;
837 				}
838 				if (fcode == 'S')
839 					m->m_sh_rwset = i;
840 				else
841 					m->m_rh_rwset = i;
842 			}
843 			break;
844 
845 		  case 'E':		/* end of line string */
846 			m->m_eol = newstr(p);
847 			break;
848 
849 		  case 'A':		/* argument vector */
850 			m->m_argv = makeargv(p);
851 			break;
852 
853 		  case 'M':		/* maximum message size */
854 			m->m_maxsize = atol(p);
855 			break;
856 
857 		  case 'L':		/* maximum line length */
858 			m->m_linelimit = atoi(p);
859 			break;
860 
861 		  case 'D':		/* working directory */
862 			m->m_execdir = newstr(p);
863 			break;
864 
865 		  case 'C':		/* default charset */
866 			m->m_defcharset = newstr(p);
867 			break;
868 
869 		  case 'T':		/* MTA Type */
870 			m->m_mtatype = newstr(p);
871 			p = strchr(m->m_mtatype, '/');
872 			if (p != NULL)
873 			{
874 				*p++ = '\0';
875 				if (*p == '\0')
876 					p = NULL;
877 			}
878 			if (p == NULL)
879 				m->m_addrtype = m->m_mtatype;
880 			else
881 			{
882 				m->m_addrtype = p;
883 				p = strchr(p, '/');
884 			}
885 			if (p != NULL)
886 			{
887 				*p++ = '\0';
888 				if (*p == '\0')
889 					p = NULL;
890 			}
891 			if (p == NULL)
892 				m->m_diagtype = m->m_mtatype;
893 			else
894 				m->m_diagtype = p;
895 			break;
896 
897 		  case 'U':		/* user id */
898 			if (isascii(*p) && !isdigit(*p))
899 			{
900 				char *q = p;
901 				struct passwd *pw;
902 
903 				while (isascii(*p) && isalnum(*p))
904 					p++;
905 				while (isascii(*p) && isspace(*p))
906 					*p++ = '\0';
907 				if (*p != '\0')
908 					*p++ = '\0';
909 				pw = sm_getpwnam(q);
910 				if (pw == NULL)
911 					syserr("readcf: mailer U= flag: unknown user %s", q);
912 				else
913 				{
914 					m->m_uid = pw->pw_uid;
915 					m->m_gid = pw->pw_gid;
916 				}
917 			}
918 			else
919 			{
920 				auto char *q;
921 
922 				m->m_uid = strtol(p, &q, 0);
923 				p = q;
924 			}
925 			while (isascii(*p) && isspace(*p))
926 				p++;
927 			if (*p == '\0')
928 				break;
929 			if (isascii(*p) && !isdigit(*p))
930 			{
931 				char *q = p;
932 				struct group *gr;
933 
934 				while (isascii(*p) && isalnum(*p))
935 					p++;
936 				*p++ = '\0';
937 				gr = getgrnam(q);
938 				if (gr == NULL)
939 					syserr("readcf: mailer U= flag: unknown group %s", q);
940 				else
941 					m->m_gid = gr->gr_gid;
942 			}
943 			else
944 			{
945 				m->m_gid = strtol(p, NULL, 0);
946 			}
947 			break;
948 		}
949 
950 		p = delimptr;
951 	}
952 
953 	/* do some rationality checking */
954 	if (m->m_argv == NULL)
955 	{
956 		syserr("M%s: A= argument required", m->m_name);
957 		return;
958 	}
959 	if (m->m_mailer == NULL)
960 	{
961 		syserr("M%s: P= argument required", m->m_name);
962 		return;
963 	}
964 
965 	if (NextMailer >= MAXMAILERS)
966 	{
967 		syserr("too many mailers defined (%d max)", MAXMAILERS);
968 		return;
969 	}
970 
971 	/* do some heuristic cleanup for back compatibility */
972 	if (bitnset(M_LIMITS, m->m_flags))
973 	{
974 		if (m->m_linelimit == 0)
975 			m->m_linelimit = SMTPLINELIM;
976 		if (ConfigLevel < 2)
977 			setbitn(M_7BITS, m->m_flags);
978 	}
979 
980 	if (ConfigLevel < 6 &&
981 	    (strcmp(m->m_mailer, "[IPC]") == 0 ||
982 	     strcmp(m->m_mailer, "[TCP]") == 0))
983 	{
984 		if (m->m_mtatype == NULL)
985 			m->m_mtatype = "dns";
986 		if (m->m_addrtype == NULL)
987 			m->m_addrtype = "rfc822";
988 		if (m->m_diagtype == NULL)
989 			m->m_diagtype = "smtp";
990 	}
991 
992 	/* enter the mailer into the symbol table */
993 	s = stab(m->m_name, ST_MAILER, ST_ENTER);
994 	if (s->s_mailer != NULL)
995 	{
996 		i = s->s_mailer->m_mno;
997 		free(s->s_mailer);
998 	}
999 	else
1000 	{
1001 		i = NextMailer++;
1002 	}
1003 	Mailer[i] = s->s_mailer = m;
1004 	m->m_mno = i;
1005 }
1006 /*
1007 **  MUNCHSTRING -- translate a string into internal form.
1008 **
1009 **	Parameters:
1010 **		p -- the string to munch.
1011 **		delimptr -- if non-NULL, set to the pointer of the
1012 **			field delimiter character.
1013 **
1014 **	Returns:
1015 **		the munched string.
1016 */
1017 
1018 char *
1019 munchstring(p, delimptr)
1020 	register char *p;
1021 	char **delimptr;
1022 {
1023 	register char *q;
1024 	bool backslash = FALSE;
1025 	bool quotemode = FALSE;
1026 	static char buf[MAXLINE];
1027 
1028 	for (q = buf; *p != '\0'; p++)
1029 	{
1030 		if (backslash)
1031 		{
1032 			/* everything is roughly literal */
1033 			backslash = FALSE;
1034 			switch (*p)
1035 			{
1036 			  case 'r':		/* carriage return */
1037 				*q++ = '\r';
1038 				continue;
1039 
1040 			  case 'n':		/* newline */
1041 				*q++ = '\n';
1042 				continue;
1043 
1044 			  case 'f':		/* form feed */
1045 				*q++ = '\f';
1046 				continue;
1047 
1048 			  case 'b':		/* backspace */
1049 				*q++ = '\b';
1050 				continue;
1051 			}
1052 			*q++ = *p;
1053 		}
1054 		else
1055 		{
1056 			if (*p == '\\')
1057 				backslash = TRUE;
1058 			else if (*p == '"')
1059 				quotemode = !quotemode;
1060 			else if (quotemode || *p != ',')
1061 				*q++ = *p;
1062 			else
1063 				break;
1064 		}
1065 	}
1066 
1067 	if (delimptr != NULL)
1068 		*delimptr = p;
1069 	*q++ = '\0';
1070 	return (buf);
1071 }
1072 /*
1073 **  MAKEARGV -- break up a string into words
1074 **
1075 **	Parameters:
1076 **		p -- the string to break up.
1077 **
1078 **	Returns:
1079 **		a char **argv (dynamically allocated)
1080 **
1081 **	Side Effects:
1082 **		munges p.
1083 */
1084 
1085 char **
1086 makeargv(p)
1087 	register char *p;
1088 {
1089 	char *q;
1090 	int i;
1091 	char **avp;
1092 	char *argv[MAXPV + 1];
1093 
1094 	/* take apart the words */
1095 	i = 0;
1096 	while (*p != '\0' && i < MAXPV)
1097 	{
1098 		q = p;
1099 		while (*p != '\0' && !(isascii(*p) && isspace(*p)))
1100 			p++;
1101 		while (isascii(*p) && isspace(*p))
1102 			*p++ = '\0';
1103 		argv[i++] = newstr(q);
1104 	}
1105 	argv[i++] = NULL;
1106 
1107 	/* now make a copy of the argv */
1108 	avp = (char **) xalloc(sizeof *avp * i);
1109 	bcopy((char *) argv, (char *) avp, sizeof *avp * i);
1110 
1111 	return (avp);
1112 }
1113 /*
1114 **  PRINTRULES -- print rewrite rules (for debugging)
1115 **
1116 **	Parameters:
1117 **		none.
1118 **
1119 **	Returns:
1120 **		none.
1121 **
1122 **	Side Effects:
1123 **		prints rewrite rules.
1124 */
1125 
1126 printrules()
1127 {
1128 	register struct rewrite *rwp;
1129 	register int ruleset;
1130 
1131 	for (ruleset = 0; ruleset < 10; ruleset++)
1132 	{
1133 		if (RewriteRules[ruleset] == NULL)
1134 			continue;
1135 		printf("\n----Rule Set %d:", ruleset);
1136 
1137 		for (rwp = RewriteRules[ruleset]; rwp != NULL; rwp = rwp->r_next)
1138 		{
1139 			printf("\nLHS:");
1140 			printav(rwp->r_lhs);
1141 			printf("RHS:");
1142 			printav(rwp->r_rhs);
1143 		}
1144 	}
1145 }
1146 /*
1147 **  PRINTMAILER -- print mailer structure (for debugging)
1148 **
1149 **	Parameters:
1150 **		m -- the mailer to print
1151 **
1152 **	Returns:
1153 **		none.
1154 */
1155 
1156 printmailer(m)
1157 	register MAILER *m;
1158 {
1159 	int j;
1160 
1161 	printf("mailer %d (%s): P=%s S=%d/%d R=%d/%d M=%ld U=%d:%d F=",
1162 		m->m_mno, m->m_name,
1163 		m->m_mailer, m->m_se_rwset, m->m_sh_rwset,
1164 		m->m_re_rwset, m->m_rh_rwset, m->m_maxsize,
1165 		m->m_uid, m->m_gid);
1166 	for (j = '\0'; j <= '\177'; j++)
1167 		if (bitnset(j, m->m_flags))
1168 			(void) putchar(j);
1169 	printf(" L=%d E=", m->m_linelimit);
1170 	xputs(m->m_eol);
1171 	if (m->m_defcharset != NULL)
1172 		printf(" C=%s", m->m_defcharset);
1173 	printf(" T=%s/%s/%s",
1174 		m->m_mtatype == NULL ? "<undefined>" : m->m_mtatype,
1175 		m->m_addrtype == NULL ? "<undefined>" : m->m_addrtype,
1176 		m->m_diagtype == NULL ? "<undefined>" : m->m_diagtype);
1177 	if (m->m_argv != NULL)
1178 	{
1179 		char **a = m->m_argv;
1180 
1181 		printf(" A=");
1182 		while (*a != NULL)
1183 		{
1184 			if (a != m->m_argv)
1185 				printf(" ");
1186 			xputs(*a++);
1187 		}
1188 	}
1189 	printf("\n");
1190 }
1191 /*
1192 **  SETOPTION -- set global processing option
1193 **
1194 **	Parameters:
1195 **		opt -- option name.
1196 **		val -- option value (as a text string).
1197 **		safe -- set if this came from a configuration file.
1198 **			Some options (if set from the command line) will
1199 **			reset the user id to avoid security problems.
1200 **		sticky -- if set, don't let other setoptions override
1201 **			this value.
1202 **		e -- the main envelope.
1203 **
1204 **	Returns:
1205 **		none.
1206 **
1207 **	Side Effects:
1208 **		Sets options as implied by the arguments.
1209 */
1210 
1211 static BITMAP	StickyOpt;		/* set if option is stuck */
1212 
1213 
1214 #if NAMED_BIND
1215 
1216 struct resolverflags
1217 {
1218 	char	*rf_name;	/* name of the flag */
1219 	long	rf_bits;	/* bits to set/clear */
1220 } ResolverFlags[] =
1221 {
1222 	"debug",	RES_DEBUG,
1223 	"aaonly",	RES_AAONLY,
1224 	"usevc",	RES_USEVC,
1225 	"primary",	RES_PRIMARY,
1226 	"igntc",	RES_IGNTC,
1227 	"recurse",	RES_RECURSE,
1228 	"defnames",	RES_DEFNAMES,
1229 	"stayopen",	RES_STAYOPEN,
1230 	"dnsrch",	RES_DNSRCH,
1231 	"true",		0,		/* to avoid error on old syntax */
1232 	NULL,		0
1233 };
1234 
1235 #endif
1236 
1237 struct optioninfo
1238 {
1239 	char	*o_name;	/* long name of option */
1240 	u_char	o_code;		/* short name of option */
1241 	bool	o_safe;		/* safe for random people to use */
1242 } OptionTab[] =
1243 {
1244 	"SevenBitInput",	'7',		TRUE,
1245 	"EightBitMode",		'8',		TRUE,
1246 	"AliasFile",		'A',		FALSE,
1247 	"AliasWait",		'a',		FALSE,
1248 	"BlankSub",		'B',		FALSE,
1249 	"MinFreeBlocks",	'b',		TRUE,
1250 	"CheckpointInterval",	'C',		TRUE,
1251 	"HoldExpensive",	'c',		FALSE,
1252 	"AutoRebuildAliases",	'D',		FALSE,
1253 	"DeliveryMode",		'd',		TRUE,
1254 	"ErrorHeader",		'E',		FALSE,
1255 	"ErrorMode",		'e',		TRUE,
1256 	"TempFileMode",		'F',		FALSE,
1257 	"SaveFromLine",		'f',		FALSE,
1258 	"MatchGECOS",		'G',		FALSE,
1259 	"HelpFile",		'H',		FALSE,
1260 	"MaxHopCount",		'h',		FALSE,
1261 	"ResolverOptions",	'I',		FALSE,
1262 	"IgnoreDots",		'i',		TRUE,
1263 	"ForwardPath",		'J',		FALSE,
1264 	"SendMimeErrors",	'j',		TRUE,
1265 	"ConnectionCacheSize",	'k',		FALSE,
1266 	"ConnectionCacheTimeout", 'K',		FALSE,
1267 	"UseErrorsTo",		'l',		FALSE,
1268 	"LogLevel",		'L',		FALSE,
1269 	"MeToo",		'm',		TRUE,
1270 	"CheckAliases",		'n',		FALSE,
1271 	"OldStyleHeaders",	'o',		TRUE,
1272 	"DaemonPortOptions",	'O',		FALSE,
1273 	"PrivacyOptions",	'p',		TRUE,
1274 	"PostmasterCopy",	'P',		FALSE,
1275 	"QueueFactor",		'q',		FALSE,
1276 	"QueueDirectory",	'Q',		FALSE,
1277 	"DontPruneRoutes",	'R',		FALSE,
1278 	"Timeout",		'r',		TRUE,
1279 	"StatusFile",		'S',		FALSE,
1280 	"SuperSafe",		's',		TRUE,
1281 	"QueueTimeout",		'T',		FALSE,
1282 	"TimeZoneSpec",		't',		FALSE,
1283 	"UserDatabaseSpec",	'U',		FALSE,
1284 	"DefaultUser",		'u',		FALSE,
1285 	"FallbackMXhost",	'V',		FALSE,
1286 	"Verbose",		'v',		TRUE,
1287 	"TryNullMXList",	'w',		TRUE,
1288 	"QueueLA",		'x',		FALSE,
1289 	"RefuseLA",		'X',		FALSE,
1290 	"RecipientFactor",	'y',		FALSE,
1291 	"ForkEachJob",		'Y',		FALSE,
1292 	"ClassFactor",		'z',		FALSE,
1293 	"RetryFactor",		'Z',		FALSE,
1294 #define O_BSP		0x80
1295 	"BrokenSmtpPeers",	O_BSP,		TRUE,
1296 #define O_QUEUESORTORD	0x81
1297 	"QueueSortOrder",	O_QUEUESORTORD,	TRUE,
1298 #define O_MQA		0x83
1299 	"MinQueueAge",		O_MQA,		TRUE,
1300 #define O_MHSA		0x84
1301 /*
1302 	"MaxHostStatAge",	O_MHSA,		TRUE,
1303 */
1304 #define O_DEFCHARSET	0x85
1305 	"DefaultCharSet",	O_DEFCHARSET,	TRUE,
1306 #define O_SSFILE	0x86
1307 	"ServiceSwitchFile",	O_SSFILE,	FALSE,
1308 #define O_DIALDELAY	0x87
1309 	"DialDelay",		O_DIALDELAY,	TRUE,
1310 #define O_NORCPTACTION	0x88
1311 	"NoRecipientAction",	O_NORCPTACTION,	TRUE,
1312 #define O_SAFEFILEENV	0x89
1313 	"SafeFileEnvironment",	O_SAFEFILEENV,	FALSE,
1314 #define O_MAXMSGSIZE	0x8a
1315 	"MaxMessageSize",	O_MAXMSGSIZE,	FALSE,
1316 #define O_COLONOKINADDR	0x8b
1317 	"ColonOkInAddr",	O_COLONOKINADDR, TRUE,
1318 
1319 	NULL,			'\0',		FALSE,
1320 };
1321 
1322 
1323 
1324 setoption(opt, val, safe, sticky, e)
1325 	u_char opt;
1326 	char *val;
1327 	bool safe;
1328 	bool sticky;
1329 	register ENVELOPE *e;
1330 {
1331 	register char *p;
1332 	register struct optioninfo *o;
1333 	char *subopt;
1334 	extern bool atobool();
1335 	extern time_t convtime();
1336 	extern int QueueLA;
1337 	extern int RefuseLA;
1338 	extern bool Warn_Q_option;
1339 
1340 	errno = 0;
1341 	if (opt == ' ')
1342 	{
1343 		/* full word options */
1344 		struct optioninfo *sel;
1345 
1346 		p = strchr(val, '=');
1347 		if (p == NULL)
1348 			p = &val[strlen(val)];
1349 		while (*--p == ' ')
1350 			continue;
1351 		while (*++p == ' ')
1352 			*p = '\0';
1353 		if (p == val)
1354 		{
1355 			syserr("readcf: null option name");
1356 			return;
1357 		}
1358 		if (*p == '=')
1359 			*p++ = '\0';
1360 		while (*p == ' ')
1361 			p++;
1362 		subopt = strchr(val, '.');
1363 		if (subopt != NULL)
1364 			*subopt++ = '\0';
1365 		sel = NULL;
1366 		for (o = OptionTab; o->o_name != NULL; o++)
1367 		{
1368 			if (strncasecmp(o->o_name, val, strlen(val)) != 0)
1369 				continue;
1370 			if (strlen(o->o_name) == strlen(val))
1371 			{
1372 				/* completely specified -- this must be it */
1373 				sel = NULL;
1374 				break;
1375 			}
1376 			if (sel != NULL)
1377 				break;
1378 			sel = o;
1379 		}
1380 		if (sel != NULL && o->o_name == NULL)
1381 			o = sel;
1382 		else if (o->o_name == NULL)
1383 		{
1384 			syserr("readcf: unknown option name %s", val);
1385 			return;
1386 		}
1387 		else if (sel != NULL)
1388 		{
1389 			syserr("readcf: ambiguous option name %s (matches %s and %s)",
1390 				val, sel->o_name, o->o_name);
1391 			return;
1392 		}
1393 		if (strlen(val) != strlen(o->o_name))
1394 		{
1395 			bool oldVerbose = Verbose;
1396 
1397 			Verbose = TRUE;
1398 			message("Option %s used as abbreviation for %s",
1399 				val, o->o_name);
1400 			Verbose = oldVerbose;
1401 		}
1402 		opt = o->o_code;
1403 		val = p;
1404 	}
1405 	else
1406 	{
1407 		for (o = OptionTab; o->o_name != NULL; o++)
1408 		{
1409 			if (o->o_code == opt)
1410 				break;
1411 		}
1412 		subopt = NULL;
1413 	}
1414 
1415 	if (tTd(37, 1))
1416 	{
1417 		printf(isascii(opt) && isprint(opt) ?
1418 			    "setoption %s (%c).%s=%s" :
1419 			    "setoption %s (0x%x).%s=%s",
1420 			o->o_name == NULL ? "<unknown>" : o->o_name,
1421 			opt,
1422 			subopt == NULL ? "" : subopt,
1423 			val);
1424 	}
1425 
1426 	/*
1427 	**  See if this option is preset for us.
1428 	*/
1429 
1430 	if (!sticky && bitnset(opt, StickyOpt))
1431 	{
1432 		if (tTd(37, 1))
1433 			printf(" (ignored)\n");
1434 		return;
1435 	}
1436 
1437 	/*
1438 	**  Check to see if this option can be specified by this user.
1439 	*/
1440 
1441 	if (!safe && RealUid == 0)
1442 		safe = TRUE;
1443 	if (!safe && !o->o_safe)
1444 	{
1445 		if (opt != 'M' || (val[0] != 'r' && val[0] != 's'))
1446 		{
1447 			if (tTd(37, 1))
1448 				printf(" (unsafe)");
1449 			if (RealUid != geteuid())
1450 			{
1451 				if (tTd(37, 1))
1452 					printf("(Resetting uid)");
1453 				(void) setgid(RealGid);
1454 				(void) setuid(RealUid);
1455 			}
1456 		}
1457 	}
1458 	if (tTd(37, 1))
1459 		printf("\n");
1460 
1461 	switch (opt & 0xff)
1462 	{
1463 	  case '7':		/* force seven-bit input */
1464 		SevenBitInput = atobool(val);
1465 		break;
1466 
1467 	  case '8':		/* handling of 8-bit input */
1468 		switch (*val)
1469 		{
1470 		  case 'r':		/* reject 8-bit, don't convert MIME */
1471 			MimeMode = 0;
1472 			break;
1473 
1474 		  case 'm':		/* convert 8-bit, convert MIME */
1475 			MimeMode = MM_CVTMIME|MM_MIME8BIT;
1476 			break;
1477 
1478 		  case 'j':		/* "just send 8" */
1479 			MimeMode = MM_PASS8BIT;
1480 			break;
1481 
1482 		  case 'p':		/* pass 8 bit, convert MIME */
1483 			MimeMode = MM_PASS8BIT|MM_CVTMIME;
1484 			break;
1485 
1486 		  case 's':		/* strict adherence */
1487 			MimeMode = MM_CVTMIME;
1488 			break;
1489 
1490 		  case 'a':		/* encode 8 bit if available */
1491 			MimeMode = MM_MIME8BIT|MM_PASS8BIT|MM_CVTMIME;
1492 			break;
1493 
1494 		  case 'c':		/* convert 8 bit to MIME, never 7 bit */
1495 			MimeMode = MM_MIME8BIT;
1496 			break;
1497 
1498 		  default:
1499 			syserr("Unknown 8-bit mode %c", *val);
1500 			exit(EX_USAGE);
1501 		}
1502 		break;
1503 
1504 	  case 'A':		/* set default alias file */
1505 		if (val[0] == '\0')
1506 			setalias("aliases");
1507 		else
1508 			setalias(val);
1509 		break;
1510 
1511 	  case 'a':		/* look N minutes for "@:@" in alias file */
1512 		if (val[0] == '\0')
1513 			SafeAlias = 5 * 60;		/* five minutes */
1514 		else
1515 			SafeAlias = convtime(val, 'm');
1516 		break;
1517 
1518 	  case 'B':		/* substitution for blank character */
1519 		SpaceSub = val[0];
1520 		if (SpaceSub == '\0')
1521 			SpaceSub = ' ';
1522 		break;
1523 
1524 	  case 'b':		/* min blocks free on queue fs/max msg size */
1525 		p = strchr(val, '/');
1526 		if (p != NULL)
1527 		{
1528 			*p++ = '\0';
1529 			MaxMessageSize = atol(p);
1530 		}
1531 		MinBlocksFree = atol(val);
1532 		break;
1533 
1534 	  case 'c':		/* don't connect to "expensive" mailers */
1535 		NoConnect = atobool(val);
1536 		break;
1537 
1538 	  case 'C':		/* checkpoint every N addresses */
1539 		CheckpointInterval = atoi(val);
1540 		break;
1541 
1542 	  case 'd':		/* delivery mode */
1543 		switch (*val)
1544 		{
1545 		  case '\0':
1546 			e->e_sendmode = SM_DELIVER;
1547 			break;
1548 
1549 		  case SM_QUEUE:	/* queue only */
1550 #ifndef QUEUE
1551 			syserr("need QUEUE to set -odqueue");
1552 #endif /* QUEUE */
1553 			/* fall through..... */
1554 
1555 		  case SM_DELIVER:	/* do everything */
1556 		  case SM_FORK:		/* fork after verification */
1557 			e->e_sendmode = *val;
1558 			break;
1559 
1560 		  default:
1561 			syserr("Unknown delivery mode %c", *val);
1562 			exit(EX_USAGE);
1563 		}
1564 		break;
1565 
1566 	  case 'D':		/* rebuild alias database as needed */
1567 		AutoRebuild = atobool(val);
1568 		break;
1569 
1570 	  case 'E':		/* error message header/header file */
1571 		if (*val != '\0')
1572 			ErrMsgFile = newstr(val);
1573 		break;
1574 
1575 	  case 'e':		/* set error processing mode */
1576 		switch (*val)
1577 		{
1578 		  case EM_QUIET:	/* be silent about it */
1579 		  case EM_MAIL:		/* mail back */
1580 		  case EM_BERKNET:	/* do berknet error processing */
1581 		  case EM_WRITE:	/* write back (or mail) */
1582 		  case EM_PRINT:	/* print errors normally (default) */
1583 			e->e_errormode = *val;
1584 			break;
1585 		}
1586 		break;
1587 
1588 	  case 'F':		/* file mode */
1589 		FileMode = atooct(val) & 0777;
1590 		break;
1591 
1592 	  case 'f':		/* save Unix-style From lines on front */
1593 		SaveFrom = atobool(val);
1594 		break;
1595 
1596 	  case 'G':		/* match recipients against GECOS field */
1597 		MatchGecos = atobool(val);
1598 		break;
1599 
1600 	  case 'g':		/* default gid */
1601   g_opt:
1602 		if (isascii(*val) && isdigit(*val))
1603 			DefGid = atoi(val);
1604 		else
1605 		{
1606 			register struct group *gr;
1607 
1608 			DefGid = -1;
1609 			gr = getgrnam(val);
1610 			if (gr == NULL)
1611 				syserr("readcf: option %c: unknown group %s",
1612 					opt, val);
1613 			else
1614 				DefGid = gr->gr_gid;
1615 		}
1616 		break;
1617 
1618 	  case 'H':		/* help file */
1619 		if (val[0] == '\0')
1620 			HelpFile = "sendmail.hf";
1621 		else
1622 			HelpFile = newstr(val);
1623 		break;
1624 
1625 	  case 'h':		/* maximum hop count */
1626 		MaxHopCount = atoi(val);
1627 		break;
1628 
1629 	  case 'I':		/* use internet domain name server */
1630 #if NAMED_BIND
1631 		for (p = val; *p != 0; )
1632 		{
1633 			bool clearmode;
1634 			char *q;
1635 			struct resolverflags *rfp;
1636 
1637 			while (*p == ' ')
1638 				p++;
1639 			if (*p == '\0')
1640 				break;
1641 			clearmode = FALSE;
1642 			if (*p == '-')
1643 				clearmode = TRUE;
1644 			else if (*p != '+')
1645 				p--;
1646 			p++;
1647 			q = p;
1648 			while (*p != '\0' && !(isascii(*p) && isspace(*p)))
1649 				p++;
1650 			if (*p != '\0')
1651 				*p++ = '\0';
1652 			for (rfp = ResolverFlags; rfp->rf_name != NULL; rfp++)
1653 			{
1654 				if (strcasecmp(q, rfp->rf_name) == 0)
1655 					break;
1656 			}
1657 			if (rfp->rf_name == NULL)
1658 				syserr("readcf: I option value %s unrecognized", q);
1659 			else if (clearmode)
1660 				_res.options &= ~rfp->rf_bits;
1661 			else
1662 				_res.options |= rfp->rf_bits;
1663 		}
1664 		if (tTd(8, 2))
1665 			printf("_res.options = %x\n", _res.options);
1666 #else
1667 		usrerr("name server (I option) specified but BIND not compiled in");
1668 #endif
1669 		break;
1670 
1671 	  case 'i':		/* ignore dot lines in message */
1672 		IgnrDot = atobool(val);
1673 		break;
1674 
1675 	  case 'j':		/* send errors in MIME (RFC 1341) format */
1676 		SendMIMEErrors = atobool(val);
1677 		break;
1678 
1679 	  case 'J':		/* .forward search path */
1680 		ForwardPath = newstr(val);
1681 		break;
1682 
1683 	  case 'k':		/* connection cache size */
1684 		MaxMciCache = atoi(val);
1685 		if (MaxMciCache < 0)
1686 			MaxMciCache = 0;
1687 		break;
1688 
1689 	  case 'K':		/* connection cache timeout */
1690 		MciCacheTimeout = convtime(val, 'm');
1691 		break;
1692 
1693 	  case 'l':		/* use Errors-To: header */
1694 		UseErrorsTo = atobool(val);
1695 		break;
1696 
1697 	  case 'L':		/* log level */
1698 		if (safe || LogLevel < atoi(val))
1699 			LogLevel = atoi(val);
1700 		break;
1701 
1702 	  case 'M':		/* define macro */
1703 		p = newstr(&val[1]);
1704 		if (!safe)
1705 			cleanstrcpy(p, p, MAXNAME);
1706 		define(val[0], p, CurEnv);
1707 		sticky = FALSE;
1708 		break;
1709 
1710 	  case 'm':		/* send to me too */
1711 		MeToo = atobool(val);
1712 		break;
1713 
1714 	  case 'n':		/* validate RHS in newaliases */
1715 		CheckAliases = atobool(val);
1716 		break;
1717 
1718 	    /* 'N' available -- was "net name" */
1719 
1720 	  case 'O':		/* daemon options */
1721 		setdaemonoptions(val);
1722 		break;
1723 
1724 	  case 'o':		/* assume old style headers */
1725 		if (atobool(val))
1726 			CurEnv->e_flags |= EF_OLDSTYLE;
1727 		else
1728 			CurEnv->e_flags &= ~EF_OLDSTYLE;
1729 		break;
1730 
1731 	  case 'p':		/* select privacy level */
1732 		p = val;
1733 		for (;;)
1734 		{
1735 			register struct prival *pv;
1736 			extern struct prival PrivacyValues[];
1737 
1738 			while (isascii(*p) && (isspace(*p) || ispunct(*p)))
1739 				p++;
1740 			if (*p == '\0')
1741 				break;
1742 			val = p;
1743 			while (isascii(*p) && isalnum(*p))
1744 				p++;
1745 			if (*p != '\0')
1746 				*p++ = '\0';
1747 
1748 			for (pv = PrivacyValues; pv->pv_name != NULL; pv++)
1749 			{
1750 				if (strcasecmp(val, pv->pv_name) == 0)
1751 					break;
1752 			}
1753 			if (pv->pv_name == NULL)
1754 				syserr("readcf: Op line: %s unrecognized", val);
1755 			PrivacyFlags |= pv->pv_flag;
1756 		}
1757 		sticky = FALSE;
1758 		break;
1759 
1760 	  case 'P':		/* postmaster copy address for returned mail */
1761 		PostMasterCopy = newstr(val);
1762 		break;
1763 
1764 	  case 'q':		/* slope of queue only function */
1765 		QueueFactor = atoi(val);
1766 		break;
1767 
1768 	  case 'Q':		/* queue directory */
1769 		if (val[0] == '\0')
1770 			QueueDir = "mqueue";
1771 		else
1772 			QueueDir = newstr(val);
1773 		if (RealUid != 0 && !safe)
1774 			Warn_Q_option = TRUE;
1775 		break;
1776 
1777 	  case 'R':		/* don't prune routes */
1778 		DontPruneRoutes = atobool(val);
1779 		break;
1780 
1781 	  case 'r':		/* read timeout */
1782 		if (subopt == NULL)
1783 			inittimeouts(val);
1784 		else
1785 			settimeout(subopt, val);
1786 		break;
1787 
1788 	  case 'S':		/* status file */
1789 		if (val[0] == '\0')
1790 			StatFile = "sendmail.st";
1791 		else
1792 			StatFile = newstr(val);
1793 		break;
1794 
1795 	  case 's':		/* be super safe, even if expensive */
1796 		SuperSafe = atobool(val);
1797 		break;
1798 
1799 	  case 'T':		/* queue timeout */
1800 		p = strchr(val, '/');
1801 		if (p != NULL)
1802 		{
1803 			*p++ = '\0';
1804 			settimeout("queuewarn", p);
1805 		}
1806 		settimeout("queuereturn", val);
1807 		break;
1808 
1809 	  case 't':		/* time zone name */
1810 		TimeZoneSpec = newstr(val);
1811 		break;
1812 
1813 	  case 'U':		/* location of user database */
1814 		UdbSpec = newstr(val);
1815 		break;
1816 
1817 	  case 'u':		/* set default uid */
1818 		for (p = val; *p != '\0'; p++)
1819 		{
1820 			if (*p == '.' || *p == '/' || *p == ':')
1821 			{
1822 				*p++ = '\0';
1823 				break;
1824 			}
1825 		}
1826 		if (isascii(*val) && isdigit(*val))
1827 			DefUid = atoi(val);
1828 		else
1829 		{
1830 			register struct passwd *pw;
1831 
1832 			DefUid = -1;
1833 			pw = sm_getpwnam(val);
1834 			if (pw == NULL)
1835 				syserr("readcf: option u: unknown user %s", val);
1836 			else
1837 			{
1838 				DefUid = pw->pw_uid;
1839 				DefGid = pw->pw_gid;
1840 			}
1841 		}
1842 		setdefuser();
1843 
1844 		/* handle the group if it is there */
1845 		if (*p == '\0')
1846 			break;
1847 		val = p;
1848 		goto g_opt;
1849 
1850 	  case 'V':		/* fallback MX host */
1851 		FallBackMX = newstr(val);
1852 		break;
1853 
1854 	  case 'v':		/* run in verbose mode */
1855 		Verbose = atobool(val);
1856 		break;
1857 
1858 	  case 'w':		/* if we are best MX, try host directly */
1859 		TryNullMXList = atobool(val);
1860 		break;
1861 
1862 	    /* 'W' available -- was wizard password */
1863 
1864 	  case 'x':		/* load avg at which to auto-queue msgs */
1865 		QueueLA = atoi(val);
1866 		break;
1867 
1868 	  case 'X':		/* load avg at which to auto-reject connections */
1869 		RefuseLA = atoi(val);
1870 		break;
1871 
1872 	  case 'y':		/* work recipient factor */
1873 		WkRecipFact = atoi(val);
1874 		break;
1875 
1876 	  case 'Y':		/* fork jobs during queue runs */
1877 		ForkQueueRuns = atobool(val);
1878 		break;
1879 
1880 	  case 'z':		/* work message class factor */
1881 		WkClassFact = atoi(val);
1882 		break;
1883 
1884 	  case 'Z':		/* work time factor */
1885 		WkTimeFact = atoi(val);
1886 		break;
1887 
1888 	  case O_BSP:		/* SMTP Peers can't handle 2-line greeting */
1889 		BrokenSmtpPeers = atobool(val);
1890 		break;
1891 
1892 	  case O_QUEUESORTORD:	/* queue sorting order */
1893 		switch (*val)
1894 		{
1895 		  case 'h':	/* Host first */
1896 		  case 'H':
1897 			QueueSortOrder = QS_BYHOST;
1898 			break;
1899 
1900 		  case 'p':	/* Priority order */
1901 		  case 'P':
1902 			QueueSortOrder = QS_BYPRIORITY;
1903 			break;
1904 
1905 		  default:
1906 			syserr("Invalid queue sort order \"%s\"", val);
1907 		}
1908 		break;
1909 
1910 	  case O_MQA:		/* minimum queue age between deliveries */
1911 		MinQueueAge = convtime(val, 'm');
1912 		break;
1913 
1914 	  case O_MHSA:		/* maximum age of cached host status */
1915 		MaxHostStatAge = convtime(val, 'm');
1916 		break;
1917 
1918 	  case O_DEFCHARSET:	/* default character set for mimefying */
1919 		DefaultCharSet = newstr(denlstring(val, TRUE, TRUE));
1920 		break;
1921 
1922 	  case O_SSFILE:	/* service switch file */
1923 		ServiceSwitchFile = newstr(val);
1924 		break;
1925 
1926 	  case O_DIALDELAY:	/* delay for dial-on-demand operation */
1927 		DialDelay = convtime(val, 's');
1928 		break;
1929 
1930 	  case O_NORCPTACTION:	/* what to do if no recipient */
1931 		if (strcasecmp(val, "none") == 0)
1932 			NoRecipientAction = NRA_NO_ACTION;
1933 		else if (strcasecmp(val, "add-to") == 0)
1934 			NoRecipientAction = NRA_ADD_TO;
1935 		else if (strcasecmp(val, "add-apparently-to") == 0)
1936 			NoRecipientAction = NRA_ADD_APPARENTLY_TO;
1937 		else if (strcasecmp(val, "add-bcc") == 0)
1938 			NoRecipientAction = NRA_ADD_BCC;
1939 		else if (strcasecmp(val, "add-to-undisclosed") == 0)
1940 			NoRecipientAction = NRA_ADD_TO_UNDISCLOSED;
1941 		else
1942 			syserr("Invalid NoRecipientAction: %s", val);
1943 
1944 	  case O_SAFEFILEENV:	/* chroot() environ for writing to files */
1945 		SafeFileEnv = newstr(val);
1946 		break;
1947 
1948 	  case O_MAXMSGSIZE:	/* maximum message size */
1949 		MaxMessageSize = atol(p);
1950 		break;
1951 
1952 	  case O_COLONOKINADDR:	/* old style handling of colon addresses */
1953 		ColonOkInAddr = atobool(p);
1954 		break;
1955 
1956 	  default:
1957 		if (tTd(37, 1))
1958 		{
1959 			if (isascii(opt) && isprint(opt))
1960 				printf("Warning: option %c unknown\n", opt);
1961 			else
1962 				printf("Warning: option 0x%x unknown\n", opt);
1963 		}
1964 		break;
1965 	}
1966 	if (sticky)
1967 		setbitn(opt, StickyOpt);
1968 	return;
1969 }
1970 /*
1971 **  SETCLASS -- set a string into a class
1972 **
1973 **	Parameters:
1974 **		class -- the class to put the string in.
1975 **		str -- the string to enter
1976 **
1977 **	Returns:
1978 **		none.
1979 **
1980 **	Side Effects:
1981 **		puts the word into the symbol table.
1982 */
1983 
1984 setclass(class, str)
1985 	int class;
1986 	char *str;
1987 {
1988 	register STAB *s;
1989 
1990 	if (tTd(37, 8))
1991 		printf("setclass(%c, %s)\n", class, str);
1992 	s = stab(str, ST_CLASS, ST_ENTER);
1993 	setbitn(class, s->s_class);
1994 }
1995 /*
1996 **  MAKEMAPENTRY -- create a map entry
1997 **
1998 **	Parameters:
1999 **		line -- the config file line
2000 **
2001 **	Returns:
2002 **		TRUE if it successfully entered the map entry.
2003 **		FALSE otherwise (usually syntax error).
2004 **
2005 **	Side Effects:
2006 **		Enters the map into the dictionary.
2007 */
2008 
2009 void
2010 makemapentry(line)
2011 	char *line;
2012 {
2013 	register char *p;
2014 	char *mapname;
2015 	char *classname;
2016 	register STAB *s;
2017 	STAB *class;
2018 
2019 	for (p = line; isascii(*p) && isspace(*p); p++)
2020 		continue;
2021 	if (!(isascii(*p) && isalnum(*p)))
2022 	{
2023 		syserr("readcf: config K line: no map name");
2024 		return;
2025 	}
2026 
2027 	mapname = p;
2028 	while ((isascii(*++p) && isalnum(*p)) || *p == '.')
2029 		continue;
2030 	if (*p != '\0')
2031 		*p++ = '\0';
2032 	while (isascii(*p) && isspace(*p))
2033 		p++;
2034 	if (!(isascii(*p) && isalnum(*p)))
2035 	{
2036 		syserr("readcf: config K line, map %s: no map class", mapname);
2037 		return;
2038 	}
2039 	classname = p;
2040 	while (isascii(*++p) && isalnum(*p))
2041 		continue;
2042 	if (*p != '\0')
2043 		*p++ = '\0';
2044 	while (isascii(*p) && isspace(*p))
2045 		p++;
2046 
2047 	/* look up the class */
2048 	class = stab(classname, ST_MAPCLASS, ST_FIND);
2049 	if (class == NULL)
2050 	{
2051 		syserr("readcf: map %s: class %s not available", mapname, classname);
2052 		return;
2053 	}
2054 
2055 	/* enter the map */
2056 	s = stab(mapname, ST_MAP, ST_ENTER);
2057 	s->s_map.map_class = &class->s_mapclass;
2058 	s->s_map.map_mname = newstr(mapname);
2059 
2060 	if (class->s_mapclass.map_parse(&s->s_map, p))
2061 		s->s_map.map_mflags |= MF_VALID;
2062 
2063 	if (tTd(37, 5))
2064 	{
2065 		printf("map %s, class %s, flags %x, file %s,\n",
2066 			s->s_map.map_mname, s->s_map.map_class->map_cname,
2067 			s->s_map.map_mflags,
2068 			s->s_map.map_file == NULL ? "(null)" : s->s_map.map_file);
2069 		printf("\tapp %s, domain %s, rebuild %s\n",
2070 			s->s_map.map_app == NULL ? "(null)" : s->s_map.map_app,
2071 			s->s_map.map_domain == NULL ? "(null)" : s->s_map.map_domain,
2072 			s->s_map.map_rebuild == NULL ? "(null)" : s->s_map.map_rebuild);
2073 	}
2074 }
2075 /*
2076 **  INITTIMEOUTS -- parse and set timeout values
2077 **
2078 **	Parameters:
2079 **		val -- a pointer to the values.  If NULL, do initial
2080 **			settings.
2081 **
2082 **	Returns:
2083 **		none.
2084 **
2085 **	Side Effects:
2086 **		Initializes the TimeOuts structure
2087 */
2088 
2089 #define SECONDS
2090 #define MINUTES	* 60
2091 #define HOUR	* 3600
2092 
2093 inittimeouts(val)
2094 	register char *val;
2095 {
2096 	register char *p;
2097 	extern time_t convtime();
2098 
2099 	if (val == NULL)
2100 	{
2101 		TimeOuts.to_initial = (time_t) 5 MINUTES;
2102 		TimeOuts.to_helo = (time_t) 5 MINUTES;
2103 		TimeOuts.to_mail = (time_t) 10 MINUTES;
2104 		TimeOuts.to_rcpt = (time_t) 1 HOUR;
2105 		TimeOuts.to_datainit = (time_t) 5 MINUTES;
2106 		TimeOuts.to_datablock = (time_t) 1 HOUR;
2107 		TimeOuts.to_datafinal = (time_t) 1 HOUR;
2108 		TimeOuts.to_rset = (time_t) 5 MINUTES;
2109 		TimeOuts.to_quit = (time_t) 2 MINUTES;
2110 		TimeOuts.to_nextcommand = (time_t) 1 HOUR;
2111 		TimeOuts.to_miscshort = (time_t) 2 MINUTES;
2112 #if IDENTPROTO
2113 		TimeOuts.to_ident = (time_t) 30 SECONDS;
2114 #else
2115 		TimeOuts.to_ident = (time_t) 0 SECONDS;
2116 #endif
2117 		TimeOuts.to_fileopen = (time_t) 60 SECONDS;
2118 		return;
2119 	}
2120 
2121 	for (;; val = p)
2122 	{
2123 		while (isascii(*val) && isspace(*val))
2124 			val++;
2125 		if (*val == '\0')
2126 			break;
2127 		for (p = val; *p != '\0' && *p != ','; p++)
2128 			continue;
2129 		if (*p != '\0')
2130 			*p++ = '\0';
2131 
2132 		if (isascii(*val) && isdigit(*val))
2133 		{
2134 			/* old syntax -- set everything */
2135 			TimeOuts.to_mail = convtime(val, 'm');
2136 			TimeOuts.to_rcpt = TimeOuts.to_mail;
2137 			TimeOuts.to_datainit = TimeOuts.to_mail;
2138 			TimeOuts.to_datablock = TimeOuts.to_mail;
2139 			TimeOuts.to_datafinal = TimeOuts.to_mail;
2140 			TimeOuts.to_nextcommand = TimeOuts.to_mail;
2141 			continue;
2142 		}
2143 		else
2144 		{
2145 			register char *q = strchr(val, ':');
2146 
2147 			if (q == NULL && (q = strchr(val, '=')) == NULL)
2148 			{
2149 				/* syntax error */
2150 				continue;
2151 			}
2152 			*q++ = '\0';
2153 			settimeout(val, q);
2154 		}
2155 	}
2156 }
2157 /*
2158 **  SETTIMEOUT -- set an individual timeout
2159 **
2160 **	Parameters:
2161 **		name -- the name of the timeout.
2162 **		val -- the value of the timeout.
2163 **
2164 **	Returns:
2165 **		none.
2166 */
2167 
2168 settimeout(name, val)
2169 	char *name;
2170 	char *val;
2171 {
2172 	register char *p;
2173 	time_t to;
2174 	extern time_t convtime();
2175 
2176 	to = convtime(val, 'm');
2177 	p = strchr(name, '.');
2178 	if (p != NULL)
2179 		*p++ = '\0';
2180 
2181 	if (strcasecmp(name, "initial") == 0)
2182 		TimeOuts.to_initial = to;
2183 	else if (strcasecmp(name, "mail") == 0)
2184 		TimeOuts.to_mail = to;
2185 	else if (strcasecmp(name, "rcpt") == 0)
2186 		TimeOuts.to_rcpt = to;
2187 	else if (strcasecmp(name, "datainit") == 0)
2188 		TimeOuts.to_datainit = to;
2189 	else if (strcasecmp(name, "datablock") == 0)
2190 		TimeOuts.to_datablock = to;
2191 	else if (strcasecmp(name, "datafinal") == 0)
2192 		TimeOuts.to_datafinal = to;
2193 	else if (strcasecmp(name, "command") == 0)
2194 		TimeOuts.to_nextcommand = to;
2195 	else if (strcasecmp(name, "rset") == 0)
2196 		TimeOuts.to_rset = to;
2197 	else if (strcasecmp(name, "helo") == 0)
2198 		TimeOuts.to_helo = to;
2199 	else if (strcasecmp(name, "quit") == 0)
2200 		TimeOuts.to_quit = to;
2201 	else if (strcasecmp(name, "misc") == 0)
2202 		TimeOuts.to_miscshort = to;
2203 	else if (strcasecmp(name, "ident") == 0)
2204 		TimeOuts.to_ident = to;
2205 	else if (strcasecmp(name, "fileopen") == 0)
2206 		TimeOuts.to_fileopen = to;
2207 	else if (strcasecmp(name, "queuewarn") == 0)
2208 	{
2209 		to = convtime(val, 'h');
2210 		if (p == NULL || strcmp(p, "*") == 0)
2211 		{
2212 			TimeOuts.to_q_warning[TOC_NORMAL] = to;
2213 			TimeOuts.to_q_warning[TOC_URGENT] = to;
2214 			TimeOuts.to_q_warning[TOC_NONURGENT] = to;
2215 		}
2216 		else if (strcasecmp(p, "normal") == 0)
2217 			TimeOuts.to_q_warning[TOC_NORMAL] = to;
2218 		else if (strcasecmp(p, "urgent") == 0)
2219 			TimeOuts.to_q_warning[TOC_URGENT] = to;
2220 		else if (strcasecmp(p, "non-urgent") == 0)
2221 			TimeOuts.to_q_warning[TOC_NONURGENT] = to;
2222 		else
2223 			syserr("settimeout: invalid queuewarn subtimeout %s", p);
2224 	}
2225 	else if (strcasecmp(name, "queuereturn") == 0)
2226 	{
2227 		to = convtime(val, 'd');
2228 		if (p == NULL || strcmp(p, "*") == 0)
2229 		{
2230 			TimeOuts.to_q_return[TOC_NORMAL] = to;
2231 			TimeOuts.to_q_return[TOC_URGENT] = to;
2232 			TimeOuts.to_q_return[TOC_NONURGENT] = to;
2233 		}
2234 		else if (strcasecmp(p, "normal") == 0)
2235 			TimeOuts.to_q_return[TOC_NORMAL] = to;
2236 		else if (strcasecmp(p, "urgent") == 0)
2237 			TimeOuts.to_q_return[TOC_URGENT] = to;
2238 		else if (strcasecmp(p, "non-urgent") == 0)
2239 			TimeOuts.to_q_return[TOC_NONURGENT] = to;
2240 		else
2241 			syserr("settimeout: invalid queuereturn subtimeout %s", p);
2242 	}
2243 	else
2244 		syserr("settimeout: invalid timeout %s", name);
2245 }
2246