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