1: /* smail.c - MH interface to SendMail/SMTP */ 2: 3: /* LINTLIBRARY */ 4: 5: /* This module implements an interface to SendMail very similar to the 6: MMDF mm_(3) routines. The sm_() routines herein talk SMTP to a 7: sendmail process, mapping SMTP reply codes into RP_-style codes. 8: */ 9: 10: #ifdef BSD42 11: /* Under 4.2BSD, the alarm handing stuff for time-outs will NOT work due to 12: the way syscalls get restarted. This really is not crucial, since we 13: expect SendMail to be well-behaved and not hang on us. The only time 14: I've ever seen Sendmail hang was with a bogus configuration file... 15: */ 16: #endif BSD42 17: 18: #ifndef BSD42 19: #undef SMTP 20: #endif not BSD42 21: #ifdef SMTP 22: #undef SENDMAIL 23: #endif SMTP 24: 25: 26: #include "../h/strings.h" 27: #include <stdio.h> 28: #include "smail.h" 29: #include "../zotnet/mts.h" 30: #include <ctype.h> 31: #include <signal.h> 32: 33: #define NOTOK (-1) 34: #define OK 0 35: #define DONE 1 36: 37: #define TRUE 1 38: #define FALSE 0 39: 40: #define NBITS ((sizeof (int)) * 8) 41: 42: #define min(a,b) ((a) < (b) ? (a) : (b)) 43: 44: 45: #define SM_OPEN 30 46: #define SM_HELO 20 47: #define SM_RSET 15 48: #define SM_MAIL 40 49: #define SM_RCPT 120 50: #define SM_DATA 20 51: #define SM_TEXT 120 52: #define SM_DOT 120 53: #define SM_QUIT 20 54: #define SM_CLOS 10 55: 56: /* */ 57: 58: int alrmser (); 59: 60: static int sm_addrs = 0; 61: static int sm_alarmed = 0; 62: #ifndef SMTP 63: static int sm_child = NOTOK; 64: #endif not SMTP 65: static int sm_debug = 0; 66: static int sm_nl = TRUE; 67: static int sm_verbose = 0; 68: 69: static FILE * sm_rfp = NULL; 70: static FILE * sm_wfp = NULL; 71: 72: static char *sm_noreply = "No reply text given"; 73: static char *sm_moreply = "; "; 74: 75: struct smtp sm_reply; /* global... */ 76: 77: 78: char *r1bindex (); 79: 80: /* */ 81: 82: #ifndef SMTP 83: 84: /* ARGSUSED */ 85: 86: int sm_init (client, server, watch, verbose, debug) 87: register char *client; 88: char *server; 89: register int watch, 90: verbose, 91: debug; 92: { 93: register int i, 94: result, 95: vecp; 96: int pdi[2], 97: pdo[2]; 98: char *vec[15]; 99: 100: if (watch) 101: verbose = TRUE; 102: sm_verbose = verbose; 103: sm_debug = debug; 104: if (sm_rfp != NULL && sm_wfp != NULL) 105: return RP_OK; 106: 107: if (pipe (pdi) == NOTOK) 108: return sm_ierror ("no pipes"); 109: if (pipe (pdo) == NOTOK) { 110: (void) close (pdi[0]); 111: (void) close (pdi[1]); 112: return sm_ierror ("no pipes"); 113: } 114: 115: for (i = 0; (sm_child = fork ()) == NOTOK && i < 5; i++) 116: sleep (5); 117: switch (sm_child) { 118: case NOTOK: 119: (void) close (pdo[0]); 120: (void) close (pdo[1]); 121: (void) close (pdi[0]); 122: (void) close (pdi[1]); 123: return sm_ierror ("unable to fork"); 124: 125: case OK: 126: if (pdo[0] != fileno (stdin)) 127: (void) dup2 (pdo[0], fileno (stdin)); 128: if (pdi[1] != fileno (stdout)) 129: (void) dup2 (pdi[1], fileno (stdout)); 130: if (pdi[1] != fileno (stderr)) 131: (void) dup2 (pdi[1], fileno (stderr)); 132: for (i = fileno (stderr) + 1; i < NBITS; i++) 133: (void) close (i); 134: 135: vecp = 0; 136: vec[vecp++] = r1bindex (sendmail, '/'); 137: vec[vecp++] = "-bs"; 138: vec[vecp++] = watch ? "-odi" : "-odb"; 139: vec[vecp++] = "-oem"; 140: vec[vecp++] = "-om"; 141: #ifndef RAND 142: if (verbose) 143: vec[vecp++] = "-ov"; 144: #endif not RAND 145: vec[vecp++] = NULL; 146: 147: (void) setgid (getegid ()); 148: (void) setuid (geteuid ()); 149: execvp (sendmail, vec); 150: fprintf (stderr, "unable to exec "); 151: perror (sendmail); 152: _exit (-1); /* NOTREACHED */ 153: 154: default: 155: (void) signal (SIGALRM, alrmser); 156: (void) signal (SIGPIPE, SIG_IGN); 157: 158: (void) close (pdi[1]); 159: (void) close (pdo[0]); 160: if ((sm_rfp = fdopen (pdi[0], "r")) == NULL 161: || (sm_wfp = fdopen (pdo[1], "w")) == NULL) { 162: (void) close (pdi[0]); 163: (void) close (pdo[1]); 164: sm_rfp = sm_wfp = NULL; 165: return sm_ierror ("unable to fdopen"); 166: } 167: sm_alarmed = 0; 168: (void) alarm (SM_OPEN); 169: result = smhear (); 170: (void) alarm (0); 171: switch (result) { 172: case 220: 173: break; 174: 175: default: 176: (void) sm_end (NOTOK); 177: return RP_RPLY; 178: } 179: if (client) 180: switch (smtalk (SM_HELO, "HELO %s", client)) { 181: case 250: 182: break; 183: 184: default: 185: (void) sm_end (NOTOK); 186: return RP_RPLY; 187: } 188: return RP_OK; 189: } 190: } 191: #else SMTP 192: 193: /* */ 194: 195: int sm_init (client, server, watch, verbose, debug) 196: register char *client, 197: *server; 198: register int watch, 199: verbose, 200: debug; 201: { 202: register int result, 203: sd1, 204: sd2; 205: 206: if (watch) 207: verbose = TRUE; 208: sm_verbose = verbose; 209: sm_debug = debug; 210: if (sm_rfp != NULL && sm_wfp != NULL) 211: return RP_OK; 212: #ifndef SENDMTS 213: if (client == NULL || *client == NULL) 214: client = LocalName (); 215: #endif not SENDMTS 216: 217: if ((sd1 = rclient (server, "tcp", "smtp")) == NOTOK) 218: return RP_BHST; 219: if ((sd2 = dup (sd1)) == NOTOK) { 220: (void) close (sd1); 221: return sm_ierror ("unable to dup"); 222: } 223: 224: (void) signal (SIGALRM, alrmser); 225: (void) signal (SIGPIPE, SIG_IGN); 226: 227: if ((sm_rfp = fdopen (sd1, "r")) == NULL 228: || (sm_wfp = fdopen (sd2, "w")) == NULL) { 229: (void) close (sd1); 230: (void) close (sd2); 231: sm_rfp = sm_wfp = NULL; 232: return sm_ierror ("unable to fdopen"); 233: } 234: sm_alarmed = 0; 235: (void) alarm (SM_OPEN); 236: result = smhear (); 237: (void) alarm (0); 238: switch (result) { 239: case 220: 240: break; 241: 242: default: 243: (void) sm_end (NOTOK); 244: return RP_RPLY; 245: } 246: if (client && *client) 247: switch (smtalk (SM_HELO, "HELO %s", client)) { 248: case 250: 249: break; 250: 251: default: 252: (void) sm_end (NOTOK); 253: return RP_RPLY; 254: } 255: 256: return RP_OK; 257: } 258: 259: 260: static int rclient (server, protocol, service) 261: char *server, 262: *protocol, 263: *service; 264: { 265: int sd; 266: char response[BUFSIZ]; 267: 268: if ((sd = client (server, protocol, service, FALSE, response)) != NOTOK) 269: return sd; 270: 271: (void) sm_ierror ("%s", response); 272: return NOTOK; 273: } 274: #endif SMTP 275: 276: /* */ 277: 278: int sm_winit (mode, from) 279: register int mode; 280: register char *from; 281: { 282: switch (smtalk (SM_MAIL, "%s FROM:<%s>", 283: mode == S_SEND ? "SEND" : mode == S_SOML ? "SOML" 284: : mode == S_SAML ? "SAML" : "MAIL", from)) { 285: case 250: 286: sm_addrs = 0; 287: return RP_OK; 288: 289: case 500: 290: case 501: 291: case 552: 292: return RP_PARM; 293: 294: default: 295: return RP_RPLY; 296: } 297: } 298: 299: /* */ 300: 301: #ifdef BERK 302: /* ARGUSED */ 303: #endif BERK 304: 305: int sm_wadr (mbox, host, path) 306: register char *mbox; 307: #ifndef BERK 308: register 309: #endif not BERK 310: char *host, 311: *path; 312: { 313: #ifndef BERK 314: switch (smtalk (SM_RCPT, host && *host ? "RCPT TO:<%s%s@%s>" 315: : "RCPT TO:<%s%s>", 316: path ? path : "", mbox, host)) { 317: #else BERK 318: switch (smtalk (SM_RCPT, "RCPT TO:%s", mbox)) { 319: #endif BERK 320: case 250: 321: case 251: 322: sm_addrs++; 323: return RP_OK; 324: 325: case 421: 326: case 450: 327: case 451: 328: case 452: 329: return RP_NO; 330: 331: case 500: 332: case 501: 333: return RP_PARM; 334: 335: case 550: 336: case 551: 337: case 552: 338: case 553: 339: return RP_USER; 340: 341: default: 342: return RP_RPLY; 343: } 344: } 345: 346: /* */ 347: 348: int sm_waend () { 349: switch (smtalk (SM_DATA, "DATA")) { 350: case 354: 351: sm_nl = TRUE; 352: return RP_OK; 353: 354: case 421: 355: case 451: 356: return RP_NO; 357: 358: case 500: 359: case 501: 360: case 503: 361: case 554: 362: return RP_NDEL; 363: 364: default: 365: return RP_RPLY; 366: } 367: } 368: 369: /* */ 370: 371: int sm_wtxt (buffer, len) 372: register char *buffer; 373: register int len; 374: { 375: register int result; 376: 377: sm_alarmed = 0; 378: (void) alarm (SM_TEXT); 379: result = sm_wstream (buffer, len); 380: (void) alarm (0); 381: 382: return (result == NOTOK ? RP_BHST : RP_OK); 383: } 384: 385: /* */ 386: 387: int sm_wtend () { 388: if (sm_wstream ((char *) NULL, 0) == NOTOK) 389: return RP_BHST; 390: 391: switch (smtalk (SM_DOT + 3 * sm_addrs, ".")) { 392: case 250: 393: case 251: 394: return RP_OK; 395: 396: case 451: 397: case 452: 398: default: 399: return RP_NO; 400: 401: case 552: 402: case 554: 403: return RP_NDEL; 404: } 405: } 406: 407: /* */ 408: 409: int sm_end (type) 410: register int type; 411: { 412: register int status; 413: struct smtp sm_note; 414: 415: #ifndef SMTP 416: switch (sm_child) { 417: case NOTOK: 418: case OK: 419: return RP_OK; 420: 421: default: 422: break; 423: } 424: #endif not SMTP 425: if (sm_rfp == NULL && sm_wfp == NULL) 426: return RP_OK; 427: 428: switch (type) { 429: case OK: 430: (void) smtalk (SM_QUIT, "QUIT"); 431: break; 432: 433: case NOTOK: 434: sm_note.code = sm_reply.code; 435: (void) strncpy (sm_note.text, sm_reply.text, 436: sm_note.length = sm_reply.length);/* fall */ 437: case DONE: 438: if (smtalk (SM_RSET, "RSET") == 250 && type == DONE) 439: return RP_OK; 440: #ifndef SMTP 441: (void) kill (sm_child, SIGKILL); 442: discard (sm_rfp); 443: discard (sm_wfp); 444: #else SMTP 445: (void) smtalk (SM_QUIT, "QUIT"); 446: #endif not SMTP 447: if (type == NOTOK) { 448: sm_reply.code = sm_note.code; 449: (void) strncpy (sm_reply.text, sm_note.text, 450: sm_reply.length = sm_note.length); 451: } 452: break; 453: } 454: if (sm_rfp != NULL) { 455: (void) alarm (SM_CLOS); 456: (void) fclose (sm_rfp); 457: (void) alarm (0); 458: } 459: if (sm_wfp != NULL) { 460: (void) alarm (SM_CLOS); 461: (void) fclose (sm_wfp); 462: (void) alarm (0); 463: } 464: 465: #ifndef SMTP 466: status = pidwait (sm_child); 467: 468: sm_child = NOTOK; 469: #else SMTP 470: status = 0; 471: #endif SMTP 472: sm_rfp = sm_wfp = NULL; 473: 474: return (status ? RP_BHST : RP_OK); 475: } 476: 477: /* */ 478: 479: /* VARARGS */ 480: 481: static int sm_ierror (fmt, a, b, c, d) 482: char *fmt, 483: *a, 484: *b, 485: *c, 486: *d; 487: { 488: (void) sprintf (sm_reply.text, fmt, a, b, c, d); 489: sm_reply.length = strlen (sm_reply.text); 490: sm_reply.code = NOTOK; 491: 492: return RP_BHST; 493: } 494: 495: /* */ 496: 497: /* VARARGS2 */ 498: 499: static int smtalk (time, fmt, a, b, c, d) 500: register int time; 501: register char *fmt; 502: { 503: register int result; 504: char buffer[BUFSIZ]; 505: 506: (void) sprintf (buffer, fmt, a, b, c, d); 507: if (sm_debug) { 508: printf ("=> %s\n", buffer); 509: (void) fflush (stdout); 510: } 511: 512: sm_alarmed = 0; 513: (void) alarm ((unsigned) time); 514: if ((result = sm_wrecord (buffer, strlen (buffer))) != NOTOK) 515: result = smhear (); 516: (void) alarm (0); 517: 518: return result; 519: } 520: 521: /* */ 522: 523: static int sm_wrecord (buffer, len) 524: register char *buffer; 525: register int len; 526: { 527: if (sm_wfp == NULL) 528: return sm_werror (); 529: 530: (void) fwrite (buffer, sizeof *buffer, len, sm_wfp); 531: fputs ("\r\n", sm_wfp); 532: (void) fflush (sm_wfp); 533: 534: return (ferror (sm_wfp) ? sm_werror () : OK); 535: } 536: 537: /* */ 538: 539: static int sm_wstream (buffer, len) 540: register char *buffer; 541: register int len; 542: { 543: register char *bp; 544: static char lc = NULL; 545: 546: if (sm_wfp == NULL) 547: return sm_werror (); 548: 549: if (buffer == NULL && len == 0) { 550: if (lc != '\n') 551: fputs ("\r\n", sm_wfp); 552: lc = NULL; 553: return (ferror (sm_wfp) ? sm_werror () : OK); 554: } 555: 556: for (bp = buffer; len > 0; bp++, len--) { 557: switch (*bp) { 558: case '\n': 559: sm_nl = TRUE; 560: (void) fputc ('\r', sm_wfp); 561: break; 562: 563: case '.': 564: if (sm_nl) 565: (void) fputc ('.', sm_wfp);/* FALL THROUGH */ 566: default: 567: sm_nl = FALSE; 568: } 569: (void) fputc (*bp, sm_wfp); 570: if (ferror (sm_wfp)) 571: return sm_werror (); 572: } 573: 574: if (bp > buffer) 575: lc = *--bp; 576: return (ferror (sm_wfp) ? sm_werror () : OK); 577: } 578: 579: /* */ 580: 581: static int sm_werror () { 582: sm_reply.length = 583: #ifdef SMTP 584: strlen (strcpy (sm_reply.text, sm_wfp == NULL ? "no socket opened" 585: : sm_alarmed ? "write to socket timed out" 586: : "error writing to socket")); 587: #else not SMTP 588: strlen (strcpy (sm_reply.text, sm_wfp == NULL ? "no pipe opened" 589: : sm_alarmed ? "write to pipe timed out" 590: : "error writing to pipe")); 591: #endif not SMTP 592: 593: return (sm_reply.code = NOTOK); 594: } 595: 596: /* */ 597: 598: static int smhear () { 599: register int i, 600: code, 601: cont, 602: rc, 603: more; 604: int bc; 605: register char *bp, 606: *rp; 607: char buffer[BUFSIZ]; 608: 609: again: ; 610: 611: sm_reply.text[sm_reply.length = 0] = NULL; 612: 613: rp = sm_reply.text, rc = sizeof sm_reply.text - 1; 614: for (more = FALSE; sm_rrecord (bp = buffer, &bc) != NOTOK;) { 615: if (sm_debug) { 616: printf ("<= %s\n", buffer); 617: (void) fflush (stdout); 618: } 619: 620: for (; bc > 0 && (!isascii (*bp) || !isdigit (*bp)); bp++, bc--) 621: continue; 622: 623: cont = FALSE; 624: code = atoi (bp); 625: bp += 3, bc -= 3; 626: for (; bc > 0 && isspace (*bp); bp++, bc--) 627: continue; 628: if (bc > 0 && *bp == '-') { 629: cont = TRUE; 630: bp++, bc--; 631: for (; bc > 0 && isspace (*bp); bp++, bc--) 632: continue; 633: } 634: 635: if (more) { 636: if (code != sm_reply.code || cont) 637: continue; 638: more = FALSE; 639: } 640: else { 641: sm_reply.code = code; 642: more = cont; 643: if (bc <= 0) { 644: (void) strcpy (bp = buffer, sm_noreply); 645: bc = strlen (sm_noreply); 646: } 647: } 648: if ((i = min (bc, rc)) > 0) { 649: (void) strncpy (rp, bp, i); 650: rp += i, rc -= i; 651: if (more && rc > strlen (sm_moreply) + 1) { 652: (void) strcpy (sm_reply.text + rc, sm_moreply); 653: rc += strlen (sm_moreply); 654: } 655: } 656: if (more) 657: continue; 658: if (sm_reply.code < 100) { 659: if (sm_verbose) { 660: printf ("%s\n", sm_reply.text); 661: (void) fflush (stdout); 662: } 663: goto again; 664: } 665: 666: sm_reply.length = rp - sm_reply.text; 667: return sm_reply.code; 668: } 669: 670: return NOTOK; 671: } 672: 673: /* */ 674: 675: static int sm_rrecord (buffer, len) 676: register char *buffer; 677: register int *len; 678: { 679: if (sm_rfp == NULL) 680: return sm_rerror (); 681: 682: buffer[*len = 0] = NULL; 683: 684: (void) fgets (buffer, BUFSIZ, sm_rfp); 685: *len = strlen (buffer); 686: if (ferror (sm_rfp) || feof (sm_rfp)) 687: return sm_rerror (); 688: if (buffer[*len - 1] != '\n') 689: while (getc (sm_rfp) != '\n' && !ferror (sm_rfp) && !feof (sm_rfp)) 690: continue; 691: else 692: if (buffer[*len - 2] == '\r') 693: *len -= 1; 694: buffer[*len - 1] = NULL; 695: 696: return OK; 697: } 698: 699: /* */ 700: 701: static int sm_rerror () { 702: sm_reply.length = 703: #ifdef SMTP 704: strlen (strcpy (sm_reply.text, sm_rfp == NULL ? "no socket opened" 705: : sm_alarmed ? "read from socket timed out" 706: : feof (sm_rfp) ? "premature end-of-file on socket" 707: : "error reading from socket")); 708: #else not SMTP 709: strlen (strcpy (sm_reply.text, sm_rfp == NULL ? "no pipe opened" 710: : sm_alarmed ? "read from pipe timed out" 711: : feof (sm_rfp) ? "premature end-of-file on pipe" 712: : "error reading from pipe")); 713: #endif not SMTP 714: 715: return (sm_reply.code = NOTOK); 716: } 717: 718: /* */ 719: 720: /* ARGSUSED */ 721: 722: static int alrmser (i) 723: int i; 724: { 725: #ifndef BSD42 726: signal (SIGALRM, alrmser); 727: #endif BSD42 728: sm_alarmed++; 729: 730: if (sm_debug) { 731: printf ("timed out...\n"); 732: (void) fflush (stdout); 733: } 734: } 735: 736: /* */ 737: 738: char *rp_string (code) 739: register int code; 740: { 741: register char *text; 742: static char buffer[BUFSIZ]; 743: 744: switch (sm_reply.code != NOTOK ? code : NOTOK) { 745: case RP_AOK: 746: text = "AOK"; 747: break; 748: 749: case RP_MOK: 750: text = "MOK"; 751: break; 752: 753: case RP_OK: 754: text = "OK"; 755: break; 756: 757: case RP_RPLY: 758: text = "RPLY"; 759: break; 760: 761: case RP_BHST: 762: default: 763: text = "BHST"; 764: (void) sprintf (buffer, "[%s] %s", text, sm_reply.text); 765: return buffer; 766: 767: case RP_PARM: 768: text = "PARM"; 769: break; 770: 771: case RP_NO: 772: text = "NO"; 773: break; 774: 775: case RP_USER: 776: text = "USER"; 777: break; 778: 779: case RP_NDEL: 780: text = "NDEL"; 781: break; 782: } 783: 784: (void) sprintf (buffer, "[%s] %3d %s", text, sm_reply.code, sm_reply.text); 785: return buffer; 786: }