xref: /csrg-svn/libexec/bugfiler/bugfiler.c (revision 25388)
1*25388Sbloom /*
2*25388Sbloom  * Copyright (c) 1983 Regents of the University of California.
3*25388Sbloom  * All rights reserved.  The Berkeley software License Agreement
4*25388Sbloom  * specifies the terms and conditions for redistribution.
5*25388Sbloom  */
6*25388Sbloom 
714552Ssam #ifndef lint
8*25388Sbloom char copyright[] =
9*25388Sbloom "@(#) Copyright (c) 1983 Regents of the University of California.\n\
10*25388Sbloom  All rights reserved.\n";
11*25388Sbloom #endif not lint
1214552Ssam 
13*25388Sbloom #ifndef lint
14*25388Sbloom static char sccsid[] = "@(#)bugfiler.c	5.3 (Berkeley) 85/11/04";
15*25388Sbloom #endif not lint
16*25388Sbloom 
1712375Sralph /*
1812375Sralph  * Bug report processing program.
1914809Ssam  * It is designed to be invoked by alias(5)
2014809Ssam  * and to be compatible with mh.
2112375Sralph  */
2212375Sralph 
2312375Sralph #include <stdio.h>
2412375Sralph #include <ctype.h>
2512375Sralph #include <signal.h>
2614809Ssam #include <pwd.h>
2714809Ssam 
2812375Sralph #include <sys/types.h>
2912375Sralph #include <sys/stat.h>
3012695Sralph #include <sys/dir.h>
3112375Sralph 
32*25388Sbloom #ifndef BUGS_NAME
3314809Ssam #define	BUGS_NAME	"4bsd-bugs"
34*25388Sbloom #endif
35*25388Sbloom #ifndef BUGS_HOME
36*25388Sbloom #define	BUGS_HOME	"@ucbarpa.BERKELEY.EDU"
37*25388Sbloom #endif
3814809Ssam #define	MAILCMD		"/usr/lib/sendmail -i -t"
3914809Ssam 
4025387Sbloom char	unixtomh[] = "/ra/bugs/bin/unixtomh";
4114809Ssam char	*bugperson = "bugs";
4214809Ssam char	*maildir = "mail";
4312375Sralph char	ackfile[] = ".ack";
4412375Sralph char	errfile[] = ".format";
4512375Sralph char	sumfile[] = "summary";
4612375Sralph char	logfile[] = "errors/log";
4714809Ssam char	redistfile[] = ".redist";
4812375Sralph char	tmpname[] = "BfXXXXXX";
4912375Sralph char	draft[] = "RpXXXXXX";
5014809Ssam char	disttmp[] = "RcXXXXXX";
5112375Sralph 
5212695Sralph char	buf[8192];
5312375Sralph char	folder[MAXNAMLEN];
5412375Sralph int	num;
5512375Sralph int	msg_prot = 0664;
5612375Sralph 
5712375Sralph int	debug;
5812375Sralph 
5912375Sralph char	*index();
6012375Sralph char	*rindex();
6112375Sralph char	*fixaddr();
6214809Ssam char	*any();
6312375Sralph 
6412375Sralph main(argc, argv)
6512375Sralph 	char *argv[];
6612375Sralph {
6712375Sralph 	register char *cp;
6812695Sralph 	register int n;
6912695Sralph 	int pfd[2];
7012375Sralph 
7115686Sralph 	if (argc > 4) {
7212375Sralph 	usage:
7312695Sralph 		fprintf(stderr, "Usage: bugfiler [-d] [-mmsg_mode] [maildir]\n");
7412375Sralph 		exit(1);
7512375Sralph 	}
7612375Sralph 	while (--argc > 0) {
7712375Sralph 		cp = *++argv;
7812695Sralph 		if (*cp == '-')
7912695Sralph 			switch (cp[1]) {
8012375Sralph 			case 'd':
8112375Sralph 				debug++;
8212375Sralph 				break;
8312695Sralph 
8412695Sralph 			case 'm':	/* set message protection */
8512695Sralph 				n = 0;
8612695Sralph 				for (cp += 2; *cp >= '0' && *cp <= '7'; )
8712695Sralph 					n = (n << 3) + (*cp++ - '0');
8812695Sralph 				msg_prot = n & 0777;
8912695Sralph 				break;
9012695Sralph 
9112375Sralph 			default:
9212375Sralph 				goto usage;
9312375Sralph 			}
9412375Sralph 		else
9512375Sralph 			maildir = cp;
9612375Sralph 	}
9712695Sralph 	if (!debug)
9812695Sralph 		freopen(logfile, "a", stderr);
9912695Sralph 
10014809Ssam 	if (bugperson) {
10114809Ssam 		struct passwd *pwd = getpwnam(bugperson);
10214809Ssam 
10314809Ssam 		if (pwd == NULL) {
10414809Ssam 			fprintf(stderr, "%s: bugs person is unknown\n",
10514809Ssam 			    bugperson);
10614809Ssam 			exit(1);
10714809Ssam 		}
10814809Ssam 		if (chdir(pwd->pw_dir) < 0) {
10914809Ssam 			fprintf(stderr, "can't chdir to %s\n", pwd->pw_dir);
11014809Ssam 			exit(1);
11114809Ssam 		}
11215686Sralph 		setuid(pwd->pw_uid);
11314809Ssam 	}
11412375Sralph 	if (chdir(maildir) < 0) {
11512375Sralph 		fprintf(stderr, "can't chdir to %s\n", maildir);
11612375Sralph 		exit(1);
11712375Sralph 	}
11812695Sralph 	umask(0);
11912695Sralph 
12012695Sralph #ifdef UNIXCOMP
12112695Sralph 	/*
12212695Sralph 	 * Convert UNIX style mail to mh style by filtering stdin through
12312695Sralph 	 * unixtomh.
12412695Sralph 	 */
12512695Sralph 	if (pipe(pfd) >= 0) {
12612695Sralph 		while ((n = fork()) == -1)
12712695Sralph 			sleep(5);
12812695Sralph 		if (n == 0) {
12912695Sralph 			close(pfd[0]);
13012695Sralph 			dup2(pfd[1], 1);
13112695Sralph 			close(pfd[1]);
13212695Sralph 			execl(unixtomh, "unixtomh", 0);
13312695Sralph 			_exit(127);
13412695Sralph 		}
13512695Sralph 		close(pfd[1]);
13612695Sralph 		dup2(pfd[0], 0);
13712695Sralph 		close(pfd[0]);
13812695Sralph 	}
13912695Sralph #endif
14012695Sralph 	while (process())
14112695Sralph 		;
14212695Sralph 	exit(0);
14312375Sralph }
14412375Sralph 
14512695Sralph /* states */
14612695Sralph 
14712695Sralph #define EOM	0	/* End of message seen */
14812695Sralph #define FLD	1	/* Looking for header lines */
14912695Sralph #define BODY	2	/* Looking for message body lines */
15012695Sralph 
15112375Sralph /* defines used for tag attributes */
15212375Sralph 
15312375Sralph #define H_REQ 01
15412695Sralph #define H_SAV 02
15512695Sralph #define H_HDR 04
15612695Sralph #define H_FND 010
15712375Sralph 
15815738Sralph #define FROM &headers[0]
15912375Sralph #define FROM_I headers[0].h_info
16012375Sralph #define SUBJECT_I headers[1].h_info
16112375Sralph #define INDEX &headers[2]
16212375Sralph #define INDEX_I headers[2].h_info
16312375Sralph #define DATE_I headers[3].h_info
16412375Sralph #define MSGID_I headers[4].h_info
16512375Sralph #define REPLYTO_I headers[5].h_info
16614981Sralph #define TO_I headers[6].h_info
16714981Sralph #define CC_I headers[7].h_info
16814981Sralph #define FIX headers[10]
16912375Sralph 
17012375Sralph struct header {
17112375Sralph 	char	*h_tag;
17212375Sralph 	int	h_flags;
17312375Sralph 	char	*h_info;
17412375Sralph } headers[] = {
17512695Sralph 	"From",		H_REQ|H_SAV|H_HDR, 0,
17614981Sralph 	"Subject",	H_REQ|H_SAV, 0,
17712375Sralph 	"Index",	H_REQ|H_SAV, 0,
17812695Sralph 	"Date",		H_SAV|H_HDR, 0,
17912695Sralph 	"Message-Id",	H_SAV|H_HDR, 0,
18012695Sralph 	"Reply-To",	H_SAV|H_HDR, 0,
18112695Sralph 	"To",		H_SAV|H_HDR, 0,
18212695Sralph 	"Cc",		H_SAV|H_HDR, 0,
18312375Sralph 	"Description",	H_REQ,       0,
18414955Sralph 	"Repeat-By",	0,	     0,
18512695Sralph 	"Fix",		0,	     0,
18612375Sralph 	0,	0,	0,
18712375Sralph };
18812375Sralph 
18912695Sralph struct header *findheader();
19012695Sralph 
19112375Sralph process()
19212375Sralph {
19312375Sralph 	register struct header *hp;
19412375Sralph 	register char *cp;
19512695Sralph 	register int c;
19612375Sralph 	char *info;
19715738Sralph 	int state, tmp, no_reply = 0;
19812695Sralph 	FILE *tfp, *fs;
19912375Sralph 
20012375Sralph 	/*
20112375Sralph 	 * Insure all headers are in a consistent
20212375Sralph 	 * state.  Anything left there is free'd.
20312375Sralph 	 */
20412375Sralph 	for (hp = headers; hp->h_tag; hp++) {
20512695Sralph 		hp->h_flags &= ~H_FND;
20612375Sralph 		if (hp->h_info) {
20712695Sralph 			free(hp->h_info);
20812375Sralph 			hp->h_info = 0;
20912375Sralph 		}
21012375Sralph 	}
21112375Sralph 	/*
21212375Sralph 	 * Read the report and make a copy.  Must conform to RFC822 and
21312375Sralph 	 * be of the form... <tag>: <info>
21412695Sralph 	 * Note that the input is expected to be in mh mail format
21512695Sralph 	 * (i.e., messages are separated by lines of ^A's).
21612375Sralph 	 */
21712695Sralph 	while ((c = getchar()) == '\001' && peekc(stdin) == '\001')
21812695Sralph 		while (getchar() != '\n')
21912695Sralph 			;
22012695Sralph 	if (c == EOF)
22112695Sralph 		return(0);	/* all done */
22212695Sralph 
22312375Sralph 	mktemp(tmpname);
22412695Sralph 	if ((tmp = creat(tmpname, msg_prot)) < 0) {
22512695Sralph 		fprintf(stderr, "cannont create %s\n", tmpname);
22612695Sralph 		exit(1);
22712695Sralph 	}
22812695Sralph 	if ((tfp = fdopen(tmp, "w")) == NULL) {
22912695Sralph 		fprintf(stderr, "cannot fdopen temp file\n");
23012695Sralph 		exit(1);
23112695Sralph 	}
23212695Sralph 
23312695Sralph 	for (state = FLD; state != EOF && state != EOM; c = getchar()) {
23412695Sralph 		switch (state) {
23512695Sralph 		case FLD:
23612695Sralph 			if (c == '\n' || c == '-')
23712695Sralph 				goto body;
23812695Sralph 			for (cp = buf; c != ':'; c = getchar()) {
23912695Sralph 				if (cp < buf+sizeof(buf)-1 && c != '\n' && c != EOF) {
24012695Sralph 					*cp++ = c;
24112695Sralph 					continue;
24212695Sralph 				}
24312695Sralph 				*cp = '\0';
24412695Sralph 				fputs(buf, tfp);
24512695Sralph 				state = EOF;
24612695Sralph 				while (c != EOF) {
24712695Sralph 					if (c == '\n')
24812695Sralph 						if ((tmp = peekc(stdin)) == EOF)
24912695Sralph 							break;
25012695Sralph 						else if (tmp == '\001') {
25112695Sralph 							state = EOM;
25212695Sralph 							break;
25312695Sralph 						}
25412695Sralph 					putc(c, tfp);
25512695Sralph 					c = getchar();
25612695Sralph 				}
25712695Sralph 				fclose(tfp);
25812695Sralph 				goto badfmt;
25912695Sralph 			}
26012695Sralph 			*cp = '\0';
26112695Sralph 			fprintf(tfp, "%s:", buf);
26212695Sralph 			hp = findheader(buf, state);
26312695Sralph 
26412695Sralph 			for (cp = buf; ; ) {
26512695Sralph 				if (cp >= buf+sizeof(buf)-1) {
26612695Sralph 					fprintf(stderr, "field truncated\n");
26712695Sralph 					while ((c = getchar()) != EOF && c != '\n')
26812695Sralph 						putc(c, tfp);
26912695Sralph 				}
27012695Sralph 				if ((c = getchar()) == EOF) {
27112695Sralph 					state = EOF;
27212695Sralph 					break;
27312695Sralph 				}
27412695Sralph 				putc(c, tfp);
27512695Sralph 				*cp++ = c;
27612695Sralph 				if (c == '\n')
27712695Sralph 					if ((c = peekc(stdin)) != ' ' && c != '\t') {
27812695Sralph 						if (c == EOF)
27912695Sralph 							state = EOF;
28012695Sralph 						else if (c == '\001')
28112695Sralph 							state = EOM;
28212695Sralph 						break;
28312695Sralph 					}
28412695Sralph 			}
28512695Sralph 			*cp = '\0';
28612695Sralph 			cp = buf;
28712695Sralph 			break;
28812695Sralph 
28912695Sralph 		body:
29012695Sralph 			state = BODY;
29112695Sralph 		case BODY:
29212695Sralph 			for (cp = buf; ; c = getchar()) {
29312695Sralph 				if (c == EOF) {
29412695Sralph 					state = EOF;
29512695Sralph 					break;
29612695Sralph 				}
29712695Sralph 				if (c == '\001' && peekc(stdin) == '\001') {
29812695Sralph 					state = EOM;
29912695Sralph 					break;
30012695Sralph 				}
30112695Sralph 				putc(c, tfp);
30212695Sralph 				*cp++ = c;
30312695Sralph 				if (cp >= buf+sizeof(buf)-1 || c == '\n')
30412695Sralph 					break;
30512695Sralph 			}
30612695Sralph 			*cp = '\0';
30712695Sralph 			if ((cp = index(buf, ':')) == NULL)
30812695Sralph 				continue;
30912695Sralph 			*cp++ = '\0';
31012695Sralph 			hp = findheader(buf, state);
31112695Sralph 		}
31212695Sralph 
31312695Sralph 		/*
31412695Sralph 		 * Don't save the info if the header wasn't found, we don't
31512695Sralph 		 * care about the info, or the header is repeated.
31612695Sralph 		 */
31712695Sralph 		if (hp == NULL || !(hp->h_flags & H_SAV) || hp->h_info)
31812375Sralph 			continue;
31912375Sralph 		while (isspace(*cp))
32012375Sralph 			cp++;
32112375Sralph 		if (*cp) {
32212375Sralph 			info = cp;
32312375Sralph 			while (*cp++);
32412375Sralph 			cp--;
32512375Sralph 			while (isspace(cp[-1]))
32612375Sralph 				*--cp = '\0';
32712375Sralph 			hp->h_info = (char *) malloc(strlen(info) + 1);
32812695Sralph 			if (hp->h_info == NULL) {
32912695Sralph 				fprintf(stderr, "ran out of memory\n");
33012375Sralph 				continue;
33112695Sralph 			}
33212375Sralph 			strcpy(hp->h_info, info);
33315738Sralph 			if (hp == FROM && chkfrom(hp) < 0)
33415738Sralph 				no_reply = 1;
33512375Sralph 			if (hp == INDEX)
33612375Sralph 				chkindex(hp);
33712375Sralph 		}
33812375Sralph 	}
33912695Sralph 	fclose(tfp);
34015738Sralph 	if (no_reply) {
34115738Sralph 		unlink(tmpname);
34215738Sralph 		exit(0);
34315738Sralph 	}
34412375Sralph 	/*
34512375Sralph 	 * Verify all the required pieces of information
34612375Sralph 	 * are present.
34712375Sralph 	 */
34812695Sralph 	for (hp = headers; hp->h_tag; hp++) {
34912375Sralph 		/*
35012375Sralph 		 * Mail the bug report back to the sender with a note
35112375Sralph 		 * explaining they must conform to the specification.
35212375Sralph 		 */
35312695Sralph 		if ((hp->h_flags & H_REQ) && !(hp->h_flags & H_FND)) {
35412695Sralph 			if (debug)
35512695Sralph 				printf("Missing %s\n", hp->h_tag);
35612695Sralph 		badfmt:
35712695Sralph 			reply(FROM_I, errfile, tmpname);
35812695Sralph 			file(tmpname, "errors");
35912695Sralph 			return(state == EOM);
36012695Sralph 		}
36112375Sralph 	}
36212375Sralph 	/*
36312695Sralph 	 * Acknowledge receipt.
36412695Sralph 	 */
36512695Sralph 	reply(FROM_I, ackfile, (char *)0);
36612695Sralph 	file(tmpname, folder);
36712695Sralph 	/*
36812375Sralph 	 * Append information about the new bug report
36912375Sralph 	 * to the summary file.
37012375Sralph 	 */
37112695Sralph 	if ((fs = fopen(sumfile, "a")) == NULL)
37212375Sralph 		fprintf(stderr, "Can't open %s\n", sumfile);
37312695Sralph 	else {
37412695Sralph 		fprintf(fs, "%14.14s/%-3d  ", folder, num);
37512695Sralph 		fprintf(fs, "%-51.51s Recv\n", INDEX_I);
37612695Sralph 		fprintf(fs, "\t\t    %-51.51s\n", SUBJECT_I);
37712375Sralph 	}
37812375Sralph 	fclose(fs);
37914809Ssam 	/*
38014809Ssam 	 * Check redistribution list and, if members,
38114809Ssam 	 * mail a copy of the bug report to these people.
38214809Ssam 	 */
38314809Ssam 	redistribute(folder, num);
38412695Sralph 	return(state == EOM);
38512375Sralph }
38612375Sralph 
38712375Sralph /*
38812695Sralph  * Lookup the string in the list of headers and return a pointer
38912695Sralph  * to the entry or NULL.
39012695Sralph  */
39112695Sralph 
39212695Sralph struct header *
39312695Sralph findheader(name, state)
39412695Sralph 	char *name;
39512695Sralph 	int state;
39612695Sralph {
39712695Sralph 	register struct header *hp;
39812695Sralph 
39912695Sralph 	if (debug)
40012695Sralph 		printf("findheader(%s, %d)\n", name, state);
40112695Sralph 
40212695Sralph 	for (hp = headers; hp->h_tag; hp++) {
40312695Sralph 		if (!streq(hp->h_tag, buf))
40412695Sralph 			continue;
40512695Sralph 		if ((hp->h_flags & H_HDR) && state != FLD)
40612695Sralph 			continue;
40712695Sralph 		hp->h_flags |= H_FND;
40812695Sralph 		return(hp);
40912695Sralph 	}
41012695Sralph 	return(NULL);
41112695Sralph }
41212695Sralph 
41312695Sralph /*
41415738Sralph  * Check the FROM line to eliminate loops.
41515738Sralph  */
41615738Sralph 
41715738Sralph chkfrom(hp)
41815738Sralph 	struct header *hp;
41915738Sralph {
42015738Sralph 	register char *cp1, *cp2;
42115738Sralph 	register char c;
42215738Sralph 
42315738Sralph 	if (debug)
42425387Sbloom 		printf("chkfrom(%s)\n", hp->h_info);
42515738Sralph 
42625387Sbloom 	if (substr(hp->h_info, BUGS_NAME))
42725387Sbloom 		return(-1);
42815738Sralph 	if (substr(hp->h_info, "MAILER-DAEMON"))
42915738Sralph 		return(-1);
43015738Sralph 	return(0);
43115738Sralph }
43215738Sralph 
43315738Sralph /*
43412375Sralph  * Check the format of the Index information.
43512375Sralph  * A side effect is to set the name of the folder if all is well.
43612375Sralph  */
43712375Sralph 
43812375Sralph chkindex(hp)
43912375Sralph 	struct header *hp;
44012375Sralph {
44112695Sralph 	register char *cp1, *cp2;
44212375Sralph 	register char c;
44312375Sralph 	struct stat stbuf;
44412375Sralph 
44512375Sralph 	if (debug)
44612695Sralph 		printf("chkindex(%s)\n", hp->h_info);
44712375Sralph 	/*
44816165Sralph 	 * Strip of leading "/", ".", "usr/", or "src/".
44912695Sralph 	 */
45012695Sralph 	cp1 = hp->h_info;
45116165Sralph 	while (*cp1 == '/' || *cp1 == '.')
45212695Sralph 		cp1++;
45314865Ssam 	while (substr(cp1, "usr/") || substr(cp1, "src/"))
45412695Sralph 		cp1 += 4;
45512695Sralph 	/*
45612375Sralph 	 * Read the folder name and remove it from the index line.
45712375Sralph 	 */
45812695Sralph 	for (cp2 = folder; ;) {
45912695Sralph 		switch (c = *cp1++) {
46012695Sralph 		case '/':
46112695Sralph 			if (cp2 == folder)
46212695Sralph 				continue;
46312375Sralph 			break;
46412695Sralph 		case '\0':
46512695Sralph 			cp1--;
46612695Sralph 			break;
46712695Sralph 		case ' ':
46812695Sralph 		case '\t':
46912695Sralph 			while (isspace(*cp1))
47012695Sralph 				cp1++;
47112695Sralph 			break;
47212695Sralph 		default:
47312695Sralph 			if (cp2 < folder+sizeof(folder)-1)
47412695Sralph 				*cp2++ = c;
47512695Sralph 			continue;
47612375Sralph 		}
47712695Sralph 		*cp2 = '\0';
47812695Sralph 		for (cp2 = hp->h_info; *cp2++ = *cp1++; )
47912695Sralph 			;
48012695Sralph 		break;
48112375Sralph 	}
48212695Sralph 	if (debug)
48312695Sralph 		printf("folder = %s\n", folder);
48412375Sralph 	/*
48512375Sralph 	 * Check to make sure we have a valid folder name
48612375Sralph 	 */
48712375Sralph 	if (stat(folder, &stbuf) == 0 && (stbuf.st_mode & S_IFMT) == S_IFDIR)
48812375Sralph 		return;
48912375Sralph 	/*
49012375Sralph 	 * The Index line is not in the correct format so clear
49112695Sralph 	 * the H_FND flag to mail back the correct format.
49212375Sralph 	 */
49312695Sralph 	hp->h_flags &= ~H_FND;
49412375Sralph }
49512375Sralph 
49612375Sralph /*
49712375Sralph  * Move or copy the file msg to the folder (directory).
49814809Ssam  * As a side effect, num is set to the number under which
49914809Ssam  * the message is filed in folder.
50012375Sralph  */
50112375Sralph 
50212375Sralph file(fname, folder)
50312375Sralph 	char *fname, *folder;
50412375Sralph {
50516504Sralph 	register char *cp;
50616504Sralph 	register int n;
50712375Sralph 	char msgname[MAXNAMLEN*2+2];
50812375Sralph 	struct stat stbuf;
50912375Sralph 	DIR *dirp;
51012375Sralph 	struct direct *d;
51112375Sralph 
51212375Sralph 	if (debug)
51312695Sralph 		printf("file(%s, %s)\n", fname, folder);
51412375Sralph 	/*
51512375Sralph 	 * Get the next number to use by finding the last message number
51612375Sralph 	 * in folder and adding one.
51712375Sralph 	 */
51812375Sralph 	if ((dirp = opendir(folder)) == NULL) {
51912375Sralph 		fprintf(stderr, "Cannot open %s/%s\n", maildir, folder);
52012375Sralph 		return;
52112375Sralph 	}
52212375Sralph 	num = 0;
52312375Sralph 	while ((d = readdir(dirp)) != NULL) {
52412375Sralph 		cp = d->d_name;
52512375Sralph 		n = 0;
52612375Sralph 		while (isdigit(*cp))
52712375Sralph 			n = n * 10 + (*cp++ - '0');
52812375Sralph 		if (*cp == '\0' && n > num)
52912375Sralph 			num = n;
53012375Sralph 	}
53112375Sralph 	closedir(dirp);
53212375Sralph 	num++;
53312375Sralph 	/*
53412375Sralph 	 * Create the destination file "folder/num" and copy fname to it.
53512375Sralph 	 */
53612375Sralph 	sprintf(msgname, "%s/%d", folder, num);
53712375Sralph 	if (link(fname, msgname) < 0) {
53812375Sralph 		int fin, fout;
53912375Sralph 
54012695Sralph 		if ((fin = open(fname, 0)) < 0) {
54112695Sralph 			fprintf(stderr, "cannot open %s\n", fname);
54212375Sralph 			return;
54312695Sralph 		}
54412695Sralph 		if ((fout = creat(msgname, msg_prot)) < 0) {
54512695Sralph 			fprintf(stderr, "cannot create %s\n", msgname);
54612375Sralph 			return;
54712695Sralph 		}
54812695Sralph 		while ((n = read(fin, buf, sizeof(buf))) > 0)
54912695Sralph 			write(fout, buf, n);
55012375Sralph 		close(fin);
55112375Sralph 		close(fout);
55212375Sralph 	}
55312375Sralph 	unlink(fname);
55412375Sralph }
55512375Sralph 
55612375Sralph /*
55714809Ssam  * Redistribute a bug report to those people indicated
55814809Ssam  * in the redistribution list file.  Perhaps should also
55914809Ssam  * annotate bug report with this information for future
56014809Ssam  * reference?
56114809Ssam  */
56214809Ssam redistribute(folder, num)
56314809Ssam 	char *folder;
56414809Ssam 	int num;
56514809Ssam {
56614809Ssam 	FILE *fredist, *fbug, *ftemp;
56714809Ssam 	char line[BUFSIZ], bug[2 * MAXNAMLEN + 1];
56814809Ssam 	register char *cp;
56914809Ssam 	int redistcnt, continuation, first;
57014809Ssam 
57114809Ssam 	fredist = fopen(redistfile, "r");
57214809Ssam 	if (fredist == NULL) {
57314809Ssam 		if (debug)
57414809Ssam 			printf("redistribute(%s, %d), no distribution list\n",
57514809Ssam 			    folder, num);
57614809Ssam 		return;
57714809Ssam 	}
57814809Ssam 	continuation = 0;
57914809Ssam 	first = 1;
58014809Ssam 	redistcnt = 0;
58114809Ssam 	while (fgets(line, sizeof (line) - 1, fredist) != NULL) {
58214809Ssam 		if (debug)
58314809Ssam 			printf("%s: %s", redistfile, line);
58414809Ssam 		if (continuation && index(line, '\\'))
58514809Ssam 			continue;
58614809Ssam 		continuation = 0;
58714809Ssam 		cp = any(line, " \t");
58814809Ssam 		if (cp == NULL)
58914809Ssam 			continue;
59014809Ssam 		*cp++ = '\0';
59114809Ssam 		if (strcmp(folder, line) == 0)
59214809Ssam 			goto found;
59314809Ssam 		if (index(cp, '\\'))
59414809Ssam 			continuation = 1;
59514809Ssam 	}
59614809Ssam 	if (debug)
59714809Ssam 		printf("no redistribution list found\n");
59814809Ssam 	fclose(fredist);
59914809Ssam 	return;
60014809Ssam found:
60114809Ssam 	mktemp(disttmp);
60214809Ssam 	ftemp = fopen(disttmp, "w+r");
60314809Ssam 	if (ftemp == NULL) {
60414809Ssam 		if (debug)
60514809Ssam 			printf("%s: couldn't create\n", disttmp);
60614809Ssam 		return;
60714809Ssam 	}
60814809Ssam again:
60914809Ssam 	if (debug)
61014809Ssam 		printf("redistribution list %s", cp);
61114809Ssam 	while (cp) {
61214809Ssam 		char *user, terminator;
61314809Ssam 
61414809Ssam 		while (*cp && (*cp == ' ' || *cp == '\t' || *cp == ','))
61514809Ssam 			cp++;
61614809Ssam 		user = cp, cp = any(cp, ", \t\n\\");
61714809Ssam 		if (cp) {
61814809Ssam 			terminator = *cp;
61914809Ssam 			*cp++ = '\0';
62014809Ssam 			if (terminator == '\n')
62114809Ssam 				cp = 0;
62214809Ssam 			if (terminator == '\\')
62314809Ssam 				continuation++;
62414809Ssam 		}
62514809Ssam 		if (*user == '\0')
62614809Ssam 			continue;
62714809Ssam 		if (debug)
62814809Ssam 			printf("copy to %s\n", user);
62914809Ssam 		if (first) {
63025387Sbloom 			fprintf(ftemp, "Resent-To: %s", user);
63114809Ssam 			first = 0;
63214809Ssam 		} else
63314809Ssam 			fprintf(ftemp, ", %s", user);
63414809Ssam 		redistcnt++;
63514809Ssam 	}
63614809Ssam 	if (!first)
63714809Ssam 		putc('\n', ftemp);
63814809Ssam 	if (continuation) {
63914809Ssam 		first = 1;
64014809Ssam 		continuation = 0;
64114809Ssam 		cp = line;
64214809Ssam 		if (fgets(line, sizeof (line) - 1, fredist))
64314809Ssam 			goto again;
64414809Ssam 	}
64514809Ssam 	fclose(fredist);
64614809Ssam 	if (redistcnt == 0)
64714809Ssam 		goto cleanup;
64825387Sbloom 	if (! SUBJECT_I) {
64925387Sbloom 		fprintf(ftemp, "Subject: ");
65014809Ssam 		fprintf(ftemp, "Untitled bug report\n");
65125387Sbloom 	}
65214809Ssam 	/*
65314809Ssam 	 * Create copy of bug report.  Perhaps we should
65414809Ssam 	 * truncate large messages and just give people
65514809Ssam 	 * a pointer to the original?
65614809Ssam 	 */
65714809Ssam 	sprintf(bug, "%s/%d", folder, num);
65814809Ssam 	fbug = fopen(bug, "r");
65914809Ssam 	if (fbug == NULL) {
66014809Ssam 		if (debug)
66114809Ssam 			printf("%s: disappeared?\n", bug);
66214809Ssam 		goto cleanup;
66314809Ssam 	}
66414809Ssam 	first = 1;
66514809Ssam 	while (fgets(line, sizeof (line) - 1, fbug)) {
66614809Ssam 		/* first blank line indicates start of mesg */
66714809Ssam 		if (first && line[0] == '\n') {
66814809Ssam 			first = 0;
66914809Ssam 		}
67014809Ssam 		fputs(line, ftemp);
67114809Ssam 	}
67214809Ssam 	fclose(fbug);
67314809Ssam 	if (first) {
67414809Ssam 		if (debug)
67514809Ssam 			printf("empty bug report?\n");
67614809Ssam 		goto cleanup;
67714809Ssam 	}
67814809Ssam 	if (dodeliver(ftemp))
67914809Ssam 		unlink(disttmp);
68014809Ssam 	fclose(ftemp);
68114809Ssam 	return;
68214809Ssam cleanup:
68314809Ssam 	fclose(ftemp);
68414809Ssam 	unlink(disttmp);
68514809Ssam }
68614809Ssam 
68714809Ssam dodeliver(fd)
68814809Ssam 	FILE *fd;
68914809Ssam {
69014809Ssam 	char buf[BUFSIZ], cmd[BUFSIZ];
69114809Ssam 	FILE *pf, *popen();
69214809Ssam 
69314809Ssam 	strcpy(cmd, MAILCMD);
69414809Ssam 	if (debug) {
69514809Ssam 		strcat(cmd, " -v");
69614809Ssam 		printf("dodeliver \"%s\"\n", cmd);
69714809Ssam 	}
69814809Ssam 	pf = popen(cmd, "w");
69914809Ssam 	if (pf == NULL) {
70014809Ssam 		if (debug)
70114809Ssam 			printf("dodeliver, \"%s\" failed\n", cmd);
70214809Ssam 		return (0);
70314809Ssam 	}
70414809Ssam 	rewind(fd);
70514809Ssam 	while (fgets(buf, sizeof (buf) - 1, fd)) {
70614809Ssam 		if (debug)
70714809Ssam 			printf("%s", buf);
70814809Ssam 		(void) fputs(buf, pf);
70914809Ssam 	}
71014809Ssam 	if (debug)
71114809Ssam 		printf("EOF\n");
71214809Ssam 	(void) pclose(pf);
71314809Ssam 	return (1);
71414809Ssam }
71514809Ssam 
71614809Ssam /*
71712375Sralph  * Mail file1 and file2 back to the sender.
71812375Sralph  */
71912375Sralph 
72012375Sralph reply(to, file1, file2)
72112375Sralph 	char	*to, *file1, *file2;
72212375Sralph {
72314809Ssam 	int pfd[2], in, w;
72412375Sralph 	FILE *fout;
72512375Sralph 
72612375Sralph 	if (debug)
72712695Sralph 		printf("reply(%s, %s, %s)\n", to, file1, file2);
72812695Sralph 
72912375Sralph 	/*
73012375Sralph 	 * Create a temporary file to put the message in.
73112375Sralph 	 */
73212375Sralph 	mktemp(draft);
73314809Ssam 	if ((fout = fopen(draft, "w+r")) == NULL) {
73412375Sralph 		fprintf(stderr, "Can't create %s\n", draft);
73512375Sralph 		return;
73612375Sralph 	}
73712375Sralph 	/*
73812375Sralph 	 * Output the proper header information.
73912375Sralph 	 */
74014809Ssam 	fprintf(fout, "Reply-To: %s%s\n", BUGS_NAME, BUGS_HOME);
74114819Skarels 	fprintf(fout, "From: %s%s (Bugs Bunny)\n", BUGS_NAME, BUGS_HOME);
74212375Sralph 	if (REPLYTO_I != NULL)
74312375Sralph 		to = REPLYTO_I;
74412375Sralph 	if ((to = fixaddr(to)) == 0) {
74512375Sralph 		fprintf(stderr, "No one to reply to\n");
74612375Sralph 		return;
74712375Sralph 	}
74812375Sralph 	fprintf(fout, "To: %s\n", to);
74912375Sralph 	if (SUBJECT_I) {
75012375Sralph 		fprintf(fout, "Subject: ");
75112375Sralph 		if ((SUBJECT_I[0] != 'R' && SUBJECT_I[0] != 'r') ||
75212375Sralph 		    (SUBJECT_I[1] != 'E' && SUBJECT_I[1] != 'e') ||
75312375Sralph 		    SUBJECT_I[2] != ':')
75412375Sralph 			fprintf(fout, "Re: ");
75512375Sralph 		fprintf(fout, "%s\n", SUBJECT_I);
75612375Sralph 	}
75712375Sralph 	if (DATE_I) {
75812375Sralph 		fprintf(fout, "In-Acknowledgement-Of: Your message of ");
75912375Sralph 		fprintf(fout, "%s.\n", DATE_I);
76012375Sralph 		if (MSGID_I)
76112375Sralph 			fprintf(fout, "             %s\n", MSGID_I);
76212375Sralph 	}
76314809Ssam 	fprintf(fout, "\n");
76412375Sralph 	if ((in = open(file1, 0)) >= 0) {
76512695Sralph 		while ((w = read(in, buf, sizeof(buf))) > 0)
76612695Sralph 			fwrite(buf, 1, w, fout);
76712375Sralph 		close(in);
76812375Sralph 	}
76912375Sralph 	if (file2 && (in = open(file2, 0)) >= 0) {
77012695Sralph 		while ((w = read(in, buf, sizeof(buf))) > 0)
77112695Sralph 			fwrite(buf, 1, w, fout);
77212375Sralph 		close(in);
77312375Sralph 	}
77414809Ssam 	if (dodeliver(fout))
77514809Ssam 		unlink(draft);
77612375Sralph 	fclose(fout);
77712375Sralph }
77812375Sralph 
77912375Sralph /*
78012375Sralph  * fix names like "xxx (something)" to "xxx" and
78112375Sralph  * "xxx <something>" to "something".
78212375Sralph  */
78312375Sralph 
78412375Sralph char *
78512375Sralph fixaddr(text)
78612375Sralph 	char *text;
78712375Sralph {
78812375Sralph 	register char *cp, *lp, c;
78912375Sralph 	char *tp;
79012375Sralph 
79112375Sralph 	if (!text)
79212375Sralph 		return(0);
79312375Sralph 	for (lp = cp = text; ; ) {
79412375Sralph 		switch (c = *cp++) {
79512375Sralph 		case '(':
79612375Sralph 			while (*cp && *cp++ != ')');
79712375Sralph 			continue;
79812375Sralph 		case '<':
79912375Sralph 			lp = text;
80012375Sralph 		case '>':
80112375Sralph 			continue;
80212375Sralph 		case '\0':
80312375Sralph 			while (lp != text && (*lp == ' ' || *lp == '\t'))
80412375Sralph 				lp--;
80512375Sralph 			*lp = c;
80612375Sralph 			return(text);
80712375Sralph 		}
80812375Sralph 		*lp++ = c;
80912375Sralph 	}
81012375Sralph }
81112375Sralph 
81212375Sralph /*
81312375Sralph  * Compare two strings and convert any upper case letters to lower case.
81412375Sralph  */
81512375Sralph 
81612695Sralph streq(s1, s2)
81712695Sralph 	register char *s1, *s2;
81812375Sralph {
81912375Sralph 	register int c;
82012375Sralph 
82112695Sralph 	while (c = *s1++)
82212695Sralph 		if ((c | 040) != (*s2++ | 040))
82312375Sralph 			return(0);
82412695Sralph 	return(*s2 == '\0');
82512375Sralph }
82612695Sralph 
82712695Sralph /*
82812695Sralph  * Return true if string s2 matches the first part of s1.
82912695Sralph  */
83012695Sralph 
83112695Sralph substr(s1, s2)
83212695Sralph 	register char *s1, *s2;
83312695Sralph {
83412695Sralph 	register int c;
83512695Sralph 
83612695Sralph 	while (c = *s2++)
83712695Sralph 		if (c != *s1++)
83812695Sralph 			return(0);
83912695Sralph 	return(1);
84012695Sralph }
84112695Sralph 
84214809Ssam char *
84314809Ssam any(cp, set)
84414809Ssam 	register char *cp;
84514809Ssam 	char *set;
84614809Ssam {
84714809Ssam 	register char *sp;
84814809Ssam 
84914809Ssam 	if (cp == 0 || set == 0)
85014809Ssam 		return (0);
85114809Ssam 	while (*cp) {
85214809Ssam 		for (sp = set; *sp; sp++)
85314809Ssam 			if (*cp == *sp)
85414809Ssam 				return (cp);
85514809Ssam 		cp++;
85614809Ssam 	}
85714809Ssam 	return ((char *)0);
85814809Ssam }
85914809Ssam 
86012695Sralph peekc(fp)
86112695Sralph FILE *fp;
86212695Sralph {
86312695Sralph 	register c;
86412695Sralph 
86512695Sralph 	c = getc(fp);
86612695Sralph 	ungetc(c, fp);
86712695Sralph 	return(c);
86812695Sralph }
869