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*46928Sbostic static char sccsid[] = "@(#)usersmtp.c	5.16 (Berkeley) 03/02/91 (with SMTP)";
1433731Sbostic #else
15*46928Sbostic static char sccsid[] = "@(#)usersmtp.c	5.16 (Berkeley) 03/02/91 (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 FILE	*SmtpOut;			/* output file */
3810054Seric FILE	*SmtpIn;			/* input file */
3910054Seric int	SmtpPid;			/* pid of mailer */
4011159Seric 
4111159Seric /* following represents the state of the SMTP connection */
4211159Seric int	SmtpState;			/* connection state, see below */
4311159Seric 
4411159Seric #define SMTP_CLOSED	0		/* connection is closed */
4511159Seric #define SMTP_OPEN	1		/* connection is open for business */
4611159Seric #define SMTP_SSD	2		/* service shutting down */
479391Seric /*
484865Seric **  SMTPINIT -- initialize SMTP.
494684Seric **
504865Seric **	Opens the connection and sends the initial protocol.
514684Seric **
524684Seric **	Parameters:
534865Seric **		m -- mailer to create connection to.
544865Seric **		pvp -- pointer to parameter vector to pass to
554865Seric **			the mailer.
564684Seric **
574684Seric **	Returns:
584865Seric **		appropriate exit status -- EX_OK on success.
5914913Seric **		If not EX_OK, it should close the connection.
604684Seric **
614684Seric **	Side Effects:
624865Seric **		creates connection and sends initial protocol.
634684Seric */
644684Seric 
6514886Seric jmp_buf	CtxGreeting;
6614886Seric 
6710175Seric smtpinit(m, pvp)
684865Seric 	struct mailer *m;
694865Seric 	char **pvp;
704684Seric {
714865Seric 	register int r;
7214886Seric 	EVENT *gte;
734865Seric 	char buf[MAXNAME];
74*46928Sbostic 	static int greettimeout();
754684Seric 
764865Seric 	/*
774865Seric 	**  Open the connection to the mailer.
784865Seric 	*/
794684Seric 
8011159Seric 	if (SmtpState == SMTP_OPEN)
8111159Seric 		syserr("smtpinit: already open");
8211159Seric 
836051Seric 	SmtpIn = SmtpOut = NULL;
8411159Seric 	SmtpState = SMTP_CLOSED;
8521065Seric 	SmtpError[0] = '\0';
8624944Seric 	SmtpPhase = "user open";
8736584Sbostic 	setproctitle("%s %s: %s", CurEnv->e_id, pvp[1], SmtpPhase);
8810175Seric 	SmtpPid = openmailer(m, pvp, (ADDRESS *) NULL, TRUE, &SmtpOut, &SmtpIn);
896051Seric 	if (SmtpPid < 0)
906051Seric 	{
917677Seric 		if (tTd(18, 1))
929391Seric 			printf("smtpinit: cannot open %s: stat %d errno %d\n",
939391Seric 			   pvp[0], ExitStat, errno);
9415139Seric 		if (CurEnv->e_xfp != NULL)
9515139Seric 		{
9621065Seric 			register char *p;
9715139Seric 			extern char *errstring();
9821065Seric 			extern char *statstring();
9915139Seric 
10021065Seric 			if (errno == 0)
10121065Seric 			{
10221065Seric 				p = statstring(ExitStat);
10321065Seric 				fprintf(CurEnv->e_xfp,
10421065Seric 					"%.3s %s.%s... %s\n",
10521065Seric 					p, pvp[1], m->m_name, p);
10621065Seric 			}
10721065Seric 			else
10821065Seric 			{
10936584Sbostic 				r = errno;
11021065Seric 				fprintf(CurEnv->e_xfp,
11121065Seric 					"421 %s.%s... Deferred: %s\n",
11221065Seric 					pvp[1], m->m_name, errstring(errno));
11336584Sbostic 				errno = r;
11421065Seric 			}
11515139Seric 		}
1166051Seric 		return (ExitStat);
1176051Seric 	}
11811159Seric 	SmtpState = SMTP_OPEN;
1194796Seric 
1204865Seric 	/*
1214865Seric 	**  Get the greeting message.
12214913Seric 	**	This should appear spontaneously.  Give it five minutes to
12314886Seric 	**	happen.
1244865Seric 	*/
1254797Seric 
12614886Seric 	if (setjmp(CtxGreeting) != 0)
12714913Seric 		goto tempfail;
12816141Seric 	gte = setevent((time_t) 300, greettimeout, 0);
12924944Seric 	SmtpPhase = "greeting wait";
13036584Sbostic 	setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase);
13110175Seric 	r = reply(m);
13214886Seric 	clrevent(gte);
1338005Seric 	if (r < 0 || REPLYTYPE(r) != 2)
13414913Seric 		goto tempfail;
1354684Seric 
1364865Seric 	/*
1374976Seric 	**  Send the HELO command.
1387963Seric 	**	My mother taught me to always introduce myself.
1394976Seric 	*/
1404976Seric 
14125050Seric 	smtpmessage("HELO %s", m, MyHostName);
14224944Seric 	SmtpPhase = "HELO wait";
14336584Sbostic 	setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase);
14410175Seric 	r = reply(m);
1458005Seric 	if (r < 0)
14614913Seric 		goto tempfail;
1478005Seric 	else if (REPLYTYPE(r) == 5)
14814913Seric 		goto unavailable;
1497963Seric 	else if (REPLYTYPE(r) != 2)
15014913Seric 		goto tempfail;
1514976Seric 
1524976Seric 	/*
1539315Seric 	**  If this is expected to be another sendmail, send some internal
1549315Seric 	**  commands.
1559315Seric 	*/
1569315Seric 
15710688Seric 	if (bitnset(M_INTERNAL, m->m_flags))
1589315Seric 	{
1599315Seric 		/* tell it to be verbose */
16010175Seric 		smtpmessage("VERB", m);
16110175Seric 		r = reply(m);
1629315Seric 		if (r < 0)
16314913Seric 			goto tempfail;
1649315Seric 
1659315Seric 		/* tell it we will be sending one transaction only */
16610175Seric 		smtpmessage("ONEX", m);
16710175Seric 		r = reply(m);
1689315Seric 		if (r < 0)
16914913Seric 			goto tempfail;
1709315Seric 	}
1719315Seric 
1729315Seric 	/*
1734865Seric 	**  Send the MAIL command.
1744865Seric 	**	Designates the sender.
1754865Seric 	*/
1764796Seric 
17716156Seric 	expand("\001g", buf, &buf[sizeof buf - 1], CurEnv);
1788436Seric 	if (CurEnv->e_from.q_mailer == LocalMailer ||
17910688Seric 	    !bitnset(M_FROMPATH, m->m_flags))
1808436Seric 	{
18110308Seric 		smtpmessage("MAIL From:<%s>", m, buf);
1828436Seric 	}
1838436Seric 	else
1848436Seric 	{
18525050Seric 		smtpmessage("MAIL From:<@%s%c%s>", m, MyHostName,
18610308Seric 			buf[0] == '@' ? ',' : ':', buf);
1878436Seric 	}
18824944Seric 	SmtpPhase = "MAIL wait";
18936584Sbostic 	setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase);
19010175Seric 	r = reply(m);
1918005Seric 	if (r < 0 || REPLYTYPE(r) == 4)
19214913Seric 		goto tempfail;
1937963Seric 	else if (r == 250)
1947963Seric 		return (EX_OK);
1957963Seric 	else if (r == 552)
19614913Seric 		goto unavailable;
19714913Seric 
19814913Seric 	/* protocol error -- close up */
19914913Seric 	smtpquit(m);
2007964Seric 	return (EX_PROTOCOL);
20114913Seric 
20214913Seric 	/* signal a temporary failure */
20314913Seric   tempfail:
20414913Seric 	smtpquit(m);
20514913Seric 	return (EX_TEMPFAIL);
20614913Seric 
20714913Seric 	/* signal service unavailable */
20814913Seric   unavailable:
20914913Seric 	smtpquit(m);
21014913Seric 	return (EX_UNAVAILABLE);
2114684Seric }
21214886Seric 
21314886Seric 
21414886Seric static
21514886Seric greettimeout()
21614886Seric {
21714886Seric 	/* timeout reading the greeting message */
21814886Seric 	longjmp(CtxGreeting, 1);
21914886Seric }
2204684Seric /*
2214976Seric **  SMTPRCPT -- designate recipient.
2224797Seric **
2234797Seric **	Parameters:
2244865Seric **		to -- address of recipient.
22510175Seric **		m -- the mailer we are sending to.
2264797Seric **
2274797Seric **	Returns:
2284865Seric **		exit status corresponding to recipient status.
2294797Seric **
2304797Seric **	Side Effects:
2314865Seric **		Sends the mail via SMTP.
2324797Seric */
2334797Seric 
23410175Seric smtprcpt(to, m)
2354865Seric 	ADDRESS *to;
23610175Seric 	register MAILER *m;
2374797Seric {
2384797Seric 	register int r;
23910308Seric 	extern char *remotename();
2404797Seric 
24110308Seric 	smtpmessage("RCPT To:<%s>", m, remotename(to->q_user, m, FALSE, TRUE));
2424865Seric 
24324944Seric 	SmtpPhase = "RCPT wait";
24436584Sbostic 	setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase);
24510175Seric 	r = reply(m);
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 
27010175Seric smtpdata(m, e)
2714865Seric 	struct mailer *m;
2726980Seric 	register ENVELOPE *e;
2734684Seric {
2744684Seric 	register int r;
2754684Seric 
2764797Seric 	/*
2774797Seric 	**  Send the data.
27810175Seric 	**	First send the command and check that it is ok.
27910175Seric 	**	Then send the data.
28010175Seric 	**	Follow it up with a dot to terminate.
28110175Seric 	**	Finally get the results of the transaction.
2824797Seric 	*/
2834797Seric 
28410175Seric 	/* send the command and check ok to proceed */
28510175Seric 	smtpmessage("DATA", m);
28624944Seric 	SmtpPhase = "DATA wait";
28736584Sbostic 	setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase);
28810175Seric 	r = reply(m);
2898005Seric 	if (r < 0 || REPLYTYPE(r) == 4)
2904797Seric 		return (EX_TEMPFAIL);
2917963Seric 	else if (r == 554)
2927963Seric 		return (EX_UNAVAILABLE);
2937963Seric 	else if (r != 354)
2947964Seric 		return (EX_PROTOCOL);
29510175Seric 
29610175Seric 	/* now output the actual message */
29710175Seric 	(*e->e_puthdr)(SmtpOut, m, CurEnv);
29810175Seric 	putline("\n", SmtpOut, m);
29910175Seric 	(*e->e_putbody)(SmtpOut, m, CurEnv);
30010175Seric 
30110175Seric 	/* terminate the message */
30210328Seric 	fprintf(SmtpOut, ".%s", m->m_eol);
30310215Seric 	if (Verbose && !HoldErrs)
30410215Seric 		nmessage(Arpa_Info, ">>> .");
30510175Seric 
30610175Seric 	/* check for the results of the transaction */
30724944Seric 	SmtpPhase = "result wait";
30836584Sbostic 	setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase);
30910175Seric 	r = reply(m);
3108005Seric 	if (r < 0 || REPLYTYPE(r) == 4)
3114797Seric 		return (EX_TEMPFAIL);
3127963Seric 	else if (r == 250)
3137963Seric 		return (EX_OK);
3147963Seric 	else if (r == 552 || r == 554)
3157963Seric 		return (EX_UNAVAILABLE);
3167964Seric 	return (EX_PROTOCOL);
3174684Seric }
3184684Seric /*
3194865Seric **  SMTPQUIT -- close the SMTP connection.
3204865Seric **
3214865Seric **	Parameters:
32215535Seric **		m -- a pointer to the mailer.
3234865Seric **
3244865Seric **	Returns:
3254865Seric **		none.
3264865Seric **
3274865Seric **	Side Effects:
3284865Seric **		sends the final protocol and closes the connection.
3294865Seric */
3304865Seric 
33115535Seric smtpquit(m)
33210175Seric 	register MAILER *m;
3334865Seric {
3349391Seric 	int i;
3354865Seric 
33610148Seric 	/* if the connection is already closed, don't bother */
33710148Seric 	if (SmtpIn == NULL)
33810148Seric 		return;
33910148Seric 
34010148Seric 	/* send the quit message if not a forced quit */
34111159Seric 	if (SmtpState == SMTP_OPEN || SmtpState == SMTP_SSD)
3429391Seric 	{
34310175Seric 		smtpmessage("QUIT", m);
34410175Seric 		(void) reply(m);
34511159Seric 		if (SmtpState == SMTP_CLOSED)
34610159Seric 			return;
3479391Seric 	}
3489391Seric 
34910148Seric 	/* now actually close the connection */
3507229Seric 	(void) fclose(SmtpIn);
3517229Seric 	(void) fclose(SmtpOut);
35210148Seric 	SmtpIn = SmtpOut = NULL;
35311159Seric 	SmtpState = SMTP_CLOSED;
35410148Seric 
35510148Seric 	/* and pick up the zombie */
35615535Seric 	i = endmailer(SmtpPid, m->m_argv[0]);
3579391Seric 	if (i != EX_OK)
35815535Seric 		syserr("smtpquit %s: stat %d", m->m_argv[0], i);
3594865Seric }
3604865Seric /*
3614684Seric **  REPLY -- read arpanet reply
3624684Seric **
3634684Seric **	Parameters:
36410175Seric **		m -- the mailer we are reading the reply from.
3654684Seric **
3664684Seric **	Returns:
3674684Seric **		reply code it reads.
3684684Seric **
3694684Seric **	Side Effects:
3704684Seric **		flushes the mail file.
3714684Seric */
3724684Seric 
37310175Seric reply(m)
37410688Seric 	MAILER *m;
3754684Seric {
3764865Seric 	(void) fflush(SmtpOut);
3774684Seric 
3787677Seric 	if (tTd(18, 1))
3794796Seric 		printf("reply\n");
3804796Seric 
3817356Seric 	/*
3827356Seric 	**  Read the input line, being careful not to hang.
3837356Seric 	*/
3847356Seric 
3854684Seric 	for (;;)
3864684Seric 	{
3874684Seric 		register int r;
3887356Seric 		register char *p;
3894684Seric 
3907685Seric 		/* actually do the read */
3919547Seric 		if (CurEnv->e_xfp != NULL)
3929547Seric 			(void) fflush(CurEnv->e_xfp);	/* for debugging */
3937356Seric 
39410054Seric 		/* if we are in the process of closing just give the code */
39511159Seric 		if (SmtpState == SMTP_CLOSED)
39610054Seric 			return (SMTPCLOSING);
39710054Seric 
39810054Seric 		/* get the line from the other side */
39910054Seric 		p = sfgets(SmtpReplyBuffer, sizeof SmtpReplyBuffer, SmtpIn);
40010054Seric 		if (p == NULL)
40110131Seric 		{
40210148Seric 			extern char MsgBuf[];		/* err.c */
40310148Seric 			extern char Arpa_TSyserr[];	/* conf.c */
40410148Seric 
40521065Seric 			/* if the remote end closed early, fake an error */
40621065Seric 			if (errno == 0)
40721065Seric # ifdef ECONNRESET
40821065Seric 				errno = ECONNRESET;
40921065Seric # else ECONNRESET
41021065Seric 				errno = EPIPE;
41121065Seric # endif ECONNRESET
41221065Seric 
41310148Seric 			message(Arpa_TSyserr, "reply: read error");
41410420Seric 			/* if debugging, pause so we can see state */
41510420Seric 			if (tTd(18, 100))
41610420Seric 				pause();
41710148Seric # ifdef LOG
41836234Skarels 			syslog(LOG_INFO, "%s", &MsgBuf[4]);
41910148Seric # endif LOG
42011159Seric 			SmtpState = SMTP_CLOSED;
42115535Seric 			smtpquit(m);
42210054Seric 			return (-1);
42310131Seric 		}
42410054Seric 		fixcrlf(SmtpReplyBuffer, TRUE);
42510054Seric 
42614900Seric 		if (CurEnv->e_xfp != NULL && index("45", SmtpReplyBuffer[0]) != NULL)
42714900Seric 		{
42814900Seric 			/* serious error -- log the previous command */
42914900Seric 			if (SmtpMsgBuffer[0] != '\0')
43014900Seric 				fprintf(CurEnv->e_xfp, ">>> %s\n", SmtpMsgBuffer);
43114900Seric 			SmtpMsgBuffer[0] = '\0';
43214900Seric 
43314900Seric 			/* now log the message as from the other side */
43414900Seric 			fprintf(CurEnv->e_xfp, "<<< %s\n", SmtpReplyBuffer);
43514900Seric 		}
43614900Seric 
43714900Seric 		/* display the input for verbose mode */
4387229Seric 		if (Verbose && !HoldErrs)
4399391Seric 			nmessage(Arpa_Info, "%s", SmtpReplyBuffer);
4407356Seric 
4417356Seric 		/* if continuation is required, we can go on */
4429391Seric 		if (SmtpReplyBuffer[3] == '-' || !isdigit(SmtpReplyBuffer[0]))
4434684Seric 			continue;
4447356Seric 
4457356Seric 		/* decode the reply code */
4469391Seric 		r = atoi(SmtpReplyBuffer);
4477356Seric 
4487356Seric 		/* extra semantics: 0xx codes are "informational" */
4494684Seric 		if (r < 100)
4504684Seric 			continue;
4517356Seric 
4529391Seric 		/* reply code 421 is "Service Shutting Down" */
45311159Seric 		if (r == SMTPCLOSING && SmtpState != SMTP_SSD)
4549391Seric 		{
45510054Seric 			/* send the quit protocol */
45611159Seric 			SmtpState = SMTP_SSD;
45715535Seric 			smtpquit(m);
4589391Seric 		}
4599391Seric 
46021065Seric 		/* save temporary failure messages for posterity */
46121065Seric 		if (SmtpReplyBuffer[0] == '4' && SmtpError[0] == '\0')
46221065Seric 			(void) strcpy(SmtpError, &SmtpReplyBuffer[4]);
46321065Seric 
4644684Seric 		return (r);
4654684Seric 	}
4664684Seric }
4674796Seric /*
4684865Seric **  SMTPMESSAGE -- send message to server
4694796Seric **
4704796Seric **	Parameters:
4714796Seric **		f -- format
47210175Seric **		m -- the mailer to control formatting.
4734796Seric **		a, b, c -- parameters
4744796Seric **
4754796Seric **	Returns:
4764796Seric **		none.
4774796Seric **
4784796Seric **	Side Effects:
4794865Seric **		writes message to SmtpOut.
4804796Seric */
4814796Seric 
4824865Seric /*VARARGS1*/
48310175Seric smtpmessage(f, m, a, b, c)
4844796Seric 	char *f;
48510175Seric 	MAILER *m;
4864796Seric {
48714900Seric 	(void) sprintf(SmtpMsgBuffer, f, a, b, c);
4887677Seric 	if (tTd(18, 1) || (Verbose && !HoldErrs))
48914900Seric 		nmessage(Arpa_Info, ">>> %s", SmtpMsgBuffer);
49011159Seric 	if (SmtpOut != NULL)
49140997Sbostic 		fprintf(SmtpOut, "%s%s", SmtpMsgBuffer,
49240997Sbostic 			m == 0 ? "\r\n" : m->m_eol);
4934796Seric }
4945182Seric 
4955182Seric # endif SMTP
496