1 # include <ctype.h>
2 # include <sysexits.h>
3 # include "sendmail.h"
4 
5 # ifndef SMTP
6 SCCSID(@(#)usersmtp.c	3.36		01/06/83	(no SMTP));
7 # else SMTP
8 
9 SCCSID(@(#)usersmtp.c	3.36		01/06/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 	extern char *canonname();
52 
53 	/*
54 	**  Open the connection to the mailer.
55 	*/
56 
57 	SmtpIn = SmtpOut = NULL;
58 	SmtpClosing = FALSE;
59 	SmtpPid = openmailer(m, pvp, (ADDRESS *) NULL, TRUE, &SmtpOut, &SmtpIn);
60 	if (SmtpPid < 0)
61 	{
62 # ifdef DEBUG
63 		if (tTd(18, 1))
64 			printf("smtpinit: cannot open %s: stat %d errno %d\n",
65 			   pvp[0], ExitStat, errno);
66 # endif DEBUG
67 		return (ExitStat);
68 	}
69 
70 	/*
71 	**  Get the greeting message.
72 	**	This should appear spontaneously.
73 	*/
74 
75 	r = reply(m);
76 	if (r < 0 || REPLYTYPE(r) != 2)
77 		return (EX_TEMPFAIL);
78 
79 	/*
80 	**  Send the HELO command.
81 	**	My mother taught me to always introduce myself.
82 	*/
83 
84 	smtpmessage("HELO %s", m, HostName);
85 	r = reply(m);
86 	if (r < 0)
87 		return (EX_TEMPFAIL);
88 	else if (REPLYTYPE(r) == 5)
89 		return (EX_UNAVAILABLE);
90 	else if (REPLYTYPE(r) != 2)
91 		return (EX_TEMPFAIL);
92 
93 	/*
94 	**  If this is expected to be another sendmail, send some internal
95 	**  commands.
96 	*/
97 
98 	if (bitset(M_INTERNAL, m->m_flags))
99 	{
100 		/* tell it to be verbose */
101 		smtpmessage("VERB", m);
102 		r = reply(m);
103 		if (r < 0)
104 			return (EX_TEMPFAIL);
105 
106 		/* tell it we will be sending one transaction only */
107 		smtpmessage("ONEX", m);
108 		r = reply(m);
109 		if (r < 0)
110 			return (EX_TEMPFAIL);
111 	}
112 
113 	/*
114 	**  Send the MAIL command.
115 	**	Designates the sender.
116 	*/
117 
118 	expand("$g", buf, &buf[sizeof buf - 1], CurEnv);
119 	if (CurEnv->e_from.q_mailer == LocalMailer ||
120 	    !bitset(M_FROMPATH, m->m_flags))
121 	{
122 		smtpmessage("MAIL From:<%s>", m, canonname(buf, 1));
123 	}
124 	else
125 	{
126 		smtpmessage("MAIL From:<@%s%c%s>", m, HostName,
127 			buf[0] == '@' ? ',' : ':', canonname(buf, 1));
128 	}
129 	r = reply(m);
130 	if (r < 0 || REPLYTYPE(r) == 4)
131 		return (EX_TEMPFAIL);
132 	else if (r == 250)
133 		return (EX_OK);
134 	else if (r == 552)
135 		return (EX_UNAVAILABLE);
136 	return (EX_PROTOCOL);
137 }
138 /*
139 **  SMTPRCPT -- designate recipient.
140 **
141 **	Parameters:
142 **		to -- address of recipient.
143 **		m -- the mailer we are sending to.
144 **
145 **	Returns:
146 **		exit status corresponding to recipient status.
147 **
148 **	Side Effects:
149 **		Sends the mail via SMTP.
150 */
151 
152 smtprcpt(to, m)
153 	ADDRESS *to;
154 	register MAILER *m;
155 {
156 	register int r;
157 	extern char *canonname();
158 
159 	smtpmessage("RCPT To:<%s>", m, canonname(to->q_user, 2));
160 
161 	r = reply(m);
162 	if (r < 0 || REPLYTYPE(r) == 4)
163 		return (EX_TEMPFAIL);
164 	else if (REPLYTYPE(r) == 2)
165 		return (EX_OK);
166 	else if (r == 550 || r == 551 || r == 553)
167 		return (EX_NOUSER);
168 	else if (r == 552 || r == 554)
169 		return (EX_UNAVAILABLE);
170 	return (EX_PROTOCOL);
171 }
172 /*
173 **  SMTPDATA -- send the data and clean up the transaction.
174 **
175 **	Parameters:
176 **		m -- mailer being sent to.
177 **		e -- the envelope for this message.
178 **
179 **	Returns:
180 **		exit status corresponding to DATA command.
181 **
182 **	Side Effects:
183 **		none.
184 */
185 
186 smtpdata(m, e)
187 	struct mailer *m;
188 	register ENVELOPE *e;
189 {
190 	register int r;
191 
192 	/*
193 	**  Send the data.
194 	**	First send the command and check that it is ok.
195 	**	Then send the data.
196 	**	Follow it up with a dot to terminate.
197 	**	Finally get the results of the transaction.
198 	*/
199 
200 	/* send the command and check ok to proceed */
201 	smtpmessage("DATA", m);
202 	r = reply(m);
203 	if (r < 0 || REPLYTYPE(r) == 4)
204 		return (EX_TEMPFAIL);
205 	else if (r == 554)
206 		return (EX_UNAVAILABLE);
207 	else if (r != 354)
208 		return (EX_PROTOCOL);
209 
210 	/* now output the actual message */
211 	(*e->e_puthdr)(SmtpOut, m, CurEnv);
212 	putline("\n", SmtpOut, m);
213 	(*e->e_putbody)(SmtpOut, m, CurEnv);
214 
215 	/* terminate the message */
216 	fprintf(SmtpOut, ".%s\n", bitset(M_CRLF, m->m_flags) ? "\r" : "");
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 LOG
317 			syslog(LOG_ERR, "%s", &MsgBuf[4]);
318 # endif LOG
319 			SmtpClosing = TRUE;
320 			smtpquit("reply error", m);
321 			return (-1);
322 		}
323 		fixcrlf(SmtpReplyBuffer, TRUE);
324 
325 		/* log the input in the transcript for future error returns */
326 		if (Verbose && !HoldErrs)
327 			nmessage(Arpa_Info, "%s", SmtpReplyBuffer);
328 		else if (CurEnv->e_xfp != NULL)
329 			fprintf(CurEnv->e_xfp, "%s\n", SmtpReplyBuffer);
330 
331 		/* if continuation is required, we can go on */
332 		if (SmtpReplyBuffer[3] == '-' || !isdigit(SmtpReplyBuffer[0]))
333 			continue;
334 
335 		/* decode the reply code */
336 		r = atoi(SmtpReplyBuffer);
337 
338 		/* extra semantics: 0xx codes are "informational" */
339 		if (r < 100)
340 			continue;
341 
342 		/* reply code 421 is "Service Shutting Down" */
343 		if (r == SMTPCLOSING)
344 		{
345 			/* send the quit protocol */
346 			smtpquit("SMTP Shutdown", m);
347 			SmtpClosing = TRUE;
348 		}
349 
350 		return (r);
351 	}
352 }
353 /*
354 **  SMTPMESSAGE -- send message to server
355 **
356 **	Parameters:
357 **		f -- format
358 **		m -- the mailer to control formatting.
359 **		a, b, c -- parameters
360 **
361 **	Returns:
362 **		none.
363 **
364 **	Side Effects:
365 **		writes message to SmtpOut.
366 */
367 
368 /*VARARGS1*/
369 smtpmessage(f, m, a, b, c)
370 	char *f;
371 	MAILER *m;
372 {
373 	char buf[100];
374 
375 	(void) sprintf(buf, f, a, b, c);
376 	if (tTd(18, 1) || (Verbose && !HoldErrs))
377 		nmessage(Arpa_Info, ">>> %s", buf);
378 	else if (CurEnv->e_xfp != NULL)
379 		fprintf(CurEnv->e_xfp, ">>> %s\n", buf);
380 	if (!SmtpClosing)
381 		fprintf(SmtpOut, "%s%s\n", bitset(M_CRLF, m->m_flags) ? "\r" : "", buf);
382 }
383 
384 # endif SMTP
385