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