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