source: trunk/third/nmh/mts/sendmail/sendmail.c @ 12455

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