3 * sendmail.c -- nmh sendmail interface
9 #include <mts/smtp/smtp.h>
10 #include <zotnet/mts/mts.h>
12 #include "h/signals.h" /* for SIGNAL() */
18 * This module implements an interface to SendMail very similar
19 * to the MMDF mm_(3) routines. The sm_() routines herein talk
20 * SMTP to a sendmail process, mapping SMTP reply codes into
25 * On older 4.2BSD machines without the POSIX function `sigaction',
26 * the alarm handing stuff for time-outs will NOT work due to the way
27 * syscalls get restarted. This is not really crucial, since SendMail
28 * is generally well-behaved in this area.
33 * It appears that some versions of Sendmail will return Code 451
34 * when they don't really want to indicate a failure.
35 * "Code 451 almost always means sendmail has deferred; we don't
36 * really want bomb out at this point since sendmail will rectify
37 * things later." So, if you define SENDMAILBUG, Code 451 is
38 * considered the same as Code 250. Yuck!
45 #define NBITS ((sizeof (int)) * 8)
48 * these codes must all be different!
50 #define SM_OPEN 90 /* Changed from 30 in case of nameserver flakiness */
61 static int sm_addrs = 0;
62 static int sm_alarmed = 0;
63 static int sm_child = NOTOK;
64 static int sm_debug = 0;
65 static int sm_nl = TRUE;
66 static int sm_verbose = 0;
68 static FILE *sm_rfp = NULL;
69 static FILE *sm_wfp = NULL;
72 static int sm_ispool = 0;
73 static char sm_tmpfil[BUFSIZ];
76 static char *sm_noreply = "No reply text given";
77 static char *sm_moreply = "; ";
79 struct smtp sm_reply; /* global... */
84 char *EHLOkeys[MAXEHLO + 1];
89 static int sm_ierror (char *fmt, ...);
90 static int smtalk (int time, char *fmt, ...);
91 static int sm_wrecord (char *, int);
92 static int sm_wstream (char *, int);
93 static int sm_werror (void);
94 static int smhear (void);
95 static int sm_rrecord (char *, int *);
96 static int sm_rerror (void);
97 static RETSIGTYPE alrmser (int);
101 sm_init (char *client, char *server, int watch, int verbose,
102 int debug, int onex, int queued, int sasl, char *saslmech, char *user)
111 sm_verbose = verbose;
113 if (sm_rfp != NULL && sm_wfp != NULL)
116 if (client == NULL || *client == '\0') {
120 client = LocalName(); /* no clientname -> LocalName */
124 return sm_ierror("SASL authentication not supported with the "
128 if (client == NULL || *client == '\0')
129 client = "localhost";
132 if (pipe (pdi) == NOTOK)
133 return sm_ierror ("no pipes");
134 if (pipe (pdo) == NOTOK) {
137 return sm_ierror ("no pipes");
140 for (i = 0; (sm_child = fork ()) == NOTOK && i < 5; i++)
149 return sm_ierror ("unable to fork");
152 if (pdo[0] != fileno (stdin))
153 dup2 (pdo[0], fileno (stdin));
154 if (pdi[1] != fileno (stdout))
155 dup2 (pdi[1], fileno (stdout));
156 if (pdi[1] != fileno (stderr))
157 dup2 (pdi[1], fileno (stderr));
158 for (i = fileno (stderr) + 1; i < NBITS; i++)
162 vec[vecp++] = r1bindex (sendmail, '/');
165 vec[vecp++] = watch ? "-odi" : queued ? "-odq" : "-odb";
166 vec[vecp++] = "-oem";
171 # endif /* not RAND */
172 #endif /* not ZMAILER */
177 execvp (sendmail, vec);
178 fprintf (stderr, "unable to exec ");
180 _exit (-1); /* NOTREACHED */
183 SIGNAL (SIGALRM, alrmser);
184 SIGNAL (SIGPIPE, SIG_IGN);
188 if ((sm_rfp = fdopen (pdi[0], "r")) == NULL
189 || (sm_wfp = fdopen (pdo[1], "w")) == NULL) {
192 sm_rfp = sm_wfp = NULL;
193 return sm_ierror ("unable to fdopen");
208 if (client && *client) {
210 result = smtalk (SM_HELO, "EHLO %s", client);
213 if (500 <= result && result <= 599)
214 result = smtalk (SM_HELO, "HELO %s", client);
228 smtalk (SM_HELO, "ONEX");
231 smtalk (SM_HELO, "VERB on");
239 sm_winit (int mode, char *from)
242 if (sm_ispool && !sm_wfp) {
243 strlen (strcpy (sm_reply.text, "unable to create new spool file"));
244 sm_reply.code = NOTOK;
249 switch (smtalk (SM_MAIL, "%s FROM:<%s>",
250 mode == S_SEND ? "SEND" : mode == S_SOML ? "SOML"
251 : mode == S_SAML ? "SAML" : "MAIL", from)) {
268 sm_wadr (char *mbox, char *host, char *path)
270 switch (smtalk (SM_RCPT, host && *host ? "RCPT TO:<%s%s@%s>"
272 path ? path : "", mbox, host)) {
282 #endif /* SENDMAILBUG */
307 switch (smtalk (SM_DATA, "DATA")) {
316 #endif /* SENDMAILBUG */
333 sm_wtxt (char *buffer, int len)
339 result = sm_wstream (buffer, len);
342 return (result == NOTOK ? RP_BHST : RP_OK);
349 if (sm_wstream ((char *) NULL, 0) == NOTOK)
352 switch (smtalk (SM_DOT + 3 * sm_addrs, ".")) {
360 #endif /* SENDMAILBUG */
387 if (sm_rfp == NULL && sm_wfp == NULL)
392 smtalk (SM_QUIT, "QUIT");
396 sm_note.code = sm_reply.code;
397 strncpy (sm_note.text, sm_reply.text, sm_note.length = sm_reply.length);/* fall */
399 if (smtalk (SM_RSET, "RSET") == 250 && type == DONE)
401 kill (sm_child, SIGKILL);
405 sm_reply.code = sm_note.code;
406 strncpy (sm_reply.text, sm_note.text, sm_reply.length = sm_note.length);
410 if (sm_rfp != NULL) {
415 if (sm_wfp != NULL) {
421 status = pidwait (sm_child, OK);
424 sm_rfp = sm_wfp = NULL;
426 return (status ? RP_BHST : RP_OK);
431 sm_ierror (char *fmt, ...)
436 vsnprintf (sm_reply.text, sizeof(sm_reply.text), fmt, ap);
439 sm_reply.length = strlen (sm_reply.text);
440 sm_reply.code = NOTOK;
447 smtalk (int time, char *fmt, ...)
454 vsnprintf (buffer, sizeof(buffer), fmt, ap);
458 printf ("=> %s\n", buffer);
466 if (strcmp (buffer, ".") == 0)
468 fprintf (sm_wfp, "%s\r\n", buffer);
474 snprintf (file, sizeof(file), "%s%c.bulk", sm_tmpfil,
475 (char) (sm_ispool + 'a' - 1));
476 if (rename (sm_tmpfil, file) == NOTOK) {
480 snprintf (sm_reply.text, sizeof(sm_reply.text),
481 "error renaming %s to %s: ", sm_tmpfil, file);
485 if ((s = strerror (errno)))
486 strncpy (bp, s, sizeof(sm_reply.text) - len);
488 snprintf (bp, sizeof(sm_reply.text) - len,
489 "unknown error %d", errno);
490 sm_reply.length = strlen (sm_reply.text);
491 sm_reply.code = NOTOK;
495 if (sm_wfp = fopen (sm_tmpfil, "w"))
496 chmod (sm_tmpfil, 0600);
507 ftruncate (fileno (sm_wfp), 0L);
508 fseek (sm_wfp, 0L, SEEK_SET);
527 printf ("<= %d\n", result);
531 sm_reply.text[sm_reply.length = 0] = NULL;
532 return (sm_reply.code = result);
537 alarm ((unsigned) time);
538 if ((result = sm_wrecord (buffer, strlen (buffer))) != NOTOK)
547 sm_wrecord (char *buffer, int len)
552 fwrite (buffer, sizeof *buffer, len, sm_wfp);
553 fputs ("\r\n", sm_wfp);
556 return (ferror (sm_wfp) ? sm_werror () : OK);
561 sm_wstream (char *buffer, int len)
569 if (buffer == NULL && len == 0) {
571 fputs ("\r\n", sm_wfp);
573 return (ferror (sm_wfp) ? sm_werror () : OK);
576 for (bp = buffer; len > 0; bp++, len--) {
580 fputc ('\r', sm_wfp);
585 fputc ('.', sm_wfp);/* FALL THROUGH */
596 return (ferror (sm_wfp) ? sm_werror () : OK);
602 * AIX by default will inline the strlen and strcpy commands by redefining
603 * them as __strlen and __strcpy respectively. This causes compile problems
604 * with the #ifdef MPOP in the middle. Should the #ifdef MPOP be removed,
605 * remove these #undefs.
615 strlen (strcpy (sm_reply.text, sm_wfp == NULL ? "no pipe opened"
616 : sm_alarmed ? "write to pipe timed out"
617 : "error writing to pipe"));
619 return (sm_reply.code = NOTOK);
626 int i, code, cont, bc, rc, more;
628 char **ehlo, buffer[BUFSIZ];
631 static int at_least_once = 0;
634 for (ehlo = EHLOkeys; *ehlo; ehlo++)
640 *(ehlo = EHLOkeys) = NULL;
645 sm_reply.text[sm_reply.length = 0] = 0;
648 rc = sizeof(sm_reply.text) - 1;
650 for (more = FALSE; sm_rrecord (bp = buffer, &bc) != NOTOK;) {
652 printf ("<= %s\n", buffer);
657 && strncmp (buffer, "250", sizeof("250") - 1) == 0
658 && (buffer[3] == '-' || doingEHLO == 2)
660 if (doingEHLO == 2) {
661 if ((*ehlo = malloc ((size_t) (strlen (buffer + 4) + 1)))) {
662 strcpy (*ehlo++, buffer + 4);
664 if (ehlo >= EHLOkeys + MAXEHLO)
674 for (; bc > 0 && (!isascii (*bp) || !isdigit (*bp)); bp++, bc--)
680 for (; bc > 0 && isspace (*bp); bp++, bc--)
682 if (bc > 0 && *bp == '-') {
685 for (; bc > 0 && isspace (*bp); bp++, bc--)
690 if (code != sm_reply.code || cont)
694 sm_reply.code = code;
697 strncpy (buffer, sm_noreply, sizeof(buffer));
699 bc = strlen (sm_noreply);
702 if ((i = min (bc, rc)) > 0) {
706 if (more && rc > strlen (sm_moreply) + 1) {
707 strncpy (sm_reply.text + rc, sm_moreply, sizeof(sm_reply.text) - rc);
708 rc += strlen (sm_moreply);
713 if (sm_reply.code < 100) {
715 printf ("%s\n", sm_reply.text);
721 sm_reply.length = rp - sm_reply.text;
723 return sm_reply.code;
731 sm_rrecord (char *buffer, int *len)
736 buffer[*len = 0] = 0;
738 fgets (buffer, BUFSIZ, sm_rfp);
739 *len = strlen (buffer);
740 if (ferror (sm_rfp) || feof (sm_rfp))
742 if (buffer[*len - 1] != '\n')
743 while (getc (sm_rfp) != '\n' && !ferror (sm_rfp) && !feof (sm_rfp))
746 if (buffer[*len - 2] == '\r')
748 buffer[*len - 1] = 0;
758 strlen (strcpy (sm_reply.text, sm_rfp == NULL ? "no pipe opened"
759 : sm_alarmed ? "read from pipe timed out"
760 : feof (sm_rfp) ? "premature end-of-file on pipe"
761 : "error reading from pipe"));
763 return (sm_reply.code = NOTOK);
770 #ifndef RELIABLE_SIGNALS
771 SIGNAL (SIGALRM, alrmser);
776 printf ("timed out...\n");
786 static char buffer[BUFSIZ];
788 switch (sm_reply.code != NOTOK ? code : NOTOK) {
808 snprintf (buffer, sizeof(buffer), "[%s] %s", text, sm_reply.text);
828 snprintf (buffer, sizeof(buffer), "[%s] %3d %s",
829 text, sm_reply.code, sm_reply.text);