14796Seric # include <ctype.h>
24684Seric # include <sysexits.h>
34865Seric # include "sendmail.h"
44684Seric 
55182Seric # ifndef SMTP
6*10159Seric SCCSID(@(#)usersmtp.c	3.35		01/05/83	(no SMTP));
75182Seric # else SMTP
84684Seric 
9*10159Seric SCCSID(@(#)usersmtp.c	3.35		01/05/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.
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;
6010148Seric 	SmtpClosing = FALSE;
614865Seric 	SmtpPid = openmailer(m, pvp, ctladdr, TRUE, &SmtpOut, &SmtpIn);
626051Seric 	if (SmtpPid < 0)
636051Seric 	{
646051Seric # ifdef DEBUG
657677Seric 		if (tTd(18, 1))
669391Seric 			printf("smtpinit: cannot open %s: stat %d errno %d\n",
679391Seric 			   pvp[0], ExitStat, errno);
686051Seric # endif DEBUG
696051Seric 		return (ExitStat);
706051Seric 	}
714796Seric 
724865Seric 	/*
734865Seric 	**  Get the greeting message.
744865Seric 	**	This should appear spontaneously.
754865Seric 	*/
764797Seric 
774865Seric 	r = reply();
788005Seric 	if (r < 0 || REPLYTYPE(r) != 2)
794865Seric 		return (EX_TEMPFAIL);
804684Seric 
814865Seric 	/*
824976Seric 	**  Send the HELO command.
837963Seric 	**	My mother taught me to always introduce myself.
844976Seric 	*/
854976Seric 
864976Seric 	smtpmessage("HELO %s", HostName);
874976Seric 	r = reply();
888005Seric 	if (r < 0)
898005Seric 		return (EX_TEMPFAIL);
908005Seric 	else if (REPLYTYPE(r) == 5)
914976Seric 		return (EX_UNAVAILABLE);
927963Seric 	else if (REPLYTYPE(r) != 2)
934976Seric 		return (EX_TEMPFAIL);
944976Seric 
954976Seric 	/*
969315Seric 	**  If this is expected to be another sendmail, send some internal
979315Seric 	**  commands.
989315Seric 	*/
999315Seric 
1009315Seric 	if (bitset(M_INTERNAL, m->m_flags))
1019315Seric 	{
1029315Seric 		/* tell it to be verbose */
1039315Seric 		smtpmessage("VERB");
1049315Seric 		r = reply();
1059315Seric 		if (r < 0)
1069315Seric 			return (EX_TEMPFAIL);
1079315Seric 
1089315Seric 		/* tell it we will be sending one transaction only */
1099315Seric 		smtpmessage("ONEX");
1109315Seric 		r = reply();
1119315Seric 		if (r < 0)
1129315Seric 			return (EX_TEMPFAIL);
1139315Seric 	}
1149315Seric 
1159315Seric 	/*
1164865Seric 	**  Send the MAIL command.
1174865Seric 	**	Designates the sender.
1184865Seric 	*/
1194796Seric 
1206980Seric 	expand("$g", buf, &buf[sizeof buf - 1], CurEnv);
1218436Seric 	if (CurEnv->e_from.q_mailer == LocalMailer ||
1228436Seric 	    !bitset(M_FULLSMTP, m->m_flags))
1238436Seric 	{
1248436Seric 		smtpmessage("MAIL From:<%s>", canonname(buf, 1));
1258436Seric 	}
1268436Seric 	else
1278436Seric 	{
1288436Seric 		smtpmessage("MAIL From:<@%s%c%s>", HostName,
1298436Seric 			    buf[0] == '@' ? ',' : ':', canonname(buf, 1));
1308436Seric 	}
1314865Seric 	r = reply();
1328005Seric 	if (r < 0 || REPLYTYPE(r) == 4)
1334865Seric 		return (EX_TEMPFAIL);
1347963Seric 	else if (r == 250)
1357963Seric 		return (EX_OK);
1367963Seric 	else if (r == 552)
1377963Seric 		return (EX_UNAVAILABLE);
1387964Seric 	return (EX_PROTOCOL);
1394684Seric }
1404684Seric /*
1414976Seric **  SMTPRCPT -- designate recipient.
1424797Seric **
1434797Seric **	Parameters:
1444865Seric **		to -- address of recipient.
1454797Seric **
1464797Seric **	Returns:
1474865Seric **		exit status corresponding to recipient status.
1484797Seric **
1494797Seric **	Side Effects:
1504865Seric **		Sends the mail via SMTP.
1514797Seric */
1524797Seric 
1534976Seric smtprcpt(to)
1544865Seric 	ADDRESS *to;
1554797Seric {
1564797Seric 	register int r;
1577685Seric 	extern char *canonname();
1584797Seric 
1598354Seric 	smtpmessage("RCPT To:<%s>", canonname(to->q_user, 2));
1604865Seric 
1614797Seric 	r = reply();
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 /*
1734865Seric **  SMTPFINISH -- finish up sending all the SMTP protocol.
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 
1866980Seric smtpfinish(m, e)
1874865Seric 	struct mailer *m;
1886980Seric 	register ENVELOPE *e;
1894684Seric {
1904684Seric 	register int r;
1914684Seric 
1924797Seric 	/*
1934797Seric 	**  Send the data.
1944797Seric 	**	Dot hiding is done here.
1954797Seric 	*/
1964797Seric 
1974865Seric 	smtpmessage("DATA");
1984796Seric 	r = reply();
1998005Seric 	if (r < 0 || REPLYTYPE(r) == 4)
2004797Seric 		return (EX_TEMPFAIL);
2017963Seric 	else if (r == 554)
2027963Seric 		return (EX_UNAVAILABLE);
2037963Seric 	else if (r != 354)
2047964Seric 		return (EX_PROTOCOL);
20510067Seric 	(*e->e_puthdr)(SmtpOut, m, CurEnv, TRUE);
20610067Seric 	fprintf(SmtpOut, "\r\n");
20710067Seric 	(*e->e_putbody)(SmtpOut, m, TRUE, CurEnv, TRUE);
2084865Seric 	smtpmessage(".");
2094796Seric 	r = reply();
2108005Seric 	if (r < 0 || REPLYTYPE(r) == 4)
2114797Seric 		return (EX_TEMPFAIL);
2127963Seric 	else if (r == 250)
2137963Seric 		return (EX_OK);
2147963Seric 	else if (r == 552 || r == 554)
2157963Seric 		return (EX_UNAVAILABLE);
2167964Seric 	return (EX_PROTOCOL);
2174684Seric }
2184684Seric /*
2194865Seric **  SMTPQUIT -- close the SMTP connection.
2204865Seric **
2214865Seric **	Parameters:
2224865Seric **		name -- name of mailer we are quitting.
2234865Seric **
2244865Seric **	Returns:
2254865Seric **		none.
2264865Seric **
2274865Seric **	Side Effects:
2284865Seric **		sends the final protocol and closes the connection.
2294865Seric */
2304865Seric 
2319391Seric smtpquit(name)
2324865Seric 	char *name;
2334865Seric {
2349391Seric 	int i;
2354865Seric 
23610148Seric 	/* if the connection is already closed, don't bother */
23710148Seric 	if (SmtpIn == NULL)
23810148Seric 		return;
23910148Seric 
24010148Seric 	/* send the quit message if not a forced quit */
24110148Seric 	if (!SmtpClosing)
2429391Seric 	{
24310148Seric 		smtpmessage("QUIT");
24410148Seric 		(void) reply();
245*10159Seric 		if (SmtpClosing)
246*10159Seric 			return;
2479391Seric 	}
2489391Seric 
24910148Seric 	/* now actually close the connection */
2507229Seric 	(void) fclose(SmtpIn);
2517229Seric 	(void) fclose(SmtpOut);
25210148Seric 	SmtpIn = SmtpOut = NULL;
25310148Seric 
25410148Seric 	/* and pick up the zombie */
2557229Seric 	i = endmailer(SmtpPid, name);
2569391Seric 	if (i != EX_OK)
2579391Seric 		syserr("smtpquit %s: stat %d", name, i);
2584865Seric }
2594865Seric /*
2604684Seric **  REPLY -- read arpanet reply
2614684Seric **
2624684Seric **	Parameters:
2634796Seric **		none.
2644684Seric **
2654684Seric **	Returns:
2664684Seric **		reply code it reads.
2674684Seric **
2684684Seric **	Side Effects:
2694684Seric **		flushes the mail file.
2704684Seric */
2714684Seric 
2724796Seric reply()
2734684Seric {
2744865Seric 	(void) fflush(SmtpOut);
2754684Seric 
2767677Seric 	if (tTd(18, 1))
2774796Seric 		printf("reply\n");
2784796Seric 
2797356Seric 	/*
2807356Seric 	**  Read the input line, being careful not to hang.
2817356Seric 	*/
2827356Seric 
2834684Seric 	for (;;)
2844684Seric 	{
2854684Seric 		register int r;
2867356Seric 		register char *p;
2874684Seric 
2887685Seric 		/* actually do the read */
2899547Seric 		if (CurEnv->e_xfp != NULL)
2909547Seric 			(void) fflush(CurEnv->e_xfp);	/* for debugging */
2917356Seric 
29210054Seric 		/* if we are in the process of closing just give the code */
29310054Seric 		if (SmtpClosing)
29410054Seric 			return (SMTPCLOSING);
29510054Seric 
29610054Seric 		/* get the line from the other side */
29710054Seric 		p = sfgets(SmtpReplyBuffer, sizeof SmtpReplyBuffer, SmtpIn);
29810054Seric 		if (p == NULL)
29910131Seric 		{
30010148Seric 			extern char MsgBuf[];		/* err.c */
30110148Seric 			extern char Arpa_TSyserr[];	/* conf.c */
30210148Seric 
30310148Seric 			message(Arpa_TSyserr, "reply: read error");
30410148Seric # ifdef LOG
30510148Seric 			syslog(LOG_ERR, "%s", &MsgBuf[4]);
30610148Seric # endif LOG
30710148Seric 			SmtpClosing = TRUE;
30810148Seric 			smtpquit("reply error");
30910054Seric 			return (-1);
31010131Seric 		}
31110054Seric 		fixcrlf(SmtpReplyBuffer, TRUE);
31210054Seric 
3137356Seric 		/* log the input in the transcript for future error returns */
3147229Seric 		if (Verbose && !HoldErrs)
3159391Seric 			nmessage(Arpa_Info, "%s", SmtpReplyBuffer);
31610131Seric 		else if (CurEnv->e_xfp != NULL)
3179547Seric 			fprintf(CurEnv->e_xfp, "%s\n", SmtpReplyBuffer);
3187356Seric 
3197356Seric 		/* if continuation is required, we can go on */
3209391Seric 		if (SmtpReplyBuffer[3] == '-' || !isdigit(SmtpReplyBuffer[0]))
3214684Seric 			continue;
3227356Seric 
3237356Seric 		/* decode the reply code */
3249391Seric 		r = atoi(SmtpReplyBuffer);
3257356Seric 
3267356Seric 		/* extra semantics: 0xx codes are "informational" */
3274684Seric 		if (r < 100)
3284684Seric 			continue;
3297356Seric 
3309391Seric 		/* reply code 421 is "Service Shutting Down" */
33110054Seric 		if (r == SMTPCLOSING)
3329391Seric 		{
33310054Seric 			/* send the quit protocol */
3349391Seric 			smtpquit("SMTP Shutdown");
3359391Seric 			SmtpClosing = TRUE;
3369391Seric 		}
3379391Seric 
3384684Seric 		return (r);
3394684Seric 	}
3404684Seric }
3414796Seric /*
3424865Seric **  SMTPMESSAGE -- send message to server
3434796Seric **
3444796Seric **	Parameters:
3454796Seric **		f -- format
3464796Seric **		a, b, c -- parameters
3474796Seric **
3484796Seric **	Returns:
3494796Seric **		none.
3504796Seric **
3514796Seric **	Side Effects:
3524865Seric **		writes message to SmtpOut.
3534796Seric */
3544796Seric 
3554865Seric /*VARARGS1*/
3564865Seric smtpmessage(f, a, b, c)
3574796Seric 	char *f;
3584796Seric {
3594796Seric 	char buf[100];
3604796Seric 
3614865Seric 	(void) sprintf(buf, f, a, b, c);
3627677Seric 	if (tTd(18, 1) || (Verbose && !HoldErrs))
3638237Seric 		nmessage(Arpa_Info, ">>> %s", buf);
36410131Seric 	else if (CurEnv->e_xfp != NULL)
3659547Seric 		fprintf(CurEnv->e_xfp, ">>> %s\n", buf);
3669391Seric 	if (!SmtpClosing)
3679391Seric 		fprintf(SmtpOut, "%s\r\n", buf);
3684796Seric }
3695182Seric 
3705182Seric # endif SMTP
371