1 # include "sendmail.h"
2 
3 SCCSID(@(#)readcf.c	3.32		09/24/82);
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 **		safe -- set if this is a system configuration file.
37 **			Non-system configuration files can not do
38 **			certain things (e.g., leave the SUID bit on
39 **			when executing mailers).
40 **
41 **	Returns:
42 **		none.
43 **
44 **	Side Effects:
45 **		Builds several internal tables.
46 */
47 
48 readcf(cfname, safe)
49 	char *cfname;
50 	bool safe;
51 {
52 	FILE *cf;
53 	char buf[MAXLINE];
54 	register char *p;
55 	struct rewrite *rwp = NULL;
56 	extern char **prescan();
57 	extern char **copyplist();
58 	int class;
59 	int ruleset = 0;
60 	char exbuf[MAXLINE];
61 	char *q;
62 
63 	cf = fopen(cfname, "r");
64 	if (cf == NULL)
65 	{
66 		syserr("cannot open %s", cfname);
67 		exit(EX_OSFILE);
68 	}
69 
70 	LineNumber = 0;
71 	while (fgetfolded(buf, sizeof buf, cf) != NULL)
72 	{
73 		switch (buf[0])
74 		{
75 		  case '\0':
76 		  case '#':		/* comment */
77 			break;
78 
79 		  case 'R':		/* rewriting rule */
80 			for (p = &buf[1]; *p != '\0' && *p != '\t'; p++)
81 				continue;
82 
83 			if (*p == '\0')
84 			{
85 				syserr("line %d: invalid rewrite line \"%s\"",
86 					LineNumber, buf);
87 				break;
88 			}
89 
90 			/* allocate space for the rule header */
91 			if (rwp == NULL)
92 			{
93 				RewriteRules[ruleset] = rwp =
94 					(struct rewrite *) xalloc(sizeof *rwp);
95 			}
96 			else
97 			{
98 				rwp->r_next = (struct rewrite *) xalloc(sizeof *rwp);
99 				rwp = rwp->r_next;
100 			}
101 			rwp->r_next = NULL;
102 
103 			/* expand and save the LHS */
104 			*p = '\0';
105 			expand(&buf[1], exbuf, &exbuf[sizeof exbuf], CurEnv);
106 			rwp->r_lhs = prescan(exbuf, '\t');
107 			if (rwp->r_lhs != NULL)
108 				rwp->r_lhs = copyplist(rwp->r_lhs, TRUE);
109 
110 			/* expand and save the RHS */
111 			while (*++p == '\t')
112 				continue;
113 			q = p;
114 			while (*p != '\0' && *p != '\t')
115 				p++;
116 			*p = '\0';
117 			expand(q, exbuf, &exbuf[sizeof exbuf], CurEnv);
118 			rwp->r_rhs = prescan(exbuf, '\t');
119 			if (rwp->r_rhs != NULL)
120 				rwp->r_rhs = copyplist(rwp->r_rhs, TRUE);
121 			break;
122 
123 		  case 'S':		/* select rewriting set */
124 			ruleset = atoi(&buf[1]);
125 			if (ruleset >= MAXRWSETS || ruleset < 0)
126 			{
127 				syserr("readcf: line %d: bad ruleset %d (%d max)",
128 					LineNumber, ruleset, MAXRWSETS);
129 				ruleset = 0;
130 			}
131 			rwp = NULL;
132 			break;
133 
134 		  case 'D':		/* macro definition */
135 			define(buf[1], newstr(&buf[2]));
136 			break;
137 
138 		  case 'H':		/* required header line */
139 			(void) chompheader(&buf[1], TRUE);
140 			break;
141 
142 		  case 'C':		/* word class */
143 		  case 'F':		/* word class from file */
144 			class = buf[1];
145 			if (!isalpha(class))
146 				goto badline;
147 			if (isupper(class))
148 				class -= 'A';
149 			else
150 				class -= 'a';
151 
152 			/* read list of words from argument or file */
153 			if (buf[0] == 'F')
154 			{
155 				/* read from file */
156 				for (p = &buf[2]; *p != '\0' && !isspace(*p); p++)
157 					continue;
158 				if (*p == '\0')
159 					p = "%s";
160 				else
161 				{
162 					*p = '\0';
163 					while (isspace(*++p))
164 						continue;
165 				}
166 				fileclass(class, &buf[2], p);
167 				break;
168 			}
169 
170 			/* scan the list of words and set class for all */
171 			for (p = &buf[2]; *p != '\0'; )
172 			{
173 				register char *wd;
174 				char delim;
175 				register STAB *s;
176 
177 				while (*p != '\0' && isspace(*p))
178 					p++;
179 				wd = p;
180 				while (*p != '\0' && !isspace(*p))
181 					p++;
182 				delim = *p;
183 				*p = '\0';
184 				if (wd[0] != '\0')
185 				{
186 					s = stab(wd, ST_CLASS, ST_ENTER);
187 					s->s_class |= 1L << class;
188 				}
189 				*p = delim;
190 			}
191 			break;
192 
193 		  case 'M':		/* define mailer */
194 			makemailer(&buf[1], safe);
195 			break;
196 
197 		  case 'O':		/* set option */
198 			if (buf[2] == '\0')
199 				Option[buf[1]] = "";
200 			else
201 				Option[buf[1]] = newstr(&buf[2]);
202 			break;
203 
204 		  case 'P':		/* set precedence */
205 			if (NumPriorities >= MAXPRIORITIES)
206 			{
207 				syserr("readcf: line %d: too many P lines, %d max",
208 					LineNumber, MAXPRIORITIES);
209 				break;
210 			}
211 			for (p = &buf[1]; *p != '\0' && *p != '='; p++)
212 				continue;
213 			if (*p == '\0')
214 				goto badline;
215 			*p = '\0';
216 			Priorities[NumPriorities].pri_name = newstr(&buf[1]);
217 			Priorities[NumPriorities].pri_val = atoi(++p);
218 			NumPriorities++;
219 			break;
220 
221 		  default:
222 		  badline:
223 			syserr("readcf: line %d: unknown control line \"%s\"",
224 				LineNumber, buf);
225 		}
226 	}
227 }
228 /*
229 **  FILECLASS -- read members of a class from a file
230 **
231 **	Parameters:
232 **		class -- class to define.
233 **		filename -- name of file to read.
234 **		fmt -- scanf string to use for match.
235 **
236 **	Returns:
237 **		none
238 **
239 **	Side Effects:
240 **
241 **		puts all lines in filename that match a scanf into
242 **			the named class.
243 */
244 
245 fileclass(class, filename, fmt)
246 	int class;
247 	char *filename;
248 	char *fmt;
249 {
250 	register FILE *f;
251 	char buf[MAXLINE];
252 
253 	f = fopen(filename, "r");
254 	if (f == NULL)
255 	{
256 		syserr("cannot open %s", filename);
257 		return;
258 	}
259 
260 	while (fgets(buf, sizeof buf, f) != NULL)
261 	{
262 		register STAB *s;
263 		char wordbuf[MAXNAME+1];
264 
265 		if (sscanf(buf, fmt, wordbuf) != 1)
266 			continue;
267 		s = stab(wordbuf, ST_CLASS, ST_ENTER);
268 		s->s_class |= 1L << class;
269 	}
270 
271 	(void) fclose(f);
272 }
273 /*
274 **  MAKEMAILER -- define a new mailer.
275 **
276 **	Parameters:
277 **		line -- description of mailer.  This is in tokens
278 **			separated by white space.  The fields are:
279 **			* the name of the mailer, as refered to
280 **			  in the rewriting rules.
281 **			* the pathname of the program to fork to
282 **			  execute it.
283 **			* the options needed by this program.
284 **			* the macro string needed to translate
285 **			  a local "from" name to one that can be
286 **			  returned to this machine.
287 **			* the argument vector (a series of parameters).
288 **		safe -- set if this is a safe configuration file.
289 **
290 **	Returns:
291 **		none.
292 **
293 **	Side Effects:
294 **		enters the mailer into the mailer table.
295 */
296 
297 # define SETWORD \
298 		{ \
299 			while (*p != '\0' && isspace(*p)) \
300 				p++; \
301 			q = p; \
302 			while (*p != '\0' && !isspace(*p)) \
303 				p++; \
304 			if (*p != '\0') \
305 				*p++ = '\0'; \
306 		}
307 
308 makemailer(line, safe)
309 	char *line;
310 	bool safe;
311 {
312 	register char *p;
313 	register char *q;
314 	register struct mailer *m;
315 	register STAB *s;
316 	int i;
317 	char *mname;
318 	char *mpath;
319 	u_long mopts;
320 	short mrset, msset;
321 	char *margv[MAXPV + 1];
322 	extern u_long mfencode();
323 	extern int NextMailer;
324 
325 	if (NextMailer >= MAXMAILERS)
326 	{
327 		syserr("readcf: line %d: too many mailers defined (%d max)",
328 			LineNumber, MAXMAILERS);
329 		return;
330 	}
331 
332 	/* collect initial information */
333 	p = line;
334 	SETWORD;
335 	mname = q;
336 	SETWORD;
337 	mpath = q;
338 	SETWORD;
339 	mopts = mfencode(q);
340 	if (!safe)
341 		mopts &= ~M_RESTR;
342 	SETWORD;
343 	msset = atoi(q);
344 	SETWORD;
345 	mrset = atoi(q);
346 
347 	if (*p == '\0')
348 	{
349 		syserr("readcf: line %d: invalid M line in configuration file",
350 			LineNumber);
351 		return;
352 	}
353 	if (msset >= MAXRWSETS || mrset >= MAXRWSETS)
354 	{
355 		syserr("readcf: line %d: invalid rewrite set, %d max",
356 			LineNumber, MAXRWSETS);
357 		return;
358 	}
359 
360 	/* allocate a mailer */
361 	m = (struct mailer *) xalloc(sizeof *m);
362 	m->m_name = newstr(mname);
363 	m->m_mailer = newstr(mpath);
364 	m->m_flags = mopts;
365 	m->m_r_rwset = mrset;
366 	m->m_s_rwset = msset;
367 	m->m_badstat = EX_UNAVAILABLE;
368 	m->m_mno = NextMailer;
369 	Mailer[NextMailer++] = m;
370 
371 	/* collect the argument vector */
372 	for (i = 0; i < MAXPV - 1 && *p != '\0'; i++)
373 	{
374 		SETWORD;
375 		margv[i] = newstr(q);
376 	}
377 	margv[i++] = NULL;
378 
379 	/* save the argv */
380 	m->m_argv = (char **) xalloc(sizeof margv[0] * i);
381 	bmove((char *) margv, (char *) m->m_argv, sizeof margv[0] * i);
382 	s = stab(m->m_name, ST_MAILER, ST_ENTER);
383 	s->s_mailer = m;
384 }
385 /*
386 **  PRINTRULES -- print rewrite rules (for debugging)
387 **
388 **	Parameters:
389 **		none.
390 **
391 **	Returns:
392 **		none.
393 **
394 **	Side Effects:
395 **		prints rewrite rules.
396 */
397 
398 # ifdef DEBUG
399 
400 printrules()
401 {
402 	register struct rewrite *rwp;
403 	register int ruleset;
404 
405 	for (ruleset = 0; ruleset < 10; ruleset++)
406 	{
407 		if (RewriteRules[ruleset] == NULL)
408 			continue;
409 		printf("\n----Rule Set %d:", ruleset);
410 
411 		for (rwp = RewriteRules[ruleset]; rwp != NULL; rwp = rwp->r_next)
412 		{
413 			printf("\nLHS:");
414 			printav(rwp->r_lhs);
415 			printf("RHS:");
416 			printav(rwp->r_rhs);
417 		}
418 	}
419 }
420 
421 # endif DEBUG
422 /*
423 **  MFENCODE -- crack mailer options
424 **
425 **	These options modify the functioning of the mailer
426 **	from the configuration table.
427 **
428 **	Parameters:
429 **		p -- pointer to vector of options.
430 **
431 **	Returns:
432 **		option list in binary.
433 **
434 **	Side Effects:
435 **		none.
436 */
437 
438 struct optlist
439 {
440 	char	opt_name;	/* external name of option */
441 	u_long	opt_value;	/* internal name of option */
442 };
443 struct optlist	OptList[] =
444 {
445 	'f',	M_FOPT,
446 	'r',	M_ROPT,
447 	'q',	M_QUIET,
448 	'S',	M_RESTR,
449 	'n',	M_NHDR,
450 	'l',	M_LOCAL,
451 	's',	M_STRIPQ,
452 	'm',	M_MUSER,
453 	'F',	M_NEEDFROM,
454 	'D',	M_NEEDDATE,
455 	'M',	M_MSGID,
456 	'u',	M_USR_UPPER,
457 	'h',	M_HST_UPPER,
458 	'x',	M_FULLNAME,
459 	'A',	M_ARPAFMT,
460 	'U',	M_UGLYUUCP,
461 	'e',	M_EXPENSIVE,
462 	'X',	M_FULLSMTP,
463 	'C',	M_CANONICAL,
464 	'\0',	0
465 };
466 
467 u_long
468 mfencode(p)
469 	register char *p;
470 {
471 	register struct optlist *o;
472 	register u_long opts = 0;
473 
474 	while (*p != '\0')
475 	{
476 		for (o = OptList; o->opt_name != '\0' && o->opt_name != *p; o++)
477 			continue;
478 		if (o->opt_name == '\0')
479 			syserr("bad mailer option %c", *p);
480 		opts |= o->opt_value;
481 		p++;
482 	}
483 	return (opts);
484 }
485 /*
486 **  MFDECODE -- decode mailer flags into external form.
487 **
488 **	Parameters:
489 **		flags -- value of flags to decode.
490 **		f -- file to write them onto.
491 **
492 **	Returns:
493 **		none.
494 **
495 **	Side Effects:
496 **		none.
497 */
498 
499 mfdecode(flags, f)
500 	u_long flags;
501 	FILE *f;
502 {
503 	register struct optlist *o;
504 
505 	putc('?', f);
506 	for (o = OptList; o->opt_name != '\0'; o++)
507 	{
508 		if ((o->opt_value & flags) == o->opt_value)
509 		{
510 			flags &= ~o->opt_value;
511 			putc(o->opt_name, f);
512 		}
513 	}
514 	putc('?', f);
515 }
516