14796Seric # include <ctype.h>
24684Seric # include <sysexits.h>
34865Seric # include "sendmail.h"
44684Seric 
55182Seric # ifndef SMTP
6*11725Seric SCCSID(@(#)usersmtp.c	3.43		03/26/83	(no SMTP));
75182Seric # else SMTP
84684Seric 
9*11725Seric SCCSID(@(#)usersmtp.c	3.43		03/26/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 */
2711159Seric 
2811159Seric /* following represents the state of the SMTP connection */
2911159Seric int	SmtpState;			/* connection state, see below */
3011159Seric 
3111159Seric #define SMTP_CLOSED	0		/* connection is closed */
3211159Seric #define SMTP_OPEN	1		/* connection is open for business */
3311159Seric #define SMTP_SSD	2		/* service shutting down */
349391Seric /*
354865Seric **  SMTPINIT -- initialize SMTP.
364684Seric **
374865Seric **	Opens the connection and sends the initial protocol.
384684Seric **
394684Seric **	Parameters:
404865Seric **		m -- mailer to create connection to.
414865Seric **		pvp -- pointer to parameter vector to pass to
424865Seric **			the mailer.
434684Seric **
444684Seric **	Returns:
454865Seric **		appropriate exit status -- EX_OK on success.
464684Seric **
474684Seric **	Side Effects:
484865Seric **		creates connection and sends initial protocol.
494684Seric */
504684Seric 
5110175Seric smtpinit(m, pvp)
524865Seric 	struct mailer *m;
534865Seric 	char **pvp;
544684Seric {
554865Seric 	register int r;
564865Seric 	char buf[MAXNAME];
574684Seric 
584865Seric 	/*
594865Seric 	**  Open the connection to the mailer.
604865Seric 	*/
614684Seric 
6211159Seric #ifdef DEBUG
6311159Seric 	if (SmtpState == SMTP_OPEN)
6411159Seric 		syserr("smtpinit: already open");
6511159Seric #endif DEBUG
6611159Seric 
676051Seric 	SmtpIn = SmtpOut = NULL;
6811159Seric 	SmtpState = SMTP_CLOSED;
6910175Seric 	SmtpPid = openmailer(m, pvp, (ADDRESS *) NULL, TRUE, &SmtpOut, &SmtpIn);
706051Seric 	if (SmtpPid < 0)
716051Seric 	{
726051Seric # ifdef DEBUG
737677Seric 		if (tTd(18, 1))
749391Seric 			printf("smtpinit: cannot open %s: stat %d errno %d\n",
759391Seric 			   pvp[0], ExitStat, errno);
766051Seric # endif DEBUG
776051Seric 		return (ExitStat);
786051Seric 	}
7911159Seric 	SmtpState = SMTP_OPEN;
804796Seric 
814865Seric 	/*
824865Seric 	**  Get the greeting message.
834865Seric 	**	This should appear spontaneously.
844865Seric 	*/
854797Seric 
8610175Seric 	r = reply(m);
878005Seric 	if (r < 0 || REPLYTYPE(r) != 2)
884865Seric 		return (EX_TEMPFAIL);
894684Seric 
904865Seric 	/*
914976Seric 	**  Send the HELO command.
927963Seric 	**	My mother taught me to always introduce myself.
934976Seric 	*/
944976Seric 
9510175Seric 	smtpmessage("HELO %s", m, HostName);
9610175Seric 	r = reply(m);
978005Seric 	if (r < 0)
988005Seric 		return (EX_TEMPFAIL);
998005Seric 	else if (REPLYTYPE(r) == 5)
1004976Seric 		return (EX_UNAVAILABLE);
1017963Seric 	else if (REPLYTYPE(r) != 2)
1024976Seric 		return (EX_TEMPFAIL);
1034976Seric 
1044976Seric 	/*
1059315Seric 	**  If this is expected to be another sendmail, send some internal
1069315Seric 	**  commands.
1079315Seric 	*/
1089315Seric 
10910688Seric 	if (bitnset(M_INTERNAL, m->m_flags))
1109315Seric 	{
1119315Seric 		/* tell it to be verbose */
11210175Seric 		smtpmessage("VERB", m);
11310175Seric 		r = reply(m);
1149315Seric 		if (r < 0)
1159315Seric 			return (EX_TEMPFAIL);
1169315Seric 
1179315Seric 		/* tell it we will be sending one transaction only */
11810175Seric 		smtpmessage("ONEX", m);
11910175Seric 		r = reply(m);
1209315Seric 		if (r < 0)
1219315Seric 			return (EX_TEMPFAIL);
1229315Seric 	}
1239315Seric 
1249315Seric 	/*
1254865Seric 	**  Send the MAIL command.
1264865Seric 	**	Designates the sender.
1274865Seric 	*/
1284796Seric 
1296980Seric 	expand("$g", buf, &buf[sizeof buf - 1], CurEnv);
1308436Seric 	if (CurEnv->e_from.q_mailer == LocalMailer ||
13110688Seric 	    !bitnset(M_FROMPATH, m->m_flags))
1328436Seric 	{
13310308Seric 		smtpmessage("MAIL From:<%s>", m, buf);
1348436Seric 	}
1358436Seric 	else
1368436Seric 	{
13710175Seric 		smtpmessage("MAIL From:<@%s%c%s>", m, HostName,
13810308Seric 			buf[0] == '@' ? ',' : ':', buf);
1398436Seric 	}
14010175Seric 	r = reply(m);
1418005Seric 	if (r < 0 || REPLYTYPE(r) == 4)
1424865Seric 		return (EX_TEMPFAIL);
1437963Seric 	else if (r == 250)
1447963Seric 		return (EX_OK);
1457963Seric 	else if (r == 552)
1467963Seric 		return (EX_UNAVAILABLE);
1477964Seric 	return (EX_PROTOCOL);
1484684Seric }
1494684Seric /*
1504976Seric **  SMTPRCPT -- designate recipient.
1514797Seric **
1524797Seric **	Parameters:
1534865Seric **		to -- address of recipient.
15410175Seric **		m -- the mailer we are sending to.
1554797Seric **
1564797Seric **	Returns:
1574865Seric **		exit status corresponding to recipient status.
1584797Seric **
1594797Seric **	Side Effects:
1604865Seric **		Sends the mail via SMTP.
1614797Seric */
1624797Seric 
16310175Seric smtprcpt(to, m)
1644865Seric 	ADDRESS *to;
16510175Seric 	register MAILER *m;
1664797Seric {
1674797Seric 	register int r;
16810308Seric 	extern char *remotename();
1694797Seric 
17010308Seric 	smtpmessage("RCPT To:<%s>", m, remotename(to->q_user, m, FALSE, TRUE));
1714865Seric 
17210175Seric 	r = reply(m);
1738005Seric 	if (r < 0 || REPLYTYPE(r) == 4)
1744865Seric 		return (EX_TEMPFAIL);
1757963Seric 	else if (REPLYTYPE(r) == 2)
1767963Seric 		return (EX_OK);
1777964Seric 	else if (r == 550 || r == 551 || r == 553)
1787964Seric 		return (EX_NOUSER);
1797964Seric 	else if (r == 552 || r == 554)
1807964Seric 		return (EX_UNAVAILABLE);
1817964Seric 	return (EX_PROTOCOL);
1824797Seric }
1834797Seric /*
18410175Seric **  SMTPDATA -- send the data and clean up the transaction.
1854684Seric **
1864684Seric **	Parameters:
1874865Seric **		m -- mailer being sent to.
1886980Seric **		e -- the envelope for this message.
1894684Seric **
1904684Seric **	Returns:
1914976Seric **		exit status corresponding to DATA command.
1924684Seric **
1934684Seric **	Side Effects:
1944865Seric **		none.
1954684Seric */
1964684Seric 
19710175Seric smtpdata(m, e)
1984865Seric 	struct mailer *m;
1996980Seric 	register ENVELOPE *e;
2004684Seric {
2014684Seric 	register int r;
2024684Seric 
2034797Seric 	/*
2044797Seric 	**  Send the data.
20510175Seric 	**	First send the command and check that it is ok.
20610175Seric 	**	Then send the data.
20710175Seric 	**	Follow it up with a dot to terminate.
20810175Seric 	**	Finally get the results of the transaction.
2094797Seric 	*/
2104797Seric 
21110175Seric 	/* send the command and check ok to proceed */
21210175Seric 	smtpmessage("DATA", m);
21310175Seric 	r = reply(m);
2148005Seric 	if (r < 0 || REPLYTYPE(r) == 4)
2154797Seric 		return (EX_TEMPFAIL);
2167963Seric 	else if (r == 554)
2177963Seric 		return (EX_UNAVAILABLE);
2187963Seric 	else if (r != 354)
2197964Seric 		return (EX_PROTOCOL);
22010175Seric 
22110175Seric 	/* now output the actual message */
22210175Seric 	(*e->e_puthdr)(SmtpOut, m, CurEnv);
22310175Seric 	putline("\n", SmtpOut, m);
22410175Seric 	(*e->e_putbody)(SmtpOut, m, CurEnv);
22510175Seric 
22610175Seric 	/* terminate the message */
22710328Seric 	fprintf(SmtpOut, ".%s", m->m_eol);
22810215Seric 	if (Verbose && !HoldErrs)
22910215Seric 		nmessage(Arpa_Info, ">>> .");
23010175Seric 
23110175Seric 	/* check for the results of the transaction */
23210175Seric 	r = reply(m);
2338005Seric 	if (r < 0 || REPLYTYPE(r) == 4)
2344797Seric 		return (EX_TEMPFAIL);
2357963Seric 	else if (r == 250)
2367963Seric 		return (EX_OK);
2377963Seric 	else if (r == 552 || r == 554)
2387963Seric 		return (EX_UNAVAILABLE);
2397964Seric 	return (EX_PROTOCOL);
2404684Seric }
2414684Seric /*
2424865Seric **  SMTPQUIT -- close the SMTP connection.
2434865Seric **
2444865Seric **	Parameters:
2454865Seric **		name -- name of mailer we are quitting.
2464865Seric **
2474865Seric **	Returns:
2484865Seric **		none.
2494865Seric **
2504865Seric **	Side Effects:
2514865Seric **		sends the final protocol and closes the connection.
2524865Seric */
2534865Seric 
25410175Seric smtpquit(name, m)
2554865Seric 	char *name;
25610175Seric 	register MAILER *m;
2574865Seric {
2589391Seric 	int i;
2594865Seric 
26010148Seric 	/* if the connection is already closed, don't bother */
26110148Seric 	if (SmtpIn == NULL)
26210148Seric 		return;
26310148Seric 
26410148Seric 	/* send the quit message if not a forced quit */
26511159Seric 	if (SmtpState == SMTP_OPEN || SmtpState == SMTP_SSD)
2669391Seric 	{
26710175Seric 		smtpmessage("QUIT", m);
26810175Seric 		(void) reply(m);
26911159Seric 		if (SmtpState == SMTP_CLOSED)
27010159Seric 			return;
2719391Seric 	}
2729391Seric 
27310148Seric 	/* now actually close the connection */
2747229Seric 	(void) fclose(SmtpIn);
2757229Seric 	(void) fclose(SmtpOut);
27610148Seric 	SmtpIn = SmtpOut = NULL;
27711159Seric 	SmtpState = SMTP_CLOSED;
27810148Seric 
27910148Seric 	/* and pick up the zombie */
2807229Seric 	i = endmailer(SmtpPid, name);
2819391Seric 	if (i != EX_OK)
2829391Seric 		syserr("smtpquit %s: stat %d", name, i);
2834865Seric }
2844865Seric /*
2854684Seric **  REPLY -- read arpanet reply
2864684Seric **
2874684Seric **	Parameters:
28810175Seric **		m -- the mailer we are reading the reply from.
2894684Seric **
2904684Seric **	Returns:
2914684Seric **		reply code it reads.
2924684Seric **
2934684Seric **	Side Effects:
2944684Seric **		flushes the mail file.
2954684Seric */
2964684Seric 
29710175Seric reply(m)
29810688Seric 	MAILER *m;
2994684Seric {
3004865Seric 	(void) fflush(SmtpOut);
3014684Seric 
3027677Seric 	if (tTd(18, 1))
3034796Seric 		printf("reply\n");
3044796Seric 
3057356Seric 	/*
3067356Seric 	**  Read the input line, being careful not to hang.
3077356Seric 	*/
3087356Seric 
3094684Seric 	for (;;)
3104684Seric 	{
3114684Seric 		register int r;
3127356Seric 		register char *p;
3134684Seric 
3147685Seric 		/* actually do the read */
3159547Seric 		if (CurEnv->e_xfp != NULL)
3169547Seric 			(void) fflush(CurEnv->e_xfp);	/* for debugging */
3177356Seric 
31810054Seric 		/* if we are in the process of closing just give the code */
31911159Seric 		if (SmtpState == SMTP_CLOSED)
32010054Seric 			return (SMTPCLOSING);
32110054Seric 
32210054Seric 		/* get the line from the other side */
32310054Seric 		p = sfgets(SmtpReplyBuffer, sizeof SmtpReplyBuffer, SmtpIn);
32410054Seric 		if (p == NULL)
32510131Seric 		{
32610148Seric 			extern char MsgBuf[];		/* err.c */
32710148Seric 			extern char Arpa_TSyserr[];	/* conf.c */
32810148Seric 
32910148Seric 			message(Arpa_TSyserr, "reply: read error");
33010420Seric # ifdef DEBUG
33110420Seric 			/* if debugging, pause so we can see state */
33210420Seric 			if (tTd(18, 100))
33310420Seric 				pause();
33410420Seric # endif DEBUG
33510148Seric # ifdef LOG
33610148Seric 			syslog(LOG_ERR, "%s", &MsgBuf[4]);
33710148Seric # endif LOG
33811159Seric 			SmtpState = SMTP_CLOSED;
33910175Seric 			smtpquit("reply error", m);
34010054Seric 			return (-1);
34110131Seric 		}
34210054Seric 		fixcrlf(SmtpReplyBuffer, TRUE);
34310054Seric 
3447356Seric 		/* log the input in the transcript for future error returns */
3457229Seric 		if (Verbose && !HoldErrs)
3469391Seric 			nmessage(Arpa_Info, "%s", SmtpReplyBuffer);
34710131Seric 		else if (CurEnv->e_xfp != NULL)
3489547Seric 			fprintf(CurEnv->e_xfp, "%s\n", SmtpReplyBuffer);
3497356Seric 
3507356Seric 		/* if continuation is required, we can go on */
3519391Seric 		if (SmtpReplyBuffer[3] == '-' || !isdigit(SmtpReplyBuffer[0]))
3524684Seric 			continue;
3537356Seric 
3547356Seric 		/* decode the reply code */
3559391Seric 		r = atoi(SmtpReplyBuffer);
3567356Seric 
3577356Seric 		/* extra semantics: 0xx codes are "informational" */
3584684Seric 		if (r < 100)
3594684Seric 			continue;
3607356Seric 
3619391Seric 		/* reply code 421 is "Service Shutting Down" */
36211159Seric 		if (r == SMTPCLOSING && SmtpState != SMTP_SSD)
3639391Seric 		{
36410054Seric 			/* send the quit protocol */
36511159Seric 			SmtpState = SMTP_SSD;
36610175Seric 			smtpquit("SMTP Shutdown", m);
3679391Seric 		}
3689391Seric 
3694684Seric 		return (r);
3704684Seric 	}
3714684Seric }
3724796Seric /*
3734865Seric **  SMTPMESSAGE -- send message to server
3744796Seric **
3754796Seric **	Parameters:
3764796Seric **		f -- format
37710175Seric **		m -- the mailer to control formatting.
3784796Seric **		a, b, c -- parameters
3794796Seric **
3804796Seric **	Returns:
3814796Seric **		none.
3824796Seric **
3834796Seric **	Side Effects:
3844865Seric **		writes message to SmtpOut.
3854796Seric */
3864796Seric 
3874865Seric /*VARARGS1*/
38810175Seric smtpmessage(f, m, a, b, c)
3894796Seric 	char *f;
39010175Seric 	MAILER *m;
3914796Seric {
392*11725Seric 	char buf[MAXLINE];
3934796Seric 
3944865Seric 	(void) sprintf(buf, f, a, b, c);
3957677Seric 	if (tTd(18, 1) || (Verbose && !HoldErrs))
3968237Seric 		nmessage(Arpa_Info, ">>> %s", buf);
39710131Seric 	else if (CurEnv->e_xfp != NULL)
3989547Seric 		fprintf(CurEnv->e_xfp, ">>> %s\n", buf);
39911159Seric 	if (SmtpOut != NULL)
40010328Seric 		fprintf(SmtpOut, "%s%s", buf, m->m_eol);
4014796Seric }
4025182Seric 
4035182Seric # endif SMTP
404