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