|
|
1.1 root 1: #ifndef lint
2: static char sccsid[] = "@(#)bugfiler.c 4.11 (Berkeley) 9/14/83";
3: #endif
4:
5: /*
6: * Bug report processing program.
7: * It is designed to be invoked by alias(5)
8: * and to be compatible with mh.
9: */
10:
11: #include <stdio.h>
12: #include <ctype.h>
13: #include <signal.h>
14: #include <pwd.h>
15:
16: #include <sys/types.h>
17: #include <sys/stat.h>
18: #include <sys/dir.h>
19:
20: #define BUGS_NAME "4bsd-bugs"
21: #define BUGS_HOME "%ucbarpa@BERKELEY"
22: #define MAILCMD "/usr/lib/sendmail -i -t"
23:
24: char unixtomh[] = "/usr/new/lib/mh/unixtomh";
25: char *bugperson = "bugs";
26: char *maildir = "mail";
27: char ackfile[] = ".ack";
28: char errfile[] = ".format";
29: char sumfile[] = "summary";
30: char logfile[] = "errors/log";
31: char redistfile[] = ".redist";
32: char tmpname[] = "BfXXXXXX";
33: char draft[] = "RpXXXXXX";
34: char disttmp[] = "RcXXXXXX";
35:
36: char buf[8192];
37: char folder[MAXNAMLEN];
38: int num;
39: int msg_prot = 0664;
40:
41: int debug;
42:
43: char *index();
44: char *rindex();
45: char *fixaddr();
46: char *any();
47:
48: main(argc, argv)
49: char *argv[];
50: {
51: register char *cp;
52: register int n;
53: int pfd[2];
54:
55: if (argc > 3) {
56: usage:
57: fprintf(stderr, "Usage: bugfiler [-d] [-mmsg_mode] [maildir]\n");
58: exit(1);
59: }
60: while (--argc > 0) {
61: cp = *++argv;
62: if (*cp == '-')
63: switch (cp[1]) {
64: case 'd':
65: debug++;
66: break;
67:
68: case 'm': /* set message protection */
69: n = 0;
70: for (cp += 2; *cp >= '0' && *cp <= '7'; )
71: n = (n << 3) + (*cp++ - '0');
72: msg_prot = n & 0777;
73: break;
74:
75: default:
76: goto usage;
77: }
78: else
79: maildir = cp;
80: }
81: if (!debug)
82: freopen(logfile, "a", stderr);
83:
84: if (bugperson) {
85: struct passwd *pwd = getpwnam(bugperson);
86:
87: if (pwd == NULL) {
88: fprintf(stderr, "%s: bugs person is unknown\n",
89: bugperson);
90: exit(1);
91: }
92: if (chdir(pwd->pw_dir) < 0) {
93: fprintf(stderr, "can't chdir to %s\n", pwd->pw_dir);
94: exit(1);
95: }
96: }
97: if (chdir(maildir) < 0) {
98: fprintf(stderr, "can't chdir to %s\n", maildir);
99: exit(1);
100: }
101: umask(0);
102:
103: #ifdef UNIXCOMP
104: /*
105: * Convert UNIX style mail to mh style by filtering stdin through
106: * unixtomh.
107: */
108: if (pipe(pfd) >= 0) {
109: while ((n = fork()) == -1)
110: sleep(5);
111: if (n == 0) {
112: close(pfd[0]);
113: dup2(pfd[1], 1);
114: close(pfd[1]);
115: execl(unixtomh, "unixtomh", 0);
116: _exit(127);
117: }
118: close(pfd[1]);
119: dup2(pfd[0], 0);
120: close(pfd[0]);
121: }
122: #endif
123: while (process())
124: ;
125: exit(0);
126: }
127:
128: /* states */
129:
130: #define EOM 0 /* End of message seen */
131: #define FLD 1 /* Looking for header lines */
132: #define BODY 2 /* Looking for message body lines */
133:
134: /* defines used for tag attributes */
135:
136: #define H_REQ 01
137: #define H_SAV 02
138: #define H_HDR 04
139: #define H_FND 010
140:
141: #define FROM_I headers[0].h_info
142: #define SUBJECT_I headers[1].h_info
143: #define INDEX &headers[2]
144: #define INDEX_I headers[2].h_info
145: #define DATE_I headers[3].h_info
146: #define MSGID_I headers[4].h_info
147: #define REPLYTO_I headers[5].h_info
148: #define TO_I headers[6].h_info
149: #define CC_I headers[7].h_info
150: #define FIX headers[10]
151:
152: struct header {
153: char *h_tag;
154: int h_flags;
155: char *h_info;
156: } headers[] = {
157: "From", H_REQ|H_SAV|H_HDR, 0,
158: "Subject", H_REQ|H_SAV, 0,
159: "Index", H_REQ|H_SAV, 0,
160: "Date", H_SAV|H_HDR, 0,
161: "Message-Id", H_SAV|H_HDR, 0,
162: "Reply-To", H_SAV|H_HDR, 0,
163: "To", H_SAV|H_HDR, 0,
164: "Cc", H_SAV|H_HDR, 0,
165: "Description", H_REQ, 0,
166: "Repeat-By", 0, 0,
167: "Fix", 0, 0,
168: 0, 0, 0,
169: };
170:
171: struct header *findheader();
172:
173: process()
174: {
175: register struct header *hp;
176: register char *cp;
177: register int c;
178: char *info;
179: int state, tmp;
180: FILE *tfp, *fs;
181:
182: /*
183: * Insure all headers are in a consistent
184: * state. Anything left there is free'd.
185: */
186: for (hp = headers; hp->h_tag; hp++) {
187: hp->h_flags &= ~H_FND;
188: if (hp->h_info) {
189: free(hp->h_info);
190: hp->h_info = 0;
191: }
192: }
193: /*
194: * Read the report and make a copy. Must conform to RFC822 and
195: * be of the form... <tag>: <info>
196: * Note that the input is expected to be in mh mail format
197: * (i.e., messages are separated by lines of ^A's).
198: */
199: while ((c = getchar()) == '\001' && peekc(stdin) == '\001')
200: while (getchar() != '\n')
201: ;
202: if (c == EOF)
203: return(0); /* all done */
204:
205: mktemp(tmpname);
206: if ((tmp = creat(tmpname, msg_prot)) < 0) {
207: fprintf(stderr, "cannont create %s\n", tmpname);
208: exit(1);
209: }
210: if ((tfp = fdopen(tmp, "w")) == NULL) {
211: fprintf(stderr, "cannot fdopen temp file\n");
212: exit(1);
213: }
214:
215: for (state = FLD; state != EOF && state != EOM; c = getchar()) {
216: switch (state) {
217: case FLD:
218: if (c == '\n' || c == '-')
219: goto body;
220: for (cp = buf; c != ':'; c = getchar()) {
221: if (cp < buf+sizeof(buf)-1 && c != '\n' && c != EOF) {
222: *cp++ = c;
223: continue;
224: }
225: *cp = '\0';
226: fputs(buf, tfp);
227: state = EOF;
228: while (c != EOF) {
229: if (c == '\n')
230: if ((tmp = peekc(stdin)) == EOF)
231: break;
232: else if (tmp == '\001') {
233: state = EOM;
234: break;
235: }
236: putc(c, tfp);
237: c = getchar();
238: }
239: fclose(tfp);
240: goto badfmt;
241: }
242: *cp = '\0';
243: fprintf(tfp, "%s:", buf);
244: hp = findheader(buf, state);
245:
246: for (cp = buf; ; ) {
247: if (cp >= buf+sizeof(buf)-1) {
248: fprintf(stderr, "field truncated\n");
249: while ((c = getchar()) != EOF && c != '\n')
250: putc(c, tfp);
251: }
252: if ((c = getchar()) == EOF) {
253: state = EOF;
254: break;
255: }
256: putc(c, tfp);
257: *cp++ = c;
258: if (c == '\n')
259: if ((c = peekc(stdin)) != ' ' && c != '\t') {
260: if (c == EOF)
261: state = EOF;
262: else if (c == '\001')
263: state = EOM;
264: break;
265: }
266: }
267: *cp = '\0';
268: cp = buf;
269: break;
270:
271: body:
272: state = BODY;
273: case BODY:
274: for (cp = buf; ; c = getchar()) {
275: if (c == EOF) {
276: state = EOF;
277: break;
278: }
279: if (c == '\001' && peekc(stdin) == '\001') {
280: state = EOM;
281: break;
282: }
283: putc(c, tfp);
284: *cp++ = c;
285: if (cp >= buf+sizeof(buf)-1 || c == '\n')
286: break;
287: }
288: *cp = '\0';
289: if ((cp = index(buf, ':')) == NULL)
290: continue;
291: *cp++ = '\0';
292: hp = findheader(buf, state);
293: }
294:
295: /*
296: * Don't save the info if the header wasn't found, we don't
297: * care about the info, or the header is repeated.
298: */
299: if (hp == NULL || !(hp->h_flags & H_SAV) || hp->h_info)
300: continue;
301: while (isspace(*cp))
302: cp++;
303: if (*cp) {
304: info = cp;
305: while (*cp++);
306: cp--;
307: while (isspace(cp[-1]))
308: *--cp = '\0';
309: hp->h_info = (char *) malloc(strlen(info) + 1);
310: if (hp->h_info == NULL) {
311: fprintf(stderr, "ran out of memory\n");
312: continue;
313: }
314: strcpy(hp->h_info, info);
315: if (hp == INDEX)
316: chkindex(hp);
317: }
318: }
319: fclose(tfp);
320: /*
321: * Verify all the required pieces of information
322: * are present.
323: */
324: for (hp = headers; hp->h_tag; hp++) {
325: /*
326: * Mail the bug report back to the sender with a note
327: * explaining they must conform to the specification.
328: */
329: if ((hp->h_flags & H_REQ) && !(hp->h_flags & H_FND)) {
330: if (debug)
331: printf("Missing %s\n", hp->h_tag);
332: badfmt:
333: reply(FROM_I, errfile, tmpname);
334: file(tmpname, "errors");
335: return(state == EOM);
336: }
337: }
338: /*
339: * Acknowledge receipt.
340: */
341: reply(FROM_I, ackfile, (char *)0);
342: file(tmpname, folder);
343: /*
344: * Append information about the new bug report
345: * to the summary file.
346: */
347: if ((fs = fopen(sumfile, "a")) == NULL)
348: fprintf(stderr, "Can't open %s\n", sumfile);
349: else {
350: fprintf(fs, "%14.14s/%-3d ", folder, num);
351: fprintf(fs, "%-51.51s Recv\n", INDEX_I);
352: fprintf(fs, "\t\t %-51.51s\n", SUBJECT_I);
353: }
354: fclose(fs);
355: /*
356: * Check redistribution list and, if members,
357: * mail a copy of the bug report to these people.
358: */
359: redistribute(folder, num);
360: return(state == EOM);
361: }
362:
363: /*
364: * Lookup the string in the list of headers and return a pointer
365: * to the entry or NULL.
366: */
367:
368: struct header *
369: findheader(name, state)
370: char *name;
371: int state;
372: {
373: register struct header *hp;
374:
375: if (debug)
376: printf("findheader(%s, %d)\n", name, state);
377:
378: for (hp = headers; hp->h_tag; hp++) {
379: if (!streq(hp->h_tag, buf))
380: continue;
381: if ((hp->h_flags & H_HDR) && state != FLD)
382: continue;
383: hp->h_flags |= H_FND;
384: return(hp);
385: }
386: return(NULL);
387: }
388:
389: /*
390: * Check the format of the Index information.
391: * A side effect is to set the name of the folder if all is well.
392: */
393:
394: chkindex(hp)
395: struct header *hp;
396: {
397: register char *cp1, *cp2;
398: register char c;
399: struct stat stbuf;
400:
401: if (debug)
402: printf("chkindex(%s)\n", hp->h_info);
403: /*
404: * Strip of leading "/", "usr/", or "src/".
405: */
406: cp1 = hp->h_info;
407: while (*cp1 == '/')
408: cp1++;
409: while (substr(cp1, "usr/") || substr(cp1, "src/"))
410: cp1 += 4;
411: /*
412: * Read the folder name and remove it from the index line.
413: */
414: for (cp2 = folder; ;) {
415: switch (c = *cp1++) {
416: case '/':
417: if (cp2 == folder)
418: continue;
419: break;
420: case '\0':
421: cp1--;
422: break;
423: case ' ':
424: case '\t':
425: while (isspace(*cp1))
426: cp1++;
427: break;
428: default:
429: if (cp2 < folder+sizeof(folder)-1)
430: *cp2++ = c;
431: continue;
432: }
433: *cp2 = '\0';
434: for (cp2 = hp->h_info; *cp2++ = *cp1++; )
435: ;
436: break;
437: }
438: if (debug)
439: printf("folder = %s\n", folder);
440: /*
441: * Check to make sure we have a valid folder name
442: */
443: if (stat(folder, &stbuf) == 0 && (stbuf.st_mode & S_IFMT) == S_IFDIR)
444: return;
445: /*
446: * The Index line is not in the correct format so clear
447: * the H_FND flag to mail back the correct format.
448: */
449: hp->h_flags &= ~H_FND;
450: }
451:
452: /*
453: * Move or copy the file msg to the folder (directory).
454: * As a side effect, num is set to the number under which
455: * the message is filed in folder.
456: */
457:
458: file(fname, folder)
459: char *fname, *folder;
460: {
461: register char *cp, n;
462: char msgname[MAXNAMLEN*2+2];
463: struct stat stbuf;
464: DIR *dirp;
465: struct direct *d;
466:
467: if (debug)
468: printf("file(%s, %s)\n", fname, folder);
469: /*
470: * Get the next number to use by finding the last message number
471: * in folder and adding one.
472: */
473: if ((dirp = opendir(folder)) == NULL) {
474: fprintf(stderr, "Cannot open %s/%s\n", maildir, folder);
475: return;
476: }
477: num = 0;
478: while ((d = readdir(dirp)) != NULL) {
479: cp = d->d_name;
480: n = 0;
481: while (isdigit(*cp))
482: n = n * 10 + (*cp++ - '0');
483: if (*cp == '\0' && n > num)
484: num = n;
485: }
486: closedir(dirp);
487: num++;
488: /*
489: * Create the destination file "folder/num" and copy fname to it.
490: */
491: sprintf(msgname, "%s/%d", folder, num);
492: if (link(fname, msgname) < 0) {
493: int fin, fout;
494:
495: if ((fin = open(fname, 0)) < 0) {
496: fprintf(stderr, "cannot open %s\n", fname);
497: return;
498: }
499: if ((fout = creat(msgname, msg_prot)) < 0) {
500: fprintf(stderr, "cannot create %s\n", msgname);
501: return;
502: }
503: while ((n = read(fin, buf, sizeof(buf))) > 0)
504: write(fout, buf, n);
505: close(fin);
506: close(fout);
507: }
508: unlink(fname);
509: }
510:
511: /*
512: * Redistribute a bug report to those people indicated
513: * in the redistribution list file. Perhaps should also
514: * annotate bug report with this information for future
515: * reference?
516: */
517: redistribute(folder, num)
518: char *folder;
519: int num;
520: {
521: FILE *fredist, *fbug, *ftemp;
522: char line[BUFSIZ], bug[2 * MAXNAMLEN + 1];
523: register char *cp;
524: int redistcnt, continuation, first;
525:
526: fredist = fopen(redistfile, "r");
527: if (fredist == NULL) {
528: if (debug)
529: printf("redistribute(%s, %d), no distribution list\n",
530: folder, num);
531: return;
532: }
533: continuation = 0;
534: first = 1;
535: redistcnt = 0;
536: while (fgets(line, sizeof (line) - 1, fredist) != NULL) {
537: if (debug)
538: printf("%s: %s", redistfile, line);
539: if (continuation && index(line, '\\'))
540: continue;
541: continuation = 0;
542: cp = any(line, " \t");
543: if (cp == NULL)
544: continue;
545: *cp++ = '\0';
546: if (strcmp(folder, line) == 0)
547: goto found;
548: if (index(cp, '\\'))
549: continuation = 1;
550: }
551: if (debug)
552: printf("no redistribution list found\n");
553: fclose(fredist);
554: return;
555: found:
556: mktemp(disttmp);
557: ftemp = fopen(disttmp, "w+r");
558: if (ftemp == NULL) {
559: if (debug)
560: printf("%s: couldn't create\n", disttmp);
561: return;
562: }
563: again:
564: if (debug)
565: printf("redistribution list %s", cp);
566: while (cp) {
567: char *user, terminator;
568:
569: while (*cp && (*cp == ' ' || *cp == '\t' || *cp == ','))
570: cp++;
571: user = cp, cp = any(cp, ", \t\n\\");
572: if (cp) {
573: terminator = *cp;
574: *cp++ = '\0';
575: if (terminator == '\n')
576: cp = 0;
577: if (terminator == '\\')
578: continuation++;
579: }
580: if (*user == '\0')
581: continue;
582: if (debug)
583: printf("copy to %s\n", user);
584: if (first) {
585: fprintf(ftemp, "To: %s", user);
586: first = 0;
587: } else
588: fprintf(ftemp, ", %s", user);
589: redistcnt++;
590: }
591: if (!first)
592: putc('\n', ftemp);
593: if (continuation) {
594: first = 1;
595: continuation = 0;
596: cp = line;
597: if (fgets(line, sizeof (line) - 1, fredist))
598: goto again;
599: }
600: fclose(fredist);
601: if (redistcnt == 0)
602: goto cleanup;
603: fprintf(ftemp, "Subject: ");
604: if (SUBJECT_I)
605: fprintf(ftemp, "%s\n", SUBJECT_I);
606: else
607: fprintf(ftemp, "Untitled bug report\n");
608: fprintf(ftemp, "\nRedistributed-by: %s%s\n", BUGS_NAME, BUGS_HOME);
609: /*
610: * Create copy of bug report. Perhaps we should
611: * truncate large messages and just give people
612: * a pointer to the original?
613: */
614: sprintf(bug, "%s/%d", folder, num);
615: fbug = fopen(bug, "r");
616: if (fbug == NULL) {
617: if (debug)
618: printf("%s: disappeared?\n", bug);
619: goto cleanup;
620: }
621: first = 1;
622: while (fgets(line, sizeof (line) - 1, fbug)) {
623: /* first blank line indicates start of mesg */
624: if (first && line[0] == '\n') {
625: first = 0;
626: continue;
627: }
628: fputs(line, ftemp);
629: }
630: fclose(fbug);
631: if (first) {
632: if (debug)
633: printf("empty bug report?\n");
634: goto cleanup;
635: }
636: if (dodeliver(ftemp))
637: unlink(disttmp);
638: fclose(ftemp);
639: return;
640: cleanup:
641: fclose(ftemp);
642: unlink(disttmp);
643: }
644:
645: dodeliver(fd)
646: FILE *fd;
647: {
648: char buf[BUFSIZ], cmd[BUFSIZ];
649: FILE *pf, *popen();
650:
651: strcpy(cmd, MAILCMD);
652: if (debug) {
653: strcat(cmd, " -v");
654: printf("dodeliver \"%s\"\n", cmd);
655: }
656: pf = popen(cmd, "w");
657: if (pf == NULL) {
658: if (debug)
659: printf("dodeliver, \"%s\" failed\n", cmd);
660: return (0);
661: }
662: rewind(fd);
663: while (fgets(buf, sizeof (buf) - 1, fd)) {
664: if (debug)
665: printf("%s", buf);
666: (void) fputs(buf, pf);
667: }
668: if (debug)
669: printf("EOF\n");
670: (void) pclose(pf);
671: return (1);
672: }
673:
674: /*
675: * Mail file1 and file2 back to the sender.
676: */
677:
678: reply(to, file1, file2)
679: char *to, *file1, *file2;
680: {
681: int pfd[2], in, w;
682: FILE *fout;
683:
684: if (debug)
685: printf("reply(%s, %s, %s)\n", to, file1, file2);
686:
687: /*
688: * Create a temporary file to put the message in.
689: */
690: mktemp(draft);
691: if ((fout = fopen(draft, "w+r")) == NULL) {
692: fprintf(stderr, "Can't create %s\n", draft);
693: return;
694: }
695: /*
696: * Output the proper header information.
697: */
698: fprintf(fout, "Reply-To: %s%s\n", BUGS_NAME, BUGS_HOME);
699: fprintf(fout, "From: %s%s (Bugs Bunny)\n", BUGS_NAME, BUGS_HOME);
700: if (REPLYTO_I != NULL)
701: to = REPLYTO_I;
702: if ((to = fixaddr(to)) == 0) {
703: fprintf(stderr, "No one to reply to\n");
704: return;
705: }
706: fprintf(fout, "To: %s\n", to);
707: if (SUBJECT_I) {
708: fprintf(fout, "Subject: ");
709: if ((SUBJECT_I[0] != 'R' && SUBJECT_I[0] != 'r') ||
710: (SUBJECT_I[1] != 'E' && SUBJECT_I[1] != 'e') ||
711: SUBJECT_I[2] != ':')
712: fprintf(fout, "Re: ");
713: fprintf(fout, "%s\n", SUBJECT_I);
714: }
715: if (DATE_I) {
716: fprintf(fout, "In-Acknowledgement-Of: Your message of ");
717: fprintf(fout, "%s.\n", DATE_I);
718: if (MSGID_I)
719: fprintf(fout, " %s\n", MSGID_I);
720: }
721: fprintf(fout, "\n");
722: if ((in = open(file1, 0)) >= 0) {
723: while ((w = read(in, buf, sizeof(buf))) > 0)
724: fwrite(buf, 1, w, fout);
725: close(in);
726: }
727: if (file2 && (in = open(file2, 0)) >= 0) {
728: while ((w = read(in, buf, sizeof(buf))) > 0)
729: fwrite(buf, 1, w, fout);
730: close(in);
731: }
732: if (dodeliver(fout))
733: unlink(draft);
734: fclose(fout);
735: }
736:
737: /*
738: * fix names like "xxx (something)" to "xxx" and
739: * "xxx <something>" to "something".
740: */
741:
742: char *
743: fixaddr(text)
744: char *text;
745: {
746: register char *cp, *lp, c;
747: char *tp;
748:
749: if (!text)
750: return(0);
751: for (lp = cp = text; ; ) {
752: switch (c = *cp++) {
753: case '(':
754: while (*cp && *cp++ != ')');
755: continue;
756: case '<':
757: lp = text;
758: case '>':
759: continue;
760: case '\0':
761: while (lp != text && (*lp == ' ' || *lp == '\t'))
762: lp--;
763: *lp = c;
764: return(text);
765: }
766: *lp++ = c;
767: }
768: }
769:
770: /*
771: * Compare two strings and convert any upper case letters to lower case.
772: */
773:
774: streq(s1, s2)
775: register char *s1, *s2;
776: {
777: register int c;
778:
779: while (c = *s1++)
780: if ((c | 040) != (*s2++ | 040))
781: return(0);
782: return(*s2 == '\0');
783: }
784:
785: /*
786: * Return true if string s2 matches the first part of s1.
787: */
788:
789: substr(s1, s2)
790: register char *s1, *s2;
791: {
792: register int c;
793:
794: while (c = *s2++)
795: if (c != *s1++)
796: return(0);
797: return(1);
798: }
799:
800: char *
801: any(cp, set)
802: register char *cp;
803: char *set;
804: {
805: register char *sp;
806:
807: if (cp == 0 || set == 0)
808: return (0);
809: while (*cp) {
810: for (sp = set; *sp; sp++)
811: if (*cp == *sp)
812: return (cp);
813: cp++;
814: }
815: return ((char *)0);
816: }
817:
818: peekc(fp)
819: FILE *fp;
820: {
821: register c;
822:
823: c = getc(fp);
824: ungetc(c, fp);
825: return(c);
826: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.