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