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