1: #ifndef lint 2: static char SccsId[] = "@(#)unixtomh.c 1.1 5/16/85"; 3: #endif 4: 5: /* 6: * This program copies the mail file in standard unix format 7: * given as $1 to the file $2 in Rand Message Handler format. 8: * The change made is to bracket each message with a line 9: * containing 4 control-A's and to split the From line into 10: * a From: field and a Date: field, with the date in Arpanet 11: * standard format. 12: * 13: * This program is designed to be called from the rand mh program 14: * ``inc'' 15: * 16: * Set SENDMAIL if you are running sendmail -- this guarantees that 17: * From: and Date: lines will appear already, and will put the info 18: * in the UNIX-From line into a Received-From: field. 19: */ 20: 21: #include <stdio.h> 22: #include <sys/types.h> 23: #include <sys/timeb.h> 24: #include <ctype.h> 25: 26: #define SENDMAIL 27: 28: struct headline { 29: char *l_from; /* The name of the sender */ 30: char *l_tty; /* His tty string (if any) */ 31: char *l_date; /* The entire date string */ 32: }; 33: 34: char *savestr(), *copyin(), *copy(), *nextword(), *calloc(); 35: char *index(); 36: 37: #define NOSTR ((char *) 0) 38: #define UUCP /* Undo strange uucp naming */ 39: 40: main(argc, argv) 41: char **argv; 42: { 43: char linebuf[BUFSIZ]; 44: register int maybe; 45: register FILE *inf, *outf; 46: int inhdr, infld; 47: 48: if (argc > 3) { 49: fprintf(stderr, "Usage: unixtomh name1 name2\n"); 50: exit(1); 51: } 52: outf = inf = NULL; 53: if (argc < 3) 54: outf = stdout; 55: if (argc < 2) 56: inf = stdin; 57: if (inf == NULL && (inf = fopen(argv[1], "r")) == NULL) { 58: perror(argv[1]); 59: exit(1); 60: } 61: if (outf == NULL && (outf = fopen(argv[2], "w")) == NULL) { 62: perror(argv[2]); 63: exit(1); 64: } 65: maybe = 1; 66: inhdr = 0; 67: infld = 0; 68: while (nullgets(linebuf, BUFSIZ, inf) > 0) { 69: if (maybe && ishead(linebuf)) { 70: fputs("\1\1\1\1\n", outf); 71: inhdr++; 72: dohead(linebuf, inf, outf); 73: continue; 74: } 75: if (strlen(linebuf) == 0) { 76: maybe = 1; 77: inhdr = 0; 78: infld = 0; 79: putc('\n', outf); 80: continue; 81: } 82: else 83: maybe = 0; 84: #ifndef SENDMAIL 85: if (inhdr && strcmpn(linebuf, "Date: ", 6) == 0) 86: continue; 87: if (inhdr && strcmpn(linebuf, "From: ", 6) == 0) 88: continue; 89: #endif SENDMAIL 90: if (infld && isspace(linebuf[0])) { 91: fputs(linebuf, outf); 92: putc('\n', outf); 93: continue; 94: } 95: if (inhdr && !isspace(linebuf[0])) { 96: char *colp, *sp; 97: 98: colp = index(linebuf, ':'); 99: sp = index(linebuf, ' '); 100: if (colp == NOSTR || sp == NOSTR || sp < colp) { 101: putc('\n', outf); 102: inhdr = 0; 103: } 104: else 105: infld = 1; 106: } 107: fputs(linebuf, outf); 108: putc('\n', outf); 109: } 110: fputs("\1\1\1\1\n", outf); 111: fflush(outf); 112: if (ferror(outf)) { 113: fprintf(stderr, "unixtomh: write: "); 114: perror(argv[2]); 115: exit(1); 116: } 117: exit(0); 118: } 119: 120: /* 121: * Get a line from the given file descriptor, don't return the 122: * terminating newline. 123: */ 124: 125: nullgets(linebuf, sz, file) 126: char linebuf[]; 127: register FILE *file; 128: { 129: register char *cp; 130: register int c, cnt; 131: 132: cp = linebuf; 133: cnt = sz; 134: do { 135: if (--cnt <= 0) { 136: *cp = 0; 137: return(1); 138: } 139: c = getc(file); 140: *cp++ = c; 141: } while (c != EOF && c != '\n'); 142: if (c == EOF && cp == linebuf+1) 143: return(0); 144: *--cp = 0; 145: return(1); 146: } 147: 148: /* 149: * Output the fields extracted from the From line -- 150: * From: and Date: Untangle UUCP stuff if appropriate. 151: */ 152: 153: dohead(line, infile, outfile) 154: char line[]; 155: register FILE *infile, *outfile; 156: { 157: register char *cp; 158: struct headline hl; 159: char parbuf[BUFSIZ]; 160: #ifdef UUCP 161: char *word(); 162: char namebuf[BUFSIZ]; 163: char linebuf[BUFSIZ]; 164: int first; 165: long curoff; 166: #endif UUCP 167: 168: parse(line, &hl, parbuf); 169: #ifndef SENDMAIL 170: putdate(hl.l_date, outfile); 171: #endif SENDMAIL 172: #ifdef UUCP 173: if (strcmp(hl.l_from, "uucp") == 0) { 174: strcpy(namebuf, ""); 175: first = 1; 176: for (;;) { 177: curoff = ftell(infile); 178: if (fgets(linebuf, BUFSIZ, infile) == NULL) 179: break; 180: if (strcmp(word(1, linebuf), ">From") != 0) 181: break; 182: if (strcmp(word(-3, linebuf), "remote") != 0) 183: break; 184: if (strcmp(word(-2, linebuf), "from") != 0) 185: break; 186: if (first) { 187: strcpy(namebuf, word(-1, linebuf)); 188: strcat(namebuf, "!"); 189: strcat(namebuf, word(2, linebuf)); 190: first = 0; 191: } 192: else { 193: strcpy(rindex(namebuf, '!')+1, 194: word(-1, linebuf)); 195: strcat(namebuf, "!"); 196: strcat(namebuf, word(2, linebuf)); 197: } 198: } 199: fseek(infile, curoff, 0); 200: #ifdef SENDMAIL 201: if (!first) 202: fprintf(outfile, "Return-Path: <%s>\n", namebuf); 203: #else SENDMAIL 204: if (first) 205: fprintf(outfile, "From: uucp\n"); 206: else 207: fprintf(outfile, "From: %s\n", namebuf); 208: #endif SENDMAIL 209: return; 210: } 211: #endif UUCP 212: #ifdef SENDMAIL 213: if (hl.l_from[0] == '<') 214: fprintf(outfile, "Return-Path: %s\n", hl.l_from); 215: else 216: fprintf(outfile, "Return-Path: <%s>\n", hl.l_from); 217: #else SENDMAIL 218: fprintf(outfile, "From: %s\n", hl.l_from); 219: #endif SENDMAIL 220: } 221: 222: #ifdef UUCP 223: 224: /* 225: * Return liberal word i from the given string. 226: * The words are numbered 1, 2, 3, . . . from the left 227: * and -1, -2, . . . from the right. 228: */ 229: 230: char * 231: word(index, str) 232: char str[]; 233: { 234: register char *cp; 235: char *secbuf; 236: register int c; 237: static char retbuf[100]; 238: char *gword(); 239: 240: cp = str; 241: if ((c = index) > 0) { 242: while (c-- > 0) 243: cp = gword(cp, retbuf); 244: return(retbuf); 245: } 246: if (c == 0) 247: return(""); 248: secbuf = (char *) alloca(strlen(str) + 1); 249: strcpy(secbuf, str); 250: rev(secbuf); 251: cp = word(-index, secbuf); 252: rev(cp); 253: return(cp); 254: } 255: 256: /* 257: * Skip leading blanks in the string, return 258: * first liberal word collected. 259: */ 260: 261: char * 262: gword(cp, buf) 263: register char *cp; 264: char buf[]; 265: { 266: register char *cp2; 267: 268: cp2 = buf; 269: while (*cp && any(*cp, " \t\n")) 270: cp++; 271: while (*cp && !any(*cp, " \t\n")) 272: *cp2++ = *cp++; 273: *cp2 = 0; 274: return(cp); 275: } 276: 277: /* 278: * Reverse the characters in the string in place 279: */ 280: 281: rev(str) 282: char str[]; 283: { 284: register char *cpl, *cpr; 285: register int s; 286: 287: s = strlen(str); 288: cpl = str; 289: cpr = &str[s-1]; 290: while (cpl < cpr) { 291: s = *cpl; 292: *cpl++ = *cpr; 293: *cpr-- = s; 294: } 295: } 296: #endif UUCP 297: 298: /* 299: * Save a string in dynamic space. 300: * This little goodie is needed for 301: * a headline detector in head.c 302: */ 303: 304: char * 305: savestr(str) 306: char str[]; 307: { 308: register char *top; 309: 310: top = calloc(strlen(str) + 1, 1); 311: if (top == NOSTR) { 312: fprintf(stderr, "unixtomh: Ran out of memory\n"); 313: exit(1); 314: } 315: copy(str, top); 316: return(top); 317: } 318: 319: /* 320: * See if the passed line buffer is a mail header. 321: * Return true if yes. Note the extreme pains to 322: * accomodate all funny formats. 323: */ 324: 325: ishead(linebuf) 326: char linebuf[]; 327: { 328: register char *cp; 329: struct headline hl; 330: char parbuf[BUFSIZ]; 331: 332: cp = linebuf; 333: if (!isname("From ", cp, 5)) 334: return(0); 335: parse(cp, &hl, parbuf); 336: if (hl.l_from == NOSTR || hl.l_date == NOSTR) { 337: fail(linebuf, "No from or date field"); 338: return(0); 339: } 340: if (!isdate(hl.l_date)) { 341: fail(linebuf, "Date field not legal date"); 342: return(0); 343: } 344: 345: /* 346: * I guess we got it! 347: */ 348: 349: return(1); 350: } 351: 352: fail(linebuf, reason) 353: char linebuf[], reason[]; 354: { 355: return; 356: } 357: 358: /* 359: * Split a headline into its useful components. 360: * Copy the line into dynamic string space, then set 361: * pointers into the copied line in the passed headline 362: * structure. Actually, it scans. 363: */ 364: 365: parse(line, hl, pbuf) 366: char line[], pbuf[]; 367: struct headline *hl; 368: { 369: register char *cp, *dp; 370: char *sp; 371: char word[BUFSIZ]; 372: 373: hl->l_from = NOSTR; 374: hl->l_tty = NOSTR; 375: hl->l_date = NOSTR; 376: cp = line; 377: sp = pbuf; 378: 379: /* 380: * Skip the first "word" of the line, which should be "From" 381: * anyway. 382: */ 383: 384: cp = nextword(cp, word); 385: dp = nextword(cp, word); 386: if (word[0] != 0) 387: hl->l_from = copyin(word, &sp); 388: if (isname(dp, "tty", 3)) { 389: cp = nextword(dp, word); 390: hl->l_tty = copyin(word, &sp); 391: if (cp != NOSTR) 392: hl->l_date = copyin(cp, &sp); 393: } 394: else 395: if (dp != NOSTR) 396: hl->l_date = copyin(dp, &sp); 397: } 398: 399: /* 400: * Copy the string on the left into the string on the right 401: * and bump the right (reference) string pointer by the length. 402: * Thus, dynamically allocate space in the right string, copying 403: * the left string into it. 404: */ 405: 406: char * 407: copyin(src, space) 408: char src[]; 409: char **space; 410: { 411: register char *cp, *top; 412: register int s; 413: 414: s = strlen(src); 415: cp = *space; 416: top = cp; 417: strcpy(cp, src); 418: cp += s + 1; 419: *space = cp; 420: return(top); 421: } 422: 423: /* 424: * See if the two passed strings agree in the first n characters. 425: * Return true if they do, gnu. 426: */ 427: 428: isname(as1, as2, acount) 429: char *as1, *as2; 430: { 431: register char *s1, *s2; 432: register count; 433: 434: s1 = as1; 435: s2 = as2; 436: count = acount; 437: if (count > 0) 438: do 439: if (*s1++ != *s2++) 440: return(0); 441: while (--count); 442: return(1); 443: } 444: 445: /* 446: * Test to see if the passed string is a ctime(3) generated 447: * date string as documented in the manual. The template 448: * below is used as the criterion of correctness. 449: * Also, we check for a possible trailing time zone using 450: * the auxtype template. 451: */ 452: 453: #define L 1 /* A lower case char */ 454: #define S 2 /* A space */ 455: #define D 3 /* A digit */ 456: #define O 4 /* An optional digit or space */ 457: #define C 5 /* A colon */ 458: #define N 6 /* A new line */ 459: #define U 7 /* An upper case char */ 460: 461: char ctypes[] = {U,L,L,S,U,L,L,S,O,D,S,D,D,C,D,D,C,D,D,S,D,D,D,D,0}; 462: char tmztypes[] = {U,L,L,S,U,L,L,S,O,D,S,D,D,C,D,D,C,D,D,S,U,U,U,S,D,D,D,D,0}; 463: 464: isdate(date) 465: char date[]; 466: { 467: register char *cp; 468: 469: cp = date; 470: if (cmatch(cp, ctypes)) 471: return(1); 472: return(cmatch(cp, tmztypes)); 473: } 474: 475: /* 476: * Match the given string against the given template. 477: * Return 1 if they match, 0 if they don't 478: */ 479: 480: cmatch(str, temp) 481: char str[], temp[]; 482: { 483: register char *cp, *tp; 484: register int c; 485: 486: cp = str; 487: tp = temp; 488: while (*cp != '\0' && *tp != 0) { 489: c = *cp++; 490: switch (*tp++) { 491: case L: 492: if (!islower(c)) 493: return(0); 494: break; 495: 496: case S: 497: if (c != ' ') 498: return(0); 499: break; 500: 501: case D: 502: if (!isdigit(c)) 503: return(0); 504: break; 505: 506: case O: 507: if (c != ' ' && !isdigit(c)) 508: return(0); 509: break; 510: 511: case C: 512: if (c != ':') 513: return(0); 514: break; 515: 516: case N: 517: if (c != '\n') 518: return(0); 519: break; 520: 521: case U: 522: if (!isupper(c)) 523: return(0); 524: break; 525: } 526: } 527: if (*cp != '\0' || *tp != 0) 528: return(0); 529: return(1); 530: } 531: 532: /* 533: * Collect a liberal (space, tab delimited) word into the word buffer 534: * passed. Also, return a pointer to the next word following that, 535: * or NOSTR if none follow. 536: */ 537: 538: char * 539: nextword(wp, wbuf) 540: char wp[], wbuf[]; 541: { 542: register char *cp, *cp2; 543: 544: if ((cp = wp) == NOSTR) { 545: copy("", wbuf); 546: return(NOSTR); 547: } 548: cp2 = wbuf; 549: while (!any(*cp, " \t") && *cp != '\0') 550: if (*cp == '"') { 551: *cp2++ = *cp++; 552: while (*cp != '\0' && *cp != '"') 553: *cp2++ = *cp++; 554: if (*cp == '"') 555: *cp2++ = *cp++; 556: } else 557: *cp2++ = *cp++; 558: *cp2 = '\0'; 559: while (any(*cp, " \t")) 560: cp++; 561: if (*cp == '\0') 562: return(NOSTR); 563: return(cp); 564: } 565: 566: /* 567: * Copy str1 to str2, return pointer to null in str2. 568: */ 569: 570: char * 571: copy(str1, str2) 572: char *str1, *str2; 573: { 574: register char *s1, *s2; 575: 576: s1 = str1; 577: s2 = str2; 578: while (*s1) 579: *s2++ = *s1++; 580: *s2 = 0; 581: return(s2); 582: } 583: 584: /* 585: * Is ch any of the characters in str? 586: */ 587: 588: any(ch, str) 589: char *str; 590: { 591: register char *f; 592: register c; 593: 594: f = str; 595: c = ch; 596: while (*f) 597: if (c == *f++) 598: return(1); 599: return(0); 600: } 601: 602: /* 603: * Convert lower case letters to upper case. 604: */ 605: 606: raise(c) 607: register int c; 608: { 609: if (c >= 'a' && c <= 'z') 610: c += 'A' - 'a'; 611: return(c); 612: }