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