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