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