1: # include <stdio.h> 2: # include <ctype.h> 3: # include <signal.h> 4: # include <sysexits.h> 5: # include <whoami.h> 6: # include "useful.h" 7: 8: static char SccsId[] = "@(#)arpa.c 4.1 7/25/83"; 9: 10: char Version[] = "@(#)Arpa-mailer version 4.1 of 7/25/83"; 11: 12: # define void int 13: 14: /* 15: ** ARPA MAILER -- Queue ARPANET mail for eventual delivery 16: ** 17: ** The standard input is stuck away in the outgoing arpanet 18: ** mail queue for delivery by the true arpanet mailer. 19: ** 20: ** CUSTOMIZED FOR THE C/70 21: ** 22: ** Usage: 23: ** /usr/lib/mailers/arpa from host user 24: ** 25: ** Positional Parameters: 26: ** from -- the person sending the mail. 27: ** host -- the host to send the mail to. 28: ** user -- the user to send the mail to. 29: ** 30: ** Flags: 31: ** -T -- debug flag. 32: ** 33: ** Files: 34: ** /usr/spool/netmail/* -- the queue file. 35: ** 36: ** Return Codes: 37: ** 0 -- all messages successfully mailed. 38: ** 2 -- user or host unknown. 39: ** 3 -- service unavailable, probably temporary 40: ** file system condition. 41: ** 4 -- syntax error in address. 42: ** 43: ** Compilation Flags: 44: ** SPOOLDIR -- the spool directory 45: ** 46: ** Compilation Instructions: 47: ** cc -n -O -s arpa-mailer.c -o arpa-mailer -lX 48: ** chmod 755 arpa-mailer 49: ** mv arpa-mailer /usr/lib/mailers/arpa 50: ** 51: ** Author: 52: ** Eric Allman, UCB/INGRES (eric@berkeley) 53: */ 54: 55: # ifdef C70 56: # define SPOOLDIR "/usr/netmail" 57: # else 58: # define SPOOLDIR "/usr/spool/netmail" 59: # endif 60: 61: 62: 63: 64: char *From; /* person sending this mail */ 65: char *To; /* current "To:" person */ 66: int State; /* the current state (for exit codes) */ 67: # ifdef DEBUG 68: bool Tflag; /* -T given */ 69: # endif DEBUG 70: char FromHost[200]; /* string to prepend to addresses */ 71: /* 72: ** MAIN -- Main program for arpa mailer 73: ** 74: ** Processes arguments, and calls sendmail successively on 75: ** the To: list. 76: ** 77: ** Algorithm: 78: ** Scan for debug flag. 79: ** Catch interrupt signals. 80: ** Collect input file name and from person. 81: ** If more than one person in the to list, and 82: ** if the input file is not a real file, 83: ** collect input into a temp file. 84: ** For each person in the to list 85: ** Send to that person. 86: ** 87: ** Parameters: 88: ** argc 89: ** argv -- as usual 90: ** 91: ** Returns: 92: ** via exit 93: ** 94: ** Side Effects: 95: ** Mail gets sent. 96: ** 97: ** Author: 98: ** Eric Allman UCB/INGRES. 99: */ 100: 101: main(argc, argv) 102: int argc; 103: char **argv; 104: { 105: register int i; 106: register char *p; 107: register int ifd; 108: char buf[512]; 109: extern int done(); 110: extern char *locv(); 111: register char *q; 112: char *lastmark; 113: 114: State = 3; 115: if (signal(SIGINT, SIG_IGN) != SIG_IGN) 116: (void) signal(SIGINT, done); 117: 118: /* process flags */ 119: argv[argc] = 0; 120: # ifdef DEBUG 121: if (strcmp(argv[1], "-T") == 0) 122: { 123: Tflag++; 124: argv++; 125: argc--; 126: printf("%s\n", Version); 127: } 128: # endif DEBUG 129: 130: if (argc != 4) 131: { 132: rexit(EX_SOFTWARE); 133: } 134: 135: /* decode parameters */ 136: From = argv[1]; 137: lastmark = &FromHost[-1]; 138: for (p = From, q = FromHost; (*q = *p) != '\0'; p++, q++) 139: { 140: if (*p == ':') 141: *q = *p = '.'; 142: if (*q == '.' || *q == '!' || *q == '@') 143: lastmark = q; 144: } 145: lastmark[1] = '\0'; 146: 147: /* start sending mail */ 148: State = sendmail(argv[2], argv[3]); 149: 150: /* all done, clean up */ 151: done(); 152: } 153: /* 154: ** DONE -- Finish up, remove temp files, etc. 155: ** 156: ** This does basic cleanup on interrupt, error, or 157: ** normal termination. It uses "State" to tell which 158: ** is happening. 159: ** 160: ** Parameters: 161: ** none 162: ** 163: ** Returns: 164: ** none 165: ** 166: ** Side Effects: 167: ** Exit(State). 168: */ 169: 170: done() 171: { 172: rexit(State); 173: } 174: 175: /* 176: ** REXIT -- exit, reporting error code if -T given 177: ** 178: ** Parameters: 179: ** e -- error code to exit with; see sysexits.h 180: ** 181: ** Returns: 182: ** none 183: ** 184: ** Side Effects: 185: ** Exit(e). 186: */ 187: rexit(e) 188: { 189: 190: # ifdef DEBUG 191: if (Tflag) 192: fprintf(stderr, "arpa-mail: return code %d\n", e); 193: # endif 194: exit(e); 195: } 196: /* 197: ** SENDMAIL -- Queue up mail for the arpanet mailer. 198: ** 199: ** The mail is inserted with proper headers into the 200: ** arpanet queue directory. 201: ** 202: ** Algorithm: 203: ** decode "to" address 204: ** if error, exit. 205: ** create a spool file name. 206: ** output the header information to spool file, 207: ** separate names in To:, CC: fields with commas. 208: ** copy the mail to the spool file. 209: ** 210: ** Parameters: 211: ** host -- the host to send to. 212: ** user -- the user to send to. 213: ** 214: ** Returns: 215: ** none 216: ** 217: ** Side Effects: 218: ** the mail is copied into a file in the network 219: ** queue directory (/usr/spool/netmail). 220: */ 221: 222: sendmail(host, user) 223: char *host; 224: char *user; 225: { 226: char spoolfile[50]; /* gets the spool file name */ 227: register int i; 228: register char *p; 229: static int callnum; /* for the final letter on spoolfile */ 230: char buf[512]; 231: register FILE *sfp; /* spool file */ 232: register int c; 233: extern char *matchhdr(); 234: 235: /* verify that the host exists */ 236: (void) strcpy(buf, "/dev/net/"); 237: (void) strcat(buf, host); 238: # ifndef C70 239: #ifdef DEBUG 240: if (!Tflag) 241: #endif DEBUG 242: if (host[0] == '\0' || access(buf, 0) < 0) 243: return (EX_NOHOST); 244: # endif C70 245: 246: /* 247: ** Create spool file name. 248: ** Format is "username000nnX", where username is 249: ** padded on the right with zeros and nn (the process 250: ** id) is padded on the left with zeros; X is a unique 251: ** sequence character. 252: */ 253: 254: # ifdef DEBUG 255: if (Tflag) 256: (void) strcpy(spoolfile, "arpa.out"); 257: else 258: # endif DEBUG 259: (void) sprintf(spoolfile, "%s/arpamail%05d%c", SPOOLDIR, getpid(), 'a' + callnum++); 260: 261: /* create spool file */ 262: sfp = fopen(spoolfile, "w"); 263: if (sfp == NULL) 264: { 265: spoolerr: 266: return (EX_OSERR); 267: } 268: # ifdef DEBUG 269: if (!Tflag) 270: # endif DEBUG 271: (void) chmod(spoolfile, 0400); 272: 273: /* 274: ** Output mailer control lines. 275: ** These lines are as follows: 276: ** /dev/net/<hostname> {target host} 277: ** user-name {at target host} 278: ** /mnt/eric {pathname of sender; not used} 279: ** eric {name of user who is sending} 280: ** These are different (but close) on the C/70. 281: */ 282: 283: # ifdef C70 284: fputs(host, sfp); 285: fputs(":", sfp); 286: fputs(user, sfp); 287: fputs(":", sfp); 288: fputs(From, sfp); 289: fputs(":\n", sfp); 290: # else 291: fputs(buf, sfp); 292: fputs("\n", sfp); 293: fputs(user, sfp); 294: fputs("\n\n", sfp); 295: fputs(From, sfp); 296: fputs("\n", sfp); 297: # endif 298: 299: /* 300: ** Output the mail 301: ** Check the first line for the date. If not found, 302: ** assume the message is not in arpanet standard format 303: ** and output a "Date:" and "From:" header. 304: */ 305: 306: if (fgets(buf, sizeof buf, stdin) == NULL) 307: { 308: /* no message */ 309: (void) unlink(spoolfile); 310: return (EX_OK); 311: } 312: if (strncmp("From ", buf, 5) == 0) 313: { 314: /* strip Unix "From" line */ 315: /* should save the date here */ 316: (void) fgets(buf, sizeof buf, stdin); 317: } 318: while (matchhdr(buf, "mail-from") != NULL || 319: matchhdr(buf, "sender-path") != NULL || 320: matchhdr(buf, "received") != NULL || 321: matchhdr(buf, "via") != NULL) 322: { 323: fputs(buf, sfp); 324: (void) fgets(buf, sizeof buf, stdin); 325: } 326: if (matchhdr(buf, "date") == NULL) 327: putdate(sfp); 328: else 329: { 330: fputs(buf, sfp); 331: (void) fgets(buf, sizeof buf, stdin); 332: } 333: if (matchhdr(buf, "from") == NULL) 334: putfrom(sfp); 335: else 336: { 337: /* hack to support sendmail -- for a while */ 338: if (index(buf, '@') == NULL) 339: putfrom(sfp); 340: else 341: fputs(buf, sfp); 342: (void) fgets(buf, sizeof buf, stdin); 343: } 344: if (!ishdr(buf)) 345: { 346: if (buf[0] != '\n') 347: putc('\n', sfp); 348: goto hdrdone; 349: } 350: 351: /* 352: ** At this point, we have a message with REAL headers. 353: ** We look at each head line and insert commas if it 354: ** is a To: or Cc: field. 355: */ 356: 357: do 358: { 359: if (!ishdr(buf)) 360: break; 361: if (!matchhdr(buf, "to") && !matchhdr(buf, "cc")) 362: { 363: fputs(buf, sfp); 364: continue; 365: } 366: /* gotcha! */ 367: fixaddr(buf, 1, sfp); 368: while (isspace(c = peekc(stdin)) && c != '\n') 369: { 370: (void) fgets(buf, BUFSIZ, stdin); 371: fixaddr(buf, 0, sfp); 372: } 373: } while (fgets(buf, BUFSIZ, stdin) != NULL); 374: 375: hdrdone: 376: /* output the rest of the header & the body of the letter */ 377: do 378: { 379: fputs(buf, sfp); 380: if (ferror(sfp)) 381: goto spoolerr; 382: } while (fgets(buf, sizeof buf, stdin) != NULL); 383: 384: /* all done! */ 385: (void) fclose(sfp); 386: return (EX_OK); 387: } 388: /* 389: ** FIXADDR -- Output header line with needed commas. 390: ** 391: ** Parameters: 392: ** buf -- header line 393: ** first -- true if this is not a continuation 394: ** 395: ** Returns: 396: ** none 397: ** 398: ** Side effects: 399: ** The contents of buf is copied onto the spool file with 400: ** with the right commas interlaced 401: ** 402: ** Called by: 403: ** sendmail 404: */ 405: 406: fixaddr(buf, first, spf) 407: char buf[]; 408: register FILE *spf; 409: { 410: register char *cp; 411: register int c; 412: char word[BUFSIZ], word2[BUFSIZ]; 413: char *gword(); 414: static char wsep[] = ", "; 415: 416: cp = buf; 417: if (first) 418: { 419: while (*cp != ':' && *cp) 420: putc(*cp++, spf); 421: if (*cp == ':') 422: { 423: fputs(": ", spf); 424: cp++; 425: } 426: } 427: else 428: while (*cp && isspace(*cp)) 429: putc(*cp++, spf); 430: cp = gword(word, cp); 431: if (strlen(word) == 0) 432: { 433: putc('\n', spf); 434: goto test; 435: } 436: for (;;) 437: { 438: cp = gword(word2, cp); 439: if (strlen(word2) == 0) 440: { 441: putaddr(word, spf); 442: break; 443: } 444: if (strcmp(word2, "%") == 0) 445: word2[0] = '@'; 446: if (strcmp(word2, "@") && strcmp(word2, "at")) 447: { 448: putaddr(word, spf); 449: fputs(wsep, spf); 450: (void) strcpy(word, word2); 451: continue; 452: } 453: fputs(word, spf); 454: if (word2[0] == '@') 455: putc('@', spf); 456: else 457: fputs(" at ", spf); 458: cp = gword(word, cp); 459: fputs(word, spf); 460: cp = gword(word, cp); 461: if (strlen(word)) 462: fputs(wsep, spf); 463: } 464: 465: test: 466: c = peekc(stdin); 467: if (isspace(c) && c != '\n') 468: fputs(",\n", spf); 469: else 470: putc('\n', spf); 471: } 472: /* 473: ** PUTADDR -- output address onto file 474: ** 475: ** Putaddr prepends the network header onto the address 476: ** unless one already exists. 477: ** 478: ** Parameters: 479: ** name -- the name to output. 480: ** fp -- the file to put it on. 481: ** 482: ** Returns: 483: ** none. 484: ** 485: ** Side Effects: 486: ** name is put onto fp. 487: */ 488: 489: putaddr(name, fp) 490: char *name; 491: FILE *fp; 492: { 493: register char *p; 494: 495: if (strlen(name) == 0) 496: return; 497: for (p = name; *p != '\0' && *p != ':' && *p != '.' && *p != '@' && 498: *p != '!' && *p != '^'; p++) 499: continue; 500: if (*p == ':') 501: *p = '.'; 502: else if (*p == '\0') 503: fputs(FromHost, fp); 504: fputs(name, fp); 505: if (*p != '@') 506: fputs("@Berkeley", fp); 507: } 508: /* 509: ** PEEKC -- peek at next character in input file 510: ** 511: ** Parameters: 512: ** fp -- stdio file buffer 513: ** 514: ** Returns: 515: ** the next character in the input or EOF 516: ** 517: ** Side effects: 518: ** None. 519: */ 520: peekc(fp) 521: register FILE *fp; 522: { 523: register int c; 524: 525: c = getc(fp); 526: (void) ungetc(c, fp); 527: return(c); 528: } 529: 530: /* 531: ** GWORD -- get the next liberal word from a string 532: ** 533: ** Parameters: 534: ** buf -- place to put scanned word 535: ** p -- place to start looking for word 536: ** 537: ** Returns: 538: ** updated value of p or 0 if no more left after this 539: ** 540: ** Side effects: 541: ** buf gets the liberal word scanned. 542: ** buf will be length 0 if there is no more input, 543: ** or if p was passed as 0 544: */ 545: char * 546: gword(buf, p) 547: char buf[]; 548: register char *p; 549: { 550: register char *sp, *dp; 551: int atfound = 0; /* weither or not a '@' found in the scan */ 552: 553: (void) strcpy(buf, ""); 554: if (p == 0) 555: return(0); 556: sp = p; 557: while (*sp && (isspace(*sp) || *sp == ',')) 558: sp++; 559: dp = buf; 560: if (*sp != '%' && *sp != '@') 561: { 562: while (*sp && !isspace(*sp) && *sp != ',' ) 563: { 564: if ( *sp == '@' || *sp == '%' ) 565: atfound++; 566: *dp++ = *sp++; 567: } 568: if ( atfound ) 569: { 570: dp--; 571: while ( *dp != '@' && *dp != '%' ) 572: dp--,sp--; 573: sp--; 574: } 575: 576: } 577: else 578: *dp++ = *sp++; 579: *dp = 0; 580: if (*sp == 0) 581: return(0); 582: return(sp); 583: } 584: /* 585: ** ISHDR -- see if the passed line is a ARPA style header line 586: ** 587: ** Parameters: 588: ** buf -- header line 589: ** 590: ** Returns: 591: ** non-zero if the line is a header line, else zero 592: ** 593: ** Side effects: 594: ** none 595: ** 596: ** Called by: 597: ** sendmail 598: */ 599: ishdr(buf) 600: char buf[]; 601: { 602: register char *p; 603: 604: p = buf; 605: 606: /* check for continuation lines */ 607: if (isspace(*p)) 608: return (1); 609: else 610: { 611: while (*p != ':' && !isspace(*p)) 612: p++; 613: while (isspace(*p)) 614: p++; 615: if (*p != ':') 616: p = 0; 617: } 618: return(p != 0); 619: } 620: /* 621: ** PUTDATE -- Put the date field into the message. 622: ** 623: ** Parameters: 624: ** fp -- file to put it onto. 625: ** 626: ** Returns: 627: ** none 628: ** 629: ** Side Effects: 630: ** output onto fp. 631: */ 632: 633: putdate(fp) 634: register FILE *fp; 635: { 636: extern char *arpadate(); 637: 638: fputs("Date: ", fp); 639: fputs(arpadate(NULL), fp); 640: fputs("\n", fp); 641: } 642: /* 643: ** PUTFROM -- Put the from field into the message. 644: ** 645: ** Parameters: 646: ** fp -- file to put it onto. 647: ** 648: ** Returns: 649: ** none 650: ** 651: ** Side Effects: 652: ** output onto fp. 653: */ 654: 655: putfrom(fp) 656: register FILE *fp; 657: { 658: 659: fputs("From: ", fp); 660: fputs(From, fp); 661: fputs("@Berkeley\n", fp); 662: }