1 # include "sendmail.h"
2 
3 SCCSID(@(#)readcf.c	3.30		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 	register struct mailer *m;
288 	register STAB *s;
289 	int i;
290 	char *mname;
291 	char *mpath;
292 	u_long mopts;
293 	short mrset, msset;
294 	char *margv[MAXPV + 1];
295 	extern u_long mfencode();
296 	extern int NextMailer;
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 	msset = atoi(q);
317 	SETWORD;
318 	mrset = atoi(q);
319 
320 	if (*p == '\0')
321 	{
322 		syserr("readcf: line %d: invalid M line in configuration file",
323 			LineNumber);
324 		return;
325 	}
326 	if (msset >= MAXRWSETS || mrset >= MAXRWSETS)
327 	{
328 		syserr("readcf: line %d: invalid rewrite set, %d max",
329 			LineNumber, MAXRWSETS);
330 		return;
331 	}
332 
333 	/* allocate a mailer */
334 	m = (struct mailer *) xalloc(sizeof *m);
335 	m->m_name = newstr(mname);
336 	m->m_mailer = newstr(mpath);
337 	m->m_flags = mopts;
338 	m->m_r_rwset = mrset;
339 	m->m_s_rwset = msset;
340 	m->m_badstat = EX_UNAVAILABLE;
341 	m->m_mno = NextMailer;
342 	Mailer[NextMailer++] = m;
343 
344 	/* collect the argument vector */
345 	for (i = 0; i < MAXPV - 1 && *p != '\0'; i++)
346 	{
347 		SETWORD;
348 		margv[i] = newstr(q);
349 	}
350 	margv[i++] = NULL;
351 
352 	/* save the argv */
353 	m->m_argv = (char **) xalloc(sizeof margv[0] * i);
354 	bmove((char *) margv, (char *) m->m_argv, sizeof margv[0] * i);
355 	s = stab(m->m_name, ST_MAILER, ST_ENTER);
356 	s->s_mailer = m;
357 }
358 /*
359 **  PRINTRULES -- print rewrite rules (for debugging)
360 **
361 **	Parameters:
362 **		none.
363 **
364 **	Returns:
365 **		none.
366 **
367 **	Side Effects:
368 **		prints rewrite rules.
369 */
370 
371 # ifdef DEBUG
372 
373 printrules()
374 {
375 	register struct rewrite *rwp;
376 	register int ruleset;
377 
378 	for (ruleset = 0; ruleset < 10; ruleset++)
379 	{
380 		if (RewriteRules[ruleset] == NULL)
381 			continue;
382 		printf("\n----Rule Set %d:", ruleset);
383 
384 		for (rwp = RewriteRules[ruleset]; rwp != NULL; rwp = rwp->r_next)
385 		{
386 			printf("\nLHS:");
387 			printav(rwp->r_lhs);
388 			printf("RHS:");
389 			printav(rwp->r_rhs);
390 		}
391 	}
392 }
393 
394 # endif DEBUG
395 /*
396 **  MFENCODE -- crack mailer options
397 **
398 **	These options modify the functioning of the mailer
399 **	from the configuration table.
400 **
401 **	Parameters:
402 **		p -- pointer to vector of options.
403 **
404 **	Returns:
405 **		option list in binary.
406 **
407 **	Side Effects:
408 **		none.
409 */
410 
411 struct optlist
412 {
413 	char	opt_name;	/* external name of option */
414 	u_long	opt_value;	/* internal name of option */
415 };
416 struct optlist	OptList[] =
417 {
418 	'f',	M_FOPT,
419 	'r',	M_ROPT,
420 	'q',	M_QUIET,
421 	'S',	M_RESTR,
422 	'n',	M_NHDR,
423 	'l',	M_LOCAL,
424 	's',	M_STRIPQ,
425 	'm',	M_MUSER,
426 	'F',	M_NEEDFROM,
427 	'D',	M_NEEDDATE,
428 	'M',	M_MSGID,
429 	'u',	M_USR_UPPER,
430 	'h',	M_HST_UPPER,
431 	'x',	M_FULLNAME,
432 	'A',	M_ARPAFMT,
433 	'U',	M_UGLYUUCP,
434 	'e',	M_EXPENSIVE,
435 	'X',	M_FULLSMTP,
436 	'\0',	0
437 };
438 
439 u_long
440 mfencode(p)
441 	register char *p;
442 {
443 	register struct optlist *o;
444 	register u_long opts = 0;
445 
446 	while (*p != '\0')
447 	{
448 		for (o = OptList; o->opt_name != '\0' && o->opt_name != *p; o++)
449 			continue;
450 		if (o->opt_name == '\0')
451 			syserr("bad mailer option %c", *p);
452 		opts |= o->opt_value;
453 		p++;
454 	}
455 	return (opts);
456 }
457 /*
458 **  MFDECODE -- decode mailer flags into external form.
459 **
460 **	Parameters:
461 **		flags -- value of flags to decode.
462 **		f -- file to write them onto.
463 **
464 **	Returns:
465 **		none.
466 **
467 **	Side Effects:
468 **		none.
469 */
470 
471 mfdecode(flags, f)
472 	u_long flags;
473 	FILE *f;
474 {
475 	register struct optlist *o;
476 
477 	putc('?', f);
478 	for (o = OptList; o->opt_name != '\0'; o++)
479 	{
480 		if ((o->opt_value & flags) == o->opt_value)
481 		{
482 			flags &= ~o->opt_value;
483 			putc(o->opt_name, f);
484 		}
485 	}
486 	putc('?', f);
487 }
488