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