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