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