source: trunk/third/sendmail/src/usersmtp.c @ 12554

Revision 12554, 25.5 KB checked in by danw, 26 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r12553, which included commits to RCS files with non-trunk default branches.
RevLine 
[12553]1/*
2 * Copyright (c) 1998 Sendmail, Inc.  All rights reserved.
3 * Copyright (c) 1983, 1995-1997 Eric P. Allman.  All rights reserved.
4 * Copyright (c) 1988, 1993
5 *      The Regents of the University of California.  All rights reserved.
6 *
7 * By using this file, you agree to the terms and conditions set
8 * forth in the LICENSE file which can be found at the top level of
9 * the sendmail distribution.
10 *
11 */
12
13# include "sendmail.h"
14
15#ifndef lint
16#if SMTP
17static char sccsid[] = "@(#)usersmtp.c  8.111 (Berkeley) 2/3/1999 (with SMTP)";
18#else
19static char sccsid[] = "@(#)usersmtp.c  8.111 (Berkeley) 2/3/1999 (without SMTP)";
20#endif
21#endif /* not lint */
22
23# include <sysexits.h>
24# include <errno.h>
25
26# if SMTP
27
28/*
29**  USERSMTP -- run SMTP protocol from the user end.
30**
31**      This protocol is described in RFC821.
32*/
33
34#define REPLYTYPE(r)    ((r) / 100)             /* first digit of reply code */
35#define REPLYCLASS(r)   (((r) / 10) % 10)       /* second digit of reply code */
36#define SMTPCLOSING     421                     /* "Service Shutting Down" */
37
38char    SmtpMsgBuffer[MAXLINE];         /* buffer for commands */
39char    SmtpReplyBuffer[MAXLINE];       /* buffer for replies */
40char    SmtpError[MAXLINE] = "";        /* save failure error messages */
41bool    SmtpNeedIntro;                  /* need "while talking" in transcript */
42
43extern void     smtpmessage __P((char *f, MAILER *m, MCI *mci, ...));
44extern int      reply __P((MAILER *, MCI *, ENVELOPE *, time_t, void (*)()));
45/*
46**  SMTPINIT -- initialize SMTP.
47**
48**      Opens the connection and sends the initial protocol.
49**
50**      Parameters:
51**              m -- mailer to create connection to.
52**              pvp -- pointer to parameter vector to pass to
53**                      the mailer.
54**
55**      Returns:
56**              none.
57**
58**      Side Effects:
59**              creates connection and sends initial protocol.
60*/
61
62void
63smtpinit(m, mci, e)
64        MAILER *m;
65        register MCI *mci;
66        ENVELOPE *e;
67{
68        register int r;
69        register char *p;
70        extern void esmtp_check __P((char *, bool, MAILER *, MCI *, ENVELOPE *));
71        extern void helo_options __P((char *, bool, MAILER *, MCI *, ENVELOPE *));
72
73        if (tTd(18, 1))
74        {
75                printf("smtpinit ");
76                mci_dump(mci, FALSE);
77        }
78
79        /*
80        **  Open the connection to the mailer.
81        */
82
83        SmtpError[0] = '\0';
84        CurHostName = mci->mci_host;            /* XXX UGLY XXX */
85        if (CurHostName == NULL)
86                CurHostName = MyHostName;
87        SmtpNeedIntro = TRUE;
88        switch (mci->mci_state)
89        {
90          case MCIS_ACTIVE:
91                /* need to clear old information */
92                smtprset(m, mci, e);
93                /* fall through */
94
95          case MCIS_OPEN:
96                return;
97
98          case MCIS_ERROR:
99          case MCIS_SSD:
100                /* shouldn't happen */
101                smtpquit(m, mci, e);
102                /* fall through */
103
104          case MCIS_CLOSED:
105                syserr("451 smtpinit: state CLOSED");
106                return;
107
108          case MCIS_OPENING:
109                break;
110        }
111
112        mci->mci_state = MCIS_OPENING;
113
114        /*
115        **  Get the greeting message.
116        **      This should appear spontaneously.  Give it five minutes to
117        **      happen.
118        */
119
120        SmtpPhase = mci->mci_phase = "client greeting";
121        sm_setproctitle(TRUE, "%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
122        r = reply(m, mci, e, TimeOuts.to_initial, esmtp_check);
123        if (r < 0)
124                goto tempfail1;
125        if (REPLYTYPE(r) == 4)
126                goto tempfail2;
127        if (REPLYTYPE(r) != 2)
128                goto unavailable;
129
130        /*
131        **  Send the HELO command.
132        **      My mother taught me to always introduce myself.
133        */
134
135        if (bitnset(M_ESMTP, m->m_flags) || bitnset(M_LMTP, m->m_flags))
136                mci->mci_flags |= MCIF_ESMTP;
137
138tryhelo:
139        if (bitnset(M_LMTP, m->m_flags))
140        {
141                smtpmessage("LHLO %s", m, mci, MyHostName);
142                SmtpPhase = mci->mci_phase = "client LHLO";
143        }
144        else if (bitset(MCIF_ESMTP, mci->mci_flags))
145        {
146                smtpmessage("EHLO %s", m, mci, MyHostName);
147                SmtpPhase = mci->mci_phase = "client EHLO";
148        }
149        else
150        {
151                smtpmessage("HELO %s", m, mci, MyHostName);
152                SmtpPhase = mci->mci_phase = "client HELO";
153        }
154        sm_setproctitle(TRUE, "%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
155        r = reply(m, mci, e, TimeOuts.to_helo, helo_options);
156        if (r < 0)
157                goto tempfail1;
158        else if (REPLYTYPE(r) == 5)
159        {
160                if (bitset(MCIF_ESMTP, mci->mci_flags) &&
161                    !bitnset(M_LMTP, m->m_flags))
162                {
163                        /* try old SMTP instead */
164                        mci->mci_flags &= ~MCIF_ESMTP;
165                        goto tryhelo;
166                }
167                goto unavailable;
168        }
169        else if (REPLYTYPE(r) != 2)
170                goto tempfail2;
171
172        /*
173        **  Check to see if we actually ended up talking to ourself.
174        **  This means we didn't know about an alias or MX, or we managed
175        **  to connect to an echo server.
176        */
177
178        p = strchr(&SmtpReplyBuffer[4], ' ');
179        if (p != NULL)
180                *p = '\0';
181        if (!bitnset(M_NOLOOPCHECK, m->m_flags) &&
182            !bitnset(M_LMTP, m->m_flags) &&
183            strcasecmp(&SmtpReplyBuffer[4], MyHostName) == 0)
184        {
185                syserr("553 %s config error: mail loops back to me (MX problem?)",
186                        CurHostName);
187                mci_setstat(mci, EX_CONFIG, NULL, NULL);
188                mci->mci_errno = 0;
189                smtpquit(m, mci, e);
190                return;
191        }
192
193        /*
194        **  If this is expected to be another sendmail, send some internal
195        **  commands.
196        */
197
198        if (bitnset(M_INTERNAL, m->m_flags))
199        {
200                /* tell it to be verbose */
201                smtpmessage("VERB", m, mci);
202                r = reply(m, mci, e, TimeOuts.to_miscshort, NULL);
203                if (r < 0)
204                        goto tempfail1;
205        }
206
207        if (mci->mci_state != MCIS_CLOSED)
208        {
209                mci->mci_state = MCIS_OPEN;
210                return;
211        }
212
213        /* got a 421 error code during startup */
214
215  tempfail1:
216        if (mci->mci_errno == 0)
217                mci->mci_errno = errno;
218        mci_setstat(mci, EX_TEMPFAIL, "4.4.2", NULL);
219        if (mci->mci_state != MCIS_CLOSED)
220                smtpquit(m, mci, e);
221        return;
222
223  tempfail2:
224        if (mci->mci_errno == 0)
225                mci->mci_errno = errno;
226        /* XXX should use code from other end iff ENHANCEDSTATUSCODES */
227        mci_setstat(mci, EX_TEMPFAIL, "4.5.0", SmtpReplyBuffer);
228        if (mci->mci_state != MCIS_CLOSED)
229                smtpquit(m, mci, e);
230        return;
231
232  unavailable:
233        mci->mci_errno = errno;
234        mci_setstat(mci, EX_UNAVAILABLE, "5.5.0", SmtpReplyBuffer);
235        smtpquit(m, mci, e);
236        return;
237}
238/*
239**  ESMTP_CHECK -- check to see if this implementation likes ESMTP protocol
240**
241**      Parameters:
242**              line -- the response line.
243**              firstline -- set if this is the first line of the reply.
244**              m -- the mailer.
245**              mci -- the mailer connection info.
246**              e -- the envelope.
247**
248**      Returns:
249**              none.
250*/
251
252void
253esmtp_check(line, firstline, m, mci, e)
254        char *line;
255        bool firstline;
256        MAILER *m;
257        register MCI *mci;
258        ENVELOPE *e;
259{
260        if (strstr(line, "ESMTP") != NULL)
261                mci->mci_flags |= MCIF_ESMTP;
262        if (strstr(line, "8BIT-OK") != NULL)
263                mci->mci_flags |= MCIF_8BITOK;
264}
265/*
266**  HELO_OPTIONS -- process the options on a HELO line.
267**
268**      Parameters:
269**              line -- the response line.
270**              firstline -- set if this is the first line of the reply.
271**              m -- the mailer.
272**              mci -- the mailer connection info.
273**              e -- the envelope.
274**
275**      Returns:
276**              none.
277*/
278
279void
280helo_options(line, firstline, m, mci, e)
281        char *line;
282        bool firstline;
283        MAILER *m;
284        register MCI *mci;
285        ENVELOPE *e;
286{
287        register char *p;
288
289        if (firstline)
290                return;
291
292        if (strlen(line) < (SIZE_T) 5)
293                return;
294        line += 4;
295        p = strchr(line, ' ');
296        if (p != NULL)
297                *p++ = '\0';
298        if (strcasecmp(line, "size") == 0)
299        {
300                mci->mci_flags |= MCIF_SIZE;
301                if (p != NULL)
302                        mci->mci_maxsize = atol(p);
303        }
304        else if (strcasecmp(line, "8bitmime") == 0)
305        {
306                mci->mci_flags |= MCIF_8BITMIME;
307                mci->mci_flags &= ~MCIF_7BIT;
308        }
309        else if (strcasecmp(line, "expn") == 0)
310                mci->mci_flags |= MCIF_EXPN;
311        else if (strcasecmp(line, "dsn") == 0)
312                mci->mci_flags |= MCIF_DSN;
313}
314/*
315**  SMTPMAILFROM -- send MAIL command
316**
317**      Parameters:
318**              m -- the mailer.
319**              mci -- the mailer connection structure.
320**              e -- the envelope (including the sender to specify).
321*/
322
323int
324smtpmailfrom(m, mci, e)
325        MAILER *m;
326        MCI *mci;
327        ENVELOPE *e;
328{
329        int r;
330        char *bufp;
331        char *bodytype;
332        char buf[MAXNAME + 1];
333        char optbuf[MAXLINE];
334
335        if (tTd(18, 2))
336                printf("smtpmailfrom: CurHost=%s\n", CurHostName);
337
338        /* set up appropriate options to include */
339        bufp = optbuf;
340        if (bitset(MCIF_SIZE, mci->mci_flags) && e->e_msgsize > 0)
341                snprintf(optbuf, sizeof optbuf, " SIZE=%ld", e->e_msgsize);
342        else
343                strcpy(optbuf, "");
344        bufp = &optbuf[strlen(optbuf)];
345
346        bodytype = e->e_bodytype;
347        if (bitset(MCIF_8BITMIME, mci->mci_flags))
348        {
349                if (bodytype == NULL &&
350                    bitset(MM_MIME8BIT, MimeMode) &&
351                    bitset(EF_HAS8BIT, e->e_flags) &&
352                    !bitset(EF_DONT_MIME, e->e_flags) &&
353                    !bitnset(M_8BITS, m->m_flags))
354                        bodytype = "8BITMIME";
355                if (bodytype != NULL &&
356                    SPACELEFT(optbuf, bufp) > strlen(bodytype) + 7)
357                {
358                        snprintf(bufp, SPACELEFT(optbuf, bufp),
359                                 " BODY=%s", bodytype);
360                        bufp += strlen(bufp);
361                }
362        }
363        else if (bitnset(M_8BITS, m->m_flags) ||
364                 !bitset(EF_HAS8BIT, e->e_flags) ||
365                 bitset(MCIF_8BITOK, mci->mci_flags))
366        {
367                /* just pass it through */
368        }
369#if MIME8TO7
370        else if (bitset(MM_CVTMIME, MimeMode) &&
371                 !bitset(EF_DONT_MIME, e->e_flags) &&
372                 (!bitset(MM_PASS8BIT, MimeMode) ||
373                  bitset(EF_IS_MIME, e->e_flags)))
374        {
375                /* must convert from 8bit MIME format to 7bit encoded */
376                mci->mci_flags |= MCIF_CVT8TO7;
377        }
378#endif
379        else if (!bitset(MM_PASS8BIT, MimeMode))
380        {
381                /* cannot just send a 8-bit version */
382                extern char MsgBuf[];
383
384                usrerr("%s does not support 8BITMIME", CurHostName);
385                mci_setstat(mci, EX_NOTSTICKY, "5.6.3", MsgBuf);
386                return EX_DATAERR;
387        }
388
389        if (bitset(MCIF_DSN, mci->mci_flags))
390        {
391                if (e->e_envid != NULL &&
392                    SPACELEFT(optbuf, bufp) > strlen(e->e_envid) + 7)
393                {
394                        snprintf(bufp, SPACELEFT(optbuf, bufp),
395                                 " ENVID=%s", e->e_envid);
396                        bufp += strlen(bufp);
397                }
398
399                /* RET= parameter */
400                if (bitset(EF_RET_PARAM, e->e_flags) &&
401                    SPACELEFT(optbuf, bufp) > 9)
402                {
403                        snprintf(bufp, SPACELEFT(optbuf, bufp),
404                                 " RET=%s",
405                                 bitset(EF_NO_BODY_RETN, e->e_flags) ?
406                                        "HDRS" : "FULL");
407                        bufp += strlen(bufp);
408                }
409        }
410
411        /*
412        **  Send the MAIL command.
413        **      Designates the sender.
414        */
415
416        mci->mci_state = MCIS_ACTIVE;
417
418        if (bitset(EF_RESPONSE, e->e_flags) &&
419            !bitnset(M_NO_NULL_FROM, m->m_flags))
420                (void) strcpy(buf, "");
421        else
422                expand("\201g", buf, sizeof buf, e);
423        if (buf[0] == '<')
424        {
425                /* strip off <angle brackets> (put back on below) */
426                bufp = &buf[strlen(buf) - 1];
427                if (*bufp == '>')
428                        *bufp = '\0';
429                bufp = &buf[1];
430        }
431        else
432                bufp = buf;
433        if (bitnset(M_LOCALMAILER, e->e_from.q_mailer->m_flags) ||
434            !bitnset(M_FROMPATH, m->m_flags))
435        {
436                smtpmessage("MAIL From:<%s>%s", m, mci, bufp, optbuf);
437        }
438        else
439        {
440                smtpmessage("MAIL From:<@%s%c%s>%s", m, mci, MyHostName,
441                        *bufp == '@' ? ',' : ':', bufp, optbuf);
442        }
443        SmtpPhase = mci->mci_phase = "client MAIL";
444        sm_setproctitle(TRUE, "%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
445        r = reply(m, mci, e, TimeOuts.to_mail, NULL);
446        if (r < 0)
447        {
448                /* communications failure */
449                mci->mci_errno = errno;
450                mci_setstat(mci, EX_TEMPFAIL, "4.4.2", NULL);
451                smtpquit(m, mci, e);
452                return EX_TEMPFAIL;
453        }
454        else if (r == 421)
455        {
456                /* service shutting down */
457                mci_setstat(mci, EX_TEMPFAIL, "4.5.0", SmtpReplyBuffer);
458                smtpquit(m, mci, e);
459                return EX_TEMPFAIL;
460        }
461        else if (REPLYTYPE(r) == 4)
462        {
463                mci_setstat(mci, EX_NOTSTICKY, smtptodsn(r), SmtpReplyBuffer);
464                return EX_TEMPFAIL;
465        }
466        else if (REPLYTYPE(r) == 2)
467        {
468                return EX_OK;
469        }
470        else if (r == 501)
471        {
472                /* syntax error in arguments */
473                mci_setstat(mci, EX_NOTSTICKY, "5.5.2", SmtpReplyBuffer);
474                return EX_DATAERR;
475        }
476        else if (r == 553)
477        {
478                /* mailbox name not allowed */
479                mci_setstat(mci, EX_NOTSTICKY, "5.1.3", SmtpReplyBuffer);
480                return EX_DATAERR;
481        }
482        else if (r == 552)
483        {
484                /* exceeded storage allocation */
485                mci_setstat(mci, EX_NOTSTICKY, "5.3.4", SmtpReplyBuffer);
486                if (bitset(MCIF_SIZE, mci->mci_flags))
487                        e->e_flags |= EF_NO_BODY_RETN;
488                return EX_UNAVAILABLE;
489        }
490        else if (REPLYTYPE(r) == 5)
491        {
492                /* unknown error */
493                mci_setstat(mci, EX_NOTSTICKY, "5.0.0", SmtpReplyBuffer);
494                return EX_UNAVAILABLE;
495        }
496
497        if (LogLevel > 1)
498        {
499                sm_syslog(LOG_CRIT, e->e_id,
500                        "%.100s: SMTP MAIL protocol error: %s",
501                        CurHostName,
502                        shortenstring(SmtpReplyBuffer, 403));
503        }
504
505        /* protocol error -- close up */
506        mci_setstat(mci, EX_PROTOCOL, "5.5.1", SmtpReplyBuffer);
507        smtpquit(m, mci, e);
508        return EX_PROTOCOL;
509}
510/*
511**  SMTPRCPT -- designate recipient.
512**
513**      Parameters:
514**              to -- address of recipient.
515**              m -- the mailer we are sending to.
516**              mci -- the connection info for this transaction.
517**              e -- the envelope for this transaction.
518**
519**      Returns:
520**              exit status corresponding to recipient status.
521**
522**      Side Effects:
523**              Sends the mail via SMTP.
524*/
525
526int
527smtprcpt(to, m, mci, e)
528        ADDRESS *to;
529        register MAILER *m;
530        MCI *mci;
531        ENVELOPE *e;
532{
533        register int r;
534        char *bufp;
535        char optbuf[MAXLINE];
536
537        strcpy(optbuf, "");
538        bufp = &optbuf[strlen(optbuf)];
539        if (bitset(MCIF_DSN, mci->mci_flags))
540        {
541                /* NOTIFY= parameter */
542                if (bitset(QHASNOTIFY, to->q_flags) &&
543                    bitset(QPRIMARY, to->q_flags) &&
544                    !bitnset(M_LOCALMAILER, m->m_flags))
545                {
546                        bool firstone = TRUE;
547
548                        strcat(bufp, " NOTIFY=");
549                        if (bitset(QPINGONSUCCESS, to->q_flags))
550                        {
551                                strcat(bufp, "SUCCESS");
552                                firstone = FALSE;
553                        }
554                        if (bitset(QPINGONFAILURE, to->q_flags))
555                        {
556                                if (!firstone)
557                                        strcat(bufp, ",");
558                                strcat(bufp, "FAILURE");
559                                firstone = FALSE;
560                        }
561                        if (bitset(QPINGONDELAY, to->q_flags))
562                        {
563                                if (!firstone)
564                                        strcat(bufp, ",");
565                                strcat(bufp, "DELAY");
566                                firstone = FALSE;
567                        }
568                        if (firstone)
569                                strcat(bufp, "NEVER");
570                        bufp += strlen(bufp);
571                }
572
573                /* ORCPT= parameter */
574                if (to->q_orcpt != NULL &&
575                    SPACELEFT(optbuf, bufp) > strlen(to->q_orcpt) + 7)
576                {
577                        snprintf(bufp, SPACELEFT(optbuf, bufp),
578                                 " ORCPT=%s", to->q_orcpt);
579                        bufp += strlen(bufp);
580                }
581        }
582
583        smtpmessage("RCPT To:<%s>%s", m, mci, to->q_user, optbuf);
584
585        SmtpPhase = mci->mci_phase = "client RCPT";
586        sm_setproctitle(TRUE, "%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
587        r = reply(m, mci, e, TimeOuts.to_rcpt, NULL);
588        to->q_rstatus = newstr(SmtpReplyBuffer);
589        to->q_status = smtptodsn(r);
590        to->q_statmta = mci->mci_host;
591        if (r < 0 || REPLYTYPE(r) == 4)
592                return EX_TEMPFAIL;
593        else if (REPLYTYPE(r) == 2)
594                return EX_OK;
595        else if (r == 550)
596        {
597                to->q_status = "5.1.1";
598                return EX_NOUSER;
599        }
600        else if (r == 551)
601        {
602                to->q_status = "5.1.6";
603                return EX_NOUSER;
604        }
605        else if (r == 553)
606        {
607                to->q_status = "5.1.3";
608                return EX_NOUSER;
609        }
610        else if (REPLYTYPE(r) == 5)
611        {
612                return EX_UNAVAILABLE;
613        }
614
615        if (LogLevel > 1)
616        {
617                sm_syslog(LOG_CRIT, e->e_id,
618                        "%.100s: SMTP RCPT protocol error: %s",
619                        CurHostName,
620                        shortenstring(SmtpReplyBuffer, 403));
621        }
622
623        mci_setstat(mci, EX_PROTOCOL, "5.5.1", SmtpReplyBuffer);
624        return EX_PROTOCOL;
625}
626/*
627**  SMTPDATA -- send the data and clean up the transaction.
628**
629**      Parameters:
630**              m -- mailer being sent to.
631**              mci -- the mailer connection information.
632**              e -- the envelope for this message.
633**
634**      Returns:
635**              exit status corresponding to DATA command.
636**
637**      Side Effects:
638**              none.
639*/
640
641static jmp_buf  CtxDataTimeout;
642static void     datatimeout __P((void));
643
644int
645smtpdata(m, mci, e)
646        MAILER *m;
647        register MCI *mci;
648        register ENVELOPE *e;
649{
650        register int r;
651        register EVENT *ev;
652        int rstat;
653        int xstat;
654        time_t timeout;
655
656        /*
657        **  Send the data.
658        **      First send the command and check that it is ok.
659        **      Then send the data.
660        **      Follow it up with a dot to terminate.
661        **      Finally get the results of the transaction.
662        */
663
664        /* send the command and check ok to proceed */
665        smtpmessage("DATA", m, mci);
666        SmtpPhase = mci->mci_phase = "client DATA 354";
667        sm_setproctitle(TRUE, "%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
668        r = reply(m, mci, e, TimeOuts.to_datainit, NULL);
669        if (r < 0 || REPLYTYPE(r) == 4)
670        {
671                smtpquit(m, mci, e);
672                return EX_TEMPFAIL;
673        }
674        else if (REPLYTYPE(r) == 5)
675        {
676                smtprset(m, mci, e);
677                return EX_UNAVAILABLE;
678        }
679        else if (REPLYTYPE(r) != 3)
680        {
681                if (LogLevel > 1)
682                {
683                        sm_syslog(LOG_CRIT, e->e_id,
684                                "%.100s: SMTP DATA-1 protocol error: %s",
685                                CurHostName,
686                                shortenstring(SmtpReplyBuffer, 403));
687                }
688                smtprset(m, mci, e);
689                mci_setstat(mci, EX_PROTOCOL, "5.5.1", SmtpReplyBuffer);
690                return (EX_PROTOCOL);
691        }
692
693        /*
694        **  Set timeout around data writes.  Make it at least large
695        **  enough for DNS timeouts on all recipients plus some fudge
696        **  factor.  The main thing is that it should not be infinite.
697        */
698
699        if (setjmp(CtxDataTimeout) != 0)
700        {
701                mci->mci_errno = errno;
702                mci->mci_state = MCIS_ERROR;
703                mci_setstat(mci, EX_TEMPFAIL, "4.4.2", NULL);
704                syserr("451 timeout writing message to %s", CurHostName);
705                smtpquit(m, mci, e);
706                return EX_TEMPFAIL;
707        }
708
709        timeout = e->e_msgsize / 16;
710        if (timeout < (time_t) 600)
711                timeout = (time_t) 600;
712        timeout += e->e_nrcpts * 300;
713        ev = setevent(timeout, datatimeout, 0);
714
715        /*
716        **  Output the actual message.
717        */
718
719        (*e->e_puthdr)(mci, e->e_header, e, M87F_OUTER);
720        (*e->e_putbody)(mci, e, NULL);
721
722        /*
723        **  Cleanup after sending message.
724        */
725
726        clrevent(ev);
727
728        if (ferror(mci->mci_out))
729        {
730                /* error during processing -- don't send the dot */
731                mci->mci_errno = EIO;
732                mci->mci_state = MCIS_ERROR;
733                mci_setstat(mci, EX_IOERR, "4.4.2", NULL);
734                smtpquit(m, mci, e);
735                return EX_IOERR;
736        }
737
738        /* terminate the message */
739        fprintf(mci->mci_out, ".%s", m->m_eol);
740        if (TrafficLogFile != NULL)
741                fprintf(TrafficLogFile, "%05d >>> .\n", (int) getpid());
742        if (Verbose)
743                nmessage(">>> .");
744
745        /* check for the results of the transaction */
746        SmtpPhase = mci->mci_phase = "client DATA status";
747        sm_setproctitle(TRUE, "%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
748        if (bitnset(M_LMTP, m->m_flags))
749                return EX_OK;
750        r = reply(m, mci, e, TimeOuts.to_datafinal, NULL);
751        if (r < 0)
752        {
753                smtpquit(m, mci, e);
754                return EX_TEMPFAIL;
755        }
756        mci->mci_state = MCIS_OPEN;
757        xstat = EX_NOTSTICKY;
758        if (r == 452)
759                rstat = EX_TEMPFAIL;
760        else if (REPLYTYPE(r) == 4)
761                rstat = xstat = EX_TEMPFAIL;
762        else if (REPLYCLASS(r) != 5)
763                rstat = xstat = EX_PROTOCOL;
764        else if (REPLYTYPE(r) == 2)
765                rstat = xstat = EX_OK;
766        else if (REPLYTYPE(r) == 5)
767                rstat = EX_UNAVAILABLE;
768        else
769                rstat = EX_PROTOCOL;
770        mci_setstat(mci, xstat, smtptodsn(r), SmtpReplyBuffer);
771        if (e->e_statmsg != NULL)
772                free(e->e_statmsg);
773        e->e_statmsg = newstr(&SmtpReplyBuffer[4]);
774        if (rstat != EX_PROTOCOL)
775                return rstat;
776        if (LogLevel > 1)
777        {
778                sm_syslog(LOG_CRIT, e->e_id,
779                        "%.100s: SMTP DATA-2 protocol error: %s",
780                        CurHostName,
781                        shortenstring(SmtpReplyBuffer, 403));
782        }
783        return rstat;
784}
785
786
787static void
788datatimeout()
789{
790        longjmp(CtxDataTimeout, 1);
791}
792/*
793**  SMTPGETSTAT -- get status code from DATA in LMTP
794**
795**      Parameters:
796**              m -- the mailer to which we are sending the message.
797**              mci -- the mailer connection structure.
798**              e -- the current envelope.
799**
800**      Returns:
801**              The exit status corresponding to the reply code.
802*/
803
804int
805smtpgetstat(m, mci, e)
806        MAILER *m;
807        MCI *mci;
808        ENVELOPE *e;
809{
810        int r;
811        int stat;
812
813        /* check for the results of the transaction */
814        r = reply(m, mci, e, TimeOuts.to_datafinal, NULL);
815        if (r < 0)
816        {
817                smtpquit(m, mci, e);
818                return EX_TEMPFAIL;
819        }
820        if (e->e_statmsg != NULL)
821                free(e->e_statmsg);
822        e->e_statmsg = newstr(&SmtpReplyBuffer[4]);
823        if (REPLYTYPE(r) == 4)
824                stat = EX_TEMPFAIL;
825        else if (REPLYCLASS(r) != 5)
826                stat = EX_PROTOCOL;
827        else if (REPLYTYPE(r) == 2)
828                stat = EX_OK;
829        else if (REPLYTYPE(r) == 5)
830                stat = EX_UNAVAILABLE;
831        else
832                stat = EX_PROTOCOL;
833        mci_setstat(mci, stat, smtptodsn(r), SmtpReplyBuffer);
834        if (LogLevel > 1 && stat == EX_PROTOCOL)
835        {
836                sm_syslog(LOG_CRIT, e->e_id,
837                        "%.100s: SMTP DATA-3 protocol error: %s",
838                        CurHostName,
839                        shortenstring(SmtpReplyBuffer, 403));
840        }
841        return stat;
842}
843/*
844**  SMTPQUIT -- close the SMTP connection.
845**
846**      Parameters:
847**              m -- a pointer to the mailer.
848**              mci -- the mailer connection information.
849**              e -- the current envelope.
850**
851**      Returns:
852**              none.
853**
854**      Side Effects:
855**              sends the final protocol and closes the connection.
856*/
857
858void
859smtpquit(m, mci, e)
860        register MAILER *m;
861        register MCI *mci;
862        ENVELOPE *e;
863{
864        bool oldSuprErrs = SuprErrs;
865
866        /*
867        **      Suppress errors here -- we may be processing a different
868        **      job when we do the quit connection, and we don't want the
869        **      new job to be penalized for something that isn't it's
870        **      problem.
871        */
872
873        SuprErrs = TRUE;
874
875        /* send the quit message if we haven't gotten I/O error */
876        if (mci->mci_state != MCIS_ERROR)
877        {
878                SmtpPhase = "client QUIT";
879                smtpmessage("QUIT", m, mci);
880                (void) reply(m, mci, e, TimeOuts.to_quit, NULL);
881                SuprErrs = oldSuprErrs;
882                if (mci->mci_state == MCIS_CLOSED)
883                        return;
884        }
885
886        /* now actually close the connection and pick up the zombie */
887        (void) endmailer(mci, e, NULL);
888
889        SuprErrs = oldSuprErrs;
890}
891/*
892**  SMTPRSET -- send a RSET (reset) command
893*/
894
895void
896smtprset(m, mci, e)
897        register MAILER *m;
898        register MCI *mci;
899        ENVELOPE *e;
900{
901        int r;
902
903        SmtpPhase = "client RSET";
904        smtpmessage("RSET", m, mci);
905        r = reply(m, mci, e, TimeOuts.to_rset, NULL);
906        if (r < 0)
907                mci->mci_state = MCIS_ERROR;
908        else if (REPLYTYPE(r) == 2)
909        {
910                mci->mci_state = MCIS_OPEN;
911                return;
912        }
913        smtpquit(m, mci, e);
914}
915/*
916**  SMTPPROBE -- check the connection state
917*/
918
919int
920smtpprobe(mci)
921        register MCI *mci;
922{
923        int r;
924        MAILER *m = mci->mci_mailer;
925        extern ENVELOPE BlankEnvelope;
926        ENVELOPE *e = &BlankEnvelope;
927
928        SmtpPhase = "client probe";
929        smtpmessage("RSET", m, mci);
930        r = reply(m, mci, e, TimeOuts.to_miscshort, NULL);
931        if (r < 0 || REPLYTYPE(r) != 2)
932                smtpquit(m, mci, e);
933        return r;
934}
935/*
936**  REPLY -- read arpanet reply
937**
938**      Parameters:
939**              m -- the mailer we are reading the reply from.
940**              mci -- the mailer connection info structure.
941**              e -- the current envelope.
942**              timeout -- the timeout for reads.
943**              pfunc -- processing function called on each line of response.
944**                      If null, no special processing is done.
945**
946**      Returns:
947**              reply code it reads.
948**
949**      Side Effects:
950**              flushes the mail file.
951*/
952
953int
954reply(m, mci, e, timeout, pfunc)
955        MAILER *m;
956        MCI *mci;
957        ENVELOPE *e;
958        time_t timeout;
959        void (*pfunc)();
960{
961        register char *bufp;
962        register int r;
963        bool firstline = TRUE;
964        char junkbuf[MAXLINE];
965
966        if (mci->mci_out != NULL)
967                (void) fflush(mci->mci_out);
968
969        if (tTd(18, 1))
970                printf("reply\n");
971
972        /*
973        **  Read the input line, being careful not to hang.
974        */
975
976        bufp = SmtpReplyBuffer;
977        for (;;)
978        {
979                register char *p;
980
981                /* actually do the read */
982                if (e->e_xfp != NULL)
983                        (void) fflush(e->e_xfp);        /* for debugging */
984
985                /* if we are in the process of closing just give the code */
986                if (mci->mci_state == MCIS_CLOSED)
987                        return (SMTPCLOSING);
988
989                if (mci->mci_out != NULL)
990                        fflush(mci->mci_out);
991
992                /* get the line from the other side */
993                p = sfgets(bufp, MAXLINE, mci->mci_in, timeout, SmtpPhase);
994                mci->mci_lastuse = curtime();
995
996                if (p == NULL)
997                {
998                        bool oldholderrs;
999                        extern char MsgBuf[];
1000
1001                        /* if the remote end closed early, fake an error */
1002                        if (errno == 0)
1003# ifdef ECONNRESET
1004                                errno = ECONNRESET;
1005# else /* ECONNRESET */
1006                                errno = EPIPE;
1007# endif /* ECONNRESET */
1008
1009                        mci->mci_errno = errno;
1010                        oldholderrs = HoldErrs;
1011                        HoldErrs = TRUE;
1012                        usrerr("451 reply: read error from %s", CurHostName);
1013
1014                        /* errors on QUIT should not be persistent */
1015                        if (strncmp(SmtpMsgBuffer, "QUIT", 4) != 0)
1016                                mci_setstat(mci, EX_TEMPFAIL, "4.4.2", MsgBuf);
1017
1018                        /* if debugging, pause so we can see state */
1019                        if (tTd(18, 100))
1020                                pause();
1021                        mci->mci_state = MCIS_ERROR;
1022                        smtpquit(m, mci, e);
1023#if XDEBUG
1024                        {
1025                                char wbuf[MAXLINE];
1026                                char *p = wbuf;
1027                                int wbufleft = sizeof wbuf;
1028
1029                                if (e->e_to != NULL)
1030                                {
1031                                        int plen;
1032
1033                                        snprintf(p, wbufleft, "%s... ",
1034                                                shortenstring(e->e_to, MAXSHORTSTR));
1035                                        plen = strlen(p);
1036                                        p += plen;
1037                                        wbufleft -= plen;
1038                                }
1039                                snprintf(p, wbufleft, "reply(%.100s) during %s",
1040                                         CurHostName == NULL ? "NO_HOST" : CurHostName,
1041                                         SmtpPhase);
1042                                checkfd012(wbuf);
1043                        }
1044#endif
1045                        HoldErrs = oldholderrs;
1046                        return (-1);
1047                }
1048                fixcrlf(bufp, TRUE);
1049
1050                /* EHLO failure is not a real error */
1051                if (e->e_xfp != NULL && (bufp[0] == '4' ||
1052                    (bufp[0] == '5' && strncmp(SmtpMsgBuffer, "EHLO", 4) != 0)))
1053                {
1054                        /* serious error -- log the previous command */
1055                        if (SmtpNeedIntro)
1056                        {
1057                                /* inform user who we are chatting with */
1058                                fprintf(CurEnv->e_xfp,
1059                                        "... while talking to %s:\n",
1060                                        CurHostName);
1061                                SmtpNeedIntro = FALSE;
1062                        }
1063                        if (SmtpMsgBuffer[0] != '\0')
1064                                fprintf(e->e_xfp, ">>> %s\n", SmtpMsgBuffer);
1065                        SmtpMsgBuffer[0] = '\0';
1066
1067                        /* now log the message as from the other side */
1068                        fprintf(e->e_xfp, "<<< %s\n", bufp);
1069                }
1070
1071                /* display the input for verbose mode */
1072                if (Verbose)
1073                        nmessage("050 %s", bufp);
1074
1075                /* ignore improperly formated input */
1076                if (!(isascii(bufp[0]) && isdigit(bufp[0])) ||
1077                    !(isascii(bufp[1]) && isdigit(bufp[1])) ||
1078                    !(isascii(bufp[2]) && isdigit(bufp[2])) ||
1079                    !(bufp[3] == ' ' || bufp[3] == '-' || bufp[3] == '\0'))
1080                        continue;
1081
1082                /* process the line */
1083                if (pfunc != NULL)
1084                        (*pfunc)(bufp, firstline, m, mci, e);
1085
1086                firstline = FALSE;
1087
1088                /* decode the reply code */
1089                r = atoi(bufp);
1090
1091                /* extra semantics: 0xx codes are "informational" */
1092                if (r < 100)
1093                        continue;
1094
1095                /* if no continuation lines, return this line */
1096                if (bufp[3] != '-')
1097                        break;
1098
1099                /* first line of real reply -- ignore rest */
1100                bufp = junkbuf;
1101        }
1102
1103        /*
1104        **  Now look at SmtpReplyBuffer -- only care about the first
1105        **  line of the response from here on out.
1106        */
1107
1108        /* save temporary failure messages for posterity */
1109        if (SmtpReplyBuffer[0] == '4' &&
1110            (bitnset(M_LMTP, m->m_flags) || SmtpError[0] == '\0'))
1111                snprintf(SmtpError, sizeof SmtpError, "%s", SmtpReplyBuffer);
1112
1113        /* reply code 421 is "Service Shutting Down" */
1114        if (r == SMTPCLOSING && mci->mci_state != MCIS_SSD)
1115        {
1116                /* send the quit protocol */
1117                mci->mci_state = MCIS_SSD;
1118                smtpquit(m, mci, e);
1119        }
1120
1121        return (r);
1122}
1123/*
1124**  SMTPMESSAGE -- send message to server
1125**
1126**      Parameters:
1127**              f -- format
1128**              m -- the mailer to control formatting.
1129**              a, b, c -- parameters
1130**
1131**      Returns:
1132**              none.
1133**
1134**      Side Effects:
1135**              writes message to mci->mci_out.
1136*/
1137
1138/*VARARGS1*/
1139void
1140#ifdef __STDC__
1141smtpmessage(char *f, MAILER *m, MCI *mci, ...)
1142#else
1143smtpmessage(f, m, mci, va_alist)
1144        char *f;
1145        MAILER *m;
1146        MCI *mci;
1147        va_dcl
1148#endif
1149{
1150        VA_LOCAL_DECL
1151
1152        VA_START(mci);
1153        (void) vsnprintf(SmtpMsgBuffer, sizeof SmtpMsgBuffer, f, ap);
1154        VA_END;
1155
1156        if (tTd(18, 1) || Verbose)
1157                nmessage(">>> %s", SmtpMsgBuffer);
1158        if (TrafficLogFile != NULL)
1159                fprintf(TrafficLogFile, "%05d >>> %s\n",
1160                        (int) getpid(), SmtpMsgBuffer);
1161        if (mci->mci_out != NULL)
1162        {
1163                fprintf(mci->mci_out, "%s%s", SmtpMsgBuffer,
1164                        m == NULL ? "\r\n" : m->m_eol);
1165        }
1166        else if (tTd(18, 1))
1167        {
1168                printf("smtpmessage: NULL mci_out\n");
1169        }
1170}
1171
1172# endif /* SMTP */
Note: See TracBrowser for help on using the repository browser.