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