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