xref: /original-bsd/usr.sbin/sendmail/src/readcf.c (revision 3a3bea3b)
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.75 (Berkeley) 03/21/95";
11 #endif /* not lint */
12 
13 # include "sendmail.h"
14 # include <pwd.h>
15 # include <grp.h>
16 #if NAMED_BIND
17 # include <resolv.h>
18 #endif
19 
20 /*
21 **  READCF -- read control file.
22 **
23 **	This routine reads the control file and builds the internal
24 **	form.
25 **
26 **	The file is formatted as a sequence of lines, each taken
27 **	atomically.  The first character of each line describes how
28 **	the line is to be interpreted.  The lines are:
29 **		Dxval		Define macro x to have value val.
30 **		Cxword		Put word into class x.
31 **		Fxfile [fmt]	Read file for lines to put into
32 **				class x.  Use scanf string 'fmt'
33 **				or "%s" if not present.  Fmt should
34 **				only produce one string-valued result.
35 **		Hname: value	Define header with field-name 'name'
36 **				and value as specified; this will be
37 **				macro expanded immediately before
38 **				use.
39 **		Sn		Use rewriting set n.
40 **		Rlhs rhs	Rewrite addresses that match lhs to
41 **				be rhs.
42 **		Mn arg=val...	Define mailer.  n is the internal name.
43 **				Args specify mailer parameters.
44 **		Oxvalue		Set option x to value.
45 **		Pname=value	Set precedence name to value.
46 **		Vversioncode[/vendorcode]
47 **				Version level/vendor name of
48 **				configuration syntax.
49 **		Kmapname mapclass arguments....
50 **				Define keyed lookup of a given class.
51 **				Arguments are class dependent.
52 **
53 **	Parameters:
54 **		cfname -- control file name.
55 **		safe -- TRUE if this is the system config file;
56 **			FALSE otherwise.
57 **		e -- the main envelope.
58 **
59 **	Returns:
60 **		none.
61 **
62 **	Side Effects:
63 **		Builds several internal tables.
64 */
65 
66 readcf(cfname, safe, e)
67 	char *cfname;
68 	bool safe;
69 	register ENVELOPE *e;
70 {
71 	FILE *cf;
72 	int ruleset = 0;
73 	int nextruleset = MAXRWSETS;
74 	char *q;
75 	struct rewrite *rwp = NULL;
76 	char *bp;
77 	auto char *ep;
78 	int nfuzzy;
79 	char *file;
80 	bool optional;
81 	int mid;
82 	char buf[MAXLINE];
83 	register char *p;
84 	extern char **copyplist();
85 	struct stat statb;
86 	char exbuf[MAXLINE];
87 	char pvpbuf[MAXLINE + MAXATOM];
88 	static char *null_list[1] = { NULL };
89 	extern char *munchstring();
90 	extern void makemapentry();
91 
92 	FileName = cfname;
93 	LineNumber = 0;
94 
95 	cf = fopen(cfname, "r");
96 	if (cf == NULL)
97 	{
98 		syserr("cannot open");
99 		exit(EX_OSFILE);
100 	}
101 
102 	if (fstat(fileno(cf), &statb) < 0)
103 	{
104 		syserr("cannot fstat");
105 		exit(EX_OSFILE);
106 	}
107 
108 	if (!S_ISREG(statb.st_mode))
109 	{
110 		syserr("not a plain file");
111 		exit(EX_OSFILE);
112 	}
113 
114 	if (OpMode != MD_TEST && bitset(S_IWGRP|S_IWOTH, statb.st_mode))
115 	{
116 		if (OpMode == MD_DAEMON || OpMode == MD_FREEZE)
117 			fprintf(stderr, "%s: WARNING: dangerous write permissions\n",
118 				FileName);
119 #ifdef LOG
120 		if (LogLevel > 0)
121 			syslog(LOG_CRIT, "%s: WARNING: dangerous write permissions",
122 				FileName);
123 #endif
124 	}
125 
126 #ifdef XLA
127 	xla_zero();
128 #endif
129 
130 	while ((bp = fgetfolded(buf, sizeof buf, cf)) != NULL)
131 	{
132 		if (bp[0] == '#')
133 		{
134 			if (bp != buf)
135 				free(bp);
136 			continue;
137 		}
138 
139 		/* do macro expansion mappings */
140 		for (p = bp; *p != '\0'; p++)
141 		{
142 			if (*p == '#' && p > bp && ConfigLevel >= 3)
143 			{
144 				/* this is an on-line comment */
145 				register char *e;
146 
147 				switch (*--p & 0377)
148 				{
149 				  case MACROEXPAND:
150 					/* it's from $# -- let it go through */
151 					p++;
152 					break;
153 
154 				  case '\\':
155 					/* it's backslash escaped */
156 					(void) strcpy(p, p + 1);
157 					break;
158 
159 				  default:
160 					/* delete preceeding white space */
161 					while (isascii(*p) && isspace(*p) && p > bp)
162 						p--;
163 					if ((e = strchr(++p, '\n')) != NULL)
164 						(void) strcpy(p, e);
165 					else
166 						p[0] = p[1] = '\0';
167 					break;
168 				}
169 				continue;
170 			}
171 
172 			if (*p != '$' || p[1] == '\0')
173 				continue;
174 
175 			if (p[1] == '$')
176 			{
177 				/* actual dollar sign.... */
178 				(void) strcpy(p, p + 1);
179 				continue;
180 			}
181 
182 			/* convert to macro expansion character */
183 			*p++ = MACROEXPAND;
184 
185 			/* convert macro name to code */
186 			*p = macid(p, &ep);
187 			if (ep != p)
188 				strcpy(p + 1, ep);
189 		}
190 
191 		/* interpret this line */
192 		errno = 0;
193 		switch (bp[0])
194 		{
195 		  case '\0':
196 		  case '#':		/* comment */
197 			break;
198 
199 		  case 'R':		/* rewriting rule */
200 			for (p = &bp[1]; *p != '\0' && *p != '\t'; p++)
201 				continue;
202 
203 			if (*p == '\0')
204 			{
205 				syserr("invalid rewrite line \"%s\" (tab expected)", bp);
206 				break;
207 			}
208 
209 			/* allocate space for the rule header */
210 			if (rwp == NULL)
211 			{
212 				RewriteRules[ruleset] = rwp =
213 					(struct rewrite *) xalloc(sizeof *rwp);
214 			}
215 			else
216 			{
217 				rwp->r_next = (struct rewrite *) xalloc(sizeof *rwp);
218 				rwp = rwp->r_next;
219 			}
220 			rwp->r_next = NULL;
221 
222 			/* expand and save the LHS */
223 			*p = '\0';
224 			expand(&bp[1], exbuf, sizeof exbuf, e);
225 			rwp->r_lhs = prescan(exbuf, '\t', pvpbuf,
226 					     sizeof pvpbuf, NULL);
227 			nfuzzy = 0;
228 			if (rwp->r_lhs != NULL)
229 			{
230 				register char **ap;
231 
232 				rwp->r_lhs = copyplist(rwp->r_lhs, TRUE);
233 
234 				/* count the number of fuzzy matches in LHS */
235 				for (ap = rwp->r_lhs; *ap != NULL; ap++)
236 				{
237 					char *botch;
238 
239 					botch = NULL;
240 					switch (**ap & 0377)
241 					{
242 					  case MATCHZANY:
243 					  case MATCHANY:
244 					  case MATCHONE:
245 					  case MATCHCLASS:
246 					  case MATCHNCLASS:
247 						nfuzzy++;
248 						break;
249 
250 					  case MATCHREPL:
251 						botch = "$0-$9";
252 						break;
253 
254 					  case CANONNET:
255 						botch = "$#";
256 						break;
257 
258 					  case CANONUSER:
259 						botch = "$:";
260 						break;
261 
262 					  case CALLSUBR:
263 						botch = "$>";
264 						break;
265 
266 					  case CONDIF:
267 						botch = "$?";
268 						break;
269 
270 					  case CONDELSE:
271 						botch = "$|";
272 						break;
273 
274 					  case CONDFI:
275 						botch = "$.";
276 						break;
277 
278 					  case HOSTBEGIN:
279 						botch = "$[";
280 						break;
281 
282 					  case HOSTEND:
283 						botch = "$]";
284 						break;
285 
286 					  case LOOKUPBEGIN:
287 						botch = "$(";
288 						break;
289 
290 					  case LOOKUPEND:
291 						botch = "$)";
292 						break;
293 					}
294 					if (botch != NULL)
295 						syserr("Inappropriate use of %s on LHS",
296 							botch);
297 				}
298 			}
299 			else
300 			{
301 				syserr("R line: null LHS");
302 				rwp->r_lhs = null_list;
303 			}
304 
305 			/* expand and save the RHS */
306 			while (*++p == '\t')
307 				continue;
308 			q = p;
309 			while (*p != '\0' && *p != '\t')
310 				p++;
311 			*p = '\0';
312 			expand(q, exbuf, sizeof exbuf, e);
313 			rwp->r_rhs = prescan(exbuf, '\t', pvpbuf,
314 					     sizeof pvpbuf, NULL);
315 			if (rwp->r_rhs != NULL)
316 			{
317 				register char **ap;
318 
319 				rwp->r_rhs = copyplist(rwp->r_rhs, TRUE);
320 
321 				/* check no out-of-bounds replacements */
322 				nfuzzy += '0';
323 				for (ap = rwp->r_rhs; *ap != NULL; ap++)
324 				{
325 					char *botch;
326 
327 					botch = NULL;
328 					switch (**ap & 0377)
329 					{
330 					  case MATCHREPL:
331 						if ((*ap)[1] <= '0' || (*ap)[1] > nfuzzy)
332 						{
333 							syserr("replacement $%c out of bounds",
334 								(*ap)[1]);
335 						}
336 						break;
337 
338 					  case MATCHZANY:
339 						botch = "$*";
340 						break;
341 
342 					  case MATCHANY:
343 						botch = "$+";
344 						break;
345 
346 					  case MATCHONE:
347 						botch = "$-";
348 						break;
349 
350 					  case MATCHCLASS:
351 						botch = "$=";
352 						break;
353 
354 					  case MATCHNCLASS:
355 						botch = "$~";
356 						break;
357 					}
358 					if (botch != NULL)
359 						syserr("Inappropriate use of %s on RHS",
360 							botch);
361 				}
362 			}
363 			else
364 			{
365 				syserr("R line: null RHS");
366 				rwp->r_rhs = null_list;
367 			}
368 			break;
369 
370 		  case 'S':		/* select rewriting set */
371 			for (p = &bp[1]; isascii(*p) && isspace(*p); p++)
372 				continue;
373 			if (!isascii(*p))
374 			{
375 				syserr("invalid argument to S line: \"%.20s\"",
376 					&bp[1]);
377 				break;
378 			}
379 			if (isdigit(*p))
380 			{
381 				ruleset = atoi(p);
382 				if (ruleset >= MAXRWSETS / 2 || ruleset < 0)
383 				{
384 					syserr("bad ruleset %d (%d max)",
385 						ruleset, MAXRWSETS / 2);
386 					ruleset = 0;
387 				}
388 			}
389 			else
390 			{
391 				STAB *s;
392 				char delim;
393 
394 				q = p;
395 				while (*p != '\0' && isascii(*p) &&
396 				       (isalnum(*p) || strchr("-_$", *p) != NULL))
397 					p++;
398 				while (isascii(*p) && isspace(*p))
399 					*p++ = '\0';
400 				delim = *p;
401 				if (delim != '\0')
402 					*p++ = '\0';
403 				s = stab(q, ST_RULESET, ST_ENTER);
404 				if (s->s_ruleset != 0)
405 					ruleset = s->s_ruleset;
406 				else if (delim == '=')
407 				{
408 					ruleset = atoi(p);
409 					if (ruleset >= MAXRWSETS / 2 || ruleset < 0)
410 					{
411 						syserr("bad ruleset %s = %d (%d max)",
412 							q, ruleset, MAXRWSETS / 2);
413 						ruleset = 0;
414 					}
415 				}
416 				else if ((ruleset = --nextruleset) < MAXRWSETS / 2)
417 				{
418 					syserr("%s: too many named rulesets (%d max)",
419 						q, MAXRWSETS / 2);
420 					ruleset = 0;
421 				}
422 				s->s_ruleset = ruleset;
423 			}
424 			rwp = NULL;
425 			break;
426 
427 		  case 'D':		/* macro definition */
428 			mid = macid(&bp[1], &ep);
429 			p = munchstring(ep, NULL);
430 			define(mid, newstr(p), e);
431 			break;
432 
433 		  case 'H':		/* required header line */
434 			(void) chompheader(&bp[1], TRUE, e);
435 			break;
436 
437 		  case 'C':		/* word class */
438 		  case 'T':		/* trusted user (set class `t') */
439 			if (bp[0] == 'C')
440 			{
441 				mid = macid(&bp[1], &ep);
442 				expand(ep, exbuf, sizeof exbuf, e);
443 				p = exbuf;
444 			}
445 			else
446 			{
447 				mid = 't';
448 				p = &bp[1];
449 			}
450 			while (*p != '\0')
451 			{
452 				register char *wd;
453 				char delim;
454 
455 				while (*p != '\0' && isascii(*p) && isspace(*p))
456 					p++;
457 				wd = p;
458 				while (*p != '\0' && !(isascii(*p) && isspace(*p)))
459 					p++;
460 				delim = *p;
461 				*p = '\0';
462 				if (wd[0] != '\0')
463 					setclass(mid, wd);
464 				*p = delim;
465 			}
466 			break;
467 
468 		  case 'F':		/* word class from file */
469 			mid = macid(&bp[1], &ep);
470 			for (p = ep; isascii(*p) && isspace(*p); )
471 				p++;
472 			if (p[0] == '-' && p[1] == 'o')
473 			{
474 				optional = TRUE;
475 				while (*p != '\0' && !(isascii(*p) && isspace(*p)))
476 					p++;
477 				while (isascii(*p) && isspace(*p))
478 					p++;
479 			}
480 			else
481 				optional = FALSE;
482 			file = p;
483 			while (*p != '\0' && !(isascii(*p) && isspace(*p)))
484 				p++;
485 			if (*p == '\0')
486 				p = "%s";
487 			else
488 			{
489 				*p = '\0';
490 				while (isascii(*++p) && isspace(*p))
491 					continue;
492 			}
493 			fileclass(bp[1], file, p, safe, optional);
494 			break;
495 
496 #ifdef XLA
497 		  case 'L':		/* extended load average description */
498 			xla_init(&bp[1]);
499 			break;
500 #endif
501 
502 		  case 'M':		/* define mailer */
503 			makemailer(&bp[1]);
504 			break;
505 
506 		  case 'O':		/* set option */
507 			setoption(bp[1], &bp[2], safe, FALSE, e);
508 			break;
509 
510 		  case 'P':		/* set precedence */
511 			if (NumPriorities >= MAXPRIORITIES)
512 			{
513 				toomany('P', MAXPRIORITIES);
514 				break;
515 			}
516 			for (p = &bp[1]; *p != '\0' && *p != '=' && *p != '\t'; p++)
517 				continue;
518 			if (*p == '\0')
519 				goto badline;
520 			*p = '\0';
521 			Priorities[NumPriorities].pri_name = newstr(&bp[1]);
522 			Priorities[NumPriorities].pri_val = atoi(++p);
523 			NumPriorities++;
524 			break;
525 
526 		  case 'V':		/* configuration syntax version */
527 			for (p = &bp[1]; isascii(*p) && isspace(*p); p++)
528 				continue;
529 			if (!isascii(*p) || !isdigit(*p))
530 			{
531 				syserr("invalid argument to V line: \"%.20s\"",
532 					&bp[1]);
533 				break;
534 			}
535 			ConfigLevel = strtol(p, &ep, 10);
536 			if (ConfigLevel >= 5)
537 			{
538 				/* level 5 configs have short name in $w */
539 				p = macvalue('w', e);
540 				if (p != NULL && (p = strchr(p, '.')) != NULL)
541 					*p = '\0';
542 			}
543 			if (*ep++ == '/')
544 			{
545 				/* extract vendor code */
546 				for (p = ep; isascii(*p) && isalpha(*p); )
547 					p++;
548 				*p = '\0';
549 
550 				if (!setvendor(ep))
551 					syserr("invalid V line vendor code: \"%s\"",
552 						ep);
553 			}
554 			break;
555 
556 		  case 'K':
557 			makemapentry(&bp[1]);
558 			break;
559 
560 		  default:
561 		  badline:
562 			syserr("unknown control line \"%s\"", bp);
563 		}
564 		if (bp != buf)
565 			free(bp);
566 	}
567 	if (ferror(cf))
568 	{
569 		syserr("I/O read error", cfname);
570 		exit(EX_OSFILE);
571 	}
572 	fclose(cf);
573 	FileName = NULL;
574 
575 	/* initialize host maps from local service tables */
576 	inithostmaps();
577 
578 	/* determine if we need to do special name-server frotz */
579 	{
580 		int nmaps;
581 		char *maptype[MAXMAPSTACK];
582 		short mapreturn[MAXMAPACTIONS];
583 
584 		nmaps = switch_map_find("hosts", maptype, mapreturn);
585 		UseNameServer = FALSE;
586 		if (nmaps > 0 && nmaps <= MAXMAPSTACK)
587 		{
588 			register int mapno;
589 
590 			for (mapno = 0; mapno < nmaps && !UseNameServer; mapno++)
591 			{
592 				if (strcmp(maptype[mapno], "dns") == 0)
593 					UseNameServer = TRUE;
594 			}
595 		}
596 
597 #ifdef HESIOD
598 		nmaps = switch_map_find("passwd", maptype, mapreturn);
599 		UseHesiod = FALSE;
600 		if (nmaps > 0 && nmaps <= MAXMAPSTACK)
601 		{
602 			register int mapno;
603 
604 			for (mapno = 0; mapno < nmaps && !UseHesiod; mapno++)
605 			{
606 				if (strcmp(maptype[mapno], "hesiod") == 0)
607 					UseHesiod = TRUE;
608 			}
609 		}
610 #endif
611 	}
612 }
613 /*
614 **  TOOMANY -- signal too many of some option
615 **
616 **	Parameters:
617 **		id -- the id of the error line
618 **		maxcnt -- the maximum possible values
619 **
620 **	Returns:
621 **		none.
622 **
623 **	Side Effects:
624 **		gives a syserr.
625 */
626 
627 toomany(id, maxcnt)
628 	char id;
629 	int maxcnt;
630 {
631 	syserr("too many %c lines, %d max", id, maxcnt);
632 }
633 /*
634 **  FILECLASS -- read members of a class from a file
635 **
636 **	Parameters:
637 **		class -- class to define.
638 **		filename -- name of file to read.
639 **		fmt -- scanf string to use for match.
640 **		safe -- if set, this is a safe read.
641 **		optional -- if set, it is not an error for the file to
642 **			not exist.
643 **
644 **	Returns:
645 **		none
646 **
647 **	Side Effects:
648 **
649 **		puts all lines in filename that match a scanf into
650 **			the named class.
651 */
652 
653 fileclass(class, filename, fmt, safe, optional)
654 	int class;
655 	char *filename;
656 	char *fmt;
657 	bool safe;
658 	bool optional;
659 {
660 	FILE *f;
661 	int sff;
662 	char buf[MAXLINE];
663 
664 	if (tTd(37, 2))
665 		printf("fileclass(%s, fmt=%s)\n", filename, fmt);
666 
667 	if (filename[0] == '|')
668 	{
669 		syserr("fileclass: pipes (F%c%s) not supported due to security problems",
670 			class, filename);
671 		return;
672 	}
673 	sff = SFF_REGONLY;
674 	if (safe)
675 		sff |= SFF_OPENASROOT;
676 	f = safefopen(filename, O_RDONLY, 0, sff);
677 	if (f == NULL && !optional)
678 	{
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 = 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 
1317 	NULL,			'\0',		FALSE,
1318 };
1319 
1320 
1321 
1322 setoption(opt, val, safe, sticky, e)
1323 	u_char opt;
1324 	char *val;
1325 	bool safe;
1326 	bool sticky;
1327 	register ENVELOPE *e;
1328 {
1329 	register char *p;
1330 	register struct optioninfo *o;
1331 	char *subopt;
1332 	extern bool atobool();
1333 	extern time_t convtime();
1334 	extern int QueueLA;
1335 	extern int RefuseLA;
1336 	extern bool Warn_Q_option;
1337 
1338 	errno = 0;
1339 	if (opt == ' ')
1340 	{
1341 		/* full word options */
1342 		struct optioninfo *sel;
1343 
1344 		p = strchr(val, '=');
1345 		if (p == NULL)
1346 			p = &val[strlen(val)];
1347 		while (*--p == ' ')
1348 			continue;
1349 		while (*++p == ' ')
1350 			*p = '\0';
1351 		if (p == val)
1352 		{
1353 			syserr("readcf: null option name");
1354 			return;
1355 		}
1356 		if (*p == '=')
1357 			*p++ = '\0';
1358 		while (*p == ' ')
1359 			p++;
1360 		subopt = strchr(val, '.');
1361 		if (subopt != NULL)
1362 			*subopt++ = '\0';
1363 		sel = NULL;
1364 		for (o = OptionTab; o->o_name != NULL; o++)
1365 		{
1366 			if (strncasecmp(o->o_name, val, strlen(val)) != 0)
1367 				continue;
1368 			if (strlen(o->o_name) == strlen(val))
1369 			{
1370 				/* completely specified -- this must be it */
1371 				sel = NULL;
1372 				break;
1373 			}
1374 			if (sel != NULL)
1375 				break;
1376 			sel = o;
1377 		}
1378 		if (sel != NULL && o->o_name == NULL)
1379 			o = sel;
1380 		else if (o->o_name == NULL)
1381 		{
1382 			syserr("readcf: unknown option name %s", val);
1383 			return;
1384 		}
1385 		else if (sel != NULL)
1386 		{
1387 			syserr("readcf: ambiguous option name %s (matches %s and %s)",
1388 				val, sel->o_name, o->o_name);
1389 			return;
1390 		}
1391 		if (strlen(val) != strlen(o->o_name))
1392 		{
1393 			bool oldVerbose = Verbose;
1394 
1395 			Verbose = TRUE;
1396 			message("Option %s used as abbreviation for %s",
1397 				val, o->o_name);
1398 			Verbose = oldVerbose;
1399 		}
1400 		opt = o->o_code;
1401 		val = p;
1402 	}
1403 	else
1404 	{
1405 		for (o = OptionTab; o->o_name != NULL; o++)
1406 		{
1407 			if (o->o_code == opt)
1408 				break;
1409 		}
1410 		subopt = NULL;
1411 	}
1412 
1413 	if (tTd(37, 1))
1414 	{
1415 		printf(isascii(opt) && isprint(opt) ?
1416 			    "setoption %s (%c).%s=%s" :
1417 			    "setoption %s (0x%x).%s=%s",
1418 			o->o_name == NULL ? "<unknown>" : o->o_name,
1419 			opt,
1420 			subopt == NULL ? "" : subopt,
1421 			val);
1422 	}
1423 
1424 	/*
1425 	**  See if this option is preset for us.
1426 	*/
1427 
1428 	if (!sticky && bitnset(opt, StickyOpt))
1429 	{
1430 		if (tTd(37, 1))
1431 			printf(" (ignored)\n");
1432 		return;
1433 	}
1434 
1435 	/*
1436 	**  Check to see if this option can be specified by this user.
1437 	*/
1438 
1439 	if (!safe && RealUid == 0)
1440 		safe = TRUE;
1441 	if (!safe && !o->o_safe)
1442 	{
1443 		if (opt != 'M' || (val[0] != 'r' && val[0] != 's'))
1444 		{
1445 			if (tTd(37, 1))
1446 				printf(" (unsafe)");
1447 			if (RealUid != geteuid())
1448 			{
1449 				if (tTd(37, 1))
1450 					printf("(Resetting uid)");
1451 				(void) setgid(RealGid);
1452 				(void) setuid(RealUid);
1453 			}
1454 		}
1455 	}
1456 	if (tTd(37, 1))
1457 		printf("\n");
1458 
1459 	switch (opt & 0xff)
1460 	{
1461 	  case '7':		/* force seven-bit input */
1462 		SevenBitInput = atobool(val);
1463 		break;
1464 
1465 	  case '8':		/* handling of 8-bit input */
1466 		switch (*val)
1467 		{
1468 		  case 'r':		/* reject 8-bit, don't convert MIME */
1469 			MimeMode = 0;
1470 			break;
1471 
1472 		  case 'm':		/* convert 8-bit, convert MIME */
1473 			MimeMode = MM_CVTMIME|MM_MIME8BIT;
1474 			break;
1475 
1476 		  case 'j':		/* "just send 8" */
1477 			MimeMode = MM_PASS8BIT;
1478 			break;
1479 
1480 		  case 'p':		/* pass 8 bit, convert MIME */
1481 			MimeMode = MM_PASS8BIT|MM_CVTMIME;
1482 			break;
1483 
1484 		  case 's':		/* strict adherence */
1485 			MimeMode = MM_CVTMIME;
1486 			break;
1487 
1488 		  case 'a':		/* encode 8 bit if available */
1489 			MimeMode = MM_MIME8BIT|MM_PASS8BIT|MM_CVTMIME;
1490 			break;
1491 
1492 		  case 'c':		/* convert 8 bit to MIME, never 7 bit */
1493 			MimeMode = MM_MIME8BIT;
1494 			break;
1495 
1496 		  default:
1497 			syserr("Unknown 8-bit mode %c", *val);
1498 			exit(EX_USAGE);
1499 		}
1500 		break;
1501 
1502 	  case 'A':		/* set default alias file */
1503 		if (val[0] == '\0')
1504 			setalias("aliases");
1505 		else
1506 			setalias(val);
1507 		break;
1508 
1509 	  case 'a':		/* look N minutes for "@:@" in alias file */
1510 		if (val[0] == '\0')
1511 			SafeAlias = 5 * 60;		/* five minutes */
1512 		else
1513 			SafeAlias = convtime(val, 'm');
1514 		break;
1515 
1516 	  case 'B':		/* substitution for blank character */
1517 		SpaceSub = val[0];
1518 		if (SpaceSub == '\0')
1519 			SpaceSub = ' ';
1520 		break;
1521 
1522 	  case 'b':		/* min blocks free on queue fs/max msg size */
1523 		p = strchr(val, '/');
1524 		if (p != NULL)
1525 		{
1526 			*p++ = '\0';
1527 			MaxMessageSize = atol(p);
1528 		}
1529 		MinBlocksFree = atol(val);
1530 		break;
1531 
1532 	  case 'c':		/* don't connect to "expensive" mailers */
1533 		NoConnect = atobool(val);
1534 		break;
1535 
1536 	  case 'C':		/* checkpoint every N addresses */
1537 		CheckpointInterval = atoi(val);
1538 		break;
1539 
1540 	  case 'd':		/* delivery mode */
1541 		switch (*val)
1542 		{
1543 		  case '\0':
1544 			e->e_sendmode = SM_DELIVER;
1545 			break;
1546 
1547 		  case SM_QUEUE:	/* queue only */
1548 #ifndef QUEUE
1549 			syserr("need QUEUE to set -odqueue");
1550 #endif /* QUEUE */
1551 			/* fall through..... */
1552 
1553 		  case SM_DELIVER:	/* do everything */
1554 		  case SM_FORK:		/* fork after verification */
1555 			e->e_sendmode = *val;
1556 			break;
1557 
1558 		  default:
1559 			syserr("Unknown delivery mode %c", *val);
1560 			exit(EX_USAGE);
1561 		}
1562 		break;
1563 
1564 	  case 'D':		/* rebuild alias database as needed */
1565 		AutoRebuild = atobool(val);
1566 		break;
1567 
1568 	  case 'E':		/* error message header/header file */
1569 		if (*val != '\0')
1570 			ErrMsgFile = newstr(val);
1571 		break;
1572 
1573 	  case 'e':		/* set error processing mode */
1574 		switch (*val)
1575 		{
1576 		  case EM_QUIET:	/* be silent about it */
1577 		  case EM_MAIL:		/* mail back */
1578 		  case EM_BERKNET:	/* do berknet error processing */
1579 		  case EM_WRITE:	/* write back (or mail) */
1580 		  case EM_PRINT:	/* print errors normally (default) */
1581 			e->e_errormode = *val;
1582 			break;
1583 		}
1584 		break;
1585 
1586 	  case 'F':		/* file mode */
1587 		FileMode = atooct(val) & 0777;
1588 		break;
1589 
1590 	  case 'f':		/* save Unix-style From lines on front */
1591 		SaveFrom = atobool(val);
1592 		break;
1593 
1594 	  case 'G':		/* match recipients against GECOS field */
1595 		MatchGecos = atobool(val);
1596 		break;
1597 
1598 	  case 'g':		/* default gid */
1599   g_opt:
1600 		if (isascii(*val) && isdigit(*val))
1601 			DefGid = atoi(val);
1602 		else
1603 		{
1604 			register struct group *gr;
1605 
1606 			DefGid = -1;
1607 			gr = getgrnam(val);
1608 			if (gr == NULL)
1609 				syserr("readcf: option %c: unknown group %s",
1610 					opt, val);
1611 			else
1612 				DefGid = gr->gr_gid;
1613 		}
1614 		break;
1615 
1616 	  case 'H':		/* help file */
1617 		if (val[0] == '\0')
1618 			HelpFile = "sendmail.hf";
1619 		else
1620 			HelpFile = newstr(val);
1621 		break;
1622 
1623 	  case 'h':		/* maximum hop count */
1624 		MaxHopCount = atoi(val);
1625 		break;
1626 
1627 	  case 'I':		/* use internet domain name server */
1628 #if NAMED_BIND
1629 		for (p = val; *p != 0; )
1630 		{
1631 			bool clearmode;
1632 			char *q;
1633 			struct resolverflags *rfp;
1634 
1635 			while (*p == ' ')
1636 				p++;
1637 			if (*p == '\0')
1638 				break;
1639 			clearmode = FALSE;
1640 			if (*p == '-')
1641 				clearmode = TRUE;
1642 			else if (*p != '+')
1643 				p--;
1644 			p++;
1645 			q = p;
1646 			while (*p != '\0' && !(isascii(*p) && isspace(*p)))
1647 				p++;
1648 			if (*p != '\0')
1649 				*p++ = '\0';
1650 			for (rfp = ResolverFlags; rfp->rf_name != NULL; rfp++)
1651 			{
1652 				if (strcasecmp(q, rfp->rf_name) == 0)
1653 					break;
1654 			}
1655 			if (rfp->rf_name == NULL)
1656 				syserr("readcf: I option value %s unrecognized", q);
1657 			else if (clearmode)
1658 				_res.options &= ~rfp->rf_bits;
1659 			else
1660 				_res.options |= rfp->rf_bits;
1661 		}
1662 		if (tTd(8, 2))
1663 			printf("_res.options = %x\n", _res.options);
1664 #else
1665 		usrerr("name server (I option) specified but BIND not compiled in");
1666 #endif
1667 		break;
1668 
1669 	  case 'i':		/* ignore dot lines in message */
1670 		IgnrDot = atobool(val);
1671 		break;
1672 
1673 	  case 'j':		/* send errors in MIME (RFC 1341) format */
1674 		SendMIMEErrors = atobool(val);
1675 		break;
1676 
1677 	  case 'J':		/* .forward search path */
1678 		ForwardPath = newstr(val);
1679 		break;
1680 
1681 	  case 'k':		/* connection cache size */
1682 		MaxMciCache = atoi(val);
1683 		if (MaxMciCache < 0)
1684 			MaxMciCache = 0;
1685 		break;
1686 
1687 	  case 'K':		/* connection cache timeout */
1688 		MciCacheTimeout = convtime(val, 'm');
1689 		break;
1690 
1691 	  case 'l':		/* use Errors-To: header */
1692 		UseErrorsTo = atobool(val);
1693 		break;
1694 
1695 	  case 'L':		/* log level */
1696 		if (safe || LogLevel < atoi(val))
1697 			LogLevel = atoi(val);
1698 		break;
1699 
1700 	  case 'M':		/* define macro */
1701 		p = newstr(&val[1]);
1702 		if (!safe)
1703 			cleanstrcpy(p, p, MAXNAME);
1704 		define(val[0], p, CurEnv);
1705 		sticky = FALSE;
1706 		break;
1707 
1708 	  case 'm':		/* send to me too */
1709 		MeToo = atobool(val);
1710 		break;
1711 
1712 	  case 'n':		/* validate RHS in newaliases */
1713 		CheckAliases = atobool(val);
1714 		break;
1715 
1716 	    /* 'N' available -- was "net name" */
1717 
1718 	  case 'O':		/* daemon options */
1719 		setdaemonoptions(val);
1720 		break;
1721 
1722 	  case 'o':		/* assume old style headers */
1723 		if (atobool(val))
1724 			CurEnv->e_flags |= EF_OLDSTYLE;
1725 		else
1726 			CurEnv->e_flags &= ~EF_OLDSTYLE;
1727 		break;
1728 
1729 	  case 'p':		/* select privacy level */
1730 		p = val;
1731 		for (;;)
1732 		{
1733 			register struct prival *pv;
1734 			extern struct prival PrivacyValues[];
1735 
1736 			while (isascii(*p) && (isspace(*p) || ispunct(*p)))
1737 				p++;
1738 			if (*p == '\0')
1739 				break;
1740 			val = p;
1741 			while (isascii(*p) && isalnum(*p))
1742 				p++;
1743 			if (*p != '\0')
1744 				*p++ = '\0';
1745 
1746 			for (pv = PrivacyValues; pv->pv_name != NULL; pv++)
1747 			{
1748 				if (strcasecmp(val, pv->pv_name) == 0)
1749 					break;
1750 			}
1751 			if (pv->pv_name == NULL)
1752 				syserr("readcf: Op line: %s unrecognized", val);
1753 			PrivacyFlags |= pv->pv_flag;
1754 		}
1755 		sticky = FALSE;
1756 		break;
1757 
1758 	  case 'P':		/* postmaster copy address for returned mail */
1759 		PostMasterCopy = newstr(val);
1760 		break;
1761 
1762 	  case 'q':		/* slope of queue only function */
1763 		QueueFactor = atoi(val);
1764 		break;
1765 
1766 	  case 'Q':		/* queue directory */
1767 		if (val[0] == '\0')
1768 			QueueDir = "mqueue";
1769 		else
1770 			QueueDir = newstr(val);
1771 		if (RealUid != 0 && !safe)
1772 			Warn_Q_option = TRUE;
1773 		break;
1774 
1775 	  case 'R':		/* don't prune routes */
1776 		DontPruneRoutes = atobool(val);
1777 		break;
1778 
1779 	  case 'r':		/* read timeout */
1780 		if (subopt == NULL)
1781 			inittimeouts(val);
1782 		else
1783 			settimeout(subopt, val);
1784 		break;
1785 
1786 	  case 'S':		/* status file */
1787 		if (val[0] == '\0')
1788 			StatFile = "sendmail.st";
1789 		else
1790 			StatFile = newstr(val);
1791 		break;
1792 
1793 	  case 's':		/* be super safe, even if expensive */
1794 		SuperSafe = atobool(val);
1795 		break;
1796 
1797 	  case 'T':		/* queue timeout */
1798 		p = strchr(val, '/');
1799 		if (p != NULL)
1800 		{
1801 			*p++ = '\0';
1802 			settimeout("queuewarn", p);
1803 		}
1804 		settimeout("queuereturn", val);
1805 		break;
1806 
1807 	  case 't':		/* time zone name */
1808 		TimeZoneSpec = newstr(val);
1809 		break;
1810 
1811 	  case 'U':		/* location of user database */
1812 		UdbSpec = newstr(val);
1813 		break;
1814 
1815 	  case 'u':		/* set default uid */
1816 		for (p = val; *p != '\0'; p++)
1817 		{
1818 			if (*p == '.' || *p == '/' || *p == ':')
1819 			{
1820 				*p++ = '\0';
1821 				break;
1822 			}
1823 		}
1824 		if (isascii(*val) && isdigit(*val))
1825 			DefUid = atoi(val);
1826 		else
1827 		{
1828 			register struct passwd *pw;
1829 
1830 			DefUid = -1;
1831 			pw = getpwnam(val);
1832 			if (pw == NULL)
1833 				syserr("readcf: option u: unknown user %s", val);
1834 			else
1835 			{
1836 				DefUid = pw->pw_uid;
1837 				DefGid = pw->pw_gid;
1838 			}
1839 		}
1840 		setdefuser();
1841 
1842 		/* handle the group if it is there */
1843 		if (*p == '\0')
1844 			break;
1845 		val = p;
1846 		goto g_opt;
1847 
1848 	  case 'V':		/* fallback MX host */
1849 		FallBackMX = newstr(val);
1850 		break;
1851 
1852 	  case 'v':		/* run in verbose mode */
1853 		Verbose = atobool(val);
1854 		break;
1855 
1856 	  case 'w':		/* if we are best MX, try host directly */
1857 		TryNullMXList = atobool(val);
1858 		break;
1859 
1860 	    /* 'W' available -- was wizard password */
1861 
1862 	  case 'x':		/* load avg at which to auto-queue msgs */
1863 		QueueLA = atoi(val);
1864 		break;
1865 
1866 	  case 'X':		/* load avg at which to auto-reject connections */
1867 		RefuseLA = atoi(val);
1868 		break;
1869 
1870 	  case 'y':		/* work recipient factor */
1871 		WkRecipFact = atoi(val);
1872 		break;
1873 
1874 	  case 'Y':		/* fork jobs during queue runs */
1875 		ForkQueueRuns = atobool(val);
1876 		break;
1877 
1878 	  case 'z':		/* work message class factor */
1879 		WkClassFact = atoi(val);
1880 		break;
1881 
1882 	  case 'Z':		/* work time factor */
1883 		WkTimeFact = atoi(val);
1884 		break;
1885 
1886 	  case O_BSP:		/* SMTP Peers can't handle 2-line greeting */
1887 		BrokenSmtpPeers = atobool(val);
1888 		break;
1889 
1890 	  case O_QUEUESORTORD:	/* queue sorting order */
1891 		switch (*val)
1892 		{
1893 		  case 'h':	/* Host first */
1894 		  case 'H':
1895 			QueueSortOrder = QS_BYHOST;
1896 			break;
1897 
1898 		  case 'p':	/* Priority order */
1899 		  case 'P':
1900 			QueueSortOrder = QS_BYPRIORITY;
1901 			break;
1902 
1903 		  default:
1904 			syserr("Invalid queue sort order \"%s\"", val);
1905 		}
1906 		break;
1907 
1908 	  case O_MQA:		/* minimum queue age between deliveries */
1909 		MinQueueAge = convtime(val, 'm');
1910 		break;
1911 
1912 	  case O_MHSA:		/* maximum age of cached host status */
1913 		MaxHostStatAge = convtime(val, 'm');
1914 		break;
1915 
1916 	  case O_DEFCHARSET:	/* default character set for mimefying */
1917 		DefaultCharSet = newstr(denlstring(val, TRUE, TRUE));
1918 		break;
1919 
1920 	  case O_SSFILE:	/* service switch file */
1921 		ServiceSwitchFile = newstr(val);
1922 		break;
1923 
1924 	  case O_DIALDELAY:	/* delay for dial-on-demand operation */
1925 		DialDelay = convtime(val, 's');
1926 		break;
1927 
1928 	  case O_NORCPTACTION:	/* what to do if no recipient */
1929 		if (strcasecmp(val, "none") == 0)
1930 			NoRecipientAction = NRA_NO_ACTION;
1931 		else if (strcasecmp(val, "add-to") == 0)
1932 			NoRecipientAction = NRA_ADD_TO;
1933 		else if (strcasecmp(val, "add-apparently-to") == 0)
1934 			NoRecipientAction = NRA_ADD_APPARENTLY_TO;
1935 		else if (strcasecmp(val, "add-bcc") == 0)
1936 			NoRecipientAction = NRA_ADD_BCC;
1937 		else if (strcasecmp(val, "add-to-undisclosed") == 0)
1938 			NoRecipientAction = NRA_ADD_TO_UNDISCLOSED;
1939 		else
1940 			syserr("Invalid NoRecipientAction: %s", val);
1941 
1942 	  case O_SAFEFILEENV:	/* chroot() environ for writing to files */
1943 		SafeFileEnv = newstr(val);
1944 		break;
1945 
1946 	  case O_MAXMSGSIZE:	/* maximum message size */
1947 		MaxMessageSize = atol(p);
1948 		break;
1949 
1950 	  default:
1951 		if (tTd(37, 1))
1952 		{
1953 			if (isascii(opt) && isprint(opt))
1954 				printf("Warning: option %c unknown\n", opt);
1955 			else
1956 				printf("Warning: option 0x%x unknown\n", opt);
1957 		}
1958 		break;
1959 	}
1960 	if (sticky)
1961 		setbitn(opt, StickyOpt);
1962 	return;
1963 }
1964 /*
1965 **  SETCLASS -- set a string into a class
1966 **
1967 **	Parameters:
1968 **		class -- the class to put the string in.
1969 **		str -- the string to enter
1970 **
1971 **	Returns:
1972 **		none.
1973 **
1974 **	Side Effects:
1975 **		puts the word into the symbol table.
1976 */
1977 
1978 setclass(class, str)
1979 	int class;
1980 	char *str;
1981 {
1982 	register STAB *s;
1983 
1984 	if (tTd(37, 8))
1985 		printf("setclass(%c, %s)\n", class, str);
1986 	s = stab(str, ST_CLASS, ST_ENTER);
1987 	setbitn(class, s->s_class);
1988 }
1989 /*
1990 **  MAKEMAPENTRY -- create a map entry
1991 **
1992 **	Parameters:
1993 **		line -- the config file line
1994 **
1995 **	Returns:
1996 **		TRUE if it successfully entered the map entry.
1997 **		FALSE otherwise (usually syntax error).
1998 **
1999 **	Side Effects:
2000 **		Enters the map into the dictionary.
2001 */
2002 
2003 void
2004 makemapentry(line)
2005 	char *line;
2006 {
2007 	register char *p;
2008 	char *mapname;
2009 	char *classname;
2010 	register STAB *s;
2011 	STAB *class;
2012 
2013 	for (p = line; isascii(*p) && isspace(*p); p++)
2014 		continue;
2015 	if (!(isascii(*p) && isalnum(*p)))
2016 	{
2017 		syserr("readcf: config K line: no map name");
2018 		return;
2019 	}
2020 
2021 	mapname = p;
2022 	while ((isascii(*++p) && isalnum(*p)) || *p == '.')
2023 		continue;
2024 	if (*p != '\0')
2025 		*p++ = '\0';
2026 	while (isascii(*p) && isspace(*p))
2027 		p++;
2028 	if (!(isascii(*p) && isalnum(*p)))
2029 	{
2030 		syserr("readcf: config K line, map %s: no map class", mapname);
2031 		return;
2032 	}
2033 	classname = p;
2034 	while (isascii(*++p) && isalnum(*p))
2035 		continue;
2036 	if (*p != '\0')
2037 		*p++ = '\0';
2038 	while (isascii(*p) && isspace(*p))
2039 		p++;
2040 
2041 	/* look up the class */
2042 	class = stab(classname, ST_MAPCLASS, ST_FIND);
2043 	if (class == NULL)
2044 	{
2045 		syserr("readcf: map %s: class %s not available", mapname, classname);
2046 		return;
2047 	}
2048 
2049 	/* enter the map */
2050 	s = stab(mapname, ST_MAP, ST_ENTER);
2051 	s->s_map.map_class = &class->s_mapclass;
2052 	s->s_map.map_mname = newstr(mapname);
2053 
2054 	if (class->s_mapclass.map_parse(&s->s_map, p))
2055 		s->s_map.map_mflags |= MF_VALID;
2056 
2057 	if (tTd(37, 5))
2058 	{
2059 		printf("map %s, class %s, flags %x, file %s,\n",
2060 			s->s_map.map_mname, s->s_map.map_class->map_cname,
2061 			s->s_map.map_mflags,
2062 			s->s_map.map_file == NULL ? "(null)" : s->s_map.map_file);
2063 		printf("\tapp %s, domain %s, rebuild %s\n",
2064 			s->s_map.map_app == NULL ? "(null)" : s->s_map.map_app,
2065 			s->s_map.map_domain == NULL ? "(null)" : s->s_map.map_domain,
2066 			s->s_map.map_rebuild == NULL ? "(null)" : s->s_map.map_rebuild);
2067 	}
2068 }
2069 /*
2070 **  INITTIMEOUTS -- parse and set timeout values
2071 **
2072 **	Parameters:
2073 **		val -- a pointer to the values.  If NULL, do initial
2074 **			settings.
2075 **
2076 **	Returns:
2077 **		none.
2078 **
2079 **	Side Effects:
2080 **		Initializes the TimeOuts structure
2081 */
2082 
2083 #define SECONDS
2084 #define MINUTES	* 60
2085 #define HOUR	* 3600
2086 
2087 inittimeouts(val)
2088 	register char *val;
2089 {
2090 	register char *p;
2091 	extern time_t convtime();
2092 
2093 	if (val == NULL)
2094 	{
2095 		TimeOuts.to_initial = (time_t) 5 MINUTES;
2096 		TimeOuts.to_helo = (time_t) 5 MINUTES;
2097 		TimeOuts.to_mail = (time_t) 10 MINUTES;
2098 		TimeOuts.to_rcpt = (time_t) 1 HOUR;
2099 		TimeOuts.to_datainit = (time_t) 5 MINUTES;
2100 		TimeOuts.to_datablock = (time_t) 1 HOUR;
2101 		TimeOuts.to_datafinal = (time_t) 1 HOUR;
2102 		TimeOuts.to_rset = (time_t) 5 MINUTES;
2103 		TimeOuts.to_quit = (time_t) 2 MINUTES;
2104 		TimeOuts.to_nextcommand = (time_t) 1 HOUR;
2105 		TimeOuts.to_miscshort = (time_t) 2 MINUTES;
2106 #if IDENTPROTO
2107 		TimeOuts.to_ident = (time_t) 30 SECONDS;
2108 #else
2109 		TimeOuts.to_ident = (time_t) 0 SECONDS;
2110 #endif
2111 		TimeOuts.to_fileopen = (time_t) 60 SECONDS;
2112 		return;
2113 	}
2114 
2115 	for (;; val = p)
2116 	{
2117 		while (isascii(*val) && isspace(*val))
2118 			val++;
2119 		if (*val == '\0')
2120 			break;
2121 		for (p = val; *p != '\0' && *p != ','; p++)
2122 			continue;
2123 		if (*p != '\0')
2124 			*p++ = '\0';
2125 
2126 		if (isascii(*val) && isdigit(*val))
2127 		{
2128 			/* old syntax -- set everything */
2129 			TimeOuts.to_mail = convtime(val, 'm');
2130 			TimeOuts.to_rcpt = TimeOuts.to_mail;
2131 			TimeOuts.to_datainit = TimeOuts.to_mail;
2132 			TimeOuts.to_datablock = TimeOuts.to_mail;
2133 			TimeOuts.to_datafinal = TimeOuts.to_mail;
2134 			TimeOuts.to_nextcommand = TimeOuts.to_mail;
2135 			continue;
2136 		}
2137 		else
2138 		{
2139 			register char *q = strchr(val, ':');
2140 
2141 			if (q == NULL && (q = strchr(val, '=')) == NULL)
2142 			{
2143 				/* syntax error */
2144 				continue;
2145 			}
2146 			*q++ = '\0';
2147 			settimeout(val, q);
2148 		}
2149 	}
2150 }
2151 /*
2152 **  SETTIMEOUT -- set an individual timeout
2153 **
2154 **	Parameters:
2155 **		name -- the name of the timeout.
2156 **		val -- the value of the timeout.
2157 **
2158 **	Returns:
2159 **		none.
2160 */
2161 
2162 settimeout(name, val)
2163 	char *name;
2164 	char *val;
2165 {
2166 	register char *p;
2167 	time_t to;
2168 	extern time_t convtime();
2169 
2170 	to = convtime(val, 'm');
2171 	p = strchr(name, '.');
2172 	if (p != NULL)
2173 		*p++ = '\0';
2174 
2175 	if (strcasecmp(name, "initial") == 0)
2176 		TimeOuts.to_initial = to;
2177 	else if (strcasecmp(name, "mail") == 0)
2178 		TimeOuts.to_mail = to;
2179 	else if (strcasecmp(name, "rcpt") == 0)
2180 		TimeOuts.to_rcpt = to;
2181 	else if (strcasecmp(name, "datainit") == 0)
2182 		TimeOuts.to_datainit = to;
2183 	else if (strcasecmp(name, "datablock") == 0)
2184 		TimeOuts.to_datablock = to;
2185 	else if (strcasecmp(name, "datafinal") == 0)
2186 		TimeOuts.to_datafinal = to;
2187 	else if (strcasecmp(name, "command") == 0)
2188 		TimeOuts.to_nextcommand = to;
2189 	else if (strcasecmp(name, "rset") == 0)
2190 		TimeOuts.to_rset = to;
2191 	else if (strcasecmp(name, "helo") == 0)
2192 		TimeOuts.to_helo = to;
2193 	else if (strcasecmp(name, "quit") == 0)
2194 		TimeOuts.to_quit = to;
2195 	else if (strcasecmp(name, "misc") == 0)
2196 		TimeOuts.to_miscshort = to;
2197 	else if (strcasecmp(name, "ident") == 0)
2198 		TimeOuts.to_ident = to;
2199 	else if (strcasecmp(name, "fileopen") == 0)
2200 		TimeOuts.to_fileopen = to;
2201 	else if (strcasecmp(name, "queuewarn") == 0)
2202 	{
2203 		to = convtime(val, 'h');
2204 		if (p == NULL || strcmp(p, "*") == 0)
2205 		{
2206 			TimeOuts.to_q_warning[TOC_NORMAL] = to;
2207 			TimeOuts.to_q_warning[TOC_URGENT] = to;
2208 			TimeOuts.to_q_warning[TOC_NONURGENT] = to;
2209 		}
2210 		else if (strcasecmp(p, "normal") == 0)
2211 			TimeOuts.to_q_warning[TOC_NORMAL] = to;
2212 		else if (strcasecmp(p, "urgent") == 0)
2213 			TimeOuts.to_q_warning[TOC_URGENT] = to;
2214 		else if (strcasecmp(p, "non-urgent") == 0)
2215 			TimeOuts.to_q_warning[TOC_NONURGENT] = to;
2216 		else
2217 			syserr("settimeout: invalid queuewarn subtimeout %s", p);
2218 	}
2219 	else if (strcasecmp(name, "queuereturn") == 0)
2220 	{
2221 		to = convtime(val, 'd');
2222 		if (p == NULL || strcmp(p, "*") == 0)
2223 		{
2224 			TimeOuts.to_q_return[TOC_NORMAL] = to;
2225 			TimeOuts.to_q_return[TOC_URGENT] = to;
2226 			TimeOuts.to_q_return[TOC_NONURGENT] = to;
2227 		}
2228 		else if (strcasecmp(p, "normal") == 0)
2229 			TimeOuts.to_q_return[TOC_NORMAL] = to;
2230 		else if (strcasecmp(p, "urgent") == 0)
2231 			TimeOuts.to_q_return[TOC_URGENT] = to;
2232 		else if (strcasecmp(p, "non-urgent") == 0)
2233 			TimeOuts.to_q_return[TOC_NONURGENT] = to;
2234 		else
2235 			syserr("settimeout: invalid queuereturn subtimeout %s", p);
2236 	}
2237 	else
2238 		syserr("settimeout: invalid timeout %s", name);
2239 }
2240