]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/sendmail/src/usersmtp.c
This commit was generated by cvs2svn to compensate for changes in r53024,
[FreeBSD/FreeBSD.git] / contrib / sendmail / src / usersmtp.c
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
17 static char sccsid[] = "@(#)usersmtp.c  8.111 (Berkeley) 2/3/1999 (with SMTP)";
18 #else
19 static 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
38 char    SmtpMsgBuffer[MAXLINE];         /* buffer for commands */
39 char    SmtpReplyBuffer[MAXLINE];       /* buffer for replies */
40 char    SmtpError[MAXLINE] = "";        /* save failure error messages */
41 bool    SmtpNeedIntro;                  /* need "while talking" in transcript */
42
43 extern void     smtpmessage __P((char *f, MAILER *m, MCI *mci, ...));
44 extern int      reply __P((MAILER *, MCI *, ENVELOPE *, time_t, void (*)()));
45 \f/*
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
62 void
63 smtpinit(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
138 tryhelo:
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 \f/*
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
252 void
253 esmtp_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 \f/*
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
279 void
280 helo_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 \f/*
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
323 int
324 smtpmailfrom(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 \f/*
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
526 int
527 smtprcpt(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 \f/*
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
641 static jmp_buf  CtxDataTimeout;
642 static void     datatimeout __P((void));
643
644 int
645 smtpdata(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
787 static void
788 datatimeout()
789 {
790         longjmp(CtxDataTimeout, 1);
791 }
792 \f/*
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
804 int
805 smtpgetstat(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 \f/*
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
858 void
859 smtpquit(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 \f/*
892 **  SMTPRSET -- send a RSET (reset) command
893 */
894
895 void
896 smtprset(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 \f/*
916 **  SMTPPROBE -- check the connection state
917 */
918
919 int
920 smtpprobe(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 \f/*
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
953 int
954 reply(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 \f/*
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*/
1139 void
1140 #ifdef __STDC__
1141 smtpmessage(char *f, MAILER *m, MCI *mci, ...)
1142 #else
1143 smtpmessage(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 */