xref: /csrg-svn/libexec/bugfiler/bugfiler.c (revision 14809)
114552Ssam #ifndef lint
2*14809Ssam static char sccsid[] = "@(#)bugfiler.c	4.6 (Berkeley) 08/23/83";
314552Ssam #endif
414552Ssam 
512375Sralph /*
612375Sralph  * Bug report processing program.
7*14809Ssam  * It is designed to be invoked by alias(5)
8*14809Ssam  * and to be compatible with mh.
912375Sralph  */
1012375Sralph 
1112375Sralph #include <stdio.h>
1212375Sralph #include <ctype.h>
1312375Sralph #include <signal.h>
14*14809Ssam #include <pwd.h>
15*14809Ssam 
1612375Sralph #include <sys/types.h>
1712375Sralph #include <sys/stat.h>
1812695Sralph #include <sys/dir.h>
1912375Sralph 
20*14809Ssam #define	BUGS_NAME	"4bsd-bugs"
21*14809Ssam #define	BUGS_HOME	"%ucbarpa@BERKELEY"
22*14809Ssam #define	MAILCMD		"/usr/lib/sendmail -i -t"
23*14809Ssam 
2414808Ssam char	unixtomh[] = "/usr/new/lib/mh/unixtomh";
25*14809Ssam #ifdef notdef
26*14809Ssam char	*bugperson = "bugs";
27*14809Ssam char	*maildir = "mail";
28*14809Ssam #else
29*14809Ssam char	*bugperson = "sam";
30*14809Ssam char	*maildir = "Mail";
31*14809Ssam #endif
3212375Sralph char	ackfile[] = ".ack";
3312375Sralph char	errfile[] = ".format";
3412375Sralph char	sumfile[] = "summary";
3512375Sralph char	logfile[] = "errors/log";
36*14809Ssam char	redistfile[] = ".redist";
3712375Sralph char	tmpname[] = "BfXXXXXX";
3812375Sralph char	draft[] = "RpXXXXXX";
39*14809Ssam char	disttmp[] = "RcXXXXXX";
4012375Sralph 
4112695Sralph char	buf[8192];
4212375Sralph char	folder[MAXNAMLEN];
4312375Sralph int	num;
4412375Sralph int	msg_prot = 0664;
4512375Sralph 
4612375Sralph int	debug;
4712375Sralph 
4812375Sralph char	*index();
4912375Sralph char	*rindex();
5012375Sralph char	*fixaddr();
51*14809Ssam char	*any();
5212375Sralph 
5312375Sralph main(argc, argv)
5412375Sralph 	char *argv[];
5512375Sralph {
5612375Sralph 	register char *cp;
5712695Sralph 	register int n;
5812695Sralph 	int pfd[2];
5912375Sralph 
6012375Sralph 	if (argc > 3) {
6112375Sralph 	usage:
6212695Sralph 		fprintf(stderr, "Usage: bugfiler [-d] [-mmsg_mode] [maildir]\n");
6312375Sralph 		exit(1);
6412375Sralph 	}
6512375Sralph 	while (--argc > 0) {
6612375Sralph 		cp = *++argv;
6712695Sralph 		if (*cp == '-')
6812695Sralph 			switch (cp[1]) {
6912375Sralph 			case 'd':
7012375Sralph 				debug++;
7112375Sralph 				break;
7212695Sralph 
7312695Sralph 			case 'm':	/* set message protection */
7412695Sralph 				n = 0;
7512695Sralph 				for (cp += 2; *cp >= '0' && *cp <= '7'; )
7612695Sralph 					n = (n << 3) + (*cp++ - '0');
7712695Sralph 				msg_prot = n & 0777;
7812695Sralph 				break;
7912695Sralph 
8012375Sralph 			default:
8112375Sralph 				goto usage;
8212375Sralph 			}
8312375Sralph 		else
8412375Sralph 			maildir = cp;
8512375Sralph 	}
8612695Sralph 	if (!debug)
8712695Sralph 		freopen(logfile, "a", stderr);
8812695Sralph 
89*14809Ssam 	if (bugperson) {
90*14809Ssam 		struct passwd *pwd = getpwnam(bugperson);
91*14809Ssam 
92*14809Ssam 		if (pwd == NULL) {
93*14809Ssam 			fprintf(stderr, "%s: bugs person is unknown\n",
94*14809Ssam 			    bugperson);
95*14809Ssam 			exit(1);
96*14809Ssam 		}
97*14809Ssam 		if (chdir(pwd->pw_dir) < 0) {
98*14809Ssam 			fprintf(stderr, "can't chdir to %s\n", pwd->pw_dir);
99*14809Ssam 			exit(1);
100*14809Ssam 		}
101*14809Ssam 	}
10212375Sralph 	if (chdir(maildir) < 0) {
10312375Sralph 		fprintf(stderr, "can't chdir to %s\n", maildir);
10412375Sralph 		exit(1);
10512375Sralph 	}
10612695Sralph 	umask(0);
10712695Sralph 
10812695Sralph #ifdef UNIXCOMP
10912695Sralph 	/*
11012695Sralph 	 * Convert UNIX style mail to mh style by filtering stdin through
11112695Sralph 	 * unixtomh.
11212695Sralph 	 */
11312695Sralph 	if (pipe(pfd) >= 0) {
11412695Sralph 		while ((n = fork()) == -1)
11512695Sralph 			sleep(5);
11612695Sralph 		if (n == 0) {
11712695Sralph 			close(pfd[0]);
11812695Sralph 			dup2(pfd[1], 1);
11912695Sralph 			close(pfd[1]);
12012695Sralph 			execl(unixtomh, "unixtomh", 0);
12112695Sralph 			_exit(127);
12212695Sralph 		}
12312695Sralph 		close(pfd[1]);
12412695Sralph 		dup2(pfd[0], 0);
12512695Sralph 		close(pfd[0]);
12612695Sralph 	}
12712695Sralph #endif
12812695Sralph 	while (process())
12912695Sralph 		;
13012695Sralph 	exit(0);
13112375Sralph }
13212375Sralph 
13312695Sralph /* states */
13412695Sralph 
13512695Sralph #define EOM	0	/* End of message seen */
13612695Sralph #define FLD	1	/* Looking for header lines */
13712695Sralph #define BODY	2	/* Looking for message body lines */
13812695Sralph 
13912375Sralph /* defines used for tag attributes */
14012375Sralph 
14112375Sralph #define H_REQ 01
14212695Sralph #define H_SAV 02
14312695Sralph #define H_HDR 04
14412695Sralph #define H_FND 010
14512375Sralph 
14612375Sralph #define FROM_I headers[0].h_info
14712375Sralph #define SUBJECT_I headers[1].h_info
14812375Sralph #define INDEX &headers[2]
14912375Sralph #define INDEX_I headers[2].h_info
15012375Sralph #define DATE_I headers[3].h_info
15112375Sralph #define MSGID_I headers[4].h_info
15212375Sralph #define REPLYTO_I headers[5].h_info
15312375Sralph #define RETURNPATH_I headers[6].h_info
15412375Sralph #define TO_I headers[7].h_info
15512375Sralph #define CC_I headers[8].h_info
15612375Sralph #define FIX headers[11]
15712375Sralph 
15812375Sralph struct header {
15912375Sralph 	char	*h_tag;
16012375Sralph 	int	h_flags;
16112375Sralph 	char	*h_info;
16212375Sralph } headers[] = {
16312695Sralph 	"From",		H_REQ|H_SAV|H_HDR, 0,
16412695Sralph 	"Subject",	H_REQ|H_SAV|H_HDR, 0,
16512375Sralph 	"Index",	H_REQ|H_SAV, 0,
16612695Sralph 	"Date",		H_SAV|H_HDR, 0,
16712695Sralph 	"Message-Id",	H_SAV|H_HDR, 0,
16812695Sralph 	"Reply-To",	H_SAV|H_HDR, 0,
16912695Sralph 	"Return-Path",	H_SAV|H_HDR, 0,
17012695Sralph 	"To",		H_SAV|H_HDR, 0,
17112695Sralph 	"Cc",		H_SAV|H_HDR, 0,
17212375Sralph 	"Description",	H_REQ,       0,
17312375Sralph 	"Repeat-By",	H_REQ,	     0,
17412695Sralph 	"Fix",		0,	     0,
17512375Sralph 	0,	0,	0,
17612375Sralph };
17712375Sralph 
17812695Sralph struct header *findheader();
17912695Sralph 
18012375Sralph process()
18112375Sralph {
18212375Sralph 	register struct header *hp;
18312375Sralph 	register char *cp;
18412695Sralph 	register int c;
18512375Sralph 	char *info;
18612695Sralph 	int state, tmp;
18712695Sralph 	FILE *tfp, *fs;
18812375Sralph 
18912375Sralph 	/*
19012375Sralph 	 * Insure all headers are in a consistent
19112375Sralph 	 * state.  Anything left there is free'd.
19212375Sralph 	 */
19312375Sralph 	for (hp = headers; hp->h_tag; hp++) {
19412695Sralph 		hp->h_flags &= ~H_FND;
19512375Sralph 		if (hp->h_info) {
19612695Sralph 			free(hp->h_info);
19712375Sralph 			hp->h_info = 0;
19812375Sralph 		}
19912375Sralph 	}
20012375Sralph 	/*
20112375Sralph 	 * Read the report and make a copy.  Must conform to RFC822 and
20212375Sralph 	 * be of the form... <tag>: <info>
20312695Sralph 	 * Note that the input is expected to be in mh mail format
20412695Sralph 	 * (i.e., messages are separated by lines of ^A's).
20512375Sralph 	 */
20612695Sralph 	while ((c = getchar()) == '\001' && peekc(stdin) == '\001')
20712695Sralph 		while (getchar() != '\n')
20812695Sralph 			;
20912695Sralph 	if (c == EOF)
21012695Sralph 		return(0);	/* all done */
21112695Sralph 
21212375Sralph 	mktemp(tmpname);
21312695Sralph 	if ((tmp = creat(tmpname, msg_prot)) < 0) {
21412695Sralph 		fprintf(stderr, "cannont create %s\n", tmpname);
21512695Sralph 		exit(1);
21612695Sralph 	}
21712695Sralph 	if ((tfp = fdopen(tmp, "w")) == NULL) {
21812695Sralph 		fprintf(stderr, "cannot fdopen temp file\n");
21912695Sralph 		exit(1);
22012695Sralph 	}
22112695Sralph 
22212695Sralph 	for (state = FLD; state != EOF && state != EOM; c = getchar()) {
22312695Sralph 		switch (state) {
22412695Sralph 		case FLD:
22512695Sralph 			if (c == '\n' || c == '-')
22612695Sralph 				goto body;
22712695Sralph 			for (cp = buf; c != ':'; c = getchar()) {
22812695Sralph 				if (cp < buf+sizeof(buf)-1 && c != '\n' && c != EOF) {
22912695Sralph 					*cp++ = c;
23012695Sralph 					continue;
23112695Sralph 				}
23212695Sralph 				*cp = '\0';
23312695Sralph 				fputs(buf, tfp);
23412695Sralph 				state = EOF;
23512695Sralph 				while (c != EOF) {
23612695Sralph 					if (c == '\n')
23712695Sralph 						if ((tmp = peekc(stdin)) == EOF)
23812695Sralph 							break;
23912695Sralph 						else if (tmp == '\001') {
24012695Sralph 							state = EOM;
24112695Sralph 							break;
24212695Sralph 						}
24312695Sralph 					putc(c, tfp);
24412695Sralph 					c = getchar();
24512695Sralph 				}
24612695Sralph 				fclose(tfp);
24712695Sralph 				goto badfmt;
24812695Sralph 			}
24912695Sralph 			*cp = '\0';
25012695Sralph 			fprintf(tfp, "%s:", buf);
25112695Sralph 			hp = findheader(buf, state);
25212695Sralph 
25312695Sralph 			for (cp = buf; ; ) {
25412695Sralph 				if (cp >= buf+sizeof(buf)-1) {
25512695Sralph 					fprintf(stderr, "field truncated\n");
25612695Sralph 					while ((c = getchar()) != EOF && c != '\n')
25712695Sralph 						putc(c, tfp);
25812695Sralph 				}
25912695Sralph 				if ((c = getchar()) == EOF) {
26012695Sralph 					state = EOF;
26112695Sralph 					break;
26212695Sralph 				}
26312695Sralph 				putc(c, tfp);
26412695Sralph 				*cp++ = c;
26512695Sralph 				if (c == '\n')
26612695Sralph 					if ((c = peekc(stdin)) != ' ' && c != '\t') {
26712695Sralph 						if (c == EOF)
26812695Sralph 							state = EOF;
26912695Sralph 						else if (c == '\001')
27012695Sralph 							state = EOM;
27112695Sralph 						break;
27212695Sralph 					}
27312695Sralph 			}
27412695Sralph 			*cp = '\0';
27512695Sralph 			cp = buf;
27612695Sralph 			break;
27712695Sralph 
27812695Sralph 		body:
27912695Sralph 			state = BODY;
28012695Sralph 		case BODY:
28112695Sralph 			for (cp = buf; ; c = getchar()) {
28212695Sralph 				if (c == EOF) {
28312695Sralph 					state = EOF;
28412695Sralph 					break;
28512695Sralph 				}
28612695Sralph 				if (c == '\001' && peekc(stdin) == '\001') {
28712695Sralph 					state = EOM;
28812695Sralph 					break;
28912695Sralph 				}
29012695Sralph 				putc(c, tfp);
29112695Sralph 				*cp++ = c;
29212695Sralph 				if (cp >= buf+sizeof(buf)-1 || c == '\n')
29312695Sralph 					break;
29412695Sralph 			}
29512695Sralph 			*cp = '\0';
29612695Sralph 			if ((cp = index(buf, ':')) == NULL)
29712695Sralph 				continue;
29812695Sralph 			*cp++ = '\0';
29912695Sralph 			hp = findheader(buf, state);
30012695Sralph 		}
30112695Sralph 
30212695Sralph 		/*
30312695Sralph 		 * Don't save the info if the header wasn't found, we don't
30412695Sralph 		 * care about the info, or the header is repeated.
30512695Sralph 		 */
30612695Sralph 		if (hp == NULL || !(hp->h_flags & H_SAV) || hp->h_info)
30712375Sralph 			continue;
30812375Sralph 		while (isspace(*cp))
30912375Sralph 			cp++;
31012375Sralph 		if (*cp) {
31112375Sralph 			info = cp;
31212375Sralph 			while (*cp++);
31312375Sralph 			cp--;
31412375Sralph 			while (isspace(cp[-1]))
31512375Sralph 				*--cp = '\0';
31612375Sralph 			hp->h_info = (char *) malloc(strlen(info) + 1);
31712695Sralph 			if (hp->h_info == NULL) {
31812695Sralph 				fprintf(stderr, "ran out of memory\n");
31912375Sralph 				continue;
32012695Sralph 			}
32112375Sralph 			strcpy(hp->h_info, info);
32212375Sralph 			if (hp == INDEX)
32312375Sralph 				chkindex(hp);
32412375Sralph 		}
32512375Sralph 	}
32612695Sralph 	fclose(tfp);
32712375Sralph 	/*
32812375Sralph 	 * Verify all the required pieces of information
32912375Sralph 	 * are present.
33012375Sralph 	 */
33112695Sralph 	for (hp = headers; hp->h_tag; hp++) {
33212375Sralph 		/*
33312375Sralph 		 * Mail the bug report back to the sender with a note
33412375Sralph 		 * explaining they must conform to the specification.
33512375Sralph 		 */
33612695Sralph 		if ((hp->h_flags & H_REQ) && !(hp->h_flags & H_FND)) {
33712695Sralph 			if (debug)
33812695Sralph 				printf("Missing %s\n", hp->h_tag);
33912695Sralph 		badfmt:
34012695Sralph 			reply(FROM_I, errfile, tmpname);
34112695Sralph 			file(tmpname, "errors");
34212695Sralph 			return(state == EOM);
34312695Sralph 		}
34412375Sralph 	}
34512375Sralph 	/*
34612695Sralph 	 * Acknowledge receipt.
34712695Sralph 	 */
34812695Sralph 	reply(FROM_I, ackfile, (char *)0);
34912695Sralph 	file(tmpname, folder);
35012695Sralph 	/*
35112375Sralph 	 * Append information about the new bug report
35212375Sralph 	 * to the summary file.
35312375Sralph 	 */
35412695Sralph 	if ((fs = fopen(sumfile, "a")) == NULL)
35512375Sralph 		fprintf(stderr, "Can't open %s\n", sumfile);
35612695Sralph 	else {
35712695Sralph 		fprintf(fs, "%14.14s/%-3d  ", folder, num);
35812695Sralph 		fprintf(fs, "%-51.51s Recv\n", INDEX_I);
35912695Sralph 		fprintf(fs, "\t\t    %-51.51s\n", SUBJECT_I);
36012375Sralph 	}
36112375Sralph 	fclose(fs);
362*14809Ssam 	/*
363*14809Ssam 	 * Check redistribution list and, if members,
364*14809Ssam 	 * mail a copy of the bug report to these people.
365*14809Ssam 	 */
366*14809Ssam 	redistribute(folder, num);
36712695Sralph 	return(state == EOM);
36812375Sralph }
36912375Sralph 
37012375Sralph /*
37112695Sralph  * Lookup the string in the list of headers and return a pointer
37212695Sralph  * to the entry or NULL.
37312695Sralph  */
37412695Sralph 
37512695Sralph struct header *
37612695Sralph findheader(name, state)
37712695Sralph 	char *name;
37812695Sralph 	int state;
37912695Sralph {
38012695Sralph 	register struct header *hp;
38112695Sralph 
38212695Sralph 	if (debug)
38312695Sralph 		printf("findheader(%s, %d)\n", name, state);
38412695Sralph 
38512695Sralph 	for (hp = headers; hp->h_tag; hp++) {
38612695Sralph 		if (!streq(hp->h_tag, buf))
38712695Sralph 			continue;
38812695Sralph 		if ((hp->h_flags & H_HDR) && state != FLD)
38912695Sralph 			continue;
39012695Sralph 		hp->h_flags |= H_FND;
39112695Sralph 		return(hp);
39212695Sralph 	}
39312695Sralph 	return(NULL);
39412695Sralph }
39512695Sralph 
39612695Sralph /*
39712375Sralph  * Check the format of the Index information.
39812375Sralph  * A side effect is to set the name of the folder if all is well.
39912375Sralph  */
40012375Sralph 
40112375Sralph chkindex(hp)
40212375Sralph 	struct header *hp;
40312375Sralph {
40412695Sralph 	register char *cp1, *cp2;
40512375Sralph 	register char c;
40612375Sralph 	struct stat stbuf;
40712375Sralph 
40812375Sralph 	if (debug)
40912695Sralph 		printf("chkindex(%s)\n", hp->h_info);
41012375Sralph 	/*
41112695Sralph 	 * Strip of leading "/", "usr/", "src/" or "sys/".
41212695Sralph 	 */
41312695Sralph 	cp1 = hp->h_info;
41412695Sralph 	while (*cp1 == '/')
41512695Sralph 		cp1++;
41612695Sralph 	while (substr(cp1, "usr/") || substr(cp1, "src/") || substr(cp1, "sys/"))
41712695Sralph 		cp1 += 4;
418*14809Ssam 	/*
419*14809Ssam 	 * Don't toss "sys/" if nothing else is given for
420*14809Ssam 	 * a folder name, this is a valid folder as well.
421*14809Ssam 	 */
422*14809Ssam 	if (index(cp1, '/') == NULL && substr(cp1 - 4, "sys/"))
423*14809Ssam 		cp1 -= 4;
42412695Sralph 	/*
42512375Sralph 	 * Read the folder name and remove it from the index line.
42612375Sralph 	 */
42712695Sralph 	for (cp2 = folder; ;) {
42812695Sralph 		switch (c = *cp1++) {
42912695Sralph 		case '/':
43012695Sralph 			if (cp2 == folder)
43112695Sralph 				continue;
43212375Sralph 			break;
43312695Sralph 		case '\0':
43412695Sralph 			cp1--;
43512695Sralph 			break;
43612695Sralph 		case ' ':
43712695Sralph 		case '\t':
43812695Sralph 			while (isspace(*cp1))
43912695Sralph 				cp1++;
44012695Sralph 			break;
44112695Sralph 		default:
44212695Sralph 			if (cp2 < folder+sizeof(folder)-1)
44312695Sralph 				*cp2++ = c;
44412695Sralph 			continue;
44512375Sralph 		}
44612695Sralph 		*cp2 = '\0';
44712695Sralph 		for (cp2 = hp->h_info; *cp2++ = *cp1++; )
44812695Sralph 			;
44912695Sralph 		break;
45012375Sralph 	}
45112695Sralph 	if (debug)
45212695Sralph 		printf("folder = %s\n", folder);
45312375Sralph 	/*
45412375Sralph 	 * Check to make sure we have a valid folder name
45512375Sralph 	 */
45612375Sralph 	if (stat(folder, &stbuf) == 0 && (stbuf.st_mode & S_IFMT) == S_IFDIR)
45712375Sralph 		return;
45812375Sralph 	/*
45912375Sralph 	 * The Index line is not in the correct format so clear
46012695Sralph 	 * the H_FND flag to mail back the correct format.
46112375Sralph 	 */
46212695Sralph 	hp->h_flags &= ~H_FND;
46312375Sralph }
46412375Sralph 
46512375Sralph /*
46612375Sralph  * Move or copy the file msg to the folder (directory).
467*14809Ssam  * As a side effect, num is set to the number under which
468*14809Ssam  * the message is filed in folder.
46912375Sralph  */
47012375Sralph 
47112375Sralph file(fname, folder)
47212375Sralph 	char *fname, *folder;
47312375Sralph {
47412375Sralph 	register char *cp, n;
47512375Sralph 	char msgname[MAXNAMLEN*2+2];
47612375Sralph 	struct stat stbuf;
47712375Sralph 	DIR *dirp;
47812375Sralph 	struct direct *d;
47912375Sralph 
48012375Sralph 	if (debug)
48112695Sralph 		printf("file(%s, %s)\n", fname, folder);
48212375Sralph 	/*
48312375Sralph 	 * Get the next number to use by finding the last message number
48412375Sralph 	 * in folder and adding one.
48512375Sralph 	 */
48612375Sralph 	if ((dirp = opendir(folder)) == NULL) {
48712375Sralph 		fprintf(stderr, "Cannot open %s/%s\n", maildir, folder);
48812375Sralph 		return;
48912375Sralph 	}
49012375Sralph 	num = 0;
49112375Sralph 	while ((d = readdir(dirp)) != NULL) {
49212375Sralph 		cp = d->d_name;
49312375Sralph 		n = 0;
49412375Sralph 		while (isdigit(*cp))
49512375Sralph 			n = n * 10 + (*cp++ - '0');
49612375Sralph 		if (*cp == '\0' && n > num)
49712375Sralph 			num = n;
49812375Sralph 	}
49912375Sralph 	closedir(dirp);
50012375Sralph 	num++;
50112375Sralph 	/*
50212375Sralph 	 * Create the destination file "folder/num" and copy fname to it.
50312375Sralph 	 */
50412375Sralph 	sprintf(msgname, "%s/%d", folder, num);
50512375Sralph 	if (link(fname, msgname) < 0) {
50612375Sralph 		int fin, fout;
50712375Sralph 
50812695Sralph 		if ((fin = open(fname, 0)) < 0) {
50912695Sralph 			fprintf(stderr, "cannot open %s\n", fname);
51012375Sralph 			return;
51112695Sralph 		}
51212695Sralph 		if ((fout = creat(msgname, msg_prot)) < 0) {
51312695Sralph 			fprintf(stderr, "cannot create %s\n", msgname);
51412375Sralph 			return;
51512695Sralph 		}
51612695Sralph 		while ((n = read(fin, buf, sizeof(buf))) > 0)
51712695Sralph 			write(fout, buf, n);
51812375Sralph 		close(fin);
51912375Sralph 		close(fout);
52012375Sralph 	}
52112375Sralph 	unlink(fname);
52212375Sralph }
52312375Sralph 
52412375Sralph /*
525*14809Ssam  * Redistribute a bug report to those people indicated
526*14809Ssam  * in the redistribution list file.  Perhaps should also
527*14809Ssam  * annotate bug report with this information for future
528*14809Ssam  * reference?
529*14809Ssam  */
530*14809Ssam redistribute(folder, num)
531*14809Ssam 	char *folder;
532*14809Ssam 	int num;
533*14809Ssam {
534*14809Ssam 	FILE *fredist, *fbug, *ftemp;
535*14809Ssam 	char line[BUFSIZ], bug[2 * MAXNAMLEN + 1];
536*14809Ssam 	register char *cp;
537*14809Ssam 	int redistcnt, continuation, first;
538*14809Ssam 
539*14809Ssam 	fredist = fopen(redistfile, "r");
540*14809Ssam 	if (fredist == NULL) {
541*14809Ssam 		if (debug)
542*14809Ssam 			printf("redistribute(%s, %d), no distribution list\n",
543*14809Ssam 			    folder, num);
544*14809Ssam 		return;
545*14809Ssam 	}
546*14809Ssam 	continuation = 0;
547*14809Ssam 	first = 1;
548*14809Ssam 	redistcnt = 0;
549*14809Ssam 	while (fgets(line, sizeof (line) - 1, fredist) != NULL) {
550*14809Ssam 		if (debug)
551*14809Ssam 			printf("%s: %s", redistfile, line);
552*14809Ssam 		if (continuation && index(line, '\\'))
553*14809Ssam 			continue;
554*14809Ssam 		continuation = 0;
555*14809Ssam 		cp = any(line, " \t");
556*14809Ssam 		if (cp == NULL)
557*14809Ssam 			continue;
558*14809Ssam 		*cp++ = '\0';
559*14809Ssam 		if (strcmp(folder, line) == 0)
560*14809Ssam 			goto found;
561*14809Ssam 		if (index(cp, '\\'))
562*14809Ssam 			continuation = 1;
563*14809Ssam 	}
564*14809Ssam 	if (debug)
565*14809Ssam 		printf("no redistribution list found\n");
566*14809Ssam 	fclose(fredist);
567*14809Ssam 	return;
568*14809Ssam found:
569*14809Ssam 	mktemp(disttmp);
570*14809Ssam 	ftemp = fopen(disttmp, "w+r");
571*14809Ssam 	if (ftemp == NULL) {
572*14809Ssam 		if (debug)
573*14809Ssam 			printf("%s: couldn't create\n", disttmp);
574*14809Ssam 		return;
575*14809Ssam 	}
576*14809Ssam again:
577*14809Ssam 	if (debug)
578*14809Ssam 		printf("redistribution list %s", cp);
579*14809Ssam 	while (cp) {
580*14809Ssam 		char *user, terminator;
581*14809Ssam 
582*14809Ssam 		while (*cp && (*cp == ' ' || *cp == '\t' || *cp == ','))
583*14809Ssam 			cp++;
584*14809Ssam 		user = cp, cp = any(cp, ", \t\n\\");
585*14809Ssam 		if (cp) {
586*14809Ssam 			terminator = *cp;
587*14809Ssam 			*cp++ = '\0';
588*14809Ssam 			if (terminator == '\n')
589*14809Ssam 				cp = 0;
590*14809Ssam 			if (terminator == '\\')
591*14809Ssam 				continuation++;
592*14809Ssam 		}
593*14809Ssam 		if (*user == '\0')
594*14809Ssam 			continue;
595*14809Ssam 		if (debug)
596*14809Ssam 			printf("copy to %s\n", user);
597*14809Ssam 		if (first) {
598*14809Ssam 			fprintf(ftemp, "To: %s", user);
599*14809Ssam 			first = 0;
600*14809Ssam 		} else
601*14809Ssam 			fprintf(ftemp, ", %s", user);
602*14809Ssam 		redistcnt++;
603*14809Ssam 	}
604*14809Ssam 	if (!first)
605*14809Ssam 		putc('\n', ftemp);
606*14809Ssam 	if (continuation) {
607*14809Ssam 		first = 1;
608*14809Ssam 		continuation = 0;
609*14809Ssam 		cp = line;
610*14809Ssam 		if (fgets(line, sizeof (line) - 1, fredist))
611*14809Ssam 			goto again;
612*14809Ssam 	}
613*14809Ssam 	fclose(fredist);
614*14809Ssam 	if (redistcnt == 0)
615*14809Ssam 		goto cleanup;
616*14809Ssam 	fprintf(ftemp, "Subject: ");
617*14809Ssam 	if (SUBJECT_I)
618*14809Ssam 		fprintf(ftemp, "%s\n", SUBJECT_I);
619*14809Ssam 	else
620*14809Ssam 		fprintf(ftemp, "Untitled bug report\n");
621*14809Ssam 	fprintf(ftemp, "\nRedistributed-by: %s%s\n", BUGS_NAME, BUGS_HOME);
622*14809Ssam 	/*
623*14809Ssam 	 * Create copy of bug report.  Perhaps we should
624*14809Ssam 	 * truncate large messages and just give people
625*14809Ssam 	 * a pointer to the original?
626*14809Ssam 	 */
627*14809Ssam 	sprintf(bug, "%s/%d", folder, num);
628*14809Ssam 	fbug = fopen(bug, "r");
629*14809Ssam 	if (fbug == NULL) {
630*14809Ssam 		if (debug)
631*14809Ssam 			printf("%s: disappeared?\n", bug);
632*14809Ssam 		goto cleanup;
633*14809Ssam 	}
634*14809Ssam 	first = 1;
635*14809Ssam 	while (fgets(line, sizeof (line) - 1, fbug)) {
636*14809Ssam 		/* first blank line indicates start of mesg */
637*14809Ssam 		if (first && line[0] == '\n') {
638*14809Ssam 			first = 0;
639*14809Ssam 			continue;
640*14809Ssam 		}
641*14809Ssam 		fputs(line, ftemp);
642*14809Ssam 	}
643*14809Ssam 	fclose(fbug);
644*14809Ssam 	if (first) {
645*14809Ssam 		if (debug)
646*14809Ssam 			printf("empty bug report?\n");
647*14809Ssam 		goto cleanup;
648*14809Ssam 	}
649*14809Ssam 	if (dodeliver(ftemp))
650*14809Ssam 		unlink(disttmp);
651*14809Ssam 	fclose(ftemp);
652*14809Ssam 	return;
653*14809Ssam cleanup:
654*14809Ssam 	fclose(ftemp);
655*14809Ssam 	unlink(disttmp);
656*14809Ssam }
657*14809Ssam 
658*14809Ssam dodeliver(fd)
659*14809Ssam 	FILE *fd;
660*14809Ssam {
661*14809Ssam 	char buf[BUFSIZ], cmd[BUFSIZ];
662*14809Ssam 	FILE *pf, *popen();
663*14809Ssam 
664*14809Ssam 	strcpy(cmd, MAILCMD);
665*14809Ssam 	if (debug) {
666*14809Ssam 		strcat(cmd, " -v");
667*14809Ssam 		printf("dodeliver \"%s\"\n", cmd);
668*14809Ssam 	}
669*14809Ssam 	pf = popen(cmd, "w");
670*14809Ssam 	if (pf == NULL) {
671*14809Ssam 		if (debug)
672*14809Ssam 			printf("dodeliver, \"%s\" failed\n", cmd);
673*14809Ssam 		return (0);
674*14809Ssam 	}
675*14809Ssam 	rewind(fd);
676*14809Ssam 	while (fgets(buf, sizeof (buf) - 1, fd)) {
677*14809Ssam 		if (debug)
678*14809Ssam 			printf("%s", buf);
679*14809Ssam 		(void) fputs(buf, pf);
680*14809Ssam 	}
681*14809Ssam 	if (debug)
682*14809Ssam 		printf("EOF\n");
683*14809Ssam 	(void) pclose(pf);
684*14809Ssam 	return (1);
685*14809Ssam }
686*14809Ssam 
687*14809Ssam /*
68812375Sralph  * Mail file1 and file2 back to the sender.
68912375Sralph  */
69012375Sralph 
69112375Sralph reply(to, file1, file2)
69212375Sralph 	char	*to, *file1, *file2;
69312375Sralph {
694*14809Ssam 	int pfd[2], in, w;
69512375Sralph 	FILE *fout;
69612375Sralph 
69712375Sralph 	if (debug)
69812695Sralph 		printf("reply(%s, %s, %s)\n", to, file1, file2);
69912695Sralph 
70012375Sralph 	/*
70112375Sralph 	 * Create a temporary file to put the message in.
70212375Sralph 	 */
70312375Sralph 	mktemp(draft);
704*14809Ssam 	if ((fout = fopen(draft, "w+r")) == NULL) {
70512375Sralph 		fprintf(stderr, "Can't create %s\n", draft);
70612375Sralph 		return;
70712375Sralph 	}
70812375Sralph 	/*
70912375Sralph 	 * Output the proper header information.
71012375Sralph 	 */
711*14809Ssam 	fprintf(fout, "Reply-To: %s%s\n", BUGS_NAME, BUGS_HOME);
71212375Sralph 	if (RETURNPATH_I != NULL)
71312375Sralph 		to = RETURNPATH_I;
71412375Sralph 	if (REPLYTO_I != NULL)
71512375Sralph 		to = REPLYTO_I;
71612375Sralph 	if ((to = fixaddr(to)) == 0) {
71712375Sralph 		fprintf(stderr, "No one to reply to\n");
71812375Sralph 		return;
71912375Sralph 	}
72012375Sralph 	fprintf(fout, "To: %s\n", to);
72112375Sralph 	if (SUBJECT_I) {
72212375Sralph 		fprintf(fout, "Subject: ");
72312375Sralph 		if ((SUBJECT_I[0] != 'R' && SUBJECT_I[0] != 'r') ||
72412375Sralph 		    (SUBJECT_I[1] != 'E' && SUBJECT_I[1] != 'e') ||
72512375Sralph 		    SUBJECT_I[2] != ':')
72612375Sralph 			fprintf(fout, "Re: ");
72712375Sralph 		fprintf(fout, "%s\n", SUBJECT_I);
72812375Sralph 	}
72912375Sralph 	if (DATE_I) {
73012375Sralph 		fprintf(fout, "In-Acknowledgement-Of: Your message of ");
73112375Sralph 		fprintf(fout, "%s.\n", DATE_I);
73212375Sralph 		if (MSGID_I)
73312375Sralph 			fprintf(fout, "             %s\n", MSGID_I);
73412375Sralph 	}
735*14809Ssam 	fprintf(fout, "\n");
73612375Sralph 	if ((in = open(file1, 0)) >= 0) {
73712695Sralph 		while ((w = read(in, buf, sizeof(buf))) > 0)
73812695Sralph 			fwrite(buf, 1, w, fout);
73912375Sralph 		close(in);
74012375Sralph 	}
74112375Sralph 	if (file2 && (in = open(file2, 0)) >= 0) {
74212695Sralph 		while ((w = read(in, buf, sizeof(buf))) > 0)
74312695Sralph 			fwrite(buf, 1, w, fout);
74412375Sralph 		close(in);
74512375Sralph 	}
746*14809Ssam 	if (dodeliver(fout))
747*14809Ssam 		unlink(draft);
74812375Sralph 	fclose(fout);
74912375Sralph }
75012375Sralph 
75112375Sralph /*
75212375Sralph  * fix names like "xxx (something)" to "xxx" and
75312375Sralph  * "xxx <something>" to "something".
75412375Sralph  */
75512375Sralph 
75612375Sralph char *
75712375Sralph fixaddr(text)
75812375Sralph 	char *text;
75912375Sralph {
76012375Sralph 	register char *cp, *lp, c;
76112375Sralph 	char *tp;
76212375Sralph 
76312375Sralph 	if (!text)
76412375Sralph 		return(0);
76512375Sralph 	for (lp = cp = text; ; ) {
76612375Sralph 		switch (c = *cp++) {
76712375Sralph 		case '(':
76812375Sralph 			while (*cp && *cp++ != ')');
76912375Sralph 			continue;
77012375Sralph 		case '<':
77112375Sralph 			lp = text;
77212375Sralph 		case '>':
77312375Sralph 			continue;
77412375Sralph 		case '\0':
77512375Sralph 			while (lp != text && (*lp == ' ' || *lp == '\t'))
77612375Sralph 				lp--;
77712375Sralph 			*lp = c;
77812375Sralph 			return(text);
77912375Sralph 		}
78012375Sralph 		*lp++ = c;
78112375Sralph 	}
78212375Sralph }
78312375Sralph 
78412375Sralph /*
78512375Sralph  * Compare two strings and convert any upper case letters to lower case.
78612375Sralph  */
78712375Sralph 
78812695Sralph streq(s1, s2)
78912695Sralph 	register char *s1, *s2;
79012375Sralph {
79112375Sralph 	register int c;
79212375Sralph 
79312695Sralph 	while (c = *s1++)
79412695Sralph 		if ((c | 040) != (*s2++ | 040))
79512375Sralph 			return(0);
79612695Sralph 	return(*s2 == '\0');
79712375Sralph }
79812695Sralph 
79912695Sralph /*
80012695Sralph  * Return true if string s2 matches the first part of s1.
80112695Sralph  */
80212695Sralph 
80312695Sralph substr(s1, s2)
80412695Sralph 	register char *s1, *s2;
80512695Sralph {
80612695Sralph 	register int c;
80712695Sralph 
80812695Sralph 	while (c = *s2++)
80912695Sralph 		if (c != *s1++)
81012695Sralph 			return(0);
81112695Sralph 	return(1);
81212695Sralph }
81312695Sralph 
814*14809Ssam char *
815*14809Ssam any(cp, set)
816*14809Ssam 	register char *cp;
817*14809Ssam 	char *set;
818*14809Ssam {
819*14809Ssam 	register char *sp;
820*14809Ssam 
821*14809Ssam 	if (cp == 0 || set == 0)
822*14809Ssam 		return (0);
823*14809Ssam 	while (*cp) {
824*14809Ssam 		for (sp = set; *sp; sp++)
825*14809Ssam 			if (*cp == *sp)
826*14809Ssam 				return (cp);
827*14809Ssam 		cp++;
828*14809Ssam 	}
829*14809Ssam 	return ((char *)0);
830*14809Ssam }
831*14809Ssam 
83212695Sralph peekc(fp)
83312695Sralph FILE *fp;
83412695Sralph {
83512695Sralph 	register c;
83612695Sralph 
83712695Sralph 	c = getc(fp);
83812695Sralph 	ungetc(c, fp);
83912695Sralph 	return(c);
84012695Sralph }
841