14796Seric # include <ctype.h>
24684Seric # include <sysexits.h>
34865Seric # include "sendmail.h"
44684Seric 
55182Seric # ifndef SMTP
6*10175Seric SCCSID(@(#)usersmtp.c	3.36		01/06/83	(no SMTP));
75182Seric # else SMTP
84684Seric 
9*10175Seric SCCSID(@(#)usersmtp.c	3.36		01/06/83);
105182Seric 
119391Seric 
129391Seric 
134684Seric /*
149391Seric **  USERSMTP -- run SMTP protocol from the user end.
159391Seric **
169391Seric **	This protocol is described in RFC821.
179391Seric */
189391Seric 
199391Seric #define REPLYTYPE(r)	((r) / 100)		/* first digit of reply code */
209391Seric #define REPLYCLASS(r)	(((r) / 10) % 10)	/* second digit of reply code */
219391Seric #define SMTPCLOSING	421			/* "Service Shutting Down" */
229391Seric 
2310054Seric char	SmtpReplyBuffer[MAXLINE];	/* buffer for replies */
2410054Seric FILE	*SmtpOut;			/* output file */
2510054Seric FILE	*SmtpIn;			/* input file */
2610054Seric int	SmtpPid;			/* pid of mailer */
2710054Seric bool	SmtpClosing;			/* set on a forced close */
289391Seric /*
294865Seric **  SMTPINIT -- initialize SMTP.
304684Seric **
314865Seric **	Opens the connection and sends the initial protocol.
324684Seric **
334684Seric **	Parameters:
344865Seric **		m -- mailer to create connection to.
354865Seric **		pvp -- pointer to parameter vector to pass to
364865Seric **			the mailer.
374684Seric **
384684Seric **	Returns:
394865Seric **		appropriate exit status -- EX_OK on success.
404684Seric **
414684Seric **	Side Effects:
424865Seric **		creates connection and sends initial protocol.
434684Seric */
444684Seric 
45*10175Seric smtpinit(m, pvp)
464865Seric 	struct mailer *m;
474865Seric 	char **pvp;
484684Seric {
494865Seric 	register int r;
504865Seric 	char buf[MAXNAME];
517685Seric 	extern char *canonname();
524684Seric 
534865Seric 	/*
544865Seric 	**  Open the connection to the mailer.
554865Seric 	*/
564684Seric 
576051Seric 	SmtpIn = SmtpOut = NULL;
5810148Seric 	SmtpClosing = FALSE;
59*10175Seric 	SmtpPid = openmailer(m, pvp, (ADDRESS *) NULL, TRUE, &SmtpOut, &SmtpIn);
606051Seric 	if (SmtpPid < 0)
616051Seric 	{
626051Seric # ifdef DEBUG
637677Seric 		if (tTd(18, 1))
649391Seric 			printf("smtpinit: cannot open %s: stat %d errno %d\n",
659391Seric 			   pvp[0], ExitStat, errno);
666051Seric # endif DEBUG
676051Seric 		return (ExitStat);
686051Seric 	}
694796Seric 
704865Seric 	/*
714865Seric 	**  Get the greeting message.
724865Seric 	**	This should appear spontaneously.
734865Seric 	*/
744797Seric 
75*10175Seric 	r = reply(m);
768005Seric 	if (r < 0 || REPLYTYPE(r) != 2)
774865Seric 		return (EX_TEMPFAIL);
784684Seric 
794865Seric 	/*
804976Seric 	**  Send the HELO command.
817963Seric 	**	My mother taught me to always introduce myself.
824976Seric 	*/
834976Seric 
84*10175Seric 	smtpmessage("HELO %s", m, HostName);
85*10175Seric 	r = reply(m);
868005Seric 	if (r < 0)
878005Seric 		return (EX_TEMPFAIL);
888005Seric 	else if (REPLYTYPE(r) == 5)
894976Seric 		return (EX_UNAVAILABLE);
907963Seric 	else if (REPLYTYPE(r) != 2)
914976Seric 		return (EX_TEMPFAIL);
924976Seric 
934976Seric 	/*
949315Seric 	**  If this is expected to be another sendmail, send some internal
959315Seric 	**  commands.
969315Seric 	*/
979315Seric 
989315Seric 	if (bitset(M_INTERNAL, m->m_flags))
999315Seric 	{
1009315Seric 		/* tell it to be verbose */
101*10175Seric 		smtpmessage("VERB", m);
102*10175Seric 		r = reply(m);
1039315Seric 		if (r < 0)
1049315Seric 			return (EX_TEMPFAIL);
1059315Seric 
1069315Seric 		/* tell it we will be sending one transaction only */
107*10175Seric 		smtpmessage("ONEX", m);
108*10175Seric 		r = reply(m);
1099315Seric 		if (r < 0)
1109315Seric 			return (EX_TEMPFAIL);
1119315Seric 	}
1129315Seric 
1139315Seric 	/*
1144865Seric 	**  Send the MAIL command.
1154865Seric 	**	Designates the sender.
1164865Seric 	*/
1174796Seric 
1186980Seric 	expand("$g", buf, &buf[sizeof buf - 1], CurEnv);
1198436Seric 	if (CurEnv->e_from.q_mailer == LocalMailer ||
120*10175Seric 	    !bitset(M_FROMPATH, m->m_flags))
1218436Seric 	{
122*10175Seric 		smtpmessage("MAIL From:<%s>", m, canonname(buf, 1));
1238436Seric 	}
1248436Seric 	else
1258436Seric 	{
126*10175Seric 		smtpmessage("MAIL From:<@%s%c%s>", m, HostName,
127*10175Seric 			buf[0] == '@' ? ',' : ':', canonname(buf, 1));
1288436Seric 	}
129*10175Seric 	r = reply(m);
1308005Seric 	if (r < 0 || REPLYTYPE(r) == 4)
1314865Seric 		return (EX_TEMPFAIL);
1327963Seric 	else if (r == 250)
1337963Seric 		return (EX_OK);
1347963Seric 	else if (r == 552)
1357963Seric 		return (EX_UNAVAILABLE);
1367964Seric 	return (EX_PROTOCOL);
1374684Seric }
1384684Seric /*
1394976Seric **  SMTPRCPT -- designate recipient.
1404797Seric **
1414797Seric **	Parameters:
1424865Seric **		to -- address of recipient.
143*10175Seric **		m -- the mailer we are sending to.
1444797Seric **
1454797Seric **	Returns:
1464865Seric **		exit status corresponding to recipient status.
1474797Seric **
1484797Seric **	Side Effects:
1494865Seric **		Sends the mail via SMTP.
1504797Seric */
1514797Seric 
152*10175Seric smtprcpt(to, m)
1534865Seric 	ADDRESS *to;
154*10175Seric 	register MAILER *m;
1554797Seric {
1564797Seric 	register int r;
1577685Seric 	extern char *canonname();
1584797Seric 
159*10175Seric 	smtpmessage("RCPT To:<%s>", m, canonname(to->q_user, 2));
1604865Seric 
161*10175Seric 	r = reply(m);
1628005Seric 	if (r < 0 || REPLYTYPE(r) == 4)
1634865Seric 		return (EX_TEMPFAIL);
1647963Seric 	else if (REPLYTYPE(r) == 2)
1657963Seric 		return (EX_OK);
1667964Seric 	else if (r == 550 || r == 551 || r == 553)
1677964Seric 		return (EX_NOUSER);
1687964Seric 	else if (r == 552 || r == 554)
1697964Seric 		return (EX_UNAVAILABLE);
1707964Seric 	return (EX_PROTOCOL);
1714797Seric }
1724797Seric /*
173*10175Seric **  SMTPDATA -- send the data and clean up the transaction.
1744684Seric **
1754684Seric **	Parameters:
1764865Seric **		m -- mailer being sent to.
1776980Seric **		e -- the envelope for this message.
1784684Seric **
1794684Seric **	Returns:
1804976Seric **		exit status corresponding to DATA command.
1814684Seric **
1824684Seric **	Side Effects:
1834865Seric **		none.
1844684Seric */
1854684Seric 
186*10175Seric smtpdata(m, e)
1874865Seric 	struct mailer *m;
1886980Seric 	register ENVELOPE *e;
1894684Seric {
1904684Seric 	register int r;
1914684Seric 
1924797Seric 	/*
1934797Seric 	**  Send the data.
194*10175Seric 	**	First send the command and check that it is ok.
195*10175Seric 	**	Then send the data.
196*10175Seric 	**	Follow it up with a dot to terminate.
197*10175Seric 	**	Finally get the results of the transaction.
1984797Seric 	*/
1994797Seric 
200*10175Seric 	/* send the command and check ok to proceed */
201*10175Seric 	smtpmessage("DATA", m);
202*10175Seric 	r = reply(m);
2038005Seric 	if (r < 0 || REPLYTYPE(r) == 4)
2044797Seric 		return (EX_TEMPFAIL);
2057963Seric 	else if (r == 554)
2067963Seric 		return (EX_UNAVAILABLE);
2077963Seric 	else if (r != 354)
2087964Seric 		return (EX_PROTOCOL);
209*10175Seric 
210*10175Seric 	/* now output the actual message */
211*10175Seric 	(*e->e_puthdr)(SmtpOut, m, CurEnv);
212*10175Seric 	putline("\n", SmtpOut, m);
213*10175Seric 	(*e->e_putbody)(SmtpOut, m, CurEnv);
214*10175Seric 
215*10175Seric 	/* terminate the message */
216*10175Seric 	fprintf(SmtpOut, ".%s\n", bitset(M_CRLF, m->m_flags) ? "\r" : "");
217*10175Seric 	nmessage(Arpa_Info, ">>> .");
218*10175Seric 
219*10175Seric 	/* check for the results of the transaction */
220*10175Seric 	r = reply(m);
2218005Seric 	if (r < 0 || REPLYTYPE(r) == 4)
2224797Seric 		return (EX_TEMPFAIL);
2237963Seric 	else if (r == 250)
2247963Seric 		return (EX_OK);
2257963Seric 	else if (r == 552 || r == 554)
2267963Seric 		return (EX_UNAVAILABLE);
2277964Seric 	return (EX_PROTOCOL);
2284684Seric }
2294684Seric /*
2304865Seric **  SMTPQUIT -- close the SMTP connection.
2314865Seric **
2324865Seric **	Parameters:
2334865Seric **		name -- name of mailer we are quitting.
2344865Seric **
2354865Seric **	Returns:
2364865Seric **		none.
2374865Seric **
2384865Seric **	Side Effects:
2394865Seric **		sends the final protocol and closes the connection.
2404865Seric */
2414865Seric 
242*10175Seric smtpquit(name, m)
2434865Seric 	char *name;
244*10175Seric 	register MAILER *m;
2454865Seric {
2469391Seric 	int i;
2474865Seric 
24810148Seric 	/* if the connection is already closed, don't bother */
24910148Seric 	if (SmtpIn == NULL)
25010148Seric 		return;
25110148Seric 
25210148Seric 	/* send the quit message if not a forced quit */
25310148Seric 	if (!SmtpClosing)
2549391Seric 	{
255*10175Seric 		smtpmessage("QUIT", m);
256*10175Seric 		(void) reply(m);
25710159Seric 		if (SmtpClosing)
25810159Seric 			return;
2599391Seric 	}
2609391Seric 
26110148Seric 	/* now actually close the connection */
2627229Seric 	(void) fclose(SmtpIn);
2637229Seric 	(void) fclose(SmtpOut);
26410148Seric 	SmtpIn = SmtpOut = NULL;
26510148Seric 
26610148Seric 	/* and pick up the zombie */
2677229Seric 	i = endmailer(SmtpPid, name);
2689391Seric 	if (i != EX_OK)
2699391Seric 		syserr("smtpquit %s: stat %d", name, i);
2704865Seric }
2714865Seric /*
2724684Seric **  REPLY -- read arpanet reply
2734684Seric **
2744684Seric **	Parameters:
275*10175Seric **		m -- the mailer we are reading the reply from.
2764684Seric **
2774684Seric **	Returns:
2784684Seric **		reply code it reads.
2794684Seric **
2804684Seric **	Side Effects:
2814684Seric **		flushes the mail file.
2824684Seric */
2834684Seric 
284*10175Seric reply(m)
2854684Seric {
2864865Seric 	(void) fflush(SmtpOut);
2874684Seric 
2887677Seric 	if (tTd(18, 1))
2894796Seric 		printf("reply\n");
2904796Seric 
2917356Seric 	/*
2927356Seric 	**  Read the input line, being careful not to hang.
2937356Seric 	*/
2947356Seric 
2954684Seric 	for (;;)
2964684Seric 	{
2974684Seric 		register int r;
2987356Seric 		register char *p;
2994684Seric 
3007685Seric 		/* actually do the read */
3019547Seric 		if (CurEnv->e_xfp != NULL)
3029547Seric 			(void) fflush(CurEnv->e_xfp);	/* for debugging */
3037356Seric 
30410054Seric 		/* if we are in the process of closing just give the code */
30510054Seric 		if (SmtpClosing)
30610054Seric 			return (SMTPCLOSING);
30710054Seric 
30810054Seric 		/* get the line from the other side */
30910054Seric 		p = sfgets(SmtpReplyBuffer, sizeof SmtpReplyBuffer, SmtpIn);
31010054Seric 		if (p == NULL)
31110131Seric 		{
31210148Seric 			extern char MsgBuf[];		/* err.c */
31310148Seric 			extern char Arpa_TSyserr[];	/* conf.c */
31410148Seric 
31510148Seric 			message(Arpa_TSyserr, "reply: read error");
31610148Seric # ifdef LOG
31710148Seric 			syslog(LOG_ERR, "%s", &MsgBuf[4]);
31810148Seric # endif LOG
31910148Seric 			SmtpClosing = TRUE;
320*10175Seric 			smtpquit("reply error", m);
32110054Seric 			return (-1);
32210131Seric 		}
32310054Seric 		fixcrlf(SmtpReplyBuffer, TRUE);
32410054Seric 
3257356Seric 		/* log the input in the transcript for future error returns */
3267229Seric 		if (Verbose && !HoldErrs)
3279391Seric 			nmessage(Arpa_Info, "%s", SmtpReplyBuffer);
32810131Seric 		else if (CurEnv->e_xfp != NULL)
3299547Seric 			fprintf(CurEnv->e_xfp, "%s\n", SmtpReplyBuffer);
3307356Seric 
3317356Seric 		/* if continuation is required, we can go on */
3329391Seric 		if (SmtpReplyBuffer[3] == '-' || !isdigit(SmtpReplyBuffer[0]))
3334684Seric 			continue;
3347356Seric 
3357356Seric 		/* decode the reply code */
3369391Seric 		r = atoi(SmtpReplyBuffer);
3377356Seric 
3387356Seric 		/* extra semantics: 0xx codes are "informational" */
3394684Seric 		if (r < 100)
3404684Seric 			continue;
3417356Seric 
3429391Seric 		/* reply code 421 is "Service Shutting Down" */
34310054Seric 		if (r == SMTPCLOSING)
3449391Seric 		{
34510054Seric 			/* send the quit protocol */
346*10175Seric 			smtpquit("SMTP Shutdown", m);
3479391Seric 			SmtpClosing = TRUE;
3489391Seric 		}
3499391Seric 
3504684Seric 		return (r);
3514684Seric 	}
3524684Seric }
3534796Seric /*
3544865Seric **  SMTPMESSAGE -- send message to server
3554796Seric **
3564796Seric **	Parameters:
3574796Seric **		f -- format
358*10175Seric **		m -- the mailer to control formatting.
3594796Seric **		a, b, c -- parameters
3604796Seric **
3614796Seric **	Returns:
3624796Seric **		none.
3634796Seric **
3644796Seric **	Side Effects:
3654865Seric **		writes message to SmtpOut.
3664796Seric */
3674796Seric 
3684865Seric /*VARARGS1*/
369*10175Seric smtpmessage(f, m, a, b, c)
3704796Seric 	char *f;
371*10175Seric 	MAILER *m;
3724796Seric {
3734796Seric 	char buf[100];
3744796Seric 
3754865Seric 	(void) sprintf(buf, f, a, b, c);
3767677Seric 	if (tTd(18, 1) || (Verbose && !HoldErrs))
3778237Seric 		nmessage(Arpa_Info, ">>> %s", buf);
37810131Seric 	else if (CurEnv->e_xfp != NULL)
3799547Seric 		fprintf(CurEnv->e_xfp, ">>> %s\n", buf);
3809391Seric 	if (!SmtpClosing)
381*10175Seric 		fprintf(SmtpOut, "%s%s\n", bitset(M_CRLF, m->m_flags) ? "\r" : "", buf);
3824796Seric }
3835182Seric 
3845182Seric # endif SMTP
385