1 /*
2  * Copyright (c) 1983 Eric P. Allman
3  * Copyright (c) 1988 Regents of the University of California.
4  * All rights reserved.
5  *
6  * %sccs.include.redist.c%
7  */
8 
9 # include "sendmail.h"
10 
11 #ifndef lint
12 #ifdef SMTP
13 static char sccsid[] = "@(#)usersmtp.c	5.20.1.2 (Berkeley) 05/29/92 (with SMTP)";
14 #else
15 static char sccsid[] = "@(#)usersmtp.c	5.20.1.2 (Berkeley) 05/29/92 (without SMTP)";
16 #endif
17 #endif /* not lint */
18 
19 # include <sysexits.h>
20 # include <errno.h>
21 
22 # ifdef SMTP
23 
24 /*
25 **  USERSMTP -- run SMTP protocol from the user end.
26 **
27 **	This protocol is described in RFC821.
28 */
29 
30 #define REPLYTYPE(r)	((r) / 100)		/* first digit of reply code */
31 #define REPLYCLASS(r)	(((r) / 10) % 10)	/* second digit of reply code */
32 #define SMTPCLOSING	421			/* "Service Shutting Down" */
33 
34 char	SmtpMsgBuffer[MAXLINE];		/* buffer for commands */
35 char	SmtpReplyBuffer[MAXLINE];	/* buffer for replies */
36 char	SmtpError[MAXLINE] = "";	/* save failure error messages */
37 int	SmtpPid;			/* pid of mailer */
38 /*
39 **  SMTPINIT -- initialize SMTP.
40 **
41 **	Opens the connection and sends the initial protocol.
42 **
43 **	Parameters:
44 **		m -- mailer to create connection to.
45 **		pvp -- pointer to parameter vector to pass to
46 **			the mailer.
47 **
48 **	Returns:
49 **		appropriate exit status -- EX_OK on success.
50 **		If not EX_OK, it should close the connection.
51 **
52 **	Side Effects:
53 **		creates connection and sends initial protocol.
54 */
55 
56 jmp_buf	CtxGreeting;
57 
58 smtpinit(m, mci, pvp)
59 	struct mailer *m;
60 	MCONINFO *mci;
61 	char **pvp;
62 {
63 	register int r;
64 	EVENT *gte;
65 	register STAB *st;
66 	char buf[MAXNAME];
67 	static int greettimeout();
68 	extern STAB *stab();
69 	extern MCONINFO *openmailer();
70 
71 	/*
72 	**  Open the connection to the mailer.
73 	*/
74 
75 	if (mci->mci_state == MCIS_OPEN)
76 		syserr("smtpinit: already open");
77 
78 	mci->mci_state = MCIS_CLOSED;
79 	SmtpError[0] = '\0';
80 	SmtpPhase = "user open";
81 	setproctitle("%s %s: %s", CurEnv->e_id, pvp[1], SmtpPhase);
82 	mci = openmailer(m, pvp, (ADDRESS *) NULL, TRUE);
83 	if (SmtpPid < 0)
84 	{
85 		if (tTd(18, 1))
86 			printf("smtpinit: cannot open %s: stat %d errno %d\n",
87 			   pvp[0], ExitStat, errno);
88 		if (CurEnv->e_xfp != NULL)
89 		{
90 			register char *p;
91 			extern char *errstring();
92 			extern char *statstring();
93 
94 			if (errno == 0)
95 			{
96 				p = statstring(ExitStat);
97 				fprintf(CurEnv->e_xfp,
98 					"%.3s %s.%s... %s\n",
99 					p, pvp[1], m->m_name, p);
100 			}
101 			else
102 			{
103 				r = errno;
104 				fprintf(CurEnv->e_xfp,
105 					"421 %s.%s... Deferred: %s\n",
106 					pvp[1], m->m_name, errstring(errno));
107 				errno = r;
108 			}
109 		}
110 		return (ExitStat);
111 	}
112 	mci->mci_state = MCIS_OPEN;
113 
114 	/*
115 	**  Get the greeting message.
116 	**	This should appear spontaneously.  Give it five minutes to
117 	**	happen.
118 	*/
119 
120 	if (setjmp(CtxGreeting) != 0)
121 		goto tempfail1;
122 	gte = setevent((time_t) 300, greettimeout, 0);
123 	SmtpPhase = "greeting wait";
124 	setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase);
125 	r = reply(m, mci);
126 	clrevent(gte);
127 	if (r < 0 || REPLYTYPE(r) != 2)
128 		goto tempfail1;
129 
130 	/*
131 	**  Send the HELO command.
132 	**	My mother taught me to always introduce myself.
133 	*/
134 
135 	smtpmessage("HELO %s", m, MyHostName);
136 	SmtpPhase = "HELO wait";
137 	setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase);
138 	r = reply(m, mci);
139 	if (r < 0)
140 		goto tempfail1;
141 	else if (REPLYTYPE(r) == 5)
142 		goto unavailable;
143 	else if (REPLYTYPE(r) != 2)
144 		goto tempfail1;
145 
146 	/*
147 	**  If this is expected to be another sendmail, send some internal
148 	**  commands.
149 	*/
150 
151 	if (bitnset(M_INTERNAL, m->m_flags))
152 	{
153 		/* tell it to be verbose */
154 		smtpmessage("VERB", m);
155 		r = reply(m, mci);
156 		if (r < 0)
157 			goto tempfail2;
158 
159 		/* tell it we will be sending one transaction only */
160 		smtpmessage("ONEX", m);
161 		r = reply(m, mci);
162 		if (r < 0)
163 			goto tempfail2;
164 	}
165 
166 	/*
167 	**  Send the MAIL command.
168 	**	Designates the sender.
169 	*/
170 
171 	expand("\001<", buf, &buf[sizeof buf - 1], CurEnv);
172 	if (CurEnv->e_from.q_mailer == LocalMailer ||
173 	    !bitnset(M_FROMPATH, m->m_flags))
174 	{
175 		smtpmessage("MAIL From:<%s>", m, buf);
176 	}
177 	else
178 	{
179 		smtpmessage("MAIL From:<@%s%c%s>", m, MyHostName,
180 			buf[0] == '@' ? ',' : ':', buf);
181 	}
182 	SmtpPhase = "MAIL wait";
183 	setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase);
184 	r = reply(m, mci);
185 	if (r < 0 || REPLYTYPE(r) == 4)
186 		goto tempfail2;
187 	else if (r == 250)
188 		return (EX_OK);
189 	else if (r == 552)
190 		goto unavailable;
191 
192 	/* protocol error -- close up */
193 	smtpquit(m, mci);
194 	return (EX_PROTOCOL);
195 
196   tempfail1:
197 	/* log this as an error to avoid sure-to-be-void connections */
198 	mci->mci_exitstat = EX_TEMPFAIL;
199 	mci->mci_errno = errno;
200 
201   tempfail2:
202 	/* signal a temporary failure */
203 	smtpquit(m, mci);
204 	return (EX_TEMPFAIL);
205 
206   unavailable:
207 	/* signal service unavailable */
208 	smtpquit(m, mci);
209 	return (EX_UNAVAILABLE);
210 }
211 
212 
213 static
214 greettimeout()
215 {
216 	/* timeout reading the greeting message */
217 	longjmp(CtxGreeting, 1);
218 }
219 /*
220 **  SMTPRCPT -- designate recipient.
221 **
222 **	Parameters:
223 **		to -- address of recipient.
224 **		m -- the mailer we are sending to.
225 **
226 **	Returns:
227 **		exit status corresponding to recipient status.
228 **
229 **	Side Effects:
230 **		Sends the mail via SMTP.
231 */
232 
233 smtprcpt(to, m, mci)
234 	ADDRESS *to;
235 	register MAILER *m;
236 	MCONINFO *mci;
237 {
238 	register int r;
239 	extern char *remotename();
240 
241 	smtpmessage("RCPT To:<%s>", m, to->q_user);
242 
243 	SmtpPhase = "RCPT wait";
244 	setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase);
245 	r = reply(m, mci);
246 	if (r < 0 || REPLYTYPE(r) == 4)
247 		return (EX_TEMPFAIL);
248 	else if (REPLYTYPE(r) == 2)
249 		return (EX_OK);
250 	else if (r == 550 || r == 551 || r == 553)
251 		return (EX_NOUSER);
252 	else if (r == 552 || r == 554)
253 		return (EX_UNAVAILABLE);
254 	return (EX_PROTOCOL);
255 }
256 /*
257 **  SMTPDATA -- send the data and clean up the transaction.
258 **
259 **	Parameters:
260 **		m -- mailer being sent to.
261 **		e -- the envelope for this message.
262 **
263 **	Returns:
264 **		exit status corresponding to DATA command.
265 **
266 **	Side Effects:
267 **		none.
268 */
269 
270 smtpdata(m, mci, e)
271 	struct mailer *m;
272 	register MCONINFO *mci;
273 	register ENVELOPE *e;
274 {
275 	register int r;
276 
277 	/*
278 	**  Send the data.
279 	**	First send the command and check that it is ok.
280 	**	Then send the data.
281 	**	Follow it up with a dot to terminate.
282 	**	Finally get the results of the transaction.
283 	*/
284 
285 	/* send the command and check ok to proceed */
286 	smtpmessage("DATA", m);
287 	SmtpPhase = "DATA wait";
288 	setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase);
289 	r = reply(m, mci);
290 	if (r < 0 || REPLYTYPE(r) == 4)
291 		return (EX_TEMPFAIL);
292 	else if (r == 554)
293 		return (EX_UNAVAILABLE);
294 	else if (r != 354)
295 		return (EX_PROTOCOL);
296 
297 	/* now output the actual message */
298 	(*e->e_puthdr)(mci->mci_out, m, CurEnv);
299 	putline("\n", mci->mci_out, m);
300 	(*e->e_putbody)(mci->mci_out, m, CurEnv);
301 
302 	/* terminate the message */
303 	fprintf(mci->mci_out, ".%s", m->m_eol);
304 	if (Verbose && !HoldErrs)
305 		nmessage(Arpa_Info, ">>> .");
306 
307 	/* check for the results of the transaction */
308 	SmtpPhase = "result wait";
309 	setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase);
310 	r = reply(m, mci);
311 	if (r < 0 || REPLYTYPE(r) == 4)
312 		return (EX_TEMPFAIL);
313 	else if (r == 250)
314 		return (EX_OK);
315 	else if (r == 552 || r == 554)
316 		return (EX_UNAVAILABLE);
317 	return (EX_PROTOCOL);
318 }
319 /*
320 **  SMTPQUIT -- close the SMTP connection.
321 **
322 **	Parameters:
323 **		m -- a pointer to the mailer.
324 **
325 **	Returns:
326 **		none.
327 **
328 **	Side Effects:
329 **		sends the final protocol and closes the connection.
330 */
331 
332 smtpquit(mci, m)
333 	register MCONINFO *mci;
334 	register MAILER *m;
335 {
336 	int i;
337 
338 	/* if the connection is already closed, don't bother */
339 	if (mci->mci_state == MCIS_CLOSED)
340 		return;
341 
342 	/* send the quit message if not a forced quit */
343 	if (mci->mci_state == MCIS_OPEN || mci->mci_state == MCIS_SSD)
344 	{
345 		smtpmessage("QUIT", m);
346 		(void) reply(m, mci);
347 		if (mci->mci_state == MCIS_CLOSED)
348 			return;
349 	}
350 
351 	/* now actually close the connection and pick up the zombie */
352 	i = endmailer(mci, m->m_argv[0]);
353 	if (i != EX_OK)
354 		syserr("smtpquit %s: stat %d", m->m_argv[0], i);
355 }
356 /*
357 **  REPLY -- read arpanet reply
358 **
359 **	Parameters:
360 **		m -- the mailer we are reading the reply from.
361 **
362 **	Returns:
363 **		reply code it reads.
364 **
365 **	Side Effects:
366 **		flushes the mail file.
367 */
368 
369 reply(mci, m)
370 	MCONINFO *mci;
371 	MAILER *m;
372 {
373 	(void) fflush(mci->mci_out);
374 
375 	if (tTd(18, 1))
376 		printf("reply\n");
377 
378 	/*
379 	**  Read the input line, being careful not to hang.
380 	*/
381 
382 	for (;;)
383 	{
384 		register int r;
385 		register char *p;
386 
387 		/* actually do the read */
388 		if (CurEnv->e_xfp != NULL)
389 			(void) fflush(CurEnv->e_xfp);	/* for debugging */
390 
391 		/* if we are in the process of closing just give the code */
392 		if (mci->mci_state == MCIS_CLOSED)
393 			return (SMTPCLOSING);
394 
395 		/* get the line from the other side */
396 		p = sfgets(SmtpReplyBuffer, sizeof SmtpReplyBuffer, mci->mci_in);
397 		if (p == NULL)
398 		{
399 			extern char MsgBuf[];		/* err.c */
400 			extern char Arpa_TSyserr[];	/* conf.c */
401 
402 			/* if the remote end closed early, fake an error */
403 			if (errno == 0)
404 # ifdef ECONNRESET
405 				errno = ECONNRESET;
406 # else ECONNRESET
407 				errno = EPIPE;
408 # endif ECONNRESET
409 
410 			message(Arpa_TSyserr, "reply: read error");
411 			/* if debugging, pause so we can see state */
412 			if (tTd(18, 100))
413 				pause();
414 # ifdef LOG
415 			syslog(LOG_INFO, "%s", &MsgBuf[4]);
416 # endif LOG
417 			mci->mci_state = MCIS_CLOSED;
418 			smtpquit(m);
419 			return (-1);
420 		}
421 		fixcrlf(SmtpReplyBuffer, TRUE);
422 
423 		if (CurEnv->e_xfp != NULL && index("45", SmtpReplyBuffer[0]) != NULL)
424 		{
425 			/* serious error -- log the previous command */
426 			if (SmtpMsgBuffer[0] != '\0')
427 				fprintf(CurEnv->e_xfp, ">>> %s\n", SmtpMsgBuffer);
428 			SmtpMsgBuffer[0] = '\0';
429 
430 			/* now log the message as from the other side */
431 			fprintf(CurEnv->e_xfp, "<<< %s\n", SmtpReplyBuffer);
432 		}
433 
434 		/* display the input for verbose mode */
435 		if (Verbose && !HoldErrs)
436 			nmessage(Arpa_Info, "%s", SmtpReplyBuffer);
437 
438 		/* if continuation is required, we can go on */
439 		if (SmtpReplyBuffer[3] == '-' || !isdigit(SmtpReplyBuffer[0]))
440 			continue;
441 
442 		/* decode the reply code */
443 		r = atoi(SmtpReplyBuffer);
444 
445 		/* extra semantics: 0xx codes are "informational" */
446 		if (r < 100)
447 			continue;
448 
449 		/* reply code 421 is "Service Shutting Down" */
450 		if (r == SMTPCLOSING && mci->mci_state != MCIS_SSD)
451 		{
452 			/* send the quit protocol */
453 			mci->mci_state = MCIS_SSD;
454 			smtpquit(m);
455 		}
456 
457 		/* save temporary failure messages for posterity */
458 		if (SmtpReplyBuffer[0] == '4' && SmtpError[0] == '\0')
459 			(void) strcpy(SmtpError, &SmtpReplyBuffer[4]);
460 
461 		return (r);
462 	}
463 }
464 /*
465 **  SMTPMESSAGE -- send message to server
466 **
467 **	Parameters:
468 **		f -- format
469 **		m -- the mailer to control formatting.
470 **		a, b, c -- parameters
471 **
472 **	Returns:
473 **		none.
474 **
475 **	Side Effects:
476 **		writes message to mci->mci_out.
477 */
478 
479 /*VARARGS1*/
480 smtpmessage(f, m, mci, a, b, c)
481 	char *f;
482 	MAILER *m;
483 	MCONINFO *mci;
484 {
485 	(void) sprintf(SmtpMsgBuffer, f, a, b, c);
486 	if (tTd(18, 1) || (Verbose && !HoldErrs))
487 		nmessage(Arpa_Info, ">>> %s", SmtpMsgBuffer);
488 	if (mci->mci_out != NULL)
489 		fprintf(mci->mci_out, "%s%s", SmtpMsgBuffer,
490 			m == 0 ? "\r\n" : m->m_eol);
491 }
492 
493 # endif SMTP
494