xref: /csrg-svn/usr.sbin/sendmail/src/readcf.c (revision 21066)
1 # include "sendmail.h"
2 
3 SCCSID(@(#)readcf.c	4.12		05/24/85);
4 
5 /*
6 **  READCF -- read control file.
7 **
8 **	This routine reads the control file and builds the internal
9 **	form.
10 **
11 **	The file is formatted as a sequence of lines, each taken
12 **	atomically.  The first character of each line describes how
13 **	the line is to be interpreted.  The lines are:
14 **		Dxval		Define macro x to have value val.
15 **		Cxword		Put word into class x.
16 **		Fxfile [fmt]	Read file for lines to put into
17 **				class x.  Use scanf string 'fmt'
18 **				or "%s" if not present.  Fmt should
19 **				only produce one string-valued result.
20 **		Hname: value	Define header with field-name 'name'
21 **				and value as specified; this will be
22 **				macro expanded immediately before
23 **				use.
24 **		Sn		Use rewriting set n.
25 **		Rlhs rhs	Rewrite addresses that match lhs to
26 **				be rhs.
27 **		Mn p f s r a	Define mailer.  n - internal name,
28 **				p - pathname, f - flags, s - rewriting
29 **				ruleset for sender, s - rewriting ruleset
30 **				for recipients, a - argument vector.
31 **		Oxvalue		Set option x to value.
32 **		Pname=value	Set precedence name to value.
33 **
34 **	Parameters:
35 **		cfname -- control file name.
36 **
37 **	Returns:
38 **		none.
39 **
40 **	Side Effects:
41 **		Builds several internal tables.
42 */
43 
44 readcf(cfname)
45 	char *cfname;
46 {
47 	FILE *cf;
48 	int ruleset = 0;
49 	char *q;
50 	char **pv;
51 	struct rewrite *rwp = NULL;
52 	char buf[MAXLINE];
53 	register char *p;
54 	extern char **prescan();
55 	extern char **copyplist();
56 	char exbuf[MAXLINE];
57 	char pvpbuf[PSBUFSIZE];
58 	extern char *fgetfolded();
59 	extern char *munchstring();
60 
61 	cf = fopen(cfname, "r");
62 	if (cf == NULL)
63 	{
64 		syserr("cannot open %s", cfname);
65 		exit(EX_OSFILE);
66 	}
67 
68 	FileName = cfname;
69 	LineNumber = 0;
70 	while (fgetfolded(buf, sizeof buf, cf) != NULL)
71 	{
72 		/* map $ into \001 (ASCII SOH) for macro expansion */
73 		for (p = buf; *p != '\0'; p++)
74 		{
75 			if (*p != '$')
76 				continue;
77 
78 			if (p[1] == '$')
79 			{
80 				/* actual dollar sign.... */
81 				strcpy(p, p + 1);
82 				continue;
83 			}
84 
85 			/* convert to macro expansion character */
86 			*p = '\001';
87 		}
88 
89 		/* interpret this line */
90 		switch (buf[0])
91 		{
92 		  case '\0':
93 		  case '#':		/* comment */
94 			break;
95 
96 		  case 'R':		/* rewriting rule */
97 			for (p = &buf[1]; *p != '\0' && *p != '\t'; p++)
98 				continue;
99 
100 			if (*p == '\0')
101 			{
102 				syserr("invalid rewrite line \"%s\"", buf);
103 				break;
104 			}
105 
106 			/* allocate space for the rule header */
107 			if (rwp == NULL)
108 			{
109 				RewriteRules[ruleset] = rwp =
110 					(struct rewrite *) xalloc(sizeof *rwp);
111 			}
112 			else
113 			{
114 				rwp->r_next = (struct rewrite *) xalloc(sizeof *rwp);
115 				rwp = rwp->r_next;
116 			}
117 			rwp->r_next = NULL;
118 
119 			/* expand and save the LHS */
120 			*p = '\0';
121 			expand(&buf[1], exbuf, &exbuf[sizeof exbuf], CurEnv);
122 			rwp->r_lhs = prescan(exbuf, '\t', pvpbuf);
123 			if (rwp->r_lhs != NULL)
124 				rwp->r_lhs = copyplist(rwp->r_lhs, TRUE);
125 
126 			/* expand and save the RHS */
127 			while (*++p == '\t')
128 				continue;
129 			q = p;
130 			while (*p != '\0' && *p != '\t')
131 				p++;
132 			*p = '\0';
133 			expand(q, exbuf, &exbuf[sizeof exbuf], CurEnv);
134 			rwp->r_rhs = prescan(exbuf, '\t', pvpbuf);
135 			if (rwp->r_rhs != NULL)
136 				rwp->r_rhs = copyplist(rwp->r_rhs, TRUE);
137 			break;
138 
139 		  case 'S':		/* select rewriting set */
140 			ruleset = atoi(&buf[1]);
141 			if (ruleset >= MAXRWSETS || ruleset < 0)
142 			{
143 				syserr("bad ruleset %d (%d max)", ruleset, MAXRWSETS);
144 				ruleset = 0;
145 			}
146 			rwp = NULL;
147 			break;
148 
149 		  case 'D':		/* macro definition */
150 			define(buf[1], newstr(munchstring(&buf[2])), CurEnv);
151 			break;
152 
153 		  case 'H':		/* required header line */
154 			(void) chompheader(&buf[1], TRUE);
155 			break;
156 
157 		  case 'C':		/* word class */
158 		  case 'F':		/* word class from file */
159 			/* read list of words from argument or file */
160 			if (buf[0] == 'F')
161 			{
162 				/* read from file */
163 				for (p = &buf[2]; *p != '\0' && !isspace(*p); p++)
164 					continue;
165 				if (*p == '\0')
166 					p = "%s";
167 				else
168 				{
169 					*p = '\0';
170 					while (isspace(*++p))
171 						continue;
172 				}
173 				fileclass(buf[1], &buf[2], p);
174 				break;
175 			}
176 
177 			/* scan the list of words and set class for all */
178 			for (p = &buf[2]; *p != '\0'; )
179 			{
180 				register char *wd;
181 				char delim;
182 
183 				while (*p != '\0' && isspace(*p))
184 					p++;
185 				wd = p;
186 				while (*p != '\0' && !isspace(*p))
187 					p++;
188 				delim = *p;
189 				*p = '\0';
190 				if (wd[0] != '\0')
191 					setclass(buf[1], wd);
192 				*p = delim;
193 			}
194 			break;
195 
196 		  case 'M':		/* define mailer */
197 			makemailer(&buf[1]);
198 			break;
199 
200 		  case 'O':		/* set option */
201 			setoption(buf[1], &buf[2], FALSE);
202 			break;
203 
204 		  case 'P':		/* set precedence */
205 			if (NumPriorities >= MAXPRIORITIES)
206 			{
207 				toomany('P', MAXPRIORITIES);
208 				break;
209 			}
210 			for (p = &buf[1]; *p != '\0' && *p != '=' && *p != '\t'; p++)
211 				continue;
212 			if (*p == '\0')
213 				goto badline;
214 			*p = '\0';
215 			Priorities[NumPriorities].pri_name = newstr(&buf[1]);
216 			Priorities[NumPriorities].pri_val = atoi(++p);
217 			NumPriorities++;
218 			break;
219 
220 		  case 'T':		/* trusted user(s) */
221 			p = &buf[1];
222 			while (*p != '\0')
223 			{
224 				while (isspace(*p))
225 					p++;
226 				q = p;
227 				while (*p != '\0' && !isspace(*p))
228 					p++;
229 				if (*p != '\0')
230 					*p++ = '\0';
231 				if (*q == '\0')
232 					continue;
233 				for (pv = TrustedUsers; *pv != NULL; pv++)
234 					continue;
235 				if (pv >= &TrustedUsers[MAXTRUST])
236 				{
237 					toomany('T', MAXTRUST);
238 					break;
239 				}
240 				*pv = newstr(q);
241 			}
242 			break;
243 
244 		  default:
245 		  badline:
246 			syserr("unknown control line \"%s\"", buf);
247 		}
248 	}
249 	FileName = NULL;
250 }
251 /*
252 **  TOOMANY -- signal too many of some option
253 **
254 **	Parameters:
255 **		id -- the id of the error line
256 **		maxcnt -- the maximum possible values
257 **
258 **	Returns:
259 **		none.
260 **
261 **	Side Effects:
262 **		gives a syserr.
263 */
264 
265 toomany(id, maxcnt)
266 	char id;
267 	int maxcnt;
268 {
269 	syserr("too many %c lines, %d max", id, maxcnt);
270 }
271 /*
272 **  FILECLASS -- read members of a class from a file
273 **
274 **	Parameters:
275 **		class -- class to define.
276 **		filename -- name of file to read.
277 **		fmt -- scanf string to use for match.
278 **
279 **	Returns:
280 **		none
281 **
282 **	Side Effects:
283 **
284 **		puts all lines in filename that match a scanf into
285 **			the named class.
286 */
287 
288 fileclass(class, filename, fmt)
289 	int class;
290 	char *filename;
291 	char *fmt;
292 {
293 	register FILE *f;
294 	char buf[MAXLINE];
295 
296 	f = fopen(filename, "r");
297 	if (f == NULL)
298 	{
299 		syserr("cannot open %s", filename);
300 		return;
301 	}
302 
303 	while (fgets(buf, sizeof buf, f) != NULL)
304 	{
305 		register STAB *s;
306 		char wordbuf[MAXNAME+1];
307 
308 		if (sscanf(buf, fmt, wordbuf) != 1)
309 			continue;
310 		s = stab(wordbuf, ST_CLASS, ST_ENTER);
311 		setbitn(class, s->s_class);
312 	}
313 
314 	(void) fclose(f);
315 }
316 /*
317 **  MAKEMAILER -- define a new mailer.
318 **
319 **	Parameters:
320 **		line -- description of mailer.  This is in labeled
321 **			fields.  The fields are:
322 **			   P -- the path to the mailer
323 **			   F -- the flags associated with the mailer
324 **			   A -- the argv for this mailer
325 **			   S -- the sender rewriting set
326 **			   R -- the recipient rewriting set
327 **			   E -- the eol string
328 **			The first word is the canonical name of the mailer.
329 **
330 **	Returns:
331 **		none.
332 **
333 **	Side Effects:
334 **		enters the mailer into the mailer table.
335 */
336 
337 makemailer(line)
338 	char *line;
339 {
340 	register char *p;
341 	register struct mailer *m;
342 	register STAB *s;
343 	int i;
344 	char fcode;
345 	extern int NextMailer;
346 	extern char **makeargv();
347 	extern char *munchstring();
348 	extern char *DelimChar;
349 	extern long atol();
350 
351 	/* allocate a mailer and set up defaults */
352 	m = (struct mailer *) xalloc(sizeof *m);
353 	bzero((char *) m, sizeof *m);
354 	m->m_mno = NextMailer;
355 	m->m_eol = "\n";
356 
357 	/* collect the mailer name */
358 	for (p = line; *p != '\0' && *p != ',' && !isspace(*p); p++)
359 		continue;
360 	if (*p != '\0')
361 		*p++ = '\0';
362 	m->m_name = newstr(line);
363 
364 	/* now scan through and assign info from the fields */
365 	while (*p != '\0')
366 	{
367 		while (*p != '\0' && (*p == ',' || isspace(*p)))
368 			p++;
369 
370 		/* p now points to field code */
371 		fcode = *p;
372 		while (*p != '\0' && *p != '=' && *p != ',')
373 			p++;
374 		if (*p++ != '=')
375 		{
376 			syserr("`=' expected");
377 			return;
378 		}
379 		while (isspace(*p))
380 			p++;
381 
382 		/* p now points to the field body */
383 		p = munchstring(p);
384 
385 		/* install the field into the mailer struct */
386 		switch (fcode)
387 		{
388 		  case 'P':		/* pathname */
389 			m->m_mailer = newstr(p);
390 			break;
391 
392 		  case 'F':		/* flags */
393 			for (; *p != '\0'; p++)
394 				setbitn(*p, m->m_flags);
395 			break;
396 
397 		  case 'S':		/* sender rewriting ruleset */
398 		  case 'R':		/* recipient rewriting ruleset */
399 			i = atoi(p);
400 			if (i < 0 || i >= MAXRWSETS)
401 			{
402 				syserr("invalid rewrite set, %d max", MAXRWSETS);
403 				return;
404 			}
405 			if (fcode == 'S')
406 				m->m_s_rwset = i;
407 			else
408 				m->m_r_rwset = i;
409 			break;
410 
411 		  case 'E':		/* end of line string */
412 			m->m_eol = newstr(p);
413 			break;
414 
415 		  case 'A':		/* argument vector */
416 			m->m_argv = makeargv(p);
417 			break;
418 
419 		  case 'M':		/* maximum message size */
420 			m->m_maxsize = atol(p);
421 			break;
422 		}
423 
424 		p = DelimChar;
425 	}
426 
427 	/* now store the mailer away */
428 	if (NextMailer >= MAXMAILERS)
429 	{
430 		syserr("too many mailers defined (%d max)", MAXMAILERS);
431 		return;
432 	}
433 	Mailer[NextMailer++] = m;
434 	s = stab(m->m_name, ST_MAILER, ST_ENTER);
435 	s->s_mailer = m;
436 }
437 /*
438 **  MUNCHSTRING -- translate a string into internal form.
439 **
440 **	Parameters:
441 **		p -- the string to munch.
442 **
443 **	Returns:
444 **		the munched string.
445 **
446 **	Side Effects:
447 **		Sets "DelimChar" to point to the string that caused us
448 **		to stop.
449 */
450 
451 char *
452 munchstring(p)
453 	register char *p;
454 {
455 	register char *q;
456 	bool backslash = FALSE;
457 	bool quotemode = FALSE;
458 	static char buf[MAXLINE];
459 	extern char *DelimChar;
460 
461 	for (q = buf; *p != '\0'; p++)
462 	{
463 		if (backslash)
464 		{
465 			/* everything is roughly literal */
466 			backslash = FALSE;
467 			switch (*p)
468 			{
469 			  case 'r':		/* carriage return */
470 				*q++ = '\r';
471 				continue;
472 
473 			  case 'n':		/* newline */
474 				*q++ = '\n';
475 				continue;
476 
477 			  case 'f':		/* form feed */
478 				*q++ = '\f';
479 				continue;
480 
481 			  case 'b':		/* backspace */
482 				*q++ = '\b';
483 				continue;
484 			}
485 			*q++ = *p;
486 		}
487 		else
488 		{
489 			if (*p == '\\')
490 				backslash = TRUE;
491 			else if (*p == '"')
492 				quotemode = !quotemode;
493 			else if (quotemode || *p != ',')
494 				*q++ = *p;
495 			else
496 				break;
497 		}
498 	}
499 
500 	DelimChar = p;
501 	*q++ = '\0';
502 	return (buf);
503 }
504 /*
505 **  MAKEARGV -- break up a string into words
506 **
507 **	Parameters:
508 **		p -- the string to break up.
509 **
510 **	Returns:
511 **		a char **argv (dynamically allocated)
512 **
513 **	Side Effects:
514 **		munges p.
515 */
516 
517 char **
518 makeargv(p)
519 	register char *p;
520 {
521 	char *q;
522 	int i;
523 	char **avp;
524 	char *argv[MAXPV + 1];
525 
526 	/* take apart the words */
527 	i = 0;
528 	while (*p != '\0' && i < MAXPV)
529 	{
530 		q = p;
531 		while (*p != '\0' && !isspace(*p))
532 			p++;
533 		while (isspace(*p))
534 			*p++ = '\0';
535 		argv[i++] = newstr(q);
536 	}
537 	argv[i++] = NULL;
538 
539 	/* now make a copy of the argv */
540 	avp = (char **) xalloc(sizeof *avp * i);
541 	bcopy((char *) argv, (char *) avp, sizeof *avp * i);
542 
543 	return (avp);
544 }
545 /*
546 **  PRINTRULES -- print rewrite rules (for debugging)
547 **
548 **	Parameters:
549 **		none.
550 **
551 **	Returns:
552 **		none.
553 **
554 **	Side Effects:
555 **		prints rewrite rules.
556 */
557 
558 # ifdef DEBUG
559 
560 printrules()
561 {
562 	register struct rewrite *rwp;
563 	register int ruleset;
564 
565 	for (ruleset = 0; ruleset < 10; ruleset++)
566 	{
567 		if (RewriteRules[ruleset] == NULL)
568 			continue;
569 		printf("\n----Rule Set %d:", ruleset);
570 
571 		for (rwp = RewriteRules[ruleset]; rwp != NULL; rwp = rwp->r_next)
572 		{
573 			printf("\nLHS:");
574 			printav(rwp->r_lhs);
575 			printf("RHS:");
576 			printav(rwp->r_rhs);
577 		}
578 	}
579 }
580 
581 # endif DEBUG
582 /*
583 **  SETOPTION -- set global processing option
584 **
585 **	Parameters:
586 **		opt -- option name.
587 **		val -- option value (as a text string).
588 **		sticky -- if set, don't let other setoptions override
589 **			this value.
590 **
591 **	Returns:
592 **		none.
593 **
594 **	Side Effects:
595 **		Sets options as implied by the arguments.
596 */
597 
598 static BITMAP	StickyOpt;		/* set if option is stuck */
599 extern char	*WizWord;		/* the stored wizard password */
600 extern char	*NetName;		/* name of home (local) network */
601 
602 setoption(opt, val, sticky)
603 	char opt;
604 	char *val;
605 	bool sticky;
606 {
607 	extern bool atobool();
608 	extern time_t convtime();
609 	extern int QueueLA;
610 	extern int RefuseLA;
611 	extern bool trusteduser();
612 	extern char *username();
613 
614 # ifdef DEBUG
615 	if (tTd(37, 1))
616 		printf("setoption %c=%s", opt, val);
617 # endif DEBUG
618 
619 	/*
620 	**  See if this option is preset for us.
621 	*/
622 
623 	if (bitnset(opt, StickyOpt))
624 	{
625 # ifdef DEBUG
626 		if (tTd(37, 1))
627 			printf(" (ignored)\n");
628 # endif DEBUG
629 		return;
630 	}
631 
632 #ifdef DEBUG
633 	if (tTd(37, 1))
634 		printf("\n");
635 #endif DEBUG
636 
637 	switch (opt)
638 	{
639 	  case 'A':		/* set default alias file */
640 		if (val[0] == '\0')
641 			AliasFile = "aliases";
642 		else
643 			AliasFile = newstr(val);
644 		break;
645 
646 	  case 'a':		/* look N minutes for "@:@" in alias file */
647 		if (val[0] == '\0')
648 			SafeAlias = 5;
649 		else
650 			SafeAlias = atoi(val);
651 		break;
652 
653 	  case 'B':		/* substitution for blank character */
654 		SpaceSub = val[0];
655 		if (SpaceSub == '\0')
656 			SpaceSub = ' ';
657 		break;
658 
659 	  case 'c':		/* don't connect to "expensive" mailers */
660 		NoConnect = atobool(val);
661 		break;
662 
663 	  case 'd':		/* delivery mode */
664 		switch (*val)
665 		{
666 		  case '\0':
667 			SendMode = SM_DELIVER;
668 			break;
669 
670 		  case SM_QUEUE:	/* queue only */
671 #ifndef QUEUE
672 			syserr("need QUEUE to set -odqueue");
673 #endif QUEUE
674 			/* fall through..... */
675 
676 		  case SM_DELIVER:	/* do everything */
677 		  case SM_FORK:		/* fork after verification */
678 			SendMode = *val;
679 			break;
680 
681 		  default:
682 			syserr("Unknown delivery mode %c", *val);
683 			exit(EX_USAGE);
684 		}
685 		break;
686 
687 	  case 'D':		/* rebuild alias database as needed */
688 		AutoRebuild = atobool(val);
689 		break;
690 
691 	  case 'e':		/* set error processing mode */
692 		switch (*val)
693 		{
694 		  case EM_QUIET:	/* be silent about it */
695 		  case EM_MAIL:		/* mail back */
696 		  case EM_BERKNET:	/* do berknet error processing */
697 		  case EM_WRITE:	/* write back (or mail) */
698 			HoldErrs = TRUE;
699 			/* fall through... */
700 
701 		  case EM_PRINT:	/* print errors normally (default) */
702 			ErrorMode = *val;
703 			break;
704 		}
705 		break;
706 
707 	  case 'F':		/* file mode */
708 		FileMode = atooct(val) & 0777;
709 		break;
710 
711 	  case 'f':		/* save Unix-style From lines on front */
712 		SaveFrom = atobool(val);
713 		break;
714 
715 	  case 'g':		/* default gid */
716 		DefGid = atoi(val);
717 		break;
718 
719 	  case 'H':		/* help file */
720 		if (val[0] == '\0')
721 			HelpFile = "sendmail.hf";
722 		else
723 			HelpFile = newstr(val);
724 		break;
725 
726 	  case 'i':		/* ignore dot lines in message */
727 		IgnrDot = atobool(val);
728 		break;
729 
730 	  case 'L':		/* log level */
731 		LogLevel = atoi(val);
732 		break;
733 
734 	  case 'M':		/* define macro */
735 		define(val[0], newstr(&val[1]), CurEnv);
736 		sticky = FALSE;
737 		break;
738 
739 	  case 'm':		/* send to me too */
740 		MeToo = atobool(val);
741 		break;
742 
743 # ifdef DAEMON
744 	  case 'N':		/* home (local?) network name */
745 		NetName = newstr(val);
746 		break;
747 # endif DAEMON
748 
749 	  case 'o':		/* assume old style headers */
750 		if (atobool(val))
751 			CurEnv->e_flags |= EF_OLDSTYLE;
752 		else
753 			CurEnv->e_flags &= ~EF_OLDSTYLE;
754 		break;
755 
756 	  case 'Q':		/* queue directory */
757 		if (val[0] == '\0')
758 			QueueDir = "mqueue";
759 		else
760 			QueueDir = newstr(val);
761 		break;
762 
763 	  case 'r':		/* read timeout */
764 		ReadTimeout = convtime(val);
765 		break;
766 
767 	  case 'S':		/* status file */
768 		if (val[0] == '\0')
769 			StatFile = "sendmail.st";
770 		else
771 			StatFile = newstr(val);
772 		break;
773 
774 	  case 's':		/* be super safe, even if expensive */
775 		SuperSafe = atobool(val);
776 		break;
777 
778 	  case 'T':		/* queue timeout */
779 		TimeOut = convtime(val);
780 		break;
781 
782 	  case 't':		/* time zone name */
783 # ifdef V6
784 		StdTimezone = newstr(val);
785 		DstTimezone = index(StdTimeZone, ',');
786 		if (DstTimezone == NULL)
787 			syserr("bad time zone spec");
788 		else
789 			*DstTimezone++ = '\0';
790 # endif V6
791 		break;
792 
793 	  case 'u':		/* set default uid */
794 		DefUid = atoi(val);
795 		break;
796 
797 	  case 'v':		/* run in verbose mode */
798 		Verbose = atobool(val);
799 		break;
800 
801 # ifdef DEBUG
802 	  case 'W':		/* set the wizards password */
803 		WizWord = newstr(val);
804 		break;
805 # endif DEBUG
806 
807 	  case 'x':		/* load avg at which to auto-queue msgs */
808 		QueueLA = atoi(val);
809 		break;
810 
811 	  case 'X':		/* load avg at which to auto-reject connections */
812 		RefuseLA = atoi(val);
813 		break;
814 
815 	  default:
816 		break;
817 	}
818 	if (sticky)
819 		setbitn(opt, StickyOpt);
820 	return;
821 }
822 /*
823 **  SETCLASS -- set a word into a class
824 **
825 **	Parameters:
826 **		class -- the class to put the word in.
827 **		word -- the word to enter
828 **
829 **	Returns:
830 **		none.
831 **
832 **	Side Effects:
833 **		puts the word into the symbol table.
834 */
835 
836 setclass(class, word)
837 	int class;
838 	char *word;
839 {
840 	register STAB *s;
841 
842 	s = stab(word, ST_CLASS, ST_ENTER);
843 	setbitn(class, s->s_class);
844 }
845