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