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