122716Sdist /*
234921Sbostic  * Copyright (c) 1983 Eric P. Allman
333731Sbostic  * Copyright (c) 1988 Regents of the University of California.
433731Sbostic  * All rights reserved.
533731Sbostic  *
642831Sbostic  * %sccs.include.redist.c%
733731Sbostic  */
822716Sdist 
933731Sbostic # include "sendmail.h"
1022716Sdist 
1133731Sbostic #ifndef lint
1233731Sbostic #ifdef SMTP
13*59285Seric static char sccsid[] = "@(#)usersmtp.c	6.27 (Berkeley) 04/26/93 (with SMTP)";
1433731Sbostic #else
15*59285Seric static char sccsid[] = "@(#)usersmtp.c	6.27 (Berkeley) 04/26/93 (without SMTP)";
1633731Sbostic #endif
1733731Sbostic #endif /* not lint */
1833731Sbostic 
194684Seric # include <sysexits.h>
2021065Seric # include <errno.h>
214684Seric 
2233731Sbostic # ifdef SMTP
234684Seric 
244684Seric /*
259391Seric **  USERSMTP -- run SMTP protocol from the user end.
269391Seric **
279391Seric **	This protocol is described in RFC821.
289391Seric */
299391Seric 
309391Seric #define REPLYTYPE(r)	((r) / 100)		/* first digit of reply code */
319391Seric #define REPLYCLASS(r)	(((r) / 10) % 10)	/* second digit of reply code */
329391Seric #define SMTPCLOSING	421			/* "Service Shutting Down" */
339391Seric 
3414900Seric char	SmtpMsgBuffer[MAXLINE];		/* buffer for commands */
3510054Seric char	SmtpReplyBuffer[MAXLINE];	/* buffer for replies */
3621065Seric char	SmtpError[MAXLINE] = "";	/* save failure error messages */
3710054Seric int	SmtpPid;			/* pid of mailer */
3858671Seric 
3958671Seric #ifdef __STDC__
4058671Seric extern	smtpmessage(char *f, MAILER *m, MCI *mci, ...);
4158671Seric #endif
429391Seric /*
434865Seric **  SMTPINIT -- initialize SMTP.
444684Seric **
454865Seric **	Opens the connection and sends the initial protocol.
464684Seric **
474684Seric **	Parameters:
484865Seric **		m -- mailer to create connection to.
494865Seric **		pvp -- pointer to parameter vector to pass to
504865Seric **			the mailer.
514684Seric **
524684Seric **	Returns:
5354967Seric **		none.
544684Seric **
554684Seric **	Side Effects:
564865Seric **		creates connection and sends initial protocol.
574684Seric */
584684Seric 
5954967Seric smtpinit(m, mci, e)
604865Seric 	struct mailer *m;
6154967Seric 	register MCI *mci;
6253751Seric 	ENVELOPE *e;
634684Seric {
644865Seric 	register int r;
6558957Seric 	register char *p;
6652107Seric 	extern STAB *stab();
67*59285Seric 	extern void helo_options();
684684Seric 
6957379Seric 	if (tTd(17, 1))
7057379Seric 	{
7157379Seric 		printf("smtpinit ");
7257379Seric 		mci_dump(mci);
7357379Seric 	}
7457379Seric 
754865Seric 	/*
764865Seric 	**  Open the connection to the mailer.
774865Seric 	*/
784684Seric 
7921065Seric 	SmtpError[0] = '\0';
8057379Seric 	CurHostName = mci->mci_host;		/* XXX UGLY XXX */
8154967Seric 	switch (mci->mci_state)
826051Seric 	{
8354967Seric 	  case MCIS_ACTIVE:
8454967Seric 		/* need to clear old information */
8554967Seric 		smtprset(m, mci, e);
8657734Seric 		/* fall through */
8715139Seric 
8854967Seric 	  case MCIS_OPEN:
8954967Seric 		return;
9054967Seric 
9154967Seric 	  case MCIS_ERROR:
9254967Seric 	  case MCIS_SSD:
9354967Seric 		/* shouldn't happen */
9454967Seric 		smtpquit(m, mci, e);
9557734Seric 		/* fall through */
9654967Seric 
9754967Seric 	  case MCIS_CLOSED:
9858151Seric 		syserr("451 smtpinit: state CLOSED");
9954967Seric 		return;
10054967Seric 
10154967Seric 	  case MCIS_OPENING:
10254967Seric 		break;
1036051Seric 	}
1044796Seric 
10554967Seric 	mci->mci_state = MCIS_OPENING;
10654967Seric 
1074865Seric 	/*
1084865Seric 	**  Get the greeting message.
10914913Seric 	**	This should appear spontaneously.  Give it five minutes to
11014886Seric 	**	happen.
1114865Seric 	*/
1124797Seric 
11357379Seric 	SmtpPhase = mci->mci_phase = "greeting wait";
11453751Seric 	setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
115*59285Seric 	r = reply(m, mci, e, TimeOuts.to_initial, NULL);
1168005Seric 	if (r < 0 || REPLYTYPE(r) != 2)
11752104Seric 		goto tempfail1;
1184684Seric 
1194865Seric 	/*
1204976Seric 	**  Send the HELO command.
1217963Seric 	**	My mother taught me to always introduce myself.
1224976Seric 	*/
1234976Seric 
124*59285Seric 	if (bitnset(M_ESMTP, m->m_flags))
125*59285Seric 		mci->mci_flags |= MCIF_ESMTP;
126*59285Seric 
127*59285Seric tryhelo:
128*59285Seric 	if (bitset(MCIF_ESMTP, mci->mci_flags))
129*59285Seric 	{
130*59285Seric 		smtpmessage("EHLO %s", m, mci, MyHostName);
131*59285Seric 		SmtpPhase = mci->mci_phase = "EHLO wait";
132*59285Seric 	}
133*59285Seric 	else
134*59285Seric 	{
135*59285Seric 		smtpmessage("HELO %s", m, mci, MyHostName);
136*59285Seric 		SmtpPhase = mci->mci_phase = "HELO wait";
137*59285Seric 	}
13853751Seric 	setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
139*59285Seric 	r = reply(m, mci, e, TimeOuts.to_helo, helo_options);
1408005Seric 	if (r < 0)
14152104Seric 		goto tempfail1;
1428005Seric 	else if (REPLYTYPE(r) == 5)
143*59285Seric 	{
144*59285Seric 		if (bitset(MCIF_ESMTP, mci->mci_flags))
145*59285Seric 		{
146*59285Seric 			/* try old SMTP instead */
147*59285Seric 			mci->mci_flags &= ~MCIF_ESMTP;
148*59285Seric 			goto tryhelo;
149*59285Seric 		}
15014913Seric 		goto unavailable;
151*59285Seric 	}
1527963Seric 	else if (REPLYTYPE(r) != 2)
15352104Seric 		goto tempfail1;
1544976Seric 
1554976Seric 	/*
15658957Seric 	**  Check to see if we actually ended up talking to ourself.
15758957Seric 	**  This means we didn't know about an alias or MX, or we managed
15858957Seric 	**  to connect to an echo server.
15958957Seric 	*/
16058957Seric 
16159026Seric 	p = strchr(&SmtpReplyBuffer[4], ' ');
16258957Seric 	if (p != NULL)
16358957Seric 		*p == '\0';
16459026Seric 	if (strcasecmp(&SmtpReplyBuffer[4], MyHostName) == 0)
16558957Seric 	{
16658957Seric 		syserr("553 %s config error: mail loops back to myself",
16758957Seric 			MyHostName);
16858957Seric 		mci->mci_exitstat = EX_CONFIG;
16958957Seric 		mci->mci_errno = 0;
17058957Seric 		smtpquit(m, mci, e);
17158957Seric 		return;
17258957Seric 	}
17358957Seric 
17458957Seric 	/*
1759315Seric 	**  If this is expected to be another sendmail, send some internal
1769315Seric 	**  commands.
1779315Seric 	*/
1789315Seric 
17910688Seric 	if (bitnset(M_INTERNAL, m->m_flags))
1809315Seric 	{
1819315Seric 		/* tell it to be verbose */
18253751Seric 		smtpmessage("VERB", m, mci);
183*59285Seric 		r = reply(m, mci, e, TimeOuts.to_miscshort, NULL);
1849315Seric 		if (r < 0)
18552104Seric 			goto tempfail2;
1869315Seric 	}
1879315Seric 
18853751Seric 	mci->mci_state = MCIS_OPEN;
18954967Seric 	return;
19053751Seric 
19153751Seric   tempfail1:
19253751Seric   tempfail2:
19353751Seric 	mci->mci_exitstat = EX_TEMPFAIL;
19457379Seric 	if (mci->mci_errno == 0)
19557379Seric 		mci->mci_errno = errno;
19657379Seric 	if (mci->mci_state != MCIS_CLOSED)
19757379Seric 		smtpquit(m, mci, e);
19854967Seric 	return;
19953751Seric 
20053751Seric   unavailable:
20153751Seric 	mci->mci_exitstat = EX_UNAVAILABLE;
20253751Seric 	mci->mci_errno = errno;
20353751Seric 	smtpquit(m, mci, e);
20454967Seric 	return;
20553751Seric }
206*59285Seric /*
207*59285Seric **  HELO_OPTIONS -- process the options on a HELO line.
208*59285Seric **
209*59285Seric **	Parameters:
210*59285Seric **		line -- the response line.
211*59285Seric **		m -- the mailer.
212*59285Seric **		mci -- the mailer connection info.
213*59285Seric **		e -- the envelope.
214*59285Seric **
215*59285Seric **	Returns:
216*59285Seric **		none.
217*59285Seric */
21853751Seric 
219*59285Seric void
220*59285Seric helo_options(line, m, mci, e)
221*59285Seric 	char *line;
222*59285Seric 	MAILER *m;
223*59285Seric 	register MCI *mci;
224*59285Seric 	ENVELOPE *e;
225*59285Seric {
226*59285Seric 	register char *p;
227*59285Seric 
228*59285Seric 	if (strlen(line) < 5)
229*59285Seric 		return;
230*59285Seric 	line += 4;
231*59285Seric 	p = strchr(line, ' ');
232*59285Seric 	if (p != NULL)
233*59285Seric 		*p++ = '\0';
234*59285Seric 	if (strcasecmp(line, "size") == 0)
235*59285Seric 	{
236*59285Seric 		mci->mci_flags |= MCIF_SIZE;
237*59285Seric 		if (p != NULL)
238*59285Seric 			mci->mci_maxsize = atol(p);
239*59285Seric 	}
240*59285Seric 	else if (strcasecmp(line, "8bitmime") == 0)
241*59285Seric 		mci->mci_flags |= MCIF_8BITMIME;
242*59285Seric 	else if (strcasecmp(line, "expn") == 0)
243*59285Seric 		mci->mci_flags |= MCIF_EXPN;
244*59285Seric }
245*59285Seric /*
246*59285Seric **  SMTPMAILFROM -- send MAIL command
247*59285Seric **
248*59285Seric **	Parameters:
249*59285Seric **		m -- the mailer.
250*59285Seric **		mci -- the mailer connection structure.
251*59285Seric **		e -- the envelope (including the sender to specify).
252*59285Seric */
253*59285Seric 
25453751Seric smtpmailfrom(m, mci, e)
25553751Seric 	struct mailer *m;
25654967Seric 	MCI *mci;
25753751Seric 	ENVELOPE *e;
25853751Seric {
25953751Seric 	int r;
26053751Seric 	char buf[MAXNAME];
261*59285Seric 	char optbuf[MAXLINE];
26253751Seric 
26357943Seric 	if (tTd(17, 2))
26457943Seric 		printf("smtpmailfrom: CurHost=%s\n", CurHostName);
26557943Seric 
266*59285Seric 	/* set up appropriate options to include */
267*59285Seric 	if (bitset(MCIF_SIZE, mci->mci_flags))
268*59285Seric 		sprintf(optbuf, " SIZE=%ld", e->e_msgsize);
269*59285Seric 	else
270*59285Seric 		strcpy(optbuf, "");
271*59285Seric 
2729315Seric 	/*
2734865Seric 	**  Send the MAIL command.
2744865Seric 	**	Designates the sender.
2754865Seric 	*/
2764796Seric 
27753751Seric 	mci->mci_state = MCIS_ACTIVE;
27853751Seric 
27958680Seric 	if (bitset(EF_RESPONSE, e->e_flags))
28058680Seric 		(void) strcpy(buf, "");
28158680Seric 	else
28258680Seric 		expand("\201g", buf, &buf[sizeof buf - 1], e);
28353751Seric 	if (e->e_from.q_mailer == LocalMailer ||
28410688Seric 	    !bitnset(M_FROMPATH, m->m_flags))
2858436Seric 	{
286*59285Seric 		smtpmessage("MAIL From:<%s>%s", m, mci, buf, optbuf);
2878436Seric 	}
2888436Seric 	else
2898436Seric 	{
290*59285Seric 		smtpmessage("MAIL From:<@%s%c%s>%s", m, mci, MyHostName,
291*59285Seric 			buf[0] == '@' ? ',' : ':', buf, optbuf);
2928436Seric 	}
29357379Seric 	SmtpPhase = mci->mci_phase = "MAIL wait";
29453751Seric 	setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
295*59285Seric 	r = reply(m, mci, e, TimeOuts.to_mail, NULL);
2968005Seric 	if (r < 0 || REPLYTYPE(r) == 4)
29753751Seric 	{
29853751Seric 		mci->mci_exitstat = EX_TEMPFAIL;
29953751Seric 		mci->mci_errno = errno;
30053751Seric 		smtpquit(m, mci, e);
30153751Seric 		return EX_TEMPFAIL;
30253751Seric 	}
3037963Seric 	else if (r == 250)
30453751Seric 	{
30553751Seric 		mci->mci_exitstat = EX_OK;
30653751Seric 		return EX_OK;
30753751Seric 	}
3087963Seric 	else if (r == 552)
30953751Seric 	{
31053751Seric 		/* signal service unavailable */
31153751Seric 		mci->mci_exitstat = EX_UNAVAILABLE;
31253751Seric 		smtpquit(m, mci, e);
31353751Seric 		return EX_UNAVAILABLE;
31453751Seric 	}
31514913Seric 
31658008Seric #ifdef LOG
31758020Seric 	if (LogLevel > 1)
31858008Seric 	{
31958008Seric 		syslog(LOG_CRIT, "%s: SMTP MAIL protocol error: %s",
32058008Seric 			e->e_id, SmtpReplyBuffer);
32158008Seric 	}
32258008Seric #endif
32358008Seric 
32414913Seric 	/* protocol error -- close up */
32553751Seric 	smtpquit(m, mci, e);
32653751Seric 	mci->mci_exitstat = EX_PROTOCOL;
32753751Seric 	return EX_PROTOCOL;
3284684Seric }
3294684Seric /*
3304976Seric **  SMTPRCPT -- designate recipient.
3314797Seric **
3324797Seric **	Parameters:
3334865Seric **		to -- address of recipient.
33410175Seric **		m -- the mailer we are sending to.
33557379Seric **		mci -- the connection info for this transaction.
33657379Seric **		e -- the envelope for this transaction.
3374797Seric **
3384797Seric **	Returns:
3394865Seric **		exit status corresponding to recipient status.
3404797Seric **
3414797Seric **	Side Effects:
3424865Seric **		Sends the mail via SMTP.
3434797Seric */
3444797Seric 
34553751Seric smtprcpt(to, m, mci, e)
3464865Seric 	ADDRESS *to;
34710175Seric 	register MAILER *m;
34854967Seric 	MCI *mci;
34953751Seric 	ENVELOPE *e;
3504797Seric {
3514797Seric 	register int r;
3524797Seric 
35353751Seric 	smtpmessage("RCPT To:<%s>", m, mci, to->q_user);
3544865Seric 
35557379Seric 	SmtpPhase = mci->mci_phase = "RCPT wait";
35653751Seric 	setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
357*59285Seric 	r = reply(m, mci, e, TimeOuts.to_rcpt, NULL);
3588005Seric 	if (r < 0 || REPLYTYPE(r) == 4)
3594865Seric 		return (EX_TEMPFAIL);
3607963Seric 	else if (REPLYTYPE(r) == 2)
3617963Seric 		return (EX_OK);
3627964Seric 	else if (r == 550 || r == 551 || r == 553)
3637964Seric 		return (EX_NOUSER);
3647964Seric 	else if (r == 552 || r == 554)
3657964Seric 		return (EX_UNAVAILABLE);
36658008Seric 
36758008Seric #ifdef LOG
36858020Seric 	if (LogLevel > 1)
36958008Seric 	{
37058008Seric 		syslog(LOG_CRIT, "%s: SMTP RCPT protocol error: %s",
37158008Seric 			e->e_id, SmtpReplyBuffer);
37258008Seric 	}
37358008Seric #endif
37458008Seric 
3757964Seric 	return (EX_PROTOCOL);
3764797Seric }
3774797Seric /*
37810175Seric **  SMTPDATA -- send the data and clean up the transaction.
3794684Seric **
3804684Seric **	Parameters:
3814865Seric **		m -- mailer being sent to.
3826980Seric **		e -- the envelope for this message.
3834684Seric **
3844684Seric **	Returns:
3854976Seric **		exit status corresponding to DATA command.
3864684Seric **
3874684Seric **	Side Effects:
3884865Seric **		none.
3894684Seric */
3904684Seric 
39153740Seric smtpdata(m, mci, e)
3924865Seric 	struct mailer *m;
39354967Seric 	register MCI *mci;
3946980Seric 	register ENVELOPE *e;
3954684Seric {
3964684Seric 	register int r;
3974684Seric 
3984797Seric 	/*
3994797Seric 	**  Send the data.
40010175Seric 	**	First send the command and check that it is ok.
40110175Seric 	**	Then send the data.
40210175Seric 	**	Follow it up with a dot to terminate.
40310175Seric 	**	Finally get the results of the transaction.
4044797Seric 	*/
4054797Seric 
40610175Seric 	/* send the command and check ok to proceed */
40753751Seric 	smtpmessage("DATA", m, mci);
40857379Seric 	SmtpPhase = mci->mci_phase = "DATA wait";
40953751Seric 	setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
410*59285Seric 	r = reply(m, mci, e, TimeOuts.to_datainit, NULL);
4118005Seric 	if (r < 0 || REPLYTYPE(r) == 4)
41257990Seric 	{
41357990Seric 		smtpquit(m, mci, e);
4144797Seric 		return (EX_TEMPFAIL);
41557990Seric 	}
4167963Seric 	else if (r == 554)
41757990Seric 	{
41857990Seric 		smtprset(m, mci, e);
4197963Seric 		return (EX_UNAVAILABLE);
42057990Seric 	}
4217963Seric 	else if (r != 354)
42257990Seric 	{
42358008Seric #ifdef LOG
42458020Seric 		if (LogLevel > 1)
42558008Seric 		{
42658008Seric 			syslog(LOG_CRIT, "%s: SMTP DATA-1 protocol error: %s",
42758008Seric 				e->e_id, SmtpReplyBuffer);
42858008Seric 		}
42958008Seric #endif
43057990Seric 		smtprset(m, mci, e);
4317964Seric 		return (EX_PROTOCOL);
43257990Seric 	}
43310175Seric 
43410175Seric 	/* now output the actual message */
43553751Seric 	(*e->e_puthdr)(mci->mci_out, m, e);
43653740Seric 	putline("\n", mci->mci_out, m);
43753751Seric 	(*e->e_putbody)(mci->mci_out, m, e);
43810175Seric 
43910175Seric 	/* terminate the message */
44053740Seric 	fprintf(mci->mci_out, ".%s", m->m_eol);
44158120Seric 	if (Verbose)
44258151Seric 		nmessage(">>> .");
44310175Seric 
44410175Seric 	/* check for the results of the transaction */
44557379Seric 	SmtpPhase = mci->mci_phase = "result wait";
44653751Seric 	setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
447*59285Seric 	r = reply(m, mci, e, TimeOuts.to_datafinal, NULL);
44853751Seric 	if (r < 0)
44957990Seric 	{
45057990Seric 		smtpquit(m, mci, e);
4514797Seric 		return (EX_TEMPFAIL);
45257990Seric 	}
45353751Seric 	mci->mci_state = MCIS_OPEN;
45458917Seric 	e->e_statmsg = newstr(&SmtpReplyBuffer[4]);
45553751Seric 	if (REPLYTYPE(r) == 4)
45653751Seric 		return (EX_TEMPFAIL);
4577963Seric 	else if (r == 250)
4587963Seric 		return (EX_OK);
4597963Seric 	else if (r == 552 || r == 554)
4607963Seric 		return (EX_UNAVAILABLE);
46158008Seric #ifdef LOG
46258020Seric 	if (LogLevel > 1)
46358008Seric 	{
46458008Seric 		syslog(LOG_CRIT, "%s: SMTP DATA-2 protocol error: %s",
46558008Seric 			e->e_id, SmtpReplyBuffer);
46658008Seric 	}
46758008Seric #endif
4687964Seric 	return (EX_PROTOCOL);
4694684Seric }
4704684Seric /*
4714865Seric **  SMTPQUIT -- close the SMTP connection.
4724865Seric **
4734865Seric **	Parameters:
47415535Seric **		m -- a pointer to the mailer.
4754865Seric **
4764865Seric **	Returns:
4774865Seric **		none.
4784865Seric **
4794865Seric **	Side Effects:
4804865Seric **		sends the final protocol and closes the connection.
4814865Seric */
4824865Seric 
48353751Seric smtpquit(m, mci, e)
48453751Seric 	register MAILER *m;
48554967Seric 	register MCI *mci;
48653751Seric 	ENVELOPE *e;
4874865Seric {
4889391Seric 	int i;
4894865Seric 
49054967Seric 	/* send the quit message if we haven't gotten I/O error */
49153751Seric 	if (mci->mci_state != MCIS_ERROR)
4929391Seric 	{
49353751Seric 		smtpmessage("QUIT", m, mci);
494*59285Seric 		(void) reply(m, mci, e, TimeOuts.to_quit, NULL);
49553740Seric 		if (mci->mci_state == MCIS_CLOSED)
49610159Seric 			return;
4979391Seric 	}
4989391Seric 
49952676Seric 	/* now actually close the connection and pick up the zombie */
50058846Seric 	i = endmailer(mci, e, m->m_argv);
5019391Seric 	if (i != EX_OK)
50258151Seric 		syserr("451 smtpquit %s: stat %d", m->m_argv[0], i);
5034865Seric }
5044865Seric /*
50554967Seric **  SMTPRSET -- send a RSET (reset) command
50654967Seric */
50754967Seric 
50854967Seric smtprset(m, mci, e)
50954967Seric 	register MAILER *m;
51054967Seric 	register MCI *mci;
51154967Seric 	ENVELOPE *e;
51254967Seric {
51354967Seric 	int r;
51454967Seric 
51554967Seric 	smtpmessage("RSET", m, mci);
516*59285Seric 	r = reply(m, mci, e, TimeOuts.to_rset, NULL);
51757734Seric 	if (r < 0)
51857734Seric 		mci->mci_state = MCIS_ERROR;
51954967Seric 	else if (REPLYTYPE(r) == 2)
52057734Seric 	{
52157734Seric 		mci->mci_state = MCIS_OPEN;
52257734Seric 		return;
52357734Seric 	}
52457734Seric 	smtpquit(m, mci, e);
52554967Seric }
52654967Seric /*
52758867Seric **  SMTPPROBE -- check the connection state
52854967Seric */
52954967Seric 
53058867Seric smtpprobe(mci)
53154967Seric 	register MCI *mci;
53254967Seric {
53354967Seric 	int r;
53454967Seric 	MAILER *m = mci->mci_mailer;
53554967Seric 	extern ENVELOPE BlankEnvelope;
53654967Seric 	ENVELOPE *e = &BlankEnvelope;
53754967Seric 
53858867Seric 	smtpmessage("RSET", m, mci);
539*59285Seric 	r = reply(m, mci, e, TimeOuts.to_miscshort, NULL);
54058061Seric 	if (r < 0 || REPLYTYPE(r) != 2)
54154967Seric 		smtpquit(m, mci, e);
54254967Seric 	return r;
54354967Seric }
54454967Seric /*
5454684Seric **  REPLY -- read arpanet reply
5464684Seric **
5474684Seric **	Parameters:
54810175Seric **		m -- the mailer we are reading the reply from.
54957379Seric **		mci -- the mailer connection info structure.
55057379Seric **		e -- the current envelope.
55157379Seric **		timeout -- the timeout for reads.
552*59285Seric **		pfunc -- processing function for second and subsequent
553*59285Seric **			lines of response -- if null, no special
554*59285Seric **			processing is done.
5554684Seric **
5564684Seric **	Returns:
5574684Seric **		reply code it reads.
5584684Seric **
5594684Seric **	Side Effects:
5604684Seric **		flushes the mail file.
5614684Seric */
5624684Seric 
563*59285Seric reply(m, mci, e, timeout, pfunc)
56453751Seric 	MAILER *m;
56554967Seric 	MCI *mci;
56653751Seric 	ENVELOPE *e;
567*59285Seric 	time_t timeout;
568*59285Seric 	void (*pfunc)();
5694684Seric {
57059014Seric 	register char *bufp;
57159014Seric 	register int r;
572*59285Seric 	bool firstline = TRUE;
57358957Seric 	char junkbuf[MAXLINE];
57458957Seric 
57557379Seric 	if (mci->mci_out != NULL)
57657379Seric 		(void) fflush(mci->mci_out);
5774684Seric 
5787677Seric 	if (tTd(18, 1))
5794796Seric 		printf("reply\n");
5804796Seric 
5817356Seric 	/*
5827356Seric 	**  Read the input line, being careful not to hang.
5837356Seric 	*/
5847356Seric 
58559014Seric 	for (bufp = SmtpReplyBuffer;; bufp = junkbuf)
5864684Seric 	{
5877356Seric 		register char *p;
58853751Seric 		extern time_t curtime();
5894684Seric 
5907685Seric 		/* actually do the read */
59153751Seric 		if (e->e_xfp != NULL)
59253751Seric 			(void) fflush(e->e_xfp);	/* for debugging */
5937356Seric 
59410054Seric 		/* if we are in the process of closing just give the code */
59553740Seric 		if (mci->mci_state == MCIS_CLOSED)
59610054Seric 			return (SMTPCLOSING);
59710054Seric 
59858680Seric 		if (mci->mci_out != NULL)
59958680Seric 			fflush(mci->mci_out);
60058680Seric 
60110054Seric 		/* get the line from the other side */
60258957Seric 		p = sfgets(bufp, MAXLINE, mci->mci_in, timeout);
60353751Seric 		mci->mci_lastuse = curtime();
60453751Seric 
60510054Seric 		if (p == NULL)
60610131Seric 		{
60710148Seric 			extern char MsgBuf[];		/* err.c */
60810148Seric 
60921065Seric 			/* if the remote end closed early, fake an error */
61021065Seric 			if (errno == 0)
61121065Seric # ifdef ECONNRESET
61221065Seric 				errno = ECONNRESET;
61356795Seric # else /* ECONNRESET */
61421065Seric 				errno = EPIPE;
61556795Seric # endif /* ECONNRESET */
61621065Seric 
61757379Seric 			mci->mci_errno = errno;
61857642Seric 			mci->mci_exitstat = EX_TEMPFAIL;
61958151Seric 			message("451 %s: reply: read error from %s",
62057642Seric 				e->e_id == NULL ? "NOQUEUE" : e->e_id,
62157203Seric 				mci->mci_host);
62210420Seric 			/* if debugging, pause so we can see state */
62310420Seric 			if (tTd(18, 100))
62410420Seric 				pause();
62510148Seric # ifdef LOG
62658020Seric 			if (LogLevel > 1)
62757203Seric 				syslog(LOG_INFO, "%s", &MsgBuf[4]);
62856795Seric # endif /* LOG */
62954967Seric 			mci->mci_state = MCIS_ERROR;
63053751Seric 			smtpquit(m, mci, e);
63110054Seric 			return (-1);
63210131Seric 		}
63358957Seric 		fixcrlf(bufp, TRUE);
63410054Seric 
63559014Seric 		if (e->e_xfp != NULL && strchr("45", bufp[0]) != NULL)
63614900Seric 		{
63714900Seric 			/* serious error -- log the previous command */
63859014Seric 			if (SmtpMsgBuffer[0] != '\0')
63959014Seric 				fprintf(e->e_xfp, ">>> %s\n", SmtpMsgBuffer);
64059014Seric 			SmtpMsgBuffer[0] = '\0';
64114900Seric 
64214900Seric 			/* now log the message as from the other side */
64358957Seric 			fprintf(e->e_xfp, "<<< %s\n", bufp);
64414900Seric 		}
64514900Seric 
64614900Seric 		/* display the input for verbose mode */
64758120Seric 		if (Verbose)
64858957Seric 			nmessage("%s", bufp);
6497356Seric 
650*59285Seric 		/* process the line */
651*59285Seric 		if (pfunc != NULL && !firstline)
652*59285Seric 			(*pfunc)(bufp, m, mci, e);
653*59285Seric 
654*59285Seric 		firstline = FALSE;
655*59285Seric 
6567356Seric 		/* if continuation is required, we can go on */
65759014Seric 		if (bufp[3] == '-')
6584684Seric 			continue;
6597356Seric 
66059014Seric 		/* ignore improperly formated input */
66159014Seric 		if (!(isascii(bufp[0]) && isdigit(bufp[0])))
66259014Seric 			continue;
66359014Seric 
6647356Seric 		/* decode the reply code */
66559014Seric 		r = atoi(bufp);
6667356Seric 
6677356Seric 		/* extra semantics: 0xx codes are "informational" */
66859014Seric 		if (r >= 100)
66959014Seric 			break;
67059014Seric 	}
6717356Seric 
67259014Seric 	/*
67359014Seric 	**  Now look at SmtpReplyBuffer -- only care about the first
67459014Seric 	**  line of the response from here on out.
67559014Seric 	*/
67658061Seric 
67759014Seric 	/* save temporary failure messages for posterity */
67859014Seric 	if (SmtpReplyBuffer[0] == '4' && SmtpError[0] == '\0')
67959014Seric 		(void) strcpy(SmtpError, SmtpReplyBuffer);
6809391Seric 
68159014Seric 	/* reply code 421 is "Service Shutting Down" */
68259014Seric 	if (r == SMTPCLOSING && mci->mci_state != MCIS_SSD)
68359014Seric 	{
68459014Seric 		/* send the quit protocol */
68559014Seric 		mci->mci_state = MCIS_SSD;
68659014Seric 		smtpquit(m, mci, e);
6874684Seric 	}
68859014Seric 
68959014Seric 	return (r);
6904684Seric }
6914796Seric /*
6924865Seric **  SMTPMESSAGE -- send message to server
6934796Seric **
6944796Seric **	Parameters:
6954796Seric **		f -- format
69610175Seric **		m -- the mailer to control formatting.
6974796Seric **		a, b, c -- parameters
6984796Seric **
6994796Seric **	Returns:
7004796Seric **		none.
7014796Seric **
7024796Seric **	Side Effects:
70353740Seric **		writes message to mci->mci_out.
7044796Seric */
7054796Seric 
7064865Seric /*VARARGS1*/
70757642Seric #ifdef __STDC__
70857642Seric smtpmessage(char *f, MAILER *m, MCI *mci, ...)
70957642Seric #else
71057642Seric smtpmessage(f, m, mci, va_alist)
7114796Seric 	char *f;
71210175Seric 	MAILER *m;
71354967Seric 	MCI *mci;
71457642Seric 	va_dcl
71557642Seric #endif
7164796Seric {
71756852Seric 	VA_LOCAL_DECL
71856852Seric 
71957135Seric 	VA_START(mci);
72056852Seric 	(void) vsprintf(SmtpMsgBuffer, f, ap);
72156852Seric 	VA_END;
72258680Seric 
72358120Seric 	if (tTd(18, 1) || Verbose)
72458151Seric 		nmessage(">>> %s", SmtpMsgBuffer);
72553740Seric 	if (mci->mci_out != NULL)
72658680Seric 	{
72753740Seric 		fprintf(mci->mci_out, "%s%s", SmtpMsgBuffer,
72854967Seric 			m == NULL ? "\r\n" : m->m_eol);
72958680Seric 	}
73059149Seric 	else if (tTd(18, 1))
73158725Seric 	{
73259149Seric 		printf("smtpmessage: NULL mci_out\n");
73358725Seric 	}
7344796Seric }
7355182Seric 
73656795Seric # endif /* SMTP */
737