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