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