1 # include <ctype.h>
2 # include <sysexits.h>
3 # include "sendmail.h"
4 
5 # ifndef SMTP
6 SCCSID(@(#)usersmtp.c	3.29		11/28/82	(no SMTP));
7 # else SMTP
8 
9 SCCSID(@(#)usersmtp.c	3.29		11/28/82);
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 static char	SmtpReplyBuffer[MAXLINE];	/* buffer for replies */
24 static FILE	*SmtpOut;			/* output file */
25 static FILE	*SmtpIn;			/* input file */
26 static int	SmtpPid;			/* pid of mailer */
27 static 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 **		ctladdr -- controlling address for this mailer.
38 **
39 **	Returns:
40 **		appropriate exit status -- EX_OK on success.
41 **
42 **	Side Effects:
43 **		creates connection and sends initial protocol.
44 */
45 
46 smtpinit(m, pvp, ctladdr)
47 	struct mailer *m;
48 	char **pvp;
49 	ADDRESS *ctladdr;
50 {
51 	register int r;
52 	char buf[MAXNAME];
53 	extern char *canonname();
54 
55 	/*
56 	**  Open the connection to the mailer.
57 	*/
58 
59 	SmtpIn = SmtpOut = NULL;
60 	SmtpPid = openmailer(m, pvp, ctladdr, TRUE, &SmtpOut, &SmtpIn);
61 	if (SmtpPid < 0)
62 	{
63 # ifdef DEBUG
64 		if (tTd(18, 1))
65 			printf("smtpinit: cannot open %s: stat %d errno %d\n",
66 			   pvp[0], ExitStat, errno);
67 # endif DEBUG
68 		return (ExitStat);
69 	}
70 
71 	/*
72 	**  Get the greeting message.
73 	**	This should appear spontaneously.
74 	*/
75 
76 	r = reply();
77 	if (r < 0 || REPLYTYPE(r) != 2)
78 		return (EX_TEMPFAIL);
79 
80 	/*
81 	**  Send the HELO command.
82 	**	My mother taught me to always introduce myself.
83 	*/
84 
85 	smtpmessage("HELO %s", HostName);
86 	r = reply();
87 	if (r < 0)
88 		return (EX_TEMPFAIL);
89 	else if (REPLYTYPE(r) == 5)
90 		return (EX_UNAVAILABLE);
91 	else if (REPLYTYPE(r) != 2)
92 		return (EX_TEMPFAIL);
93 
94 	/*
95 	**  If this is expected to be another sendmail, send some internal
96 	**  commands.
97 	*/
98 
99 	if (bitset(M_INTERNAL, m->m_flags))
100 	{
101 		/* tell it to be verbose */
102 		smtpmessage("VERB");
103 		r = reply();
104 		if (r < 0)
105 			return (EX_TEMPFAIL);
106 
107 		/* tell it we will be sending one transaction only */
108 		smtpmessage("ONEX");
109 		r = reply();
110 		if (r < 0)
111 			return (EX_TEMPFAIL);
112 	}
113 
114 	/*
115 	**  Send the MAIL command.
116 	**	Designates the sender.
117 	*/
118 
119 	expand("$g", buf, &buf[sizeof buf - 1], CurEnv);
120 	if (CurEnv->e_from.q_mailer == LocalMailer ||
121 	    !bitset(M_FULLSMTP, m->m_flags))
122 	{
123 		smtpmessage("MAIL From:<%s>", canonname(buf, 1));
124 	}
125 	else
126 	{
127 		smtpmessage("MAIL From:<@%s%c%s>", HostName,
128 			    buf[0] == '@' ? ',' : ':', canonname(buf, 1));
129 	}
130 	r = reply();
131 	if (r < 0 || REPLYTYPE(r) == 4)
132 		return (EX_TEMPFAIL);
133 	else if (r == 250)
134 		return (EX_OK);
135 	else if (r == 552)
136 		return (EX_UNAVAILABLE);
137 	return (EX_PROTOCOL);
138 }
139 /*
140 **  SMTPRCPT -- designate recipient.
141 **
142 **	Parameters:
143 **		to -- address of recipient.
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)
153 	ADDRESS *to;
154 {
155 	register int r;
156 	extern char *canonname();
157 
158 	smtpmessage("RCPT To:<%s>", canonname(to->q_user, 2));
159 
160 	r = reply();
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 **  SMTPFINISH -- finish up sending all the SMTP protocol.
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 smtpfinish(m, e)
186 	struct mailer *m;
187 	register ENVELOPE *e;
188 {
189 	register int r;
190 
191 	/*
192 	**  Send the data.
193 	**	Dot hiding is done here.
194 	*/
195 
196 	smtpmessage("DATA");
197 	r = reply();
198 	if (r < 0 || REPLYTYPE(r) == 4)
199 		return (EX_TEMPFAIL);
200 	else if (r == 554)
201 		return (EX_UNAVAILABLE);
202 	else if (r != 354)
203 		return (EX_PROTOCOL);
204 	(*e->e_puthdr)(SmtpOut, m, CurEnv);
205 	fprintf(SmtpOut, "\n");
206 	(*e->e_putbody)(SmtpOut, m, TRUE);
207 	smtpmessage(".");
208 	r = reply();
209 	if (r < 0 || REPLYTYPE(r) == 4)
210 		return (EX_TEMPFAIL);
211 	else if (r == 250)
212 		return (EX_OK);
213 	else if (r == 552 || r == 554)
214 		return (EX_UNAVAILABLE);
215 	return (EX_PROTOCOL);
216 }
217 /*
218 **  SMTPQUIT -- close the SMTP connection.
219 **
220 **	Parameters:
221 **		name -- name of mailer we are quitting.
222 **
223 **	Returns:
224 **		none.
225 **
226 **	Side Effects:
227 **		sends the final protocol and closes the connection.
228 */
229 
230 smtpquit(name)
231 	char *name;
232 {
233 	int i;
234 
235 	if (SmtpClosing)
236 	{
237 		SmtpClosing = FALSE;
238 		return;
239 	}
240 
241 	smtpmessage("QUIT");
242 	i = reply();
243 	if (i != 221)
244 		syserr("smtpquit %s: reply %d", name, i);
245 	(void) fclose(SmtpIn);
246 	(void) fclose(SmtpOut);
247 	i = endmailer(SmtpPid, name);
248 	if (i != EX_OK)
249 		syserr("smtpquit %s: stat %d", name, i);
250 }
251 /*
252 **  REPLY -- read arpanet reply
253 **
254 **	Parameters:
255 **		none.
256 **
257 **	Returns:
258 **		reply code it reads.
259 **
260 **	Side Effects:
261 **		flushes the mail file.
262 */
263 
264 reply()
265 {
266 	(void) fflush(SmtpOut);
267 
268 	if (tTd(18, 1))
269 		printf("reply\n");
270 
271 	/*
272 	**  Read the input line, being careful not to hang.
273 	*/
274 
275 	for (;;)
276 	{
277 		register int r;
278 		register char *p;
279 
280 		/* actually do the read */
281 		if (Xscript != NULL)
282 			(void) fflush(Xscript);		/* for debugging */
283 		if (!SmtpClosing)
284 		{
285 			p = sfgets(SmtpReplyBuffer, sizeof SmtpReplyBuffer, SmtpIn);
286 			if (p == NULL)
287 				return (-1);
288 			fixcrlf(SmtpReplyBuffer, TRUE);
289 		}
290 
291 		/* log the input in the transcript for future error returns */
292 		if (Verbose && !HoldErrs)
293 			nmessage(Arpa_Info, "%s", SmtpReplyBuffer);
294 		if (Xscript != NULL)
295 			fprintf(Xscript, "%s\n", SmtpReplyBuffer);
296 
297 		/* if continuation is required, we can go on */
298 		if (SmtpReplyBuffer[3] == '-' || !isdigit(SmtpReplyBuffer[0]))
299 			continue;
300 
301 		/* decode the reply code */
302 		r = atoi(SmtpReplyBuffer);
303 
304 		/* extra semantics: 0xx codes are "informational" */
305 		if (r < 100)
306 			continue;
307 
308 		/* reply code 421 is "Service Shutting Down" */
309 		if (r == SMTPCLOSING && !SmtpClosing)
310 		{
311 			smtpquit("SMTP Shutdown");
312 			SmtpClosing = TRUE;
313 		}
314 
315 		return (r);
316 	}
317 }
318 /*
319 **  SMTPMESSAGE -- send message to server
320 **
321 **	Parameters:
322 **		f -- format
323 **		a, b, c -- parameters
324 **
325 **	Returns:
326 **		none.
327 **
328 **	Side Effects:
329 **		writes message to SmtpOut.
330 */
331 
332 /*VARARGS1*/
333 smtpmessage(f, a, b, c)
334 	char *f;
335 {
336 	char buf[100];
337 
338 	(void) sprintf(buf, f, a, b, c);
339 	if (tTd(18, 1) || (Verbose && !HoldErrs))
340 		nmessage(Arpa_Info, ">>> %s", buf);
341 	if (Xscript != NULL)
342 		fprintf(Xscript, ">>> %s\n", buf);
343 	if (!SmtpClosing)
344 		fprintf(SmtpOut, "%s\r\n", buf);
345 }
346 
347 # endif SMTP
348