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