14796Seric # include <ctype.h>
24684Seric # include <sysexits.h>
34865Seric # include "sendmail.h"
44684Seric 
55182Seric # ifndef SMTP
6*10054Seric SCCSID(@(#)usersmtp.c	3.31		01/01/83	(no SMTP));
75182Seric # else SMTP
84684Seric 
9*10054Seric SCCSID(@(#)usersmtp.c	3.31		01/01/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 
23*10054Seric char	SmtpReplyBuffer[MAXLINE];	/* buffer for replies */
24*10054Seric FILE	*SmtpOut;			/* output file */
25*10054Seric FILE	*SmtpIn;			/* input file */
26*10054Seric int	SmtpPid;			/* pid of mailer */
27*10054Seric 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.
374865Seric **		ctladdr -- controlling address for this mailer.
384684Seric **
394684Seric **	Returns:
404865Seric **		appropriate exit status -- EX_OK on success.
414684Seric **
424684Seric **	Side Effects:
434865Seric **		creates connection and sends initial protocol.
444684Seric */
454684Seric 
464865Seric smtpinit(m, pvp, ctladdr)
474865Seric 	struct mailer *m;
484865Seric 	char **pvp;
494865Seric 	ADDRESS *ctladdr;
504684Seric {
514865Seric 	register int r;
524865Seric 	char buf[MAXNAME];
537685Seric 	extern char *canonname();
544684Seric 
554865Seric 	/*
564865Seric 	**  Open the connection to the mailer.
574865Seric 	*/
584684Seric 
596051Seric 	SmtpIn = SmtpOut = NULL;
604865Seric 	SmtpPid = openmailer(m, pvp, ctladdr, TRUE, &SmtpOut, &SmtpIn);
616051Seric 	if (SmtpPid < 0)
626051Seric 	{
636051Seric # ifdef DEBUG
647677Seric 		if (tTd(18, 1))
659391Seric 			printf("smtpinit: cannot open %s: stat %d errno %d\n",
669391Seric 			   pvp[0], ExitStat, errno);
676051Seric # endif DEBUG
686051Seric 		return (ExitStat);
696051Seric 	}
704796Seric 
714865Seric 	/*
724865Seric 	**  Get the greeting message.
734865Seric 	**	This should appear spontaneously.
744865Seric 	*/
754797Seric 
764865Seric 	r = reply();
778005Seric 	if (r < 0 || REPLYTYPE(r) != 2)
784865Seric 		return (EX_TEMPFAIL);
794684Seric 
804865Seric 	/*
814976Seric 	**  Send the HELO command.
827963Seric 	**	My mother taught me to always introduce myself.
834976Seric 	*/
844976Seric 
854976Seric 	smtpmessage("HELO %s", HostName);
864976Seric 	r = reply();
878005Seric 	if (r < 0)
888005Seric 		return (EX_TEMPFAIL);
898005Seric 	else if (REPLYTYPE(r) == 5)
904976Seric 		return (EX_UNAVAILABLE);
917963Seric 	else if (REPLYTYPE(r) != 2)
924976Seric 		return (EX_TEMPFAIL);
934976Seric 
944976Seric 	/*
959315Seric 	**  If this is expected to be another sendmail, send some internal
969315Seric 	**  commands.
979315Seric 	*/
989315Seric 
999315Seric 	if (bitset(M_INTERNAL, m->m_flags))
1009315Seric 	{
1019315Seric 		/* tell it to be verbose */
1029315Seric 		smtpmessage("VERB");
1039315Seric 		r = reply();
1049315Seric 		if (r < 0)
1059315Seric 			return (EX_TEMPFAIL);
1069315Seric 
1079315Seric 		/* tell it we will be sending one transaction only */
1089315Seric 		smtpmessage("ONEX");
1099315Seric 		r = reply();
1109315Seric 		if (r < 0)
1119315Seric 			return (EX_TEMPFAIL);
1129315Seric 	}
1139315Seric 
1149315Seric 	/*
1154865Seric 	**  Send the MAIL command.
1164865Seric 	**	Designates the sender.
1174865Seric 	*/
1184796Seric 
1196980Seric 	expand("$g", buf, &buf[sizeof buf - 1], CurEnv);
1208436Seric 	if (CurEnv->e_from.q_mailer == LocalMailer ||
1218436Seric 	    !bitset(M_FULLSMTP, m->m_flags))
1228436Seric 	{
1238436Seric 		smtpmessage("MAIL From:<%s>", canonname(buf, 1));
1248436Seric 	}
1258436Seric 	else
1268436Seric 	{
1278436Seric 		smtpmessage("MAIL From:<@%s%c%s>", HostName,
1288436Seric 			    buf[0] == '@' ? ',' : ':', canonname(buf, 1));
1298436Seric 	}
1304865Seric 	r = reply();
1318005Seric 	if (r < 0 || REPLYTYPE(r) == 4)
1324865Seric 		return (EX_TEMPFAIL);
1337963Seric 	else if (r == 250)
1347963Seric 		return (EX_OK);
1357963Seric 	else if (r == 552)
1367963Seric 		return (EX_UNAVAILABLE);
1377964Seric 	return (EX_PROTOCOL);
1384684Seric }
1394684Seric /*
1404976Seric **  SMTPRCPT -- designate recipient.
1414797Seric **
1424797Seric **	Parameters:
1434865Seric **		to -- address of recipient.
1444797Seric **
1454797Seric **	Returns:
1464865Seric **		exit status corresponding to recipient status.
1474797Seric **
1484797Seric **	Side Effects:
1494865Seric **		Sends the mail via SMTP.
1504797Seric */
1514797Seric 
1524976Seric smtprcpt(to)
1534865Seric 	ADDRESS *to;
1544797Seric {
1554797Seric 	register int r;
1567685Seric 	extern char *canonname();
1574797Seric 
1588354Seric 	smtpmessage("RCPT To:<%s>", canonname(to->q_user, 2));
1594865Seric 
1604797Seric 	r = reply();
1618005Seric 	if (r < 0 || REPLYTYPE(r) == 4)
1624865Seric 		return (EX_TEMPFAIL);
1637963Seric 	else if (REPLYTYPE(r) == 2)
1647963Seric 		return (EX_OK);
1657964Seric 	else if (r == 550 || r == 551 || r == 553)
1667964Seric 		return (EX_NOUSER);
1677964Seric 	else if (r == 552 || r == 554)
1687964Seric 		return (EX_UNAVAILABLE);
1697964Seric 	return (EX_PROTOCOL);
1704797Seric }
1714797Seric /*
1724865Seric **  SMTPFINISH -- finish up sending all the SMTP protocol.
1734684Seric **
1744684Seric **	Parameters:
1754865Seric **		m -- mailer being sent to.
1766980Seric **		e -- the envelope for this message.
1774684Seric **
1784684Seric **	Returns:
1794976Seric **		exit status corresponding to DATA command.
1804684Seric **
1814684Seric **	Side Effects:
1824865Seric **		none.
1834684Seric */
1844684Seric 
1856980Seric smtpfinish(m, e)
1864865Seric 	struct mailer *m;
1876980Seric 	register ENVELOPE *e;
1884684Seric {
1894684Seric 	register int r;
1904684Seric 
1914797Seric 	/*
1924797Seric 	**  Send the data.
1934797Seric 	**	Dot hiding is done here.
1944797Seric 	*/
1954797Seric 
1964865Seric 	smtpmessage("DATA");
1974796Seric 	r = reply();
1988005Seric 	if (r < 0 || REPLYTYPE(r) == 4)
1994797Seric 		return (EX_TEMPFAIL);
2007963Seric 	else if (r == 554)
2017963Seric 		return (EX_UNAVAILABLE);
2027963Seric 	else if (r != 354)
2037964Seric 		return (EX_PROTOCOL);
2046980Seric 	(*e->e_puthdr)(SmtpOut, m, CurEnv);
2056980Seric 	fprintf(SmtpOut, "\n");
2069547Seric 	(*e->e_putbody)(SmtpOut, m, TRUE, CurEnv);
2074865Seric 	smtpmessage(".");
2084796Seric 	r = reply();
2098005Seric 	if (r < 0 || REPLYTYPE(r) == 4)
2104797Seric 		return (EX_TEMPFAIL);
2117963Seric 	else if (r == 250)
2127963Seric 		return (EX_OK);
2137963Seric 	else if (r == 552 || r == 554)
2147963Seric 		return (EX_UNAVAILABLE);
2157964Seric 	return (EX_PROTOCOL);
2164684Seric }
2174684Seric /*
2184865Seric **  SMTPQUIT -- close the SMTP connection.
2194865Seric **
2204865Seric **	Parameters:
2214865Seric **		name -- name of mailer we are quitting.
2224865Seric **
2234865Seric **	Returns:
2244865Seric **		none.
2254865Seric **
2264865Seric **	Side Effects:
2274865Seric **		sends the final protocol and closes the connection.
2284865Seric */
2294865Seric 
2309391Seric smtpquit(name)
2314865Seric 	char *name;
2324865Seric {
2339391Seric 	int i;
2344865Seric 
2359391Seric 	if (SmtpClosing)
2369391Seric 	{
2379391Seric 		SmtpClosing = FALSE;
2387229Seric 		return;
2399391Seric 	}
2409391Seric 
2417229Seric 	smtpmessage("QUIT");
2429391Seric 	i = reply();
2439391Seric 	if (i != 221)
2449391Seric 		syserr("smtpquit %s: reply %d", name, i);
2457229Seric 	(void) fclose(SmtpIn);
2467229Seric 	(void) fclose(SmtpOut);
2477229Seric 	i = endmailer(SmtpPid, name);
2489391Seric 	if (i != EX_OK)
2499391Seric 		syserr("smtpquit %s: stat %d", name, i);
2504865Seric }
2514865Seric /*
2524684Seric **  REPLY -- read arpanet reply
2534684Seric **
2544684Seric **	Parameters:
2554796Seric **		none.
2564684Seric **
2574684Seric **	Returns:
2584684Seric **		reply code it reads.
2594684Seric **
2604684Seric **	Side Effects:
2614684Seric **		flushes the mail file.
2624684Seric */
2634684Seric 
2644796Seric reply()
2654684Seric {
2664865Seric 	(void) fflush(SmtpOut);
2674684Seric 
2687677Seric 	if (tTd(18, 1))
2694796Seric 		printf("reply\n");
2704796Seric 
2717356Seric 	/*
2727356Seric 	**  Read the input line, being careful not to hang.
2737356Seric 	*/
2747356Seric 
2754684Seric 	for (;;)
2764684Seric 	{
2774684Seric 		register int r;
2787356Seric 		register char *p;
2794684Seric 
2807685Seric 		/* actually do the read */
2819547Seric 		if (CurEnv->e_xfp != NULL)
2829547Seric 			(void) fflush(CurEnv->e_xfp);	/* for debugging */
2837356Seric 
284*10054Seric 		/* if we are in the process of closing just give the code */
285*10054Seric 		if (SmtpClosing)
286*10054Seric 			return (SMTPCLOSING);
287*10054Seric 
288*10054Seric 		/* get the line from the other side */
289*10054Seric 		p = sfgets(SmtpReplyBuffer, sizeof SmtpReplyBuffer, SmtpIn);
290*10054Seric 		if (p == NULL)
291*10054Seric 			return (-1);
292*10054Seric 		fixcrlf(SmtpReplyBuffer, TRUE);
293*10054Seric 
2947356Seric 		/* log the input in the transcript for future error returns */
2957229Seric 		if (Verbose && !HoldErrs)
2969391Seric 			nmessage(Arpa_Info, "%s", SmtpReplyBuffer);
2979547Seric 		if (CurEnv->e_xfp != NULL)
2989547Seric 			fprintf(CurEnv->e_xfp, "%s\n", SmtpReplyBuffer);
2997356Seric 
3007356Seric 		/* if continuation is required, we can go on */
3019391Seric 		if (SmtpReplyBuffer[3] == '-' || !isdigit(SmtpReplyBuffer[0]))
3024684Seric 			continue;
3037356Seric 
3047356Seric 		/* decode the reply code */
3059391Seric 		r = atoi(SmtpReplyBuffer);
3067356Seric 
3077356Seric 		/* extra semantics: 0xx codes are "informational" */
3084684Seric 		if (r < 100)
3094684Seric 			continue;
3107356Seric 
3119391Seric 		/* reply code 421 is "Service Shutting Down" */
312*10054Seric 		if (r == SMTPCLOSING)
3139391Seric 		{
314*10054Seric 			/* send the quit protocol */
3159391Seric 			smtpquit("SMTP Shutdown");
3169391Seric 			SmtpClosing = TRUE;
3179391Seric 		}
3189391Seric 
3194684Seric 		return (r);
3204684Seric 	}
3214684Seric }
3224796Seric /*
3234865Seric **  SMTPMESSAGE -- send message to server
3244796Seric **
3254796Seric **	Parameters:
3264796Seric **		f -- format
3274796Seric **		a, b, c -- parameters
3284796Seric **
3294796Seric **	Returns:
3304796Seric **		none.
3314796Seric **
3324796Seric **	Side Effects:
3334865Seric **		writes message to SmtpOut.
3344796Seric */
3354796Seric 
3364865Seric /*VARARGS1*/
3374865Seric smtpmessage(f, a, b, c)
3384796Seric 	char *f;
3394796Seric {
3404796Seric 	char buf[100];
3414796Seric 
3424865Seric 	(void) sprintf(buf, f, a, b, c);
3437677Seric 	if (tTd(18, 1) || (Verbose && !HoldErrs))
3448237Seric 		nmessage(Arpa_Info, ">>> %s", buf);
3459547Seric 	if (CurEnv->e_xfp != NULL)
3469547Seric 		fprintf(CurEnv->e_xfp, ">>> %s\n", buf);
3479391Seric 	if (!SmtpClosing)
3489391Seric 		fprintf(SmtpOut, "%s\r\n", buf);
3494796Seric }
3505182Seric 
3515182Seric # endif SMTP
352