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