14796Seric # include <ctype.h>
24684Seric # include <sysexits.h>
3*21065Seric # include <errno.h>
44865Seric # include "sendmail.h"
54684Seric 
65182Seric # ifndef SMTP
7*21065Seric SCCSID(@(#)usersmtp.c	4.12		05/24/85	(no SMTP));
85182Seric # else SMTP
94684Seric 
10*21065Seric SCCSID(@(#)usersmtp.c	4.12		05/24/85);
115182Seric 
129391Seric 
139391Seric 
144684Seric /*
159391Seric **  USERSMTP -- run SMTP protocol from the user end.
169391Seric **
179391Seric **	This protocol is described in RFC821.
189391Seric */
199391Seric 
209391Seric #define REPLYTYPE(r)	((r) / 100)		/* first digit of reply code */
219391Seric #define REPLYCLASS(r)	(((r) / 10) % 10)	/* second digit of reply code */
229391Seric #define SMTPCLOSING	421			/* "Service Shutting Down" */
239391Seric 
2414900Seric char	SmtpMsgBuffer[MAXLINE];		/* buffer for commands */
2510054Seric char	SmtpReplyBuffer[MAXLINE];	/* buffer for replies */
26*21065Seric char	SmtpError[MAXLINE] = "";	/* save failure error messages */
2710054Seric FILE	*SmtpOut;			/* output file */
2810054Seric FILE	*SmtpIn;			/* input file */
2910054Seric int	SmtpPid;			/* pid of mailer */
3011159Seric 
3111159Seric /* following represents the state of the SMTP connection */
3211159Seric int	SmtpState;			/* connection state, see below */
3311159Seric 
3411159Seric #define SMTP_CLOSED	0		/* connection is closed */
3511159Seric #define SMTP_OPEN	1		/* connection is open for business */
3611159Seric #define SMTP_SSD	2		/* service shutting down */
379391Seric /*
384865Seric **  SMTPINIT -- initialize SMTP.
394684Seric **
404865Seric **	Opens the connection and sends the initial protocol.
414684Seric **
424684Seric **	Parameters:
434865Seric **		m -- mailer to create connection to.
444865Seric **		pvp -- pointer to parameter vector to pass to
454865Seric **			the mailer.
464684Seric **
474684Seric **	Returns:
484865Seric **		appropriate exit status -- EX_OK on success.
4914913Seric **		If not EX_OK, it should close the connection.
504684Seric **
514684Seric **	Side Effects:
524865Seric **		creates connection and sends initial protocol.
534684Seric */
544684Seric 
5514886Seric jmp_buf	CtxGreeting;
5614886Seric 
5710175Seric smtpinit(m, pvp)
584865Seric 	struct mailer *m;
594865Seric 	char **pvp;
604684Seric {
614865Seric 	register int r;
6214886Seric 	EVENT *gte;
634865Seric 	char buf[MAXNAME];
6414886Seric 	extern greettimeout();
654684Seric 
664865Seric 	/*
674865Seric 	**  Open the connection to the mailer.
684865Seric 	*/
694684Seric 
7011159Seric #ifdef DEBUG
7111159Seric 	if (SmtpState == SMTP_OPEN)
7211159Seric 		syserr("smtpinit: already open");
7311159Seric #endif DEBUG
7411159Seric 
756051Seric 	SmtpIn = SmtpOut = NULL;
7611159Seric 	SmtpState = SMTP_CLOSED;
77*21065Seric 	SmtpError[0] = '\0';
7810175Seric 	SmtpPid = openmailer(m, pvp, (ADDRESS *) NULL, TRUE, &SmtpOut, &SmtpIn);
796051Seric 	if (SmtpPid < 0)
806051Seric 	{
816051Seric # ifdef DEBUG
827677Seric 		if (tTd(18, 1))
839391Seric 			printf("smtpinit: cannot open %s: stat %d errno %d\n",
849391Seric 			   pvp[0], ExitStat, errno);
856051Seric # endif DEBUG
8615139Seric 		if (CurEnv->e_xfp != NULL)
8715139Seric 		{
88*21065Seric 			register char *p;
8915139Seric 			extern char *errstring();
90*21065Seric 			extern char *statstring();
9115139Seric 
92*21065Seric 			if (errno == 0)
93*21065Seric 			{
94*21065Seric 				p = statstring(ExitStat);
95*21065Seric 				fprintf(CurEnv->e_xfp,
96*21065Seric 					"%.3s %s.%s... %s\n",
97*21065Seric 					p, pvp[1], m->m_name, p);
98*21065Seric 			}
99*21065Seric 			else
100*21065Seric 			{
101*21065Seric 				fprintf(CurEnv->e_xfp,
102*21065Seric 					"421 %s.%s... Deferred: %s\n",
103*21065Seric 					pvp[1], m->m_name, errstring(errno));
104*21065Seric 			}
10515139Seric 		}
1066051Seric 		return (ExitStat);
1076051Seric 	}
10811159Seric 	SmtpState = SMTP_OPEN;
1094796Seric 
1104865Seric 	/*
1114865Seric 	**  Get the greeting message.
11214913Seric 	**	This should appear spontaneously.  Give it five minutes to
11314886Seric 	**	happen.
1144865Seric 	*/
1154797Seric 
11614886Seric 	if (setjmp(CtxGreeting) != 0)
11714913Seric 		goto tempfail;
11816141Seric 	gte = setevent((time_t) 300, greettimeout, 0);
11910175Seric 	r = reply(m);
12014886Seric 	clrevent(gte);
1218005Seric 	if (r < 0 || REPLYTYPE(r) != 2)
12214913Seric 		goto tempfail;
1234684Seric 
1244865Seric 	/*
1254976Seric 	**  Send the HELO command.
1267963Seric 	**	My mother taught me to always introduce myself.
1274976Seric 	*/
1284976Seric 
12910175Seric 	smtpmessage("HELO %s", m, HostName);
13010175Seric 	r = reply(m);
1318005Seric 	if (r < 0)
13214913Seric 		goto tempfail;
1338005Seric 	else if (REPLYTYPE(r) == 5)
13414913Seric 		goto unavailable;
1357963Seric 	else if (REPLYTYPE(r) != 2)
13614913Seric 		goto tempfail;
1374976Seric 
1384976Seric 	/*
1399315Seric 	**  If this is expected to be another sendmail, send some internal
1409315Seric 	**  commands.
1419315Seric 	*/
1429315Seric 
14310688Seric 	if (bitnset(M_INTERNAL, m->m_flags))
1449315Seric 	{
1459315Seric 		/* tell it to be verbose */
14610175Seric 		smtpmessage("VERB", m);
14710175Seric 		r = reply(m);
1489315Seric 		if (r < 0)
14914913Seric 			goto tempfail;
1509315Seric 
1519315Seric 		/* tell it we will be sending one transaction only */
15210175Seric 		smtpmessage("ONEX", m);
15310175Seric 		r = reply(m);
1549315Seric 		if (r < 0)
15514913Seric 			goto tempfail;
1569315Seric 	}
1579315Seric 
1589315Seric 	/*
1594865Seric 	**  Send the MAIL command.
1604865Seric 	**	Designates the sender.
1614865Seric 	*/
1624796Seric 
16316156Seric 	expand("\001g", buf, &buf[sizeof buf - 1], CurEnv);
1648436Seric 	if (CurEnv->e_from.q_mailer == LocalMailer ||
16510688Seric 	    !bitnset(M_FROMPATH, m->m_flags))
1668436Seric 	{
16710308Seric 		smtpmessage("MAIL From:<%s>", m, buf);
1688436Seric 	}
1698436Seric 	else
1708436Seric 	{
17110175Seric 		smtpmessage("MAIL From:<@%s%c%s>", m, HostName,
17210308Seric 			buf[0] == '@' ? ',' : ':', buf);
1738436Seric 	}
17410175Seric 	r = reply(m);
1758005Seric 	if (r < 0 || REPLYTYPE(r) == 4)
17614913Seric 		goto tempfail;
1777963Seric 	else if (r == 250)
1787963Seric 		return (EX_OK);
1797963Seric 	else if (r == 552)
18014913Seric 		goto unavailable;
18114913Seric 
18214913Seric 	/* protocol error -- close up */
18314913Seric 	smtpquit(m);
1847964Seric 	return (EX_PROTOCOL);
18514913Seric 
18614913Seric 	/* signal a temporary failure */
18714913Seric   tempfail:
18814913Seric 	smtpquit(m);
18916891Seric 	CurEnv->e_flags &= ~EF_FATALERRS;
19014913Seric 	return (EX_TEMPFAIL);
19114913Seric 
19214913Seric 	/* signal service unavailable */
19314913Seric   unavailable:
19414913Seric 	smtpquit(m);
19514913Seric 	return (EX_UNAVAILABLE);
1964684Seric }
19714886Seric 
19814886Seric 
19914886Seric static
20014886Seric greettimeout()
20114886Seric {
20214886Seric 	/* timeout reading the greeting message */
20314886Seric 	longjmp(CtxGreeting, 1);
20414886Seric }
2054684Seric /*
2064976Seric **  SMTPRCPT -- designate recipient.
2074797Seric **
2084797Seric **	Parameters:
2094865Seric **		to -- address of recipient.
21010175Seric **		m -- the mailer we are sending to.
2114797Seric **
2124797Seric **	Returns:
2134865Seric **		exit status corresponding to recipient status.
2144797Seric **
2154797Seric **	Side Effects:
2164865Seric **		Sends the mail via SMTP.
2174797Seric */
2184797Seric 
21910175Seric smtprcpt(to, m)
2204865Seric 	ADDRESS *to;
22110175Seric 	register MAILER *m;
2224797Seric {
2234797Seric 	register int r;
22410308Seric 	extern char *remotename();
2254797Seric 
22610308Seric 	smtpmessage("RCPT To:<%s>", m, remotename(to->q_user, m, FALSE, TRUE));
2274865Seric 
22810175Seric 	r = reply(m);
2298005Seric 	if (r < 0 || REPLYTYPE(r) == 4)
2304865Seric 		return (EX_TEMPFAIL);
2317963Seric 	else if (REPLYTYPE(r) == 2)
2327963Seric 		return (EX_OK);
2337964Seric 	else if (r == 550 || r == 551 || r == 553)
2347964Seric 		return (EX_NOUSER);
2357964Seric 	else if (r == 552 || r == 554)
2367964Seric 		return (EX_UNAVAILABLE);
2377964Seric 	return (EX_PROTOCOL);
2384797Seric }
2394797Seric /*
24010175Seric **  SMTPDATA -- send the data and clean up the transaction.
2414684Seric **
2424684Seric **	Parameters:
2434865Seric **		m -- mailer being sent to.
2446980Seric **		e -- the envelope for this message.
2454684Seric **
2464684Seric **	Returns:
2474976Seric **		exit status corresponding to DATA command.
2484684Seric **
2494684Seric **	Side Effects:
2504865Seric **		none.
2514684Seric */
2524684Seric 
25310175Seric smtpdata(m, e)
2544865Seric 	struct mailer *m;
2556980Seric 	register ENVELOPE *e;
2564684Seric {
2574684Seric 	register int r;
2584684Seric 
2594797Seric 	/*
2604797Seric 	**  Send the data.
26110175Seric 	**	First send the command and check that it is ok.
26210175Seric 	**	Then send the data.
26310175Seric 	**	Follow it up with a dot to terminate.
26410175Seric 	**	Finally get the results of the transaction.
2654797Seric 	*/
2664797Seric 
26710175Seric 	/* send the command and check ok to proceed */
26810175Seric 	smtpmessage("DATA", m);
26910175Seric 	r = reply(m);
2708005Seric 	if (r < 0 || REPLYTYPE(r) == 4)
2714797Seric 		return (EX_TEMPFAIL);
2727963Seric 	else if (r == 554)
2737963Seric 		return (EX_UNAVAILABLE);
2747963Seric 	else if (r != 354)
2757964Seric 		return (EX_PROTOCOL);
27610175Seric 
27710175Seric 	/* now output the actual message */
27810175Seric 	(*e->e_puthdr)(SmtpOut, m, CurEnv);
27910175Seric 	putline("\n", SmtpOut, m);
28010175Seric 	(*e->e_putbody)(SmtpOut, m, CurEnv);
28110175Seric 
28210175Seric 	/* terminate the message */
28310328Seric 	fprintf(SmtpOut, ".%s", m->m_eol);
28410215Seric 	if (Verbose && !HoldErrs)
28510215Seric 		nmessage(Arpa_Info, ">>> .");
28610175Seric 
28710175Seric 	/* check for the results of the transaction */
28810175Seric 	r = reply(m);
2898005Seric 	if (r < 0 || REPLYTYPE(r) == 4)
2904797Seric 		return (EX_TEMPFAIL);
2917963Seric 	else if (r == 250)
2927963Seric 		return (EX_OK);
2937963Seric 	else if (r == 552 || r == 554)
2947963Seric 		return (EX_UNAVAILABLE);
2957964Seric 	return (EX_PROTOCOL);
2964684Seric }
2974684Seric /*
2984865Seric **  SMTPQUIT -- close the SMTP connection.
2994865Seric **
3004865Seric **	Parameters:
30115535Seric **		m -- a pointer to the mailer.
3024865Seric **
3034865Seric **	Returns:
3044865Seric **		none.
3054865Seric **
3064865Seric **	Side Effects:
3074865Seric **		sends the final protocol and closes the connection.
3084865Seric */
3094865Seric 
31015535Seric smtpquit(m)
31110175Seric 	register MAILER *m;
3124865Seric {
3139391Seric 	int i;
3144865Seric 
31510148Seric 	/* if the connection is already closed, don't bother */
31610148Seric 	if (SmtpIn == NULL)
31710148Seric 		return;
31810148Seric 
31910148Seric 	/* send the quit message if not a forced quit */
32011159Seric 	if (SmtpState == SMTP_OPEN || SmtpState == SMTP_SSD)
3219391Seric 	{
32210175Seric 		smtpmessage("QUIT", m);
32310175Seric 		(void) reply(m);
32411159Seric 		if (SmtpState == SMTP_CLOSED)
32510159Seric 			return;
3269391Seric 	}
3279391Seric 
32810148Seric 	/* now actually close the connection */
3297229Seric 	(void) fclose(SmtpIn);
3307229Seric 	(void) fclose(SmtpOut);
33110148Seric 	SmtpIn = SmtpOut = NULL;
33211159Seric 	SmtpState = SMTP_CLOSED;
33310148Seric 
33410148Seric 	/* and pick up the zombie */
33515535Seric 	i = endmailer(SmtpPid, m->m_argv[0]);
3369391Seric 	if (i != EX_OK)
33715535Seric 		syserr("smtpquit %s: stat %d", m->m_argv[0], i);
3384865Seric }
3394865Seric /*
3404684Seric **  REPLY -- read arpanet reply
3414684Seric **
3424684Seric **	Parameters:
34310175Seric **		m -- the mailer we are reading the reply from.
3444684Seric **
3454684Seric **	Returns:
3464684Seric **		reply code it reads.
3474684Seric **
3484684Seric **	Side Effects:
3494684Seric **		flushes the mail file.
3504684Seric */
3514684Seric 
35210175Seric reply(m)
35310688Seric 	MAILER *m;
3544684Seric {
3554865Seric 	(void) fflush(SmtpOut);
3564684Seric 
3577677Seric 	if (tTd(18, 1))
3584796Seric 		printf("reply\n");
3594796Seric 
3607356Seric 	/*
3617356Seric 	**  Read the input line, being careful not to hang.
3627356Seric 	*/
3637356Seric 
3644684Seric 	for (;;)
3654684Seric 	{
3664684Seric 		register int r;
3677356Seric 		register char *p;
3684684Seric 
3697685Seric 		/* actually do the read */
3709547Seric 		if (CurEnv->e_xfp != NULL)
3719547Seric 			(void) fflush(CurEnv->e_xfp);	/* for debugging */
3727356Seric 
37310054Seric 		/* if we are in the process of closing just give the code */
37411159Seric 		if (SmtpState == SMTP_CLOSED)
37510054Seric 			return (SMTPCLOSING);
37610054Seric 
37710054Seric 		/* get the line from the other side */
37810054Seric 		p = sfgets(SmtpReplyBuffer, sizeof SmtpReplyBuffer, SmtpIn);
37910054Seric 		if (p == NULL)
38010131Seric 		{
38110148Seric 			extern char MsgBuf[];		/* err.c */
38210148Seric 			extern char Arpa_TSyserr[];	/* conf.c */
38310148Seric 
384*21065Seric 			/* if the remote end closed early, fake an error */
385*21065Seric 			if (errno == 0)
386*21065Seric # ifdef ECONNRESET
387*21065Seric 				errno = ECONNRESET;
388*21065Seric # else ECONNRESET
389*21065Seric 				errno = EPIPE;
390*21065Seric # endif ECONNRESET
391*21065Seric 
39210148Seric 			message(Arpa_TSyserr, "reply: read error");
39310420Seric # ifdef DEBUG
39410420Seric 			/* if debugging, pause so we can see state */
39510420Seric 			if (tTd(18, 100))
39610420Seric 				pause();
39710420Seric # endif DEBUG
39810148Seric # ifdef LOG
39918573Smiriam 			syslog(LOG_MAIL, "%s", &MsgBuf[4]);
40010148Seric # endif LOG
40111159Seric 			SmtpState = SMTP_CLOSED;
40215535Seric 			smtpquit(m);
40310054Seric 			return (-1);
40410131Seric 		}
40510054Seric 		fixcrlf(SmtpReplyBuffer, TRUE);
40610054Seric 
40714900Seric 		if (CurEnv->e_xfp != NULL && index("45", SmtpReplyBuffer[0]) != NULL)
40814900Seric 		{
40914900Seric 			/* serious error -- log the previous command */
41014900Seric 			if (SmtpMsgBuffer[0] != '\0')
41114900Seric 				fprintf(CurEnv->e_xfp, ">>> %s\n", SmtpMsgBuffer);
41214900Seric 			SmtpMsgBuffer[0] = '\0';
41314900Seric 
41414900Seric 			/* now log the message as from the other side */
41514900Seric 			fprintf(CurEnv->e_xfp, "<<< %s\n", SmtpReplyBuffer);
41614900Seric 		}
41714900Seric 
41814900Seric 		/* display the input for verbose mode */
4197229Seric 		if (Verbose && !HoldErrs)
4209391Seric 			nmessage(Arpa_Info, "%s", SmtpReplyBuffer);
4217356Seric 
4227356Seric 		/* if continuation is required, we can go on */
4239391Seric 		if (SmtpReplyBuffer[3] == '-' || !isdigit(SmtpReplyBuffer[0]))
4244684Seric 			continue;
4257356Seric 
4267356Seric 		/* decode the reply code */
4279391Seric 		r = atoi(SmtpReplyBuffer);
4287356Seric 
4297356Seric 		/* extra semantics: 0xx codes are "informational" */
4304684Seric 		if (r < 100)
4314684Seric 			continue;
4327356Seric 
4339391Seric 		/* reply code 421 is "Service Shutting Down" */
43411159Seric 		if (r == SMTPCLOSING && SmtpState != SMTP_SSD)
4359391Seric 		{
43610054Seric 			/* send the quit protocol */
43711159Seric 			SmtpState = SMTP_SSD;
43815535Seric 			smtpquit(m);
4399391Seric 		}
4409391Seric 
441*21065Seric 		/* save temporary failure messages for posterity */
442*21065Seric 		if (SmtpReplyBuffer[0] == '4' && SmtpError[0] == '\0')
443*21065Seric 			(void) strcpy(SmtpError, &SmtpReplyBuffer[4]);
444*21065Seric 
4454684Seric 		return (r);
4464684Seric 	}
4474684Seric }
4484796Seric /*
4494865Seric **  SMTPMESSAGE -- send message to server
4504796Seric **
4514796Seric **	Parameters:
4524796Seric **		f -- format
45310175Seric **		m -- the mailer to control formatting.
4544796Seric **		a, b, c -- parameters
4554796Seric **
4564796Seric **	Returns:
4574796Seric **		none.
4584796Seric **
4594796Seric **	Side Effects:
4604865Seric **		writes message to SmtpOut.
4614796Seric */
4624796Seric 
4634865Seric /*VARARGS1*/
46410175Seric smtpmessage(f, m, a, b, c)
4654796Seric 	char *f;
46610175Seric 	MAILER *m;
4674796Seric {
46814900Seric 	(void) sprintf(SmtpMsgBuffer, f, a, b, c);
4697677Seric 	if (tTd(18, 1) || (Verbose && !HoldErrs))
47014900Seric 		nmessage(Arpa_Info, ">>> %s", SmtpMsgBuffer);
47111159Seric 	if (SmtpOut != NULL)
47214900Seric 		fprintf(SmtpOut, "%s%s", SmtpMsgBuffer, m->m_eol);
4734796Seric }
4745182Seric 
4755182Seric # endif SMTP
476