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