1 #ifndef lint 2 static char sccsid[] = "@(#)unixtomh.c 5.1 86/11/25"; 3 #endif not lint 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 } 613