1 # include "sendmail.h"
2 
3 static char SccsId[] = "@(#)readcf.c	3.15	09/24/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 	STAB *s;
283 
284 	if (NextMailer >= MAXMAILERS)
285 	{
286 		syserr("Too many mailers defined");
287 		return;
288 	}
289 
290 	/* collect initial information */
291 	p = line;
292 	SETWORD;
293 	mname = q;
294 	SETWORD;
295 	mpath = q;
296 	SETWORD;
297 	mopts = crackopts(q);
298 	if (!safe)
299 		mopts &= ~M_RESTR;
300 	SETWORD;
301 	mfrom = q;
302 
303 	if (*p == '\0')
304 	{
305 		syserr("invalid M line in configuration file");
306 		return;
307 	}
308 
309 	/* allocate a mailer */
310 	m = (struct mailer *) xalloc(sizeof *m);
311 	m->m_name = newstr(mname);
312 	m->m_mailer = newstr(mpath);
313 	m->m_flags = mopts;
314 	m->m_from = newstr(mfrom);
315 	m->m_badstat = EX_UNAVAILABLE;
316 	m->m_sendq = NULL;
317 	m->m_mno = NextMailer;
318 	Mailer[NextMailer++] = m;
319 
320 	/* collect the argument vector */
321 	for (i = 0; i < MAXPV - 1 && *p != '\0'; i++)
322 	{
323 		SETWORD;
324 		margv[i] = newstr(q);
325 	}
326 	margv[i++] = NULL;
327 
328 	/* save the argv */
329 	m->m_argv = (char **) xalloc((unsigned) (sizeof margv[0] * i));
330 	bmove((char *) margv, (char *) m->m_argv, sizeof margv[0] * i);
331 	s = stab(m->m_name, ST_MAILER, ST_ENTER);
332 	s->s_mailer = m;
333 }
334 /*
335 **  PRINTRULES -- print rewrite rules (for debugging)
336 **
337 **	Parameters:
338 **		none.
339 **
340 **	Returns:
341 **		none.
342 **
343 **	Side Effects:
344 **		prints rewrite rules.
345 */
346 
347 # ifdef DEBUG
348 
349 printrules()
350 {
351 	register struct rewrite *rwp;
352 	register int ruleset;
353 
354 	for (ruleset = 0; ruleset < 10; ruleset++)
355 	{
356 		if (RewriteRules[ruleset] == NULL)
357 			continue;
358 		printf("\n----Rule Set %d:\n", ruleset);
359 
360 		for (rwp = RewriteRules[ruleset]; rwp != NULL; rwp = rwp->r_next)
361 		{
362 			register char **av;
363 
364 			printf("\n");
365 			for (av = rwp->r_lhs; *av != NULL; av++)
366 			{
367 				xputs(*av);
368 				putchar('_');
369 			}
370 			printf("\n\t");
371 			for (av = rwp->r_rhs; *av != NULL; av++)
372 			{
373 				xputs(*av);
374 				putchar('_');
375 			}
376 			printf("\n");
377 		}
378 	}
379 }
380 
381 # endif DEBUG
382 /*
383 **  CRACKOPTS -- crack mailer options
384 **
385 **	These options modify the functioning of the mailer
386 **	from the configuration table.
387 **
388 **	Parameters:
389 **		p -- pointer to vector of options.
390 **
391 **	Returns:
392 **		option list in binary.
393 **
394 **	Side Effects:
395 **		none.
396 */
397 
398 struct optlist
399 {
400 	char	opt_name;	/* external name of option */
401 	int	opt_value;	/* internal name of option */
402 };
403 struct optlist	OptList[] =
404 {
405 	'f',	M_FOPT,
406 	'r',	M_ROPT,
407 	'q',	M_QUIET,
408 	'S',	M_RESTR,
409 	'n',	M_NHDR,
410 	'l',	M_LOCAL,
411 	's',	M_STRIPQ,
412 	'm',	M_MUSER,
413 	'F',	M_NEEDFROM,
414 	'D',	M_NEEDDATE,
415 	'M',	M_MSGID,
416 	'u',	M_USR_UPPER,
417 	'h',	M_HST_UPPER,
418 	'x',	M_FULLNAME,
419 	'A',	M_ARPAFMT,
420 	'\0',	0
421 };
422 
423 crackopts(p)
424 	register char *p;
425 {
426 	register struct optlist *o;
427 	register int opts = 0;
428 
429 	while (*p != '\0')
430 	{
431 		for (o = OptList; o->opt_name != '\0' && o->opt_name != *p; o++)
432 			continue;
433 		if (o->opt_name == '\0')
434 			syserr("bad mailer option %c", *p);
435 		opts |= o->opt_value;
436 		p++;
437 	}
438 	return (opts);
439 }
440