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