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