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