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