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