xref: /csrg-svn/libexec/bugfiler/bugfiler.c (revision 12375)
1*12375Sralph /*	bugfiler.c	4.1	83/05/11	*/
2*12375Sralph /*
3*12375Sralph  * Bug report processing program.
4*12375Sralph  * It is designed to be invoked by alias(5) and to be compatible with mh.
5*12375Sralph  */
6*12375Sralph 
7*12375Sralph #include <stdio.h>
8*12375Sralph #include <ctype.h>
9*12375Sralph #include <signal.h>
10*12375Sralph #include <sys/types.h>
11*12375Sralph #include <sys/stat.h>
12*12375Sralph #include <dir.h>
13*12375Sralph 
14*12375Sralph char	deliver[] = "/usr/local/lib/mh/deliver";
15*12375Sralph char	unixtomh[] = "/usr/local/lib/mh/unixtomh";
16*12375Sralph char	*maildir = "/ra/bugs/mail";
17*12375Sralph char	ackfile[] = ".ack";
18*12375Sralph char	errfile[] = ".format";
19*12375Sralph char	sumfile[] = "summary";
20*12375Sralph char	logfile[] = "errors/log";
21*12375Sralph char	tmpname[] = "BfXXXXXX";
22*12375Sralph char	draft[] = "RpXXXXXX";
23*12375Sralph 
24*12375Sralph char	line[BUFSIZ];
25*12375Sralph char	folder[MAXNAMLEN];
26*12375Sralph int	num;
27*12375Sralph int	msg_prot = 0664;
28*12375Sralph 
29*12375Sralph int	debug;
30*12375Sralph 
31*12375Sralph char	*index();
32*12375Sralph char	*rindex();
33*12375Sralph char	*fixaddr();
34*12375Sralph 
35*12375Sralph main(argc, argv)
36*12375Sralph 	char *argv[];
37*12375Sralph {
38*12375Sralph 	register char *cp;
39*12375Sralph 
40*12375Sralph 	if (argc > 3) {
41*12375Sralph 	usage:
42*12375Sralph 		fprintf(stderr, "Usage: bugfiler [-d] [maildir]\n");
43*12375Sralph 		exit(1);
44*12375Sralph 	}
45*12375Sralph 	while (--argc > 0) {
46*12375Sralph 		cp = *++argv;
47*12375Sralph 		if (*cp == '-') while (*++cp)
48*12375Sralph 			switch (*cp) {
49*12375Sralph 			case 'd':
50*12375Sralph 				debug++;
51*12375Sralph 				break;
52*12375Sralph 			default:
53*12375Sralph 				goto usage;
54*12375Sralph 			}
55*12375Sralph 		else
56*12375Sralph 			maildir = cp;
57*12375Sralph 	}
58*12375Sralph 	if (chdir(maildir) < 0) {
59*12375Sralph 		fprintf(stderr, "can't chdir to %s\n", maildir);
60*12375Sralph 		exit(1);
61*12375Sralph 	}
62*12375Sralph 	if (freopen(logfile, "a", stderr) == NULL)
63*12375Sralph 		freopen("/dev/null", "w", stderr);
64*12375Sralph 	exit(process());
65*12375Sralph }
66*12375Sralph 
67*12375Sralph /* defines used for tag attributes */
68*12375Sralph 
69*12375Sralph #define H_REQ 01
70*12375Sralph #define H_OPT 02
71*12375Sralph #define H_SAV 04
72*12375Sralph 
73*12375Sralph #define FROM_I headers[0].h_info
74*12375Sralph #define SUBJECT_I headers[1].h_info
75*12375Sralph #define INDEX &headers[2]
76*12375Sralph #define INDEX_I headers[2].h_info
77*12375Sralph #define DATE_I headers[3].h_info
78*12375Sralph #define MSGID_I headers[4].h_info
79*12375Sralph #define REPLYTO_I headers[5].h_info
80*12375Sralph #define RETURNPATH_I headers[6].h_info
81*12375Sralph #define TO_I headers[7].h_info
82*12375Sralph #define CC_I headers[8].h_info
83*12375Sralph #define FIX headers[11]
84*12375Sralph 
85*12375Sralph struct header {
86*12375Sralph 	char	*h_tag;
87*12375Sralph 	int	h_flags;
88*12375Sralph 	char	*h_info;
89*12375Sralph } headers[] = {
90*12375Sralph 	"From",		H_REQ|H_SAV, 0,
91*12375Sralph 	"Subject",	H_REQ|H_SAV, 0,
92*12375Sralph 	"Index",	H_REQ|H_SAV, 0,
93*12375Sralph 	"Date",		H_OPT|H_SAV, 0,
94*12375Sralph 	"Message-Id",	H_OPT|H_SAV, 0,
95*12375Sralph 	"Reply-To",	H_OPT|H_SAV, 0,
96*12375Sralph 	"Return-Path",	H_OPT|H_SAV, 0,
97*12375Sralph 	"To",		H_OPT|H_SAV, 0,
98*12375Sralph 	"Cc",		H_OPT|H_SAV, 0,
99*12375Sralph 	"Description",	H_REQ,       0,
100*12375Sralph 	"Repeat-By",	H_REQ,	     0,
101*12375Sralph 	"Fix",		H_OPT,	     0,
102*12375Sralph 	0,	0,	0,
103*12375Sralph };
104*12375Sralph 
105*12375Sralph process()
106*12375Sralph {
107*12375Sralph 	register struct header *hp;
108*12375Sralph 	register char *cp;
109*12375Sralph 	char *info;
110*12375Sralph 	int tmp, pfd[2];
111*12375Sralph 	FILE *fs;
112*12375Sralph 
113*12375Sralph 	/*
114*12375Sralph 	 * Insure all headers are in a consistent
115*12375Sralph 	 * state.  Anything left there is free'd.
116*12375Sralph 	 */
117*12375Sralph 	for (hp = headers; hp->h_tag; hp++) {
118*12375Sralph 		if (hp->h_info) {
119*12375Sralph 			if (hp->h_info != (char *) 1)
120*12375Sralph 				free(hp->h_info);
121*12375Sralph 			hp->h_info = 0;
122*12375Sralph 		}
123*12375Sralph 	}
124*12375Sralph #ifdef UNIXCOMP
125*12375Sralph 	/*
126*12375Sralph 	 * Convert UNIX style mail to mh style by filtering stdin through
127*12375Sralph 	 * unixtomh.
128*12375Sralph 	 */
129*12375Sralph 	if (pipe(pfd) >= 0) {
130*12375Sralph 		register int n;
131*12375Sralph 
132*12375Sralph 		while ((n = fork()) == -1)
133*12375Sralph 			sleep(5);
134*12375Sralph 		if (n == 0) {
135*12375Sralph 			close(pfd[0]);
136*12375Sralph 			dup2(pfd[1], 1);
137*12375Sralph 			close(pfd[1]);
138*12375Sralph 			execl(unixtomh, "unixtomh", 0);
139*12375Sralph 			_exit(127);
140*12375Sralph 		}
141*12375Sralph 		close(pfd[1]);
142*12375Sralph 		dup2(pfd[0], 0);
143*12375Sralph 		close(pfd[0]);
144*12375Sralph 	}
145*12375Sralph #endif
146*12375Sralph 	/*
147*12375Sralph 	 * Read the report and make a copy.  Must conform to RFC822 and
148*12375Sralph 	 * be of the form... <tag>: <info>
149*12375Sralph 	 */
150*12375Sralph 	mktemp(tmpname);
151*12375Sralph 	if ((tmp = creat(tmpname, msg_prot)) < 0)
152*12375Sralph 		return(1);
153*12375Sralph 	while ((cp = fgets(line, sizeof(line), stdin)) != NULL) {
154*12375Sralph 		if (line[0] == '\01')
155*12375Sralph 			continue;
156*12375Sralph 		write(tmp, cp, strlen(cp));
157*12375Sralph 		cp = index(cp, ':');
158*12375Sralph 		if (cp == 0)
159*12375Sralph 			continue;
160*12375Sralph 		*cp++ = '\0';
161*12375Sralph 		for (hp = headers; hp->h_tag; hp++)
162*12375Sralph 			if (streq(hp->h_tag, line))
163*12375Sralph 				break;
164*12375Sralph 		if (hp->h_tag == 0)
165*12375Sralph 			continue;
166*12375Sralph 		if (!(hp->h_flags & H_SAV)) {
167*12375Sralph 			hp->h_info = (char *) 1;
168*12375Sralph 			continue;
169*12375Sralph 		}
170*12375Sralph 		while (isspace(*cp))
171*12375Sralph 			cp++;
172*12375Sralph 		if (*cp) {
173*12375Sralph 			info = cp;
174*12375Sralph 			while (*cp++);
175*12375Sralph 			cp--;
176*12375Sralph 			while (isspace(cp[-1]))
177*12375Sralph 				*--cp = '\0';
178*12375Sralph 			hp->h_info = (char *) malloc(strlen(info) + 1);
179*12375Sralph 			if (hp->h_info == NULL)
180*12375Sralph 				continue;
181*12375Sralph 			strcpy(hp->h_info, info);
182*12375Sralph 			if (hp == INDEX)
183*12375Sralph 				chkindex(hp);
184*12375Sralph 		}
185*12375Sralph 	}
186*12375Sralph 	close(tmp);
187*12375Sralph 	/*
188*12375Sralph 	 * Verify all the required pieces of information
189*12375Sralph 	 * are present.
190*12375Sralph 	 */
191*12375Sralph 	for (hp = headers; hp->h_tag; hp++)
192*12375Sralph 		if ((hp->h_flags & H_REQ) && !hp->h_info)
193*12375Sralph 			break;
194*12375Sralph 	if (hp->h_tag) {
195*12375Sralph 		/*
196*12375Sralph 		 * Mail the bug report back to the sender with a note
197*12375Sralph 		 * explaining they must conform to the specification.
198*12375Sralph 		 */
199*12375Sralph 		if (debug)
200*12375Sralph 			fprintf(stderr, "Missing %s\n", hp->h_tag);
201*12375Sralph 		reply(FROM_I, errfile, tmpname);
202*12375Sralph 		file(tmpname, "errors");
203*12375Sralph 		return(0);
204*12375Sralph 	}
205*12375Sralph 	else {	/* Acknowledge receipt */
206*12375Sralph 		reply(FROM_I, ackfile, (char *)0);
207*12375Sralph 		file(tmpname, folder);
208*12375Sralph 	}
209*12375Sralph 	/*
210*12375Sralph 	 * Append information about the new bug report
211*12375Sralph 	 * to the summary file.
212*12375Sralph 	 */
213*12375Sralph 	if ((fs = fopen(sumfile, "a")) == NULL) {
214*12375Sralph 		fprintf(stderr, "Can't open %s\n", sumfile);
215*12375Sralph 		return(1);
216*12375Sralph 	}
217*12375Sralph 	fprintf(fs, "%14.14s/%-3d  %s\n\t\t    %s\n", folder, num, INDEX_I, SUBJECT_I);
218*12375Sralph 	fclose(fs);
219*12375Sralph 	return(0);
220*12375Sralph }
221*12375Sralph 
222*12375Sralph /*
223*12375Sralph  * Check the format of the Index information.
224*12375Sralph  * A side effect is to set the name of the folder if all is well.
225*12375Sralph  */
226*12375Sralph 
227*12375Sralph chkindex(hp)
228*12375Sralph 	struct header *hp;
229*12375Sralph {
230*12375Sralph 	register char *cp1, *cp2, *cp3, *cp4;
231*12375Sralph 	register char c;
232*12375Sralph 	struct stat stbuf;
233*12375Sralph 
234*12375Sralph 	if (debug)
235*12375Sralph 		fprintf(stderr, "chkindex(%s)\n", hp->h_info);
236*12375Sralph 	/*
237*12375Sralph 	 * Read the folder name and remove it from the index line.
238*12375Sralph 	 */
239*12375Sralph 	for (cp1 = hp->h_info, cp2 = NULL, cp3 = folder, cp4 == NULL; ;) {
240*12375Sralph 		c = *cp1++;
241*12375Sralph 		if (c == '\0' || isspace(c) || cp3 >= folder+sizeof(folder)-1) {
242*12375Sralph 			if (cp4 == NULL)
243*12375Sralph 				*cp3 = '\0';
244*12375Sralph 			else
245*12375Sralph 				*cp4 = '\0';
246*12375Sralph 			if (cp2 == NULL) {
247*12375Sralph 				cp2 = cp1 - 1;
248*12375Sralph 				while (isspace(*cp2))
249*12375Sralph 					cp2++;
250*12375Sralph 			}
251*12375Sralph 			for (cp3 = hp->h_info; *cp3++ = *cp2++; );
252*12375Sralph 			break;
253*12375Sralph 		} else {
254*12375Sralph 			if (c == '/') {
255*12375Sralph 				cp2 = cp1;
256*12375Sralph 				cp4 = cp3;
257*12375Sralph 			}
258*12375Sralph 			*cp3++ = c;
259*12375Sralph 		}
260*12375Sralph 	}
261*12375Sralph 	/*
262*12375Sralph 	 * Check to see if a Fix is included.
263*12375Sralph 	if ((cp1 = rindex(hp->h_info, ' ')) == NULL) {
264*12375Sralph 		if ((cp1 = rindex(hp->h_info, '\t')) != NULL)
265*12375Sralph 			cp1++;
266*12375Sralph 	} else
267*12375Sralph 		cp1++;
268*12375Sralph 	if (cp1 != NULL && streq(cp1, FIX.h_tag))
269*12375Sralph 		FIX.h_flags = H_REQ;
270*12375Sralph 	else
271*12375Sralph 		FIX.h_flags = 0;
272*12375Sralph 	 */
273*12375Sralph 	/*
274*12375Sralph 	 * Check to make sure we have a valid folder name
275*12375Sralph 	 */
276*12375Sralph 	if (stat(folder, &stbuf) == 0 && (stbuf.st_mode & S_IFMT) == S_IFDIR)
277*12375Sralph 		return;
278*12375Sralph 	/*
279*12375Sralph 	 * The Index line is not in the correct format so clear
280*12375Sralph 	 * the h_info line to mail back the correct format.
281*12375Sralph 	 */
282*12375Sralph 	hp->h_info = 0;
283*12375Sralph }
284*12375Sralph 
285*12375Sralph /*
286*12375Sralph  * Move or copy the file msg to the folder (directory).
287*12375Sralph  * A side effect is to set num to the number of the file in folder.
288*12375Sralph  */
289*12375Sralph 
290*12375Sralph file(fname, folder)
291*12375Sralph 	char *fname, *folder;
292*12375Sralph {
293*12375Sralph 	register char *cp, n;
294*12375Sralph 	char msgname[MAXNAMLEN*2+2];
295*12375Sralph 	struct stat stbuf;
296*12375Sralph 	DIR *dirp;
297*12375Sralph 	struct direct *d;
298*12375Sralph 
299*12375Sralph 	if (debug)
300*12375Sralph 		fprintf(stderr, "file(%s, %s)\n", fname, folder);
301*12375Sralph 	/*
302*12375Sralph 	 * Get the next number to use by finding the last message number
303*12375Sralph 	 * in folder and adding one.
304*12375Sralph 	 */
305*12375Sralph 	if ((dirp = opendir(folder)) == NULL) {
306*12375Sralph 		fprintf(stderr, "Cannot open %s/%s\n", maildir, folder);
307*12375Sralph 		return;
308*12375Sralph 	}
309*12375Sralph 	num = 0;
310*12375Sralph 	while ((d = readdir(dirp)) != NULL) {
311*12375Sralph 		cp = d->d_name;
312*12375Sralph 		n = 0;
313*12375Sralph 		while (isdigit(*cp))
314*12375Sralph 			n = n * 10 + (*cp++ - '0');
315*12375Sralph 		if (*cp == '\0' && n > num)
316*12375Sralph 			num = n;
317*12375Sralph 	}
318*12375Sralph 	closedir(dirp);
319*12375Sralph 	num++;
320*12375Sralph 	/*
321*12375Sralph 	 * Create the destination file "folder/num" and copy fname to it.
322*12375Sralph 	 */
323*12375Sralph 	sprintf(msgname, "%s/%d", folder, num);
324*12375Sralph 	if (link(fname, msgname) < 0) {
325*12375Sralph 		int fin, fout;
326*12375Sralph 
327*12375Sralph 		if ((fin = open(fname, 0)) < 0)
328*12375Sralph 			return;
329*12375Sralph 		if ((fout = open(msgname, 1)) < 0)
330*12375Sralph 			return;
331*12375Sralph 		while ((n = read(fin, line, sizeof(line))) > 0)
332*12375Sralph 			write(fout, line, n);
333*12375Sralph 		close(fin);
334*12375Sralph 		close(fout);
335*12375Sralph 	}
336*12375Sralph 	unlink(fname);
337*12375Sralph }
338*12375Sralph 
339*12375Sralph /*
340*12375Sralph  * Mail file1 and file2 back to the sender.
341*12375Sralph  */
342*12375Sralph 
343*12375Sralph reply(to, file1, file2)
344*12375Sralph 	char	*to, *file1, *file2;
345*12375Sralph {
346*12375Sralph 	int (*istat)(), (*qstat)();
347*12375Sralph 	int pid, w, status, pfd[2], in;
348*12375Sralph 	FILE *fout;
349*12375Sralph 
350*12375Sralph 	if (debug)
351*12375Sralph 		fprintf(stderr, "reply(%s, %s, %s)\n", to, file1, file2);
352*12375Sralph 	/*
353*12375Sralph 	 * Create a temporary file to put the message in.
354*12375Sralph 	 */
355*12375Sralph 	mktemp(draft);
356*12375Sralph 	if ((fout = fopen(draft, "w")) == NULL) {
357*12375Sralph 		fprintf(stderr, "Can't create %s\n", draft);
358*12375Sralph 		return;
359*12375Sralph 	}
360*12375Sralph 	/*
361*12375Sralph 	 * Output the proper header information.
362*12375Sralph 	 */
363*12375Sralph 	fprintf(fout, "Reply-To: 4bsd-bugs@BERKELEY\n");
364*12375Sralph 	if (RETURNPATH_I != NULL)
365*12375Sralph 		to = RETURNPATH_I;
366*12375Sralph 	if (REPLYTO_I != NULL)
367*12375Sralph 		to = REPLYTO_I;
368*12375Sralph 	if ((to = fixaddr(to)) == 0) {
369*12375Sralph 		fprintf(stderr, "No one to reply to\n");
370*12375Sralph 		return;
371*12375Sralph 	}
372*12375Sralph 	fprintf(fout, "To: %s\n", to);
373*12375Sralph 	if (SUBJECT_I) {
374*12375Sralph 		fprintf(fout, "Subject: ");
375*12375Sralph 		if ((SUBJECT_I[0] != 'R' && SUBJECT_I[0] != 'r') ||
376*12375Sralph 		    (SUBJECT_I[1] != 'E' && SUBJECT_I[1] != 'e') ||
377*12375Sralph 		    SUBJECT_I[2] != ':')
378*12375Sralph 			fprintf(fout, "Re: ");
379*12375Sralph 		fprintf(fout, "%s\n", SUBJECT_I);
380*12375Sralph 	}
381*12375Sralph 	if (DATE_I) {
382*12375Sralph 		fprintf(fout, "In-Acknowledgement-Of: Your message of ");
383*12375Sralph 		fprintf(fout, "%s.\n", DATE_I);
384*12375Sralph 		if (MSGID_I)
385*12375Sralph 			fprintf(fout, "             %s\n", MSGID_I);
386*12375Sralph 	}
387*12375Sralph 	fprintf(fout, "----------\n");
388*12375Sralph 	if ((in = open(file1, 0)) >= 0) {
389*12375Sralph 		while ((w = read(in, line, sizeof(line))) > 0)
390*12375Sralph 			fwrite(line, 1, w, fout);
391*12375Sralph 		close(in);
392*12375Sralph 	}
393*12375Sralph 	if (file2 && (in = open(file2, 0)) >= 0) {
394*12375Sralph 		while ((w = read(in, line, sizeof(line))) > 0)
395*12375Sralph 			fwrite(line, 1, w, fout);
396*12375Sralph 		close(in);
397*12375Sralph 	}
398*12375Sralph 	fclose(fout);
399*12375Sralph 	while ((pid = fork()) == -1)
400*12375Sralph 		sleep(5);
401*12375Sralph 	if (pid == 0) {
402*12375Sralph 		execl(deliver, "deliver", draft, 0);
403*12375Sralph 		_exit(127);
404*12375Sralph 	}
405*12375Sralph 	istat = signal(SIGINT, SIG_IGN);
406*12375Sralph 	qstat = signal(SIGQUIT, SIG_IGN);
407*12375Sralph 	while ((w = wait(&status)) != -1 && w != pid);
408*12375Sralph 	signal(SIGINT, istat);
409*12375Sralph 	signal(SIGQUIT, qstat);
410*12375Sralph 	if (w != -1 && status == 0)
411*12375Sralph 		unlink(draft);
412*12375Sralph }
413*12375Sralph 
414*12375Sralph /*
415*12375Sralph  * fix names like "xxx (something)" to "xxx" and
416*12375Sralph  * "xxx <something>" to "something".
417*12375Sralph  */
418*12375Sralph 
419*12375Sralph char *
420*12375Sralph fixaddr(text)
421*12375Sralph 	char *text;
422*12375Sralph {
423*12375Sralph 	register char *cp, *lp, c;
424*12375Sralph 	char *tp;
425*12375Sralph 
426*12375Sralph 	if (!text)
427*12375Sralph 		return(0);
428*12375Sralph 	for (lp = cp = text; ; ) {
429*12375Sralph 		switch (c = *cp++) {
430*12375Sralph 		case '(':
431*12375Sralph 			while (*cp && *cp++ != ')');
432*12375Sralph 			continue;
433*12375Sralph 		case '<':
434*12375Sralph 			lp = text;
435*12375Sralph 		case '>':
436*12375Sralph 			continue;
437*12375Sralph 		case '\0':
438*12375Sralph 			while (lp != text && (*lp == ' ' || *lp == '\t'))
439*12375Sralph 				lp--;
440*12375Sralph 			*lp = c;
441*12375Sralph 			return(text);
442*12375Sralph 		}
443*12375Sralph 		*lp++ = c;
444*12375Sralph 	}
445*12375Sralph }
446*12375Sralph 
447*12375Sralph /*
448*12375Sralph  * Compare two strings and convert any upper case letters to lower case.
449*12375Sralph  */
450*12375Sralph 
451*12375Sralph streq(c1, c2)
452*12375Sralph 	register char *c1, *c2;
453*12375Sralph {
454*12375Sralph 	register int c;
455*12375Sralph 
456*12375Sralph 	while (c = *c1++)
457*12375Sralph 		if ((c | 040) != (*c2++ | 040))
458*12375Sralph 			return(0);
459*12375Sralph 	return(*c2 == '\0');
460*12375Sralph }
461