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