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