This commit was manufactured by cvs2svn to create tag 'FreeBSD-release/1.0'.
[unix-history] / usr.sbin / sendmail / src / usersmtp.c
CommitLineData
15637ed4
RG
1/*
2 * Copyright (c) 1983 Eric P. Allman
78ed81a3 3 * Copyright (c) 1988, 1993
4 * The Regents of the University of California. All rights reserved.
15637ed4
RG
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. All advertising materials mentioning features or use of this software
15 * must display the following acknowledgement:
16 * This product includes software developed by the University of
17 * California, Berkeley and its contributors.
18 * 4. Neither the name of the University nor the names of its contributors
19 * may be used to endorse or promote products derived from this software
20 * without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 */
34
35# include "sendmail.h"
36
37#ifndef lint
38#ifdef SMTP
78ed81a3 39static char sccsid[] = "@(#)usersmtp.c 8.4 (Berkeley) 7/13/93 (with SMTP)";
15637ed4 40#else
78ed81a3 41static char sccsid[] = "@(#)usersmtp.c 8.4 (Berkeley) 7/13/93 (without SMTP)";
15637ed4
RG
42#endif
43#endif /* not lint */
44
45# include <sysexits.h>
46# include <errno.h>
47
48# ifdef SMTP
49
50/*
51** USERSMTP -- run SMTP protocol from the user end.
52**
53** This protocol is described in RFC821.
54*/
55
56#define REPLYTYPE(r) ((r) / 100) /* first digit of reply code */
57#define REPLYCLASS(r) (((r) / 10) % 10) /* second digit of reply code */
58#define SMTPCLOSING 421 /* "Service Shutting Down" */
59
60char SmtpMsgBuffer[MAXLINE]; /* buffer for commands */
61char SmtpReplyBuffer[MAXLINE]; /* buffer for replies */
62char SmtpError[MAXLINE] = ""; /* save failure error messages */
15637ed4
RG
63int SmtpPid; /* pid of mailer */
64
78ed81a3 65#ifdef __STDC__
66extern smtpmessage(char *f, MAILER *m, MCI *mci, ...);
67#endif
15637ed4
RG
68\f/*
69** SMTPINIT -- initialize SMTP.
70**
71** Opens the connection and sends the initial protocol.
72**
73** Parameters:
74** m -- mailer to create connection to.
75** pvp -- pointer to parameter vector to pass to
76** the mailer.
77**
78** Returns:
78ed81a3 79** none.
15637ed4
RG
80**
81** Side Effects:
82** creates connection and sends initial protocol.
83*/
84
78ed81a3 85smtpinit(m, mci, e)
15637ed4 86 struct mailer *m;
78ed81a3 87 register MCI *mci;
88 ENVELOPE *e;
15637ed4
RG
89{
90 register int r;
78ed81a3 91 register char *p;
92 extern void esmtp_check();
93 extern void helo_options();
94
95 if (tTd(18, 1))
96 {
97 printf("smtpinit ");
98 mci_dump(mci);
99 }
15637ed4
RG
100
101 /*
102 ** Open the connection to the mailer.
103 */
104
15637ed4 105 SmtpError[0] = '\0';
78ed81a3 106 CurHostName = mci->mci_host; /* XXX UGLY XXX */
107 switch (mci->mci_state)
15637ed4 108 {
78ed81a3 109 case MCIS_ACTIVE:
110 /* need to clear old information */
111 smtprset(m, mci, e);
112 /* fall through */
15637ed4 113
78ed81a3 114 case MCIS_OPEN:
115 return;
116
117 case MCIS_ERROR:
118 case MCIS_SSD:
119 /* shouldn't happen */
120 smtpquit(m, mci, e);
121 /* fall through */
122
123 case MCIS_CLOSED:
124 syserr("451 smtpinit: state CLOSED");
125 return;
126
127 case MCIS_OPENING:
128 break;
15637ed4 129 }
78ed81a3 130
131 mci->mci_state = MCIS_OPENING;
15637ed4
RG
132
133 /*
134 ** Get the greeting message.
135 ** This should appear spontaneously. Give it five minutes to
136 ** happen.
137 */
138
78ed81a3 139 SmtpPhase = mci->mci_phase = "client greeting";
140 setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
141 r = reply(m, mci, e, TimeOuts.to_initial, esmtp_check);
15637ed4 142 if (r < 0 || REPLYTYPE(r) != 2)
78ed81a3 143 goto tempfail1;
15637ed4
RG
144
145 /*
146 ** Send the HELO command.
147 ** My mother taught me to always introduce myself.
148 */
149
78ed81a3 150 if (bitnset(M_ESMTP, m->m_flags))
151 mci->mci_flags |= MCIF_ESMTP;
152
153tryhelo:
154 if (bitset(MCIF_ESMTP, mci->mci_flags))
155 {
156 smtpmessage("EHLO %s", m, mci, MyHostName);
157 SmtpPhase = mci->mci_phase = "client EHLO";
158 }
159 else
160 {
161 smtpmessage("HELO %s", m, mci, MyHostName);
162 SmtpPhase = mci->mci_phase = "client HELO";
163 }
164 setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
165 r = reply(m, mci, e, TimeOuts.to_helo, helo_options);
15637ed4 166 if (r < 0)
78ed81a3 167 goto tempfail1;
15637ed4 168 else if (REPLYTYPE(r) == 5)
78ed81a3 169 {
170 if (bitset(MCIF_ESMTP, mci->mci_flags))
171 {
172 /* try old SMTP instead */
173 mci->mci_flags &= ~MCIF_ESMTP;
174 goto tryhelo;
175 }
15637ed4 176 goto unavailable;
78ed81a3 177 }
15637ed4 178 else if (REPLYTYPE(r) != 2)
78ed81a3 179 goto tempfail1;
180
181 /*
182 ** Check to see if we actually ended up talking to ourself.
183 ** This means we didn't know about an alias or MX, or we managed
184 ** to connect to an echo server.
185 */
186
187 p = strchr(&SmtpReplyBuffer[4], ' ');
188 if (p != NULL)
189 *p = '\0';
190 if (strcasecmp(&SmtpReplyBuffer[4], MyHostName) == 0)
191 {
192 syserr("553 %s config error: mail loops back to myself",
193 MyHostName);
194 mci->mci_exitstat = EX_CONFIG;
195 mci->mci_errno = 0;
196 smtpquit(m, mci, e);
197 return;
198 }
15637ed4
RG
199
200 /*
201 ** If this is expected to be another sendmail, send some internal
202 ** commands.
203 */
204
205 if (bitnset(M_INTERNAL, m->m_flags))
206 {
207 /* tell it to be verbose */
78ed81a3 208 smtpmessage("VERB", m, mci);
209 r = reply(m, mci, e, TimeOuts.to_miscshort, NULL);
15637ed4 210 if (r < 0)
78ed81a3 211 goto tempfail2;
212 }
15637ed4 213
78ed81a3 214 mci->mci_state = MCIS_OPEN;
215 return;
216
217 tempfail1:
218 tempfail2:
219 mci->mci_exitstat = EX_TEMPFAIL;
220 if (mci->mci_errno == 0)
221 mci->mci_errno = errno;
222 if (mci->mci_state != MCIS_CLOSED)
223 smtpquit(m, mci, e);
224 return;
225
226 unavailable:
227 mci->mci_exitstat = EX_UNAVAILABLE;
228 mci->mci_errno = errno;
229 smtpquit(m, mci, e);
230 return;
231}
232\f/*
233** ESMTP_CHECK -- check to see if this implementation likes ESMTP protocol
234**
235**
236** Parameters:
237** line -- the response line.
238** m -- the mailer.
239** mci -- the mailer connection info.
240** e -- the envelope.
241**
242** Returns:
243** none.
244*/
245
246void
247esmtp_check(line, m, mci, e)
248 char *line;
249 MAILER *m;
250 register MCI *mci;
251 ENVELOPE *e;
252{
253 if (strlen(line) < 5)
254 return;
255 line += 4;
256 if (strncmp(line, "ESMTP ", 6) == 0)
257 mci->mci_flags |= MCIF_ESMTP;
258}
259\f/*
260** HELO_OPTIONS -- process the options on a HELO line.
261**
262** Parameters:
263** line -- the response line.
264** m -- the mailer.
265** mci -- the mailer connection info.
266** e -- the envelope.
267**
268** Returns:
269** none.
270*/
271
272void
273helo_options(line, m, mci, e)
274 char *line;
275 MAILER *m;
276 register MCI *mci;
277 ENVELOPE *e;
278{
279 register char *p;
280
281 if (strlen(line) < 5)
282 return;
283 line += 4;
284 p = strchr(line, ' ');
285 if (p != NULL)
286 *p++ = '\0';
287 if (strcasecmp(line, "size") == 0)
288 {
289 mci->mci_flags |= MCIF_SIZE;
290 if (p != NULL)
291 mci->mci_maxsize = atol(p);
15637ed4 292 }
78ed81a3 293 else if (strcasecmp(line, "8bitmime") == 0)
294 mci->mci_flags |= MCIF_8BITMIME;
295 else if (strcasecmp(line, "expn") == 0)
296 mci->mci_flags |= MCIF_EXPN;
297}
298\f/*
299** SMTPMAILFROM -- send MAIL command
300**
301** Parameters:
302** m -- the mailer.
303** mci -- the mailer connection structure.
304** e -- the envelope (including the sender to specify).
305*/
306
307smtpmailfrom(m, mci, e)
308 struct mailer *m;
309 MCI *mci;
310 ENVELOPE *e;
311{
312 int r;
313 char buf[MAXNAME];
314 char optbuf[MAXLINE];
315
316 if (tTd(18, 2))
317 printf("smtpmailfrom: CurHost=%s\n", CurHostName);
318
319 /* set up appropriate options to include */
320 if (bitset(MCIF_SIZE, mci->mci_flags))
321 sprintf(optbuf, " SIZE=%ld", e->e_msgsize);
322 else
323 strcpy(optbuf, "");
15637ed4
RG
324
325 /*
326 ** Send the MAIL command.
327 ** Designates the sender.
328 */
329
78ed81a3 330 mci->mci_state = MCIS_ACTIVE;
331
332 if (bitset(EF_RESPONSE, e->e_flags) &&
333 !bitnset(M_NO_NULL_FROM, m->m_flags))
334 (void) strcpy(buf, "");
335 else
336 expand("\201g", buf, &buf[sizeof buf - 1], e);
337 if (e->e_from.q_mailer == LocalMailer ||
15637ed4
RG
338 !bitnset(M_FROMPATH, m->m_flags))
339 {
78ed81a3 340 smtpmessage("MAIL From:<%s>%s", m, mci, buf, optbuf);
15637ed4
RG
341 }
342 else
343 {
78ed81a3 344 smtpmessage("MAIL From:<@%s%c%s>%s", m, mci, MyHostName,
345 buf[0] == '@' ? ',' : ':', buf, optbuf);
15637ed4 346 }
78ed81a3 347 SmtpPhase = mci->mci_phase = "client MAIL";
348 setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
349 r = reply(m, mci, e, TimeOuts.to_mail, NULL);
15637ed4 350 if (r < 0 || REPLYTYPE(r) == 4)
78ed81a3 351 {
352 mci->mci_exitstat = EX_TEMPFAIL;
353 mci->mci_errno = errno;
354 smtpquit(m, mci, e);
355 return EX_TEMPFAIL;
356 }
15637ed4 357 else if (r == 250)
78ed81a3 358 {
359 mci->mci_exitstat = EX_OK;
360 return EX_OK;
361 }
15637ed4 362 else if (r == 552)
78ed81a3 363 {
364 /* signal service unavailable */
365 mci->mci_exitstat = EX_UNAVAILABLE;
366 smtpquit(m, mci, e);
367 return EX_UNAVAILABLE;
368 }
15637ed4 369
78ed81a3 370#ifdef LOG
371 if (LogLevel > 1)
372 {
373 syslog(LOG_CRIT, "%s: SMTP MAIL protocol error: %s",
374 e->e_id, SmtpReplyBuffer);
375 }
376#endif
15637ed4 377
78ed81a3 378 /* protocol error -- close up */
379 smtpquit(m, mci, e);
380 mci->mci_exitstat = EX_PROTOCOL;
381 return EX_PROTOCOL;
15637ed4
RG
382}
383\f/*
384** SMTPRCPT -- designate recipient.
385**
386** Parameters:
387** to -- address of recipient.
388** m -- the mailer we are sending to.
78ed81a3 389** mci -- the connection info for this transaction.
390** e -- the envelope for this transaction.
15637ed4
RG
391**
392** Returns:
393** exit status corresponding to recipient status.
394**
395** Side Effects:
396** Sends the mail via SMTP.
397*/
398
78ed81a3 399smtprcpt(to, m, mci, e)
15637ed4
RG
400 ADDRESS *to;
401 register MAILER *m;
78ed81a3 402 MCI *mci;
403 ENVELOPE *e;
15637ed4
RG
404{
405 register int r;
15637ed4 406
78ed81a3 407 smtpmessage("RCPT To:<%s>", m, mci, to->q_user);
15637ed4 408
78ed81a3 409 SmtpPhase = mci->mci_phase = "client RCPT";
410 setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
411 r = reply(m, mci, e, TimeOuts.to_rcpt, NULL);
15637ed4
RG
412 if (r < 0 || REPLYTYPE(r) == 4)
413 return (EX_TEMPFAIL);
414 else if (REPLYTYPE(r) == 2)
415 return (EX_OK);
416 else if (r == 550 || r == 551 || r == 553)
417 return (EX_NOUSER);
418 else if (r == 552 || r == 554)
419 return (EX_UNAVAILABLE);
78ed81a3 420
421#ifdef LOG
422 if (LogLevel > 1)
423 {
424 syslog(LOG_CRIT, "%s: SMTP RCPT protocol error: %s",
425 e->e_id, SmtpReplyBuffer);
426 }
427#endif
428
15637ed4
RG
429 return (EX_PROTOCOL);
430}
431\f/*
432** SMTPDATA -- send the data and clean up the transaction.
433**
434** Parameters:
435** m -- mailer being sent to.
436** e -- the envelope for this message.
437**
438** Returns:
439** exit status corresponding to DATA command.
440**
441** Side Effects:
442** none.
443*/
444
78ed81a3 445static jmp_buf CtxDataTimeout;
446
447smtpdata(m, mci, e)
15637ed4 448 struct mailer *m;
78ed81a3 449 register MCI *mci;
15637ed4
RG
450 register ENVELOPE *e;
451{
452 register int r;
78ed81a3 453 register EVENT *ev;
454 time_t timeout;
455 static int datatimeout();
15637ed4
RG
456
457 /*
458 ** Send the data.
459 ** First send the command and check that it is ok.
460 ** Then send the data.
461 ** Follow it up with a dot to terminate.
462 ** Finally get the results of the transaction.
463 */
464
465 /* send the command and check ok to proceed */
78ed81a3 466 smtpmessage("DATA", m, mci);
467 SmtpPhase = mci->mci_phase = "client DATA 354";
468 setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
469 r = reply(m, mci, e, TimeOuts.to_datainit, NULL);
15637ed4 470 if (r < 0 || REPLYTYPE(r) == 4)
78ed81a3 471 {
472 smtpquit(m, mci, e);
15637ed4 473 return (EX_TEMPFAIL);
78ed81a3 474 }
15637ed4 475 else if (r == 554)
78ed81a3 476 {
477 smtprset(m, mci, e);
15637ed4 478 return (EX_UNAVAILABLE);
78ed81a3 479 }
15637ed4 480 else if (r != 354)
78ed81a3 481 {
482#ifdef LOG
483 if (LogLevel > 1)
484 {
485 syslog(LOG_CRIT, "%s: SMTP DATA-1 protocol error: %s",
486 e->e_id, SmtpReplyBuffer);
487 }
488#endif
489 smtprset(m, mci, e);
15637ed4 490 return (EX_PROTOCOL);
78ed81a3 491 }
492
493 /*
494 ** Set timeout around data writes. Make it at least large
495 ** enough for DNS timeouts on all recipients plus some fudge
496 ** factor. The main thing is that it should not be infinite.
497 */
498
499 if (setjmp(CtxDataTimeout) != 0)
500 {
501 mci->mci_errno = errno;
502 mci->mci_exitstat = EX_TEMPFAIL;
503 mci->mci_state = MCIS_ERROR;
504 syserr("451 timeout writing message to %s", mci->mci_host);
505 smtpquit(m, mci, e);
506 return EX_TEMPFAIL;
507 }
508
509 timeout = e->e_msgsize / 16;
510 if (timeout < (time_t) 60)
511 timeout = (time_t) 60;
512 timeout += e->e_nrcpts * 90;
513 ev = setevent(timeout, datatimeout, 0);
15637ed4
RG
514
515 /* now output the actual message */
78ed81a3 516 (*e->e_puthdr)(mci->mci_out, m, e);
517 putline("\n", mci->mci_out, m);
518 (*e->e_putbody)(mci->mci_out, m, e, NULL);
519
520 clrevent(ev);
15637ed4
RG
521
522 /* terminate the message */
78ed81a3 523 fprintf(mci->mci_out, ".%s", m->m_eol);
524 if (TrafficLogFile != NULL)
525 fprintf(TrafficLogFile, "%05d >>> .\n", getpid());
526 if (Verbose)
527 nmessage(">>> .");
15637ed4
RG
528
529 /* check for the results of the transaction */
78ed81a3 530 SmtpPhase = mci->mci_phase = "client DATA 250";
531 setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
532 r = reply(m, mci, e, TimeOuts.to_datafinal, NULL);
533 if (r < 0)
534 {
535 smtpquit(m, mci, e);
536 return (EX_TEMPFAIL);
537 }
538 mci->mci_state = MCIS_OPEN;
539 e->e_statmsg = newstr(&SmtpReplyBuffer[4]);
540 if (REPLYTYPE(r) == 4)
15637ed4
RG
541 return (EX_TEMPFAIL);
542 else if (r == 250)
543 return (EX_OK);
544 else if (r == 552 || r == 554)
545 return (EX_UNAVAILABLE);
78ed81a3 546#ifdef LOG
547 if (LogLevel > 1)
548 {
549 syslog(LOG_CRIT, "%s: SMTP DATA-2 protocol error: %s",
550 e->e_id, SmtpReplyBuffer);
551 }
552#endif
15637ed4
RG
553 return (EX_PROTOCOL);
554}
78ed81a3 555
556
557static int
558datatimeout()
559{
560 longjmp(CtxDataTimeout, 1);
561}
15637ed4
RG
562\f/*
563** SMTPQUIT -- close the SMTP connection.
564**
565** Parameters:
566** m -- a pointer to the mailer.
567**
568** Returns:
569** none.
570**
571** Side Effects:
572** sends the final protocol and closes the connection.
573*/
574
78ed81a3 575smtpquit(m, mci, e)
15637ed4 576 register MAILER *m;
78ed81a3 577 register MCI *mci;
578 ENVELOPE *e;
15637ed4
RG
579{
580 int i;
581
78ed81a3 582 /* send the quit message if we haven't gotten I/O error */
583 if (mci->mci_state != MCIS_ERROR)
15637ed4 584 {
78ed81a3 585 SmtpPhase = "client QUIT";
586 smtpmessage("QUIT", m, mci);
587 (void) reply(m, mci, e, TimeOuts.to_quit, NULL);
588 if (mci->mci_state == MCIS_CLOSED)
15637ed4
RG
589 return;
590 }
591
78ed81a3 592 /* now actually close the connection and pick up the zombie */
593 i = endmailer(mci, e, m->m_argv);
15637ed4 594 if (i != EX_OK)
78ed81a3 595 syserr("451 smtpquit %s: stat %d", m->m_argv[0], i);
596}
597\f/*
598** SMTPRSET -- send a RSET (reset) command
599*/
600
601smtprset(m, mci, e)
602 register MAILER *m;
603 register MCI *mci;
604 ENVELOPE *e;
605{
606 int r;
607
608 SmtpPhase = "client RSET";
609 smtpmessage("RSET", m, mci);
610 r = reply(m, mci, e, TimeOuts.to_rset, NULL);
611 if (r < 0)
612 mci->mci_state = MCIS_ERROR;
613 else if (REPLYTYPE(r) == 2)
614 {
615 mci->mci_state = MCIS_OPEN;
616 return;
617 }
618 smtpquit(m, mci, e);
619}
620\f/*
621** SMTPPROBE -- check the connection state
622*/
623
624smtpprobe(mci)
625 register MCI *mci;
626{
627 int r;
628 MAILER *m = mci->mci_mailer;
629 extern ENVELOPE BlankEnvelope;
630 ENVELOPE *e = &BlankEnvelope;
631
632 SmtpPhase = "client probe";
633 smtpmessage("RSET", m, mci);
634 r = reply(m, mci, e, TimeOuts.to_miscshort, NULL);
635 if (r < 0 || REPLYTYPE(r) != 2)
636 smtpquit(m, mci, e);
637 return r;
15637ed4
RG
638}
639\f/*
640** REPLY -- read arpanet reply
641**
642** Parameters:
643** m -- the mailer we are reading the reply from.
78ed81a3 644** mci -- the mailer connection info structure.
645** e -- the current envelope.
646** timeout -- the timeout for reads.
647** pfunc -- processing function for second and subsequent
648** lines of response -- if null, no special
649** processing is done.
15637ed4
RG
650**
651** Returns:
652** reply code it reads.
653**
654** Side Effects:
655** flushes the mail file.
656*/
657
78ed81a3 658reply(m, mci, e, timeout, pfunc)
15637ed4 659 MAILER *m;
78ed81a3 660 MCI *mci;
661 ENVELOPE *e;
662 time_t timeout;
663 void (*pfunc)();
15637ed4 664{
78ed81a3 665 register char *bufp;
666 register int r;
667 bool firstline = TRUE;
668 char junkbuf[MAXLINE];
669
670 if (mci->mci_out != NULL)
671 (void) fflush(mci->mci_out);
15637ed4
RG
672
673 if (tTd(18, 1))
674 printf("reply\n");
675
676 /*
677 ** Read the input line, being careful not to hang.
678 */
679
78ed81a3 680 for (bufp = SmtpReplyBuffer;; bufp = junkbuf)
15637ed4 681 {
15637ed4 682 register char *p;
78ed81a3 683 extern time_t curtime();
15637ed4
RG
684
685 /* actually do the read */
78ed81a3 686 if (e->e_xfp != NULL)
687 (void) fflush(e->e_xfp); /* for debugging */
15637ed4
RG
688
689 /* if we are in the process of closing just give the code */
78ed81a3 690 if (mci->mci_state == MCIS_CLOSED)
15637ed4
RG
691 return (SMTPCLOSING);
692
78ed81a3 693 if (mci->mci_out != NULL)
694 fflush(mci->mci_out);
695
15637ed4 696 /* get the line from the other side */
78ed81a3 697 p = sfgets(bufp, MAXLINE, mci->mci_in, timeout, SmtpPhase);
698 mci->mci_lastuse = curtime();
699
15637ed4
RG
700 if (p == NULL)
701 {
78ed81a3 702 bool oldholderrs;
15637ed4 703 extern char MsgBuf[]; /* err.c */
15637ed4
RG
704
705 /* if the remote end closed early, fake an error */
706 if (errno == 0)
707# ifdef ECONNRESET
708 errno = ECONNRESET;
78ed81a3 709# else /* ECONNRESET */
15637ed4 710 errno = EPIPE;
78ed81a3 711# endif /* ECONNRESET */
712
713 mci->mci_errno = errno;
714 mci->mci_exitstat = EX_TEMPFAIL;
715 oldholderrs = HoldErrs;
716 HoldErrs = TRUE;
717 usrerr("451 reply: read error from %s", mci->mci_host);
15637ed4 718
15637ed4
RG
719 /* if debugging, pause so we can see state */
720 if (tTd(18, 100))
721 pause();
78ed81a3 722 mci->mci_state = MCIS_ERROR;
723 smtpquit(m, mci, e);
724#ifdef XDEBUG
725 {
726 char wbuf[MAXLINE];
727 sprintf(wbuf, "%s... reply(%s) during %s",
728 e->e_to, mci->mci_host, SmtpPhase);
729 checkfd012(wbuf);
730 }
731#endif
732 HoldErrs = oldholderrs;
15637ed4
RG
733 return (-1);
734 }
78ed81a3 735 fixcrlf(bufp, TRUE);
15637ed4 736
78ed81a3 737 /* EHLO failure is not a real error */
738 if (e->e_xfp != NULL && (bufp[0] == '4' ||
739 (bufp[0] == '5' && strncmp(SmtpMsgBuffer, "EHLO", 4) != 0)))
15637ed4
RG
740 {
741 /* serious error -- log the previous command */
742 if (SmtpMsgBuffer[0] != '\0')
78ed81a3 743 fprintf(e->e_xfp, ">>> %s\n", SmtpMsgBuffer);
15637ed4
RG
744 SmtpMsgBuffer[0] = '\0';
745
746 /* now log the message as from the other side */
78ed81a3 747 fprintf(e->e_xfp, "<<< %s\n", bufp);
15637ed4
RG
748 }
749
750 /* display the input for verbose mode */
78ed81a3 751 if (Verbose)
752 nmessage("050 %s", bufp);
753
754 /* process the line */
755 if (pfunc != NULL && !firstline)
756 (*pfunc)(bufp, m, mci, e);
757
758 firstline = FALSE;
15637ed4
RG
759
760 /* if continuation is required, we can go on */
78ed81a3 761 if (bufp[3] == '-')
762 continue;
763
764 /* ignore improperly formated input */
765 if (!(isascii(bufp[0]) && isdigit(bufp[0])))
15637ed4
RG
766 continue;
767
768 /* decode the reply code */
78ed81a3 769 r = atoi(bufp);
15637ed4
RG
770
771 /* extra semantics: 0xx codes are "informational" */
78ed81a3 772 if (r >= 100)
773 break;
774 }
15637ed4 775
78ed81a3 776 /*
777 ** Now look at SmtpReplyBuffer -- only care about the first
778 ** line of the response from here on out.
779 */
15637ed4 780
78ed81a3 781 /* save temporary failure messages for posterity */
782 if (SmtpReplyBuffer[0] == '4' && SmtpError[0] == '\0')
783 (void) strcpy(SmtpError, SmtpReplyBuffer);
15637ed4 784
78ed81a3 785 /* reply code 421 is "Service Shutting Down" */
786 if (r == SMTPCLOSING && mci->mci_state != MCIS_SSD)
787 {
788 /* send the quit protocol */
789 mci->mci_state = MCIS_SSD;
790 smtpquit(m, mci, e);
15637ed4 791 }
78ed81a3 792
793 return (r);
15637ed4
RG
794}
795\f/*
796** SMTPMESSAGE -- send message to server
797**
798** Parameters:
799** f -- format
800** m -- the mailer to control formatting.
801** a, b, c -- parameters
802**
803** Returns:
804** none.
805**
806** Side Effects:
78ed81a3 807** writes message to mci->mci_out.
15637ed4
RG
808*/
809
810/*VARARGS1*/
78ed81a3 811#ifdef __STDC__
812smtpmessage(char *f, MAILER *m, MCI *mci, ...)
813#else
814smtpmessage(f, m, mci, va_alist)
15637ed4
RG
815 char *f;
816 MAILER *m;
78ed81a3 817 MCI *mci;
818 va_dcl
819#endif
15637ed4 820{
78ed81a3 821 VA_LOCAL_DECL
822
823 VA_START(mci);
824 (void) vsprintf(SmtpMsgBuffer, f, ap);
825 VA_END;
826
827 if (tTd(18, 1) || Verbose)
828 nmessage(">>> %s", SmtpMsgBuffer);
829 if (TrafficLogFile != NULL)
830 fprintf(TrafficLogFile, "%05d >>> %s\n", getpid(), SmtpMsgBuffer);
831 if (mci->mci_out != NULL)
832 {
833 fprintf(mci->mci_out, "%s%s", SmtpMsgBuffer,
834 m == NULL ? "\r\n" : m->m_eol);
835 }
836 else if (tTd(18, 1))
837 {
838 printf("smtpmessage: NULL mci_out\n");
839 }
15637ed4
RG
840}
841
78ed81a3 842# endif /* SMTP */