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