1 /*
2  * Copyright (c) 1992 Eric P. Allman
3  * Copyright (c) 1992 Regents of the University of California.
4  * All rights reserved.
5  *
6  * %sccs.include.redist.c%
7  */
8 
9 #ifndef lint
10 char copyright[] =
11 "@(#) Copyright (c) 1992 The Regents of the University of California.\n\
12  All rights reserved.\n";
13 static char sccsid[] = "@(#)mldistrib.c	5.6 (Berkeley) 07/20/92";
14 #endif /* not lint */
15 
16 #include <sys/types.h>
17 #include <stdio.h>
18 #include <ctype.h>
19 #include <sysexits.h>
20 #include <paths.h>
21 
22 #define TRUE		1
23 #define FALSE		0
24 typedef char		BOOL;
25 
26 #define CHARNULL	((char *) NULL)
27 #define MAXMAILOPTS	20
28 
29 enum copymode {RETAIN, DISCARD};
30 char *myname;
31 int  debug;
32 
33 main(argc, argv)
34 	int argc;
35 	char **argv;
36 {
37 	BOOL seen_precedence;
38 	register FILE *mailfp;
39 	enum copymode mode;
40 	register char *p;
41 	char *ml_name;
42 	char *ml_owner;
43 	char *mailer_opts[MAXMAILOPTS+1];
44 	char **next_opt = mailer_opts;
45 	char c;
46 	extern FILE *openmailer();
47 	extern char *readheadertag();
48 	extern void copyheader();
49 	extern void dropheader();
50 	extern void copybody();
51 
52 	myname = argv[0];
53 	argc--, argv++;
54 	while (*argv[0] == '-')
55 	{
56 		if (strcmp(argv[0], "-d") == 0)
57 		{
58 			debug++;
59 			argv++;
60 			argc--;
61 			continue;
62 		}
63 		if (next_opt >= &mailer_opts[MAXMAILOPTS])
64 		{
65 			fprintf(stderr,
66 			    "%s: too many mailer options\n", myname);
67 			exit(EX_USAGE);
68 		}
69 		*next_opt++ = *argv++;
70 		argc--;
71 	}
72 	*next_opt = NULL;
73 
74 	/* parse arguments */
75 	if (argc < 3)
76 	{
77 		fprintf(stderr,
78 		    "Usage: %s [-mailopts ...] listname ownername member...\n",
79 		    myname);
80 		exit(EX_USAGE);
81 	}
82 
83 	ml_name = *argv++;
84 	ml_owner = *argv++;
85 
86 	/* consume and discard leading "From_" line */
87 	while ((c = fgetc(stdin)) != EOF && c != '\n')
88 		continue;
89 
90 	/* open the connection to the mailer */
91 	mailfp = openmailer(ml_owner, next_opt - mailer_opts, mailer_opts,
92 	    argc, argv);
93 
94 	/* output the Resent-xxx: fields */
95 	fprintf(mailfp, "Resent-To:	%s\n", ml_name);
96 	fprintf(mailfp, "Resent-From:	%s\n", ml_owner);
97 	fprintf(mailfp, "Sender:	%s\n", ml_owner);
98 
99 	/*
100 	**  Consume header
101 	**
102 	**	Errors-To:	discard
103 	**	Precedence:	retain; mark that it has been seen
104 	**	Received:	discard
105 	**	Resent-*:	discard
106 	**	Return-Path:	discard
107 	**	Via:		discard
108 	**	X-Mailer:	discard
109 	**	others		retain
110 	*/
111 
112 	seen_precedence = FALSE;
113 
114 	while ((p = readheadertag(stdin)) != CHARNULL)
115 	{
116 		extern BOOL sameword();
117 
118 		mode = RETAIN;
119 		switch (p[0])
120 		{
121 		  case 'e':
122 		  case 'E':
123 			if (sameword(p, "errors-to", 10))
124 				mode = DISCARD;
125 			break;
126 
127 		  case 'p':
128 		  case 'P':
129 			if (sameword(p, "precedence", 11))
130 				seen_precedence = TRUE;
131 			break;
132 
133 		  case 'r':
134 		  case 'R':
135 			if (sameword(p, "return-path", 12) ||
136 #ifdef notyet
137 			    sameword(p, "received", 9) ||
138 #endif
139 			    sameword(p, "resent-", 7))
140 				mode = DISCARD;
141 			break;
142 
143 		  case 's':
144 		  case 'S':
145 			if (sameword(p, "sender", 7))
146 				mode = DISCARD;
147 			break;
148 
149 		  case 'v':
150 		  case 'V':
151 			if (sameword(p, "via", 4))
152 				mode = DISCARD;
153 			break;
154 
155 		  case 'x':
156 		  case 'X':
157 			if (sameword(p, "x-mailer", 9))
158 				mode = DISCARD;
159 			break;
160 		}
161 
162 		switch (mode)
163 		{
164 		  case RETAIN:
165 			fprintf(mailfp, "%s", p);
166 			copyheader(stdin, mailfp);
167 			break;
168 
169 		  case DISCARD:
170 			dropheader(stdin);
171 			break;
172 		}
173 	}
174 
175 	/* if no precedence was given, make it bulk mail */
176 	if (!seen_precedence)
177 		fprintf(mailfp, "Precedence: bulk\n");
178 
179 	/* copy the body of the message */
180 	copybody(stdin, mailfp);
181 
182 	/* clean up the connection */
183 	exit (my_pclose(mailfp));
184 }
185 
186 
187 
188 /*
189 **  OPENMAILER -- open a connection to the mailer
190 */
191 
192 FILE *
193 openmailer(from, nopts, opts, argc, argv)
194 	char *from;
195 	int nopts, argc;
196 	char **opts, **argv;
197 {
198 	register char **argp;
199 	register FILE *mailfp;
200 	char **args;
201 	char *name;
202 	static char mailer[] = _PATH_SENDMAIL;
203 	extern int strlen();
204 	extern FILE *my_popen();
205 	extern char *malloc(), *rindex();
206 
207 	/*
208 	 * allocate space for argv; 4 args below, a null,
209 	 * and options and arguments from caller.
210 	 */
211 	args = (char **) malloc((nopts + argc + 5) * sizeof(char *));
212 	if (args == (char **) NULL)
213 	{
214 		fprintf(stderr,
215 		    "%s: arg list too long; can't allocate memory!?\n", myname);
216 		exit(EX_SOFTWARE);
217 	}
218 	argp = args;
219 	if ((name = rindex(mailer, '/')) != CHARNULL)
220 		name++;
221 	else
222 		name = mailer;
223 	*argp++ = name;
224 	*argp++ = "-f";
225 	*argp++ = from;
226 	*argp++ = "-oi";
227 	bcopy((char *) opts, (char *) argp, nopts * sizeof(*opts));
228 	argp += nopts;
229 	bcopy((char *) argv, (char *) argp, argc * sizeof(*argv));
230 	argp += argc;
231 	*argp = CHARNULL;
232 
233 	if (debug)
234 	{
235 		printf("| %s, args:\n", _PATH_SENDMAIL);
236 		for (argp = args; *argp; argp++)
237 			printf("  %s\n", *argp);
238 		printf("--------\n");
239 		return (stdout);
240 	}
241 	mailfp = my_popen(mailer, args, "w");
242 	if (mailfp == NULL)
243 	{
244 		fprintf(stderr, "%s: Unable to popen %s\n", myname,
245 		   _PATH_SENDMAIL);
246 		exit(EX_OSFILE);
247 	}
248 
249 	return (mailfp);
250 }
251 
252 
253 
254 /*
255 **  DROPHEADER -- drop a single header field
256 */
257 
258 void
259 dropheader(infp)
260 	register FILE *infp;
261 {
262 	register int c;
263 
264 	while ((c = fgetc(infp)) != EOF)
265 	{
266 		if (c == '\n')
267 		{
268 			/* look at next character to check for continuation */
269 			c = fgetc(infp);
270 			if (c == ' ' || c == '\t')
271 				continue;
272 			if (c != EOF)
273 				ungetc(c, infp);
274 			break;
275 		}
276 	}
277 }
278 
279 
280 
281 /*
282 **  COPYHEADER -- copy a single header field
283 */
284 
285 void
286 copyheader(infp, outfp)
287 	register FILE *infp;
288 	register FILE *outfp;
289 {
290 	register int c;
291 
292 	while ((c = fgetc(infp)) != EOF)
293 	{
294 		(void) fputc(c, outfp);
295 		if (c == '\n')
296 		{
297 			/* look at next character to check for continuation */
298 			c = fgetc(infp);
299 			if (c == ' ' || c == '\t')
300 			{
301 				(void) fputc(c, outfp);
302 				continue;
303 			}
304 			if (c != EOF)
305 				ungetc(c, infp);
306 			break;
307 		}
308 	}
309 }
310 
311 
312 
313 /*
314 **  READHEADERTAG -- read and return the name of a header field
315 */
316 
317 #define MAXHDRTAG	60
318 
319 char *
320 readheadertag(infp)
321 	register FILE *infp;
322 {
323 	register int c;
324 	register char *bp;
325 	int i;
326 	static char buf[MAXHDRTAG + 1];
327 	extern char *strchr();
328 
329 	c = fgetc(infp);
330 	if (c == EOF)
331 		return (CHARNULL);
332 	if (c == '\n')
333 	{
334 		ungetc(c, infp);
335 		return (CHARNULL);
336 	}
337 
338 	bp = buf;
339 	i = sizeof buf;
340 	do
341 	{
342 		*bp++ = c;
343 		c = fgetc(infp);
344 	} while (--i > 0 && c != EOF && c != '\0' &&
345 		 strchr(" \t\n:", c) == CHARNULL);
346 	if (c != EOF)
347 		ungetc(c, infp);
348 	*bp++ = '\0';
349 	return (buf);
350 }
351 
352 
353 
354 /*
355 **  COPYBODY -- copy the body of a message
356 */
357 
358 void
359 copybody(infp, outfp)
360 	register FILE *infp;
361 	register FILE *outfp;
362 {
363 	register int c;
364 
365 	while ((c = fgetc(infp)) != EOF)
366 		fputc(c, outfp);
367 }
368 
369 
370 
371 /*
372 **  SAMEWORD -- return true if two words are identical.  The first
373 **		word is case insignificant; the second must be lower case.
374 */
375 
376 BOOL
377 sameword(test, pat, len)
378 	register char *test;
379 	register char *pat;
380 	int len;
381 {
382 	for (; --len >= 0; test++, pat++)
383 	{
384 		if (*test == *pat)
385 			continue;
386 		if (isupper(*test) && tolower(*test) == *pat)
387 			continue;
388 		return (FALSE);
389 	}
390 	return (TRUE);
391 }
392 
393 
394 
395 /*
396  * from libc popen:
397 static char sccsid[] = "@(#)popen.c	5.12 (Berkeley) 4/6/90";
398  *
399  * This code is derived from software written by Ken Arnold and
400  * published in UNIX Review, Vol. 6, No. 8.
401  *
402  * modified to avoid sh, be safe for setuid/setgid programs,
403  * and simplified to support only one popen'ed stream.
404  */
405 
406 
407 #include <sys/signal.h>
408 #include <sys/wait.h>
409 #include <errno.h>
410 #include <unistd.h>
411 /*
412 #include <stdio.h>
413 #include <paths.h>
414 */
415 
416 static pid_t pid;
417 
418 FILE *
419 my_popen(program, args, type)
420 	char *program, *type;
421 	char **args;
422 {
423 	FILE *iop;
424 	int pdes[2];
425 	char *malloc();
426 
427 	if (*type != 'r' && *type != 'w' || type[1])
428 		return(NULL);
429 
430 	if (pipe(pdes) < 0)
431 		return(NULL);
432 	switch (pid = vfork()) {
433 	case -1:			/* error */
434 		(void)close(pdes[0]);
435 		(void)close(pdes[1]);
436 		return(NULL);
437 		/* NOTREACHED */
438 	case 0:				/* child */
439 		if (*type == 'r') {
440 			if (pdes[1] != STDOUT_FILENO) {
441 				(void)dup2(pdes[1], STDOUT_FILENO);
442 				(void)close(pdes[1]);
443 			}
444 			(void)close(pdes[0]);
445 		} else {
446 			if (pdes[0] != STDIN_FILENO) {
447 				(void)dup2(pdes[0], STDIN_FILENO);
448 				(void)close(pdes[0]);
449 			}
450 			(void)close(pdes[1]);
451 		}
452 		execv(program, args);
453 		_exit(127);
454 		/* NOTREACHED */
455 	}
456 	/* parent; assume fdopen can't fail...  */
457 	if (*type == 'r') {
458 		iop = fdopen(pdes[0], type);
459 		(void)close(pdes[1]);
460 	} else {
461 		iop = fdopen(pdes[1], type);
462 		(void)close(pdes[0]);
463 	}
464 	return (iop);
465 }
466 
467 my_pclose(iop)
468 	FILE *iop;
469 {
470 	extern int errno;
471 	register int fdes;
472 	int omask;
473 	int pstat;
474 	pid_t wpid;
475 
476 	(void)fclose(iop);
477 	omask = sigblock(sigmask(SIGINT)|sigmask(SIGQUIT)|sigmask(SIGHUP));
478 	do {
479 		wpid = waitpid(pid, &pstat, 0);
480 	} while (wpid == -1 && errno == EINTR);
481 	(void)sigsetmask(omask);
482 	pid = 0;
483 	return (pid == -1 ? -1 : pstat);
484 }
485