122716Sdist /*
234921Sbostic  * Copyright (c) 1983 Eric P. Allman
333731Sbostic  * Copyright (c) 1988 Regents of the University of California.
433731Sbostic  * All rights reserved.
533731Sbostic  *
642831Sbostic  * %sccs.include.redist.c%
733731Sbostic  */
822716Sdist 
933731Sbostic # include "sendmail.h"
1022716Sdist 
1133731Sbostic #ifndef lint
1233731Sbostic #ifdef SMTP
13*54967Seric static char sccsid[] = "@(#)usersmtp.c	5.22 (Berkeley) 07/11/92 (with SMTP)";
1433731Sbostic #else
15*54967Seric static char sccsid[] = "@(#)usersmtp.c	5.22 (Berkeley) 07/11/92 (without SMTP)";
1633731Sbostic #endif
1733731Sbostic #endif /* not lint */
1833731Sbostic 
194684Seric # include <sysexits.h>
2021065Seric # include <errno.h>
214684Seric 
2233731Sbostic # ifdef SMTP
234684Seric 
244684Seric /*
259391Seric **  USERSMTP -- run SMTP protocol from the user end.
269391Seric **
279391Seric **	This protocol is described in RFC821.
289391Seric */
299391Seric 
309391Seric #define REPLYTYPE(r)	((r) / 100)		/* first digit of reply code */
319391Seric #define REPLYCLASS(r)	(((r) / 10) % 10)	/* second digit of reply code */
329391Seric #define SMTPCLOSING	421			/* "Service Shutting Down" */
339391Seric 
3414900Seric char	SmtpMsgBuffer[MAXLINE];		/* buffer for commands */
3510054Seric char	SmtpReplyBuffer[MAXLINE];	/* buffer for replies */
3621065Seric char	SmtpError[MAXLINE] = "";	/* save failure error messages */
3710054Seric int	SmtpPid;			/* pid of mailer */
389391Seric /*
394865Seric **  SMTPINIT -- initialize SMTP.
404684Seric **
414865Seric **	Opens the connection and sends the initial protocol.
424684Seric **
434684Seric **	Parameters:
444865Seric **		m -- mailer to create connection to.
454865Seric **		pvp -- pointer to parameter vector to pass to
464865Seric **			the mailer.
474684Seric **
484684Seric **	Returns:
49*54967Seric **		none.
504684Seric **
514684Seric **	Side Effects:
524865Seric **		creates connection and sends initial protocol.
534684Seric */
544684Seric 
5514886Seric jmp_buf	CtxGreeting;
5614886Seric 
57*54967Seric smtpinit(m, mci, e)
584865Seric 	struct mailer *m;
59*54967Seric 	register MCI *mci;
6053751Seric 	ENVELOPE *e;
614684Seric {
624865Seric 	register int r;
6314886Seric 	EVENT *gte;
6446928Sbostic 	static int greettimeout();
6552107Seric 	extern STAB *stab();
66*54967Seric 	extern MCI *openmailer();
674684Seric 
684865Seric 	/*
694865Seric 	**  Open the connection to the mailer.
704865Seric 	*/
714684Seric 
7221065Seric 	SmtpError[0] = '\0';
73*54967Seric 	switch (mci->mci_state)
746051Seric 	{
75*54967Seric 	  case MCIS_ACTIVE:
76*54967Seric 		/* need to clear old information */
77*54967Seric 		smtprset(m, mci, e);
78*54967Seric 		mci->mci_state = MCIS_OPEN;
7915139Seric 
80*54967Seric 	  case MCIS_OPEN:
81*54967Seric 		return;
82*54967Seric 
83*54967Seric 	  case MCIS_ERROR:
84*54967Seric 	  case MCIS_SSD:
85*54967Seric 		/* shouldn't happen */
86*54967Seric 		smtpquit(m, mci, e);
87*54967Seric 
88*54967Seric 	  case MCIS_CLOSED:
89*54967Seric 		syserr("smtpinit: state CLOSED");
90*54967Seric 		return;
91*54967Seric 
92*54967Seric 	  case MCIS_OPENING:
93*54967Seric 		break;
946051Seric 	}
954796Seric 
96*54967Seric 	mci->mci_phase = "user open";
97*54967Seric 	mci->mci_state = MCIS_OPENING;
98*54967Seric 
994865Seric 	/*
1004865Seric 	**  Get the greeting message.
10114913Seric 	**	This should appear spontaneously.  Give it five minutes to
10214886Seric 	**	happen.
1034865Seric 	*/
1044797Seric 
10514886Seric 	if (setjmp(CtxGreeting) != 0)
10652104Seric 		goto tempfail1;
10716141Seric 	gte = setevent((time_t) 300, greettimeout, 0);
10853751Seric 	mci->mci_phase = "greeting wait";
10953751Seric 	setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
11053751Seric 	r = reply(m, mci, e);
11114886Seric 	clrevent(gte);
1128005Seric 	if (r < 0 || REPLYTYPE(r) != 2)
11352104Seric 		goto tempfail1;
1144684Seric 
1154865Seric 	/*
1164976Seric 	**  Send the HELO command.
1177963Seric 	**	My mother taught me to always introduce myself.
1184976Seric 	*/
1194976Seric 
12053751Seric 	smtpmessage("HELO %s", m, mci, MyHostName);
12153751Seric 	mci->mci_phase = "HELO wait";
12253751Seric 	setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
12353751Seric 	r = reply(m, mci, e);
1248005Seric 	if (r < 0)
12552104Seric 		goto tempfail1;
1268005Seric 	else if (REPLYTYPE(r) == 5)
12714913Seric 		goto unavailable;
1287963Seric 	else if (REPLYTYPE(r) != 2)
12952104Seric 		goto tempfail1;
1304976Seric 
1314976Seric 	/*
1329315Seric 	**  If this is expected to be another sendmail, send some internal
1339315Seric 	**  commands.
1349315Seric 	*/
1359315Seric 
13610688Seric 	if (bitnset(M_INTERNAL, m->m_flags))
1379315Seric 	{
1389315Seric 		/* tell it to be verbose */
13953751Seric 		smtpmessage("VERB", m, mci);
14053751Seric 		r = reply(m, mci, e);
1419315Seric 		if (r < 0)
14252104Seric 			goto tempfail2;
1439315Seric 	}
1449315Seric 
14553751Seric 	mci->mci_state = MCIS_OPEN;
146*54967Seric 	return;
14753751Seric 
14853751Seric   tempfail1:
14953751Seric   tempfail2:
15053751Seric 	mci->mci_exitstat = EX_TEMPFAIL;
15153751Seric 	mci->mci_errno = errno;
15253751Seric 	smtpquit(m, mci, e);
153*54967Seric 	return;
15453751Seric 
15553751Seric   unavailable:
15653751Seric 	mci->mci_exitstat = EX_UNAVAILABLE;
15753751Seric 	mci->mci_errno = errno;
15853751Seric 	smtpquit(m, mci, e);
159*54967Seric 	return;
16053751Seric }
16153751Seric 
16253751Seric smtpmailfrom(m, mci, e)
16353751Seric 	struct mailer *m;
164*54967Seric 	MCI *mci;
16553751Seric 	ENVELOPE *e;
16653751Seric {
16753751Seric 	int r;
16853751Seric 	char buf[MAXNAME];
16953751Seric 
1709315Seric 	/*
1714865Seric 	**  Send the MAIL command.
1724865Seric 	**	Designates the sender.
1734865Seric 	*/
1744796Seric 
17553751Seric 	mci->mci_state = MCIS_ACTIVE;
17653751Seric 
17753751Seric 	expand("\001<", buf, &buf[sizeof buf - 1], e);
17853751Seric 	if (e->e_from.q_mailer == LocalMailer ||
17910688Seric 	    !bitnset(M_FROMPATH, m->m_flags))
1808436Seric 	{
18153751Seric 		smtpmessage("MAIL From:<%s>", m, mci, buf);
1828436Seric 	}
1838436Seric 	else
1848436Seric 	{
18553751Seric 		smtpmessage("MAIL From:<@%s%c%s>", m, mci, MyHostName,
18610308Seric 			buf[0] == '@' ? ',' : ':', buf);
1878436Seric 	}
18853751Seric 	mci->mci_phase = "MAIL wait";
18953751Seric 	setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
19053751Seric 	r = reply(m, mci, e);
1918005Seric 	if (r < 0 || REPLYTYPE(r) == 4)
19253751Seric 	{
19353751Seric 		mci->mci_exitstat = EX_TEMPFAIL;
19453751Seric 		mci->mci_errno = errno;
19553751Seric 		smtpquit(m, mci, e);
19653751Seric 		return EX_TEMPFAIL;
19753751Seric 	}
1987963Seric 	else if (r == 250)
19953751Seric 	{
20053751Seric 		mci->mci_exitstat = EX_OK;
20153751Seric 		return EX_OK;
20253751Seric 	}
2037963Seric 	else if (r == 552)
20453751Seric 	{
20553751Seric 		/* signal service unavailable */
20653751Seric 		mci->mci_exitstat = EX_UNAVAILABLE;
20753751Seric 		smtpquit(m, mci, e);
20853751Seric 		return EX_UNAVAILABLE;
20953751Seric 	}
21014913Seric 
21114913Seric 	/* protocol error -- close up */
21253751Seric 	smtpquit(m, mci, e);
21353751Seric 	mci->mci_exitstat = EX_PROTOCOL;
21453751Seric 	return EX_PROTOCOL;
2154684Seric }
21614886Seric 
21714886Seric 
21814886Seric static
21914886Seric greettimeout()
22014886Seric {
22114886Seric 	/* timeout reading the greeting message */
22214886Seric 	longjmp(CtxGreeting, 1);
22314886Seric }
2244684Seric /*
2254976Seric **  SMTPRCPT -- designate recipient.
2264797Seric **
2274797Seric **	Parameters:
2284865Seric **		to -- address of recipient.
22910175Seric **		m -- the mailer we are sending to.
2304797Seric **
2314797Seric **	Returns:
2324865Seric **		exit status corresponding to recipient status.
2334797Seric **
2344797Seric **	Side Effects:
2354865Seric **		Sends the mail via SMTP.
2364797Seric */
2374797Seric 
23853751Seric smtprcpt(to, m, mci, e)
2394865Seric 	ADDRESS *to;
24010175Seric 	register MAILER *m;
241*54967Seric 	MCI *mci;
24253751Seric 	ENVELOPE *e;
2434797Seric {
2444797Seric 	register int r;
24510308Seric 	extern char *remotename();
2464797Seric 
24753751Seric 	smtpmessage("RCPT To:<%s>", m, mci, to->q_user);
2484865Seric 
24953751Seric 	mci->mci_phase = "RCPT wait";
25053751Seric 	setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
25153751Seric 	r = reply(m, mci, e);
2528005Seric 	if (r < 0 || REPLYTYPE(r) == 4)
2534865Seric 		return (EX_TEMPFAIL);
2547963Seric 	else if (REPLYTYPE(r) == 2)
2557963Seric 		return (EX_OK);
2567964Seric 	else if (r == 550 || r == 551 || r == 553)
2577964Seric 		return (EX_NOUSER);
2587964Seric 	else if (r == 552 || r == 554)
2597964Seric 		return (EX_UNAVAILABLE);
2607964Seric 	return (EX_PROTOCOL);
2614797Seric }
2624797Seric /*
26310175Seric **  SMTPDATA -- send the data and clean up the transaction.
2644684Seric **
2654684Seric **	Parameters:
2664865Seric **		m -- mailer being sent to.
2676980Seric **		e -- the envelope for this message.
2684684Seric **
2694684Seric **	Returns:
2704976Seric **		exit status corresponding to DATA command.
2714684Seric **
2724684Seric **	Side Effects:
2734865Seric **		none.
2744684Seric */
2754684Seric 
27653740Seric smtpdata(m, mci, e)
2774865Seric 	struct mailer *m;
278*54967Seric 	register MCI *mci;
2796980Seric 	register ENVELOPE *e;
2804684Seric {
2814684Seric 	register int r;
2824684Seric 
2834797Seric 	/*
2844797Seric 	**  Send the data.
28510175Seric 	**	First send the command and check that it is ok.
28610175Seric 	**	Then send the data.
28710175Seric 	**	Follow it up with a dot to terminate.
28810175Seric 	**	Finally get the results of the transaction.
2894797Seric 	*/
2904797Seric 
29110175Seric 	/* send the command and check ok to proceed */
29253751Seric 	smtpmessage("DATA", m, mci);
29353751Seric 	mci->mci_phase = "DATA wait";
29453751Seric 	setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
29553751Seric 	r = reply(m, mci, e);
2968005Seric 	if (r < 0 || REPLYTYPE(r) == 4)
2974797Seric 		return (EX_TEMPFAIL);
2987963Seric 	else if (r == 554)
2997963Seric 		return (EX_UNAVAILABLE);
3007963Seric 	else if (r != 354)
3017964Seric 		return (EX_PROTOCOL);
30210175Seric 
30310175Seric 	/* now output the actual message */
30453751Seric 	(*e->e_puthdr)(mci->mci_out, m, e);
30553740Seric 	putline("\n", mci->mci_out, m);
30653751Seric 	(*e->e_putbody)(mci->mci_out, m, e);
30710175Seric 
30810175Seric 	/* terminate the message */
30953740Seric 	fprintf(mci->mci_out, ".%s", m->m_eol);
31010215Seric 	if (Verbose && !HoldErrs)
31110215Seric 		nmessage(Arpa_Info, ">>> .");
31210175Seric 
31310175Seric 	/* check for the results of the transaction */
31453751Seric 	mci->mci_phase = "result wait";
31553751Seric 	setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
31653751Seric 	r = reply(m, mci, e);
31753751Seric 	if (r < 0)
3184797Seric 		return (EX_TEMPFAIL);
31953751Seric 	mci->mci_state = MCIS_OPEN;
32053751Seric 	if (REPLYTYPE(r) == 4)
32153751Seric 		return (EX_TEMPFAIL);
3227963Seric 	else if (r == 250)
3237963Seric 		return (EX_OK);
3247963Seric 	else if (r == 552 || r == 554)
3257963Seric 		return (EX_UNAVAILABLE);
3267964Seric 	return (EX_PROTOCOL);
3274684Seric }
3284684Seric /*
3294865Seric **  SMTPQUIT -- close the SMTP connection.
3304865Seric **
3314865Seric **	Parameters:
33215535Seric **		m -- a pointer to the mailer.
3334865Seric **
3344865Seric **	Returns:
3354865Seric **		none.
3364865Seric **
3374865Seric **	Side Effects:
3384865Seric **		sends the final protocol and closes the connection.
3394865Seric */
3404865Seric 
34153751Seric smtpquit(m, mci, e)
34253751Seric 	register MAILER *m;
343*54967Seric 	register MCI *mci;
34453751Seric 	ENVELOPE *e;
3454865Seric {
3469391Seric 	int i;
3474865Seric 
348*54967Seric 	/* send the quit message if we haven't gotten I/O error */
34953751Seric 	if (mci->mci_state != MCIS_ERROR)
3509391Seric 	{
35153751Seric 		smtpmessage("QUIT", m, mci);
35253751Seric 		(void) reply(m, mci, e);
35353740Seric 		if (mci->mci_state == MCIS_CLOSED)
35410159Seric 			return;
3559391Seric 	}
3569391Seric 
35752676Seric 	/* now actually close the connection and pick up the zombie */
35852676Seric 	i = endmailer(mci, m->m_argv[0]);
3599391Seric 	if (i != EX_OK)
36015535Seric 		syserr("smtpquit %s: stat %d", m->m_argv[0], i);
3614865Seric }
3624865Seric /*
363*54967Seric **  SMTPRSET -- send a RSET (reset) command
364*54967Seric */
365*54967Seric 
366*54967Seric smtprset(m, mci, e)
367*54967Seric 	register MAILER *m;
368*54967Seric 	register MCI *mci;
369*54967Seric 	ENVELOPE *e;
370*54967Seric {
371*54967Seric 	int r;
372*54967Seric 
373*54967Seric 	smtpmessage("RSET", m, mci);
374*54967Seric 	r = reply(m, mci, e);
375*54967Seric 	if (r < 0 || REPLYTYPE(r) == 4)
376*54967Seric 		return EX_TEMPFAIL;
377*54967Seric 	else if (REPLYTYPE(r) == 2)
378*54967Seric 		return EX_OK;
379*54967Seric 	else
380*54967Seric 		return EX_PROTOCOL;
381*54967Seric }
382*54967Seric /*
383*54967Seric **  SMTPNOOP -- send a NOOP (no operation) command to check the connection state
384*54967Seric */
385*54967Seric 
386*54967Seric smtpnoop(mci)
387*54967Seric 	register MCI *mci;
388*54967Seric {
389*54967Seric 	int r;
390*54967Seric 	MAILER *m = mci->mci_mailer;
391*54967Seric 	extern ENVELOPE BlankEnvelope;
392*54967Seric 	ENVELOPE *e = &BlankEnvelope;
393*54967Seric 
394*54967Seric 	smtpmessage("NOOP", m, mci);
395*54967Seric 	r = reply(m, mci, e);
396*54967Seric 	if (REPLYTYPE(r) != 2)
397*54967Seric 		smtpquit(m, mci, e);
398*54967Seric 	return r;
399*54967Seric }
400*54967Seric /*
4014684Seric **  REPLY -- read arpanet reply
4024684Seric **
4034684Seric **	Parameters:
40410175Seric **		m -- the mailer we are reading the reply from.
4054684Seric **
4064684Seric **	Returns:
4074684Seric **		reply code it reads.
4084684Seric **
4094684Seric **	Side Effects:
4104684Seric **		flushes the mail file.
4114684Seric */
4124684Seric 
41353751Seric reply(m, mci, e)
41453751Seric 	MAILER *m;
415*54967Seric 	MCI *mci;
41653751Seric 	ENVELOPE *e;
4174684Seric {
41853740Seric 	(void) fflush(mci->mci_out);
4194684Seric 
4207677Seric 	if (tTd(18, 1))
4214796Seric 		printf("reply\n");
4224796Seric 
4237356Seric 	/*
4247356Seric 	**  Read the input line, being careful not to hang.
4257356Seric 	*/
4267356Seric 
4274684Seric 	for (;;)
4284684Seric 	{
4294684Seric 		register int r;
4307356Seric 		register char *p;
43153751Seric 		extern time_t curtime();
4324684Seric 
4337685Seric 		/* actually do the read */
43453751Seric 		if (e->e_xfp != NULL)
43553751Seric 			(void) fflush(e->e_xfp);	/* for debugging */
4367356Seric 
43710054Seric 		/* if we are in the process of closing just give the code */
43853740Seric 		if (mci->mci_state == MCIS_CLOSED)
43910054Seric 			return (SMTPCLOSING);
44010054Seric 
44110054Seric 		/* get the line from the other side */
44253740Seric 		p = sfgets(SmtpReplyBuffer, sizeof SmtpReplyBuffer, mci->mci_in);
44353751Seric 		mci->mci_lastuse = curtime();
44453751Seric 
44510054Seric 		if (p == NULL)
44610131Seric 		{
44710148Seric 			extern char MsgBuf[];		/* err.c */
44810148Seric 			extern char Arpa_TSyserr[];	/* conf.c */
44910148Seric 
45021065Seric 			/* if the remote end closed early, fake an error */
45121065Seric 			if (errno == 0)
45221065Seric # ifdef ECONNRESET
45321065Seric 				errno = ECONNRESET;
45421065Seric # else ECONNRESET
45521065Seric 				errno = EPIPE;
45621065Seric # endif ECONNRESET
45721065Seric 
45810148Seric 			message(Arpa_TSyserr, "reply: read error");
45910420Seric 			/* if debugging, pause so we can see state */
46010420Seric 			if (tTd(18, 100))
46110420Seric 				pause();
46210148Seric # ifdef LOG
46336234Skarels 			syslog(LOG_INFO, "%s", &MsgBuf[4]);
46410148Seric # endif LOG
465*54967Seric 			mci->mci_state = MCIS_ERROR;
46653751Seric 			smtpquit(m, mci, e);
46710054Seric 			return (-1);
46810131Seric 		}
46910054Seric 		fixcrlf(SmtpReplyBuffer, TRUE);
47010054Seric 
47153751Seric 		if (e->e_xfp != NULL && index("45", SmtpReplyBuffer[0]) != NULL)
47214900Seric 		{
47314900Seric 			/* serious error -- log the previous command */
47414900Seric 			if (SmtpMsgBuffer[0] != '\0')
47553751Seric 				fprintf(e->e_xfp, ">>> %s\n", SmtpMsgBuffer);
47614900Seric 			SmtpMsgBuffer[0] = '\0';
47714900Seric 
47814900Seric 			/* now log the message as from the other side */
47953751Seric 			fprintf(e->e_xfp, "<<< %s\n", SmtpReplyBuffer);
48014900Seric 		}
48114900Seric 
48214900Seric 		/* display the input for verbose mode */
4837229Seric 		if (Verbose && !HoldErrs)
4849391Seric 			nmessage(Arpa_Info, "%s", SmtpReplyBuffer);
4857356Seric 
4867356Seric 		/* if continuation is required, we can go on */
4879391Seric 		if (SmtpReplyBuffer[3] == '-' || !isdigit(SmtpReplyBuffer[0]))
4884684Seric 			continue;
4897356Seric 
4907356Seric 		/* decode the reply code */
4919391Seric 		r = atoi(SmtpReplyBuffer);
4927356Seric 
4937356Seric 		/* extra semantics: 0xx codes are "informational" */
4944684Seric 		if (r < 100)
4954684Seric 			continue;
4967356Seric 
4979391Seric 		/* reply code 421 is "Service Shutting Down" */
49853740Seric 		if (r == SMTPCLOSING && mci->mci_state != MCIS_SSD)
4999391Seric 		{
50010054Seric 			/* send the quit protocol */
50153740Seric 			mci->mci_state = MCIS_SSD;
50253751Seric 			smtpquit(m, mci, e);
5039391Seric 		}
5049391Seric 
50521065Seric 		/* save temporary failure messages for posterity */
50621065Seric 		if (SmtpReplyBuffer[0] == '4' && SmtpError[0] == '\0')
50721065Seric 			(void) strcpy(SmtpError, &SmtpReplyBuffer[4]);
50821065Seric 
5094684Seric 		return (r);
5104684Seric 	}
5114684Seric }
5124796Seric /*
5134865Seric **  SMTPMESSAGE -- send message to server
5144796Seric **
5154796Seric **	Parameters:
5164796Seric **		f -- format
51710175Seric **		m -- the mailer to control formatting.
5184796Seric **		a, b, c -- parameters
5194796Seric **
5204796Seric **	Returns:
5214796Seric **		none.
5224796Seric **
5234796Seric **	Side Effects:
52453740Seric **		writes message to mci->mci_out.
5254796Seric */
5264796Seric 
5274865Seric /*VARARGS1*/
52853740Seric smtpmessage(f, m, mci, a, b, c)
5294796Seric 	char *f;
53010175Seric 	MAILER *m;
531*54967Seric 	MCI *mci;
5324796Seric {
53314900Seric 	(void) sprintf(SmtpMsgBuffer, f, a, b, c);
5347677Seric 	if (tTd(18, 1) || (Verbose && !HoldErrs))
53514900Seric 		nmessage(Arpa_Info, ">>> %s", SmtpMsgBuffer);
53653740Seric 	if (mci->mci_out != NULL)
53753740Seric 		fprintf(mci->mci_out, "%s%s", SmtpMsgBuffer,
538*54967Seric 			m == NULL ? "\r\n" : m->m_eol);
5394796Seric }
5405182Seric 
5415182Seric # endif SMTP
542