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*53740Seric static char sccsid[] = "@(#)usersmtp.c	5.20.1.2 (Berkeley) 05/29/92 (with SMTP)";
1433731Sbostic #else
15*53740Seric static char sccsid[] = "@(#)usersmtp.c	5.20.1.2 (Berkeley) 05/29/92 (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 */
389391Seric /*
394865Seric **  SMTPINIT -- initialize SMTP.
404684Seric **
414865Seric **	Opens the connection and sends the initial protocol.
424684Seric **
434684Seric **	Parameters:
444865Seric **		m -- mailer to create connection to.
454865Seric **		pvp -- pointer to parameter vector to pass to
464865Seric **			the mailer.
474684Seric **
484684Seric **	Returns:
494865Seric **		appropriate exit status -- EX_OK on success.
5014913Seric **		If not EX_OK, it should close the connection.
514684Seric **
524684Seric **	Side Effects:
534865Seric **		creates connection and sends initial protocol.
544684Seric */
554684Seric 
5614886Seric jmp_buf	CtxGreeting;
5714886Seric 
58*53740Seric smtpinit(m, mci, pvp)
594865Seric 	struct mailer *m;
60*53740Seric 	MCONINFO *mci;
614865Seric 	char **pvp;
624684Seric {
634865Seric 	register int r;
6414886Seric 	EVENT *gte;
6552107Seric 	register STAB *st;
664865Seric 	char buf[MAXNAME];
6746928Sbostic 	static int greettimeout();
6852107Seric 	extern STAB *stab();
69*53740Seric 	extern MCONINFO *openmailer();
704684Seric 
714865Seric 	/*
724865Seric 	**  Open the connection to the mailer.
734865Seric 	*/
744684Seric 
75*53740Seric 	if (mci->mci_state == MCIS_OPEN)
7611159Seric 		syserr("smtpinit: already open");
7711159Seric 
78*53740Seric 	mci->mci_state = MCIS_CLOSED;
7921065Seric 	SmtpError[0] = '\0';
8024944Seric 	SmtpPhase = "user open";
8136584Sbostic 	setproctitle("%s %s: %s", CurEnv->e_id, pvp[1], SmtpPhase);
8252676Seric 	mci = openmailer(m, pvp, (ADDRESS *) NULL, TRUE);
836051Seric 	if (SmtpPid < 0)
846051Seric 	{
857677Seric 		if (tTd(18, 1))
869391Seric 			printf("smtpinit: cannot open %s: stat %d errno %d\n",
879391Seric 			   pvp[0], ExitStat, errno);
8815139Seric 		if (CurEnv->e_xfp != NULL)
8915139Seric 		{
9021065Seric 			register char *p;
9115139Seric 			extern char *errstring();
9221065Seric 			extern char *statstring();
9315139Seric 
9421065Seric 			if (errno == 0)
9521065Seric 			{
9621065Seric 				p = statstring(ExitStat);
9721065Seric 				fprintf(CurEnv->e_xfp,
9821065Seric 					"%.3s %s.%s... %s\n",
9921065Seric 					p, pvp[1], m->m_name, p);
10021065Seric 			}
10121065Seric 			else
10221065Seric 			{
10336584Sbostic 				r = errno;
10421065Seric 				fprintf(CurEnv->e_xfp,
10521065Seric 					"421 %s.%s... Deferred: %s\n",
10621065Seric 					pvp[1], m->m_name, errstring(errno));
10736584Sbostic 				errno = r;
10821065Seric 			}
10915139Seric 		}
1106051Seric 		return (ExitStat);
1116051Seric 	}
112*53740Seric 	mci->mci_state = MCIS_OPEN;
1134796Seric 
1144865Seric 	/*
1154865Seric 	**  Get the greeting message.
11614913Seric 	**	This should appear spontaneously.  Give it five minutes to
11714886Seric 	**	happen.
1184865Seric 	*/
1194797Seric 
12014886Seric 	if (setjmp(CtxGreeting) != 0)
12152104Seric 		goto tempfail1;
12216141Seric 	gte = setevent((time_t) 300, greettimeout, 0);
12324944Seric 	SmtpPhase = "greeting wait";
12436584Sbostic 	setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase);
125*53740Seric 	r = reply(m, mci);
12614886Seric 	clrevent(gte);
1278005Seric 	if (r < 0 || REPLYTYPE(r) != 2)
12852104Seric 		goto tempfail1;
1294684Seric 
1304865Seric 	/*
1314976Seric 	**  Send the HELO command.
1327963Seric 	**	My mother taught me to always introduce myself.
1334976Seric 	*/
1344976Seric 
13525050Seric 	smtpmessage("HELO %s", m, MyHostName);
13624944Seric 	SmtpPhase = "HELO wait";
13736584Sbostic 	setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase);
138*53740Seric 	r = reply(m, mci);
1398005Seric 	if (r < 0)
14052104Seric 		goto tempfail1;
1418005Seric 	else if (REPLYTYPE(r) == 5)
14214913Seric 		goto unavailable;
1437963Seric 	else if (REPLYTYPE(r) != 2)
14452104Seric 		goto tempfail1;
1454976Seric 
1464976Seric 	/*
1479315Seric 	**  If this is expected to be another sendmail, send some internal
1489315Seric 	**  commands.
1499315Seric 	*/
1509315Seric 
15110688Seric 	if (bitnset(M_INTERNAL, m->m_flags))
1529315Seric 	{
1539315Seric 		/* tell it to be verbose */
15410175Seric 		smtpmessage("VERB", m);
155*53740Seric 		r = reply(m, mci);
1569315Seric 		if (r < 0)
15752104Seric 			goto tempfail2;
1589315Seric 
1599315Seric 		/* tell it we will be sending one transaction only */
16010175Seric 		smtpmessage("ONEX", m);
161*53740Seric 		r = reply(m, mci);
1629315Seric 		if (r < 0)
16352104Seric 			goto tempfail2;
1649315Seric 	}
1659315Seric 
1669315Seric 	/*
1674865Seric 	**  Send the MAIL command.
1684865Seric 	**	Designates the sender.
1694865Seric 	*/
1704796Seric 
17151951Seric 	expand("\001<", buf, &buf[sizeof buf - 1], CurEnv);
1728436Seric 	if (CurEnv->e_from.q_mailer == LocalMailer ||
17310688Seric 	    !bitnset(M_FROMPATH, m->m_flags))
1748436Seric 	{
17510308Seric 		smtpmessage("MAIL From:<%s>", m, buf);
1768436Seric 	}
1778436Seric 	else
1788436Seric 	{
17925050Seric 		smtpmessage("MAIL From:<@%s%c%s>", m, MyHostName,
18010308Seric 			buf[0] == '@' ? ',' : ':', buf);
1818436Seric 	}
18224944Seric 	SmtpPhase = "MAIL wait";
18336584Sbostic 	setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase);
184*53740Seric 	r = reply(m, mci);
1858005Seric 	if (r < 0 || REPLYTYPE(r) == 4)
18652104Seric 		goto tempfail2;
1877963Seric 	else if (r == 250)
1887963Seric 		return (EX_OK);
1897963Seric 	else if (r == 552)
19014913Seric 		goto unavailable;
19114913Seric 
19214913Seric 	/* protocol error -- close up */
193*53740Seric 	smtpquit(m, mci);
1947964Seric 	return (EX_PROTOCOL);
19514913Seric 
19652104Seric   tempfail1:
19752107Seric 	/* log this as an error to avoid sure-to-be-void connections */
198*53740Seric 	mci->mci_exitstat = EX_TEMPFAIL;
199*53740Seric 	mci->mci_errno = errno;
20052104Seric 
20152104Seric   tempfail2:
20252107Seric 	/* signal a temporary failure */
203*53740Seric 	smtpquit(m, mci);
20414913Seric 	return (EX_TEMPFAIL);
20514913Seric 
20652107Seric   unavailable:
20714913Seric 	/* signal service unavailable */
208*53740Seric 	smtpquit(m, mci);
20914913Seric 	return (EX_UNAVAILABLE);
2104684Seric }
21114886Seric 
21214886Seric 
21314886Seric static
21414886Seric greettimeout()
21514886Seric {
21614886Seric 	/* timeout reading the greeting message */
21714886Seric 	longjmp(CtxGreeting, 1);
21814886Seric }
2194684Seric /*
2204976Seric **  SMTPRCPT -- designate recipient.
2214797Seric **
2224797Seric **	Parameters:
2234865Seric **		to -- address of recipient.
22410175Seric **		m -- the mailer we are sending to.
2254797Seric **
2264797Seric **	Returns:
2274865Seric **		exit status corresponding to recipient status.
2284797Seric **
2294797Seric **	Side Effects:
2304865Seric **		Sends the mail via SMTP.
2314797Seric */
2324797Seric 
233*53740Seric smtprcpt(to, m, mci)
2344865Seric 	ADDRESS *to;
23510175Seric 	register MAILER *m;
236*53740Seric 	MCONINFO *mci;
2374797Seric {
2384797Seric 	register int r;
23910308Seric 	extern char *remotename();
2404797Seric 
24152647Seric 	smtpmessage("RCPT To:<%s>", m, to->q_user);
2424865Seric 
24324944Seric 	SmtpPhase = "RCPT wait";
24436584Sbostic 	setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase);
245*53740Seric 	r = reply(m, mci);
2468005Seric 	if (r < 0 || REPLYTYPE(r) == 4)
2474865Seric 		return (EX_TEMPFAIL);
2487963Seric 	else if (REPLYTYPE(r) == 2)
2497963Seric 		return (EX_OK);
2507964Seric 	else if (r == 550 || r == 551 || r == 553)
2517964Seric 		return (EX_NOUSER);
2527964Seric 	else if (r == 552 || r == 554)
2537964Seric 		return (EX_UNAVAILABLE);
2547964Seric 	return (EX_PROTOCOL);
2554797Seric }
2564797Seric /*
25710175Seric **  SMTPDATA -- send the data and clean up the transaction.
2584684Seric **
2594684Seric **	Parameters:
2604865Seric **		m -- mailer being sent to.
2616980Seric **		e -- the envelope for this message.
2624684Seric **
2634684Seric **	Returns:
2644976Seric **		exit status corresponding to DATA command.
2654684Seric **
2664684Seric **	Side Effects:
2674865Seric **		none.
2684684Seric */
2694684Seric 
270*53740Seric smtpdata(m, mci, e)
2714865Seric 	struct mailer *m;
272*53740Seric 	register MCONINFO *mci;
2736980Seric 	register ENVELOPE *e;
2744684Seric {
2754684Seric 	register int r;
2764684Seric 
2774797Seric 	/*
2784797Seric 	**  Send the data.
27910175Seric 	**	First send the command and check that it is ok.
28010175Seric 	**	Then send the data.
28110175Seric 	**	Follow it up with a dot to terminate.
28210175Seric 	**	Finally get the results of the transaction.
2834797Seric 	*/
2844797Seric 
28510175Seric 	/* send the command and check ok to proceed */
28610175Seric 	smtpmessage("DATA", m);
28724944Seric 	SmtpPhase = "DATA wait";
28836584Sbostic 	setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase);
289*53740Seric 	r = reply(m, mci);
2908005Seric 	if (r < 0 || REPLYTYPE(r) == 4)
2914797Seric 		return (EX_TEMPFAIL);
2927963Seric 	else if (r == 554)
2937963Seric 		return (EX_UNAVAILABLE);
2947963Seric 	else if (r != 354)
2957964Seric 		return (EX_PROTOCOL);
29610175Seric 
29710175Seric 	/* now output the actual message */
298*53740Seric 	(*e->e_puthdr)(mci->mci_out, m, CurEnv);
299*53740Seric 	putline("\n", mci->mci_out, m);
300*53740Seric 	(*e->e_putbody)(mci->mci_out, m, CurEnv);
30110175Seric 
30210175Seric 	/* terminate the message */
303*53740Seric 	fprintf(mci->mci_out, ".%s", m->m_eol);
30410215Seric 	if (Verbose && !HoldErrs)
30510215Seric 		nmessage(Arpa_Info, ">>> .");
30610175Seric 
30710175Seric 	/* check for the results of the transaction */
30824944Seric 	SmtpPhase = "result wait";
30936584Sbostic 	setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase);
310*53740Seric 	r = reply(m, mci);
3118005Seric 	if (r < 0 || REPLYTYPE(r) == 4)
3124797Seric 		return (EX_TEMPFAIL);
3137963Seric 	else if (r == 250)
3147963Seric 		return (EX_OK);
3157963Seric 	else if (r == 552 || r == 554)
3167963Seric 		return (EX_UNAVAILABLE);
3177964Seric 	return (EX_PROTOCOL);
3184684Seric }
3194684Seric /*
3204865Seric **  SMTPQUIT -- close the SMTP connection.
3214865Seric **
3224865Seric **	Parameters:
32315535Seric **		m -- a pointer to the mailer.
3244865Seric **
3254865Seric **	Returns:
3264865Seric **		none.
3274865Seric **
3284865Seric **	Side Effects:
3294865Seric **		sends the final protocol and closes the connection.
3304865Seric */
3314865Seric 
332*53740Seric smtpquit(mci, m)
33352676Seric 	register MCONINFO *mci;
334*53740Seric 	register MAILER *m;
3354865Seric {
3369391Seric 	int i;
3374865Seric 
33810148Seric 	/* if the connection is already closed, don't bother */
339*53740Seric 	if (mci->mci_state == MCIS_CLOSED)
34010148Seric 		return;
34110148Seric 
34210148Seric 	/* send the quit message if not a forced quit */
343*53740Seric 	if (mci->mci_state == MCIS_OPEN || mci->mci_state == MCIS_SSD)
3449391Seric 	{
34510175Seric 		smtpmessage("QUIT", m);
346*53740Seric 		(void) reply(m, mci);
347*53740Seric 		if (mci->mci_state == MCIS_CLOSED)
34810159Seric 			return;
3499391Seric 	}
3509391Seric 
35152676Seric 	/* now actually close the connection and pick up the zombie */
35252676Seric 	i = endmailer(mci, m->m_argv[0]);
3539391Seric 	if (i != EX_OK)
35415535Seric 		syserr("smtpquit %s: stat %d", m->m_argv[0], i);
3554865Seric }
3564865Seric /*
3574684Seric **  REPLY -- read arpanet reply
3584684Seric **
3594684Seric **	Parameters:
36010175Seric **		m -- the mailer we are reading the reply from.
3614684Seric **
3624684Seric **	Returns:
3634684Seric **		reply code it reads.
3644684Seric **
3654684Seric **	Side Effects:
3664684Seric **		flushes the mail file.
3674684Seric */
3684684Seric 
369*53740Seric reply(mci, m)
370*53740Seric 	MCONINFO *mci;
37110688Seric 	MAILER *m;
3724684Seric {
373*53740Seric 	(void) fflush(mci->mci_out);
3744684Seric 
3757677Seric 	if (tTd(18, 1))
3764796Seric 		printf("reply\n");
3774796Seric 
3787356Seric 	/*
3797356Seric 	**  Read the input line, being careful not to hang.
3807356Seric 	*/
3817356Seric 
3824684Seric 	for (;;)
3834684Seric 	{
3844684Seric 		register int r;
3857356Seric 		register char *p;
3864684Seric 
3877685Seric 		/* actually do the read */
3889547Seric 		if (CurEnv->e_xfp != NULL)
3899547Seric 			(void) fflush(CurEnv->e_xfp);	/* for debugging */
3907356Seric 
39110054Seric 		/* if we are in the process of closing just give the code */
392*53740Seric 		if (mci->mci_state == MCIS_CLOSED)
39310054Seric 			return (SMTPCLOSING);
39410054Seric 
39510054Seric 		/* get the line from the other side */
396*53740Seric 		p = sfgets(SmtpReplyBuffer, sizeof SmtpReplyBuffer, mci->mci_in);
39710054Seric 		if (p == NULL)
39810131Seric 		{
39910148Seric 			extern char MsgBuf[];		/* err.c */
40010148Seric 			extern char Arpa_TSyserr[];	/* conf.c */
40110148Seric 
40221065Seric 			/* if the remote end closed early, fake an error */
40321065Seric 			if (errno == 0)
40421065Seric # ifdef ECONNRESET
40521065Seric 				errno = ECONNRESET;
40621065Seric # else ECONNRESET
40721065Seric 				errno = EPIPE;
40821065Seric # endif ECONNRESET
40921065Seric 
41010148Seric 			message(Arpa_TSyserr, "reply: read error");
41110420Seric 			/* if debugging, pause so we can see state */
41210420Seric 			if (tTd(18, 100))
41310420Seric 				pause();
41410148Seric # ifdef LOG
41536234Skarels 			syslog(LOG_INFO, "%s", &MsgBuf[4]);
41610148Seric # endif LOG
417*53740Seric 			mci->mci_state = MCIS_CLOSED;
41815535Seric 			smtpquit(m);
41910054Seric 			return (-1);
42010131Seric 		}
42110054Seric 		fixcrlf(SmtpReplyBuffer, TRUE);
42210054Seric 
42314900Seric 		if (CurEnv->e_xfp != NULL && index("45", SmtpReplyBuffer[0]) != NULL)
42414900Seric 		{
42514900Seric 			/* serious error -- log the previous command */
42614900Seric 			if (SmtpMsgBuffer[0] != '\0')
42714900Seric 				fprintf(CurEnv->e_xfp, ">>> %s\n", SmtpMsgBuffer);
42814900Seric 			SmtpMsgBuffer[0] = '\0';
42914900Seric 
43014900Seric 			/* now log the message as from the other side */
43114900Seric 			fprintf(CurEnv->e_xfp, "<<< %s\n", SmtpReplyBuffer);
43214900Seric 		}
43314900Seric 
43414900Seric 		/* display the input for verbose mode */
4357229Seric 		if (Verbose && !HoldErrs)
4369391Seric 			nmessage(Arpa_Info, "%s", SmtpReplyBuffer);
4377356Seric 
4387356Seric 		/* if continuation is required, we can go on */
4399391Seric 		if (SmtpReplyBuffer[3] == '-' || !isdigit(SmtpReplyBuffer[0]))
4404684Seric 			continue;
4417356Seric 
4427356Seric 		/* decode the reply code */
4439391Seric 		r = atoi(SmtpReplyBuffer);
4447356Seric 
4457356Seric 		/* extra semantics: 0xx codes are "informational" */
4464684Seric 		if (r < 100)
4474684Seric 			continue;
4487356Seric 
4499391Seric 		/* reply code 421 is "Service Shutting Down" */
450*53740Seric 		if (r == SMTPCLOSING && mci->mci_state != MCIS_SSD)
4519391Seric 		{
45210054Seric 			/* send the quit protocol */
453*53740Seric 			mci->mci_state = MCIS_SSD;
45415535Seric 			smtpquit(m);
4559391Seric 		}
4569391Seric 
45721065Seric 		/* save temporary failure messages for posterity */
45821065Seric 		if (SmtpReplyBuffer[0] == '4' && SmtpError[0] == '\0')
45921065Seric 			(void) strcpy(SmtpError, &SmtpReplyBuffer[4]);
46021065Seric 
4614684Seric 		return (r);
4624684Seric 	}
4634684Seric }
4644796Seric /*
4654865Seric **  SMTPMESSAGE -- send message to server
4664796Seric **
4674796Seric **	Parameters:
4684796Seric **		f -- format
46910175Seric **		m -- the mailer to control formatting.
4704796Seric **		a, b, c -- parameters
4714796Seric **
4724796Seric **	Returns:
4734796Seric **		none.
4744796Seric **
4754796Seric **	Side Effects:
476*53740Seric **		writes message to mci->mci_out.
4774796Seric */
4784796Seric 
4794865Seric /*VARARGS1*/
480*53740Seric smtpmessage(f, m, mci, a, b, c)
4814796Seric 	char *f;
48210175Seric 	MAILER *m;
483*53740Seric 	MCONINFO *mci;
4844796Seric {
48514900Seric 	(void) sprintf(SmtpMsgBuffer, f, a, b, c);
4867677Seric 	if (tTd(18, 1) || (Verbose && !HoldErrs))
48714900Seric 		nmessage(Arpa_Info, ">>> %s", SmtpMsgBuffer);
488*53740Seric 	if (mci->mci_out != NULL)
489*53740Seric 		fprintf(mci->mci_out, "%s%s", SmtpMsgBuffer,
49040997Sbostic 			m == 0 ? "\r\n" : m->m_eol);
4914796Seric }
4925182Seric 
4935182Seric # endif SMTP
494