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