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