implement SMTP auto-shutdown on 421 codes; clean up some error processing
[unix-history] / usr / src / usr.sbin / sendmail / src / usersmtp.c
CommitLineData
42281a7d 1# include <ctype.h>
d2eb2478 2# include <sysexits.h>
e6b0a75b 3# include "sendmail.h"
d2eb2478 4
884a20cb 5# ifndef SMTP
80482eb5 6SCCSID(@(#)usersmtp.c 3.29 %G% (no SMTP));
884a20cb
EA
7# else SMTP
8
80482eb5
EA
9SCCSID(@(#)usersmtp.c 3.29 %G%);
10
11
d2eb2478
EA
12
13/*
80482eb5
EA
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
23static char SmtpReplyBuffer[MAXLINE]; /* buffer for replies */
24static FILE *SmtpOut; /* output file */
25static FILE *SmtpIn; /* input file */
26static int SmtpPid; /* pid of mailer */
27static bool SmtpClosing; /* set on a forced close */
28\f/*
e6b0a75b 29** SMTPINIT -- initialize SMTP.
d2eb2478 30**
e6b0a75b 31** Opens the connection and sends the initial protocol.
d2eb2478
EA
32**
33** Parameters:
e6b0a75b
EA
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.
d2eb2478
EA
38**
39** Returns:
e6b0a75b 40** appropriate exit status -- EX_OK on success.
d2eb2478
EA
41**
42** Side Effects:
e6b0a75b 43** creates connection and sends initial protocol.
d2eb2478
EA
44*/
45
e6b0a75b
EA
46smtpinit(m, pvp, ctladdr)
47 struct mailer *m;
48 char **pvp;
49 ADDRESS *ctladdr;
d2eb2478 50{
e6b0a75b
EA
51 register int r;
52 char buf[MAXNAME];
2439b900 53 extern char *canonname();
d2eb2478 54
e6b0a75b
EA
55 /*
56 ** Open the connection to the mailer.
57 */
d2eb2478 58
1ea752a1 59 SmtpIn = SmtpOut = NULL;
e6b0a75b 60 SmtpPid = openmailer(m, pvp, ctladdr, TRUE, &SmtpOut, &SmtpIn);
1ea752a1
EA
61 if (SmtpPid < 0)
62 {
1ea752a1 63# ifdef DEBUG
9678c96d 64 if (tTd(18, 1))
80482eb5
EA
65 printf("smtpinit: cannot open %s: stat %d errno %d\n",
66 pvp[0], ExitStat, errno);
1ea752a1
EA
67# endif DEBUG
68 return (ExitStat);
69 }
d2eb2478 70
e6b0a75b
EA
71 /*
72 ** Get the greeting message.
73 ** This should appear spontaneously.
74 */
3c6123ce 75
e6b0a75b 76 r = reply();
3ff6d543 77 if (r < 0 || REPLYTYPE(r) != 2)
e6b0a75b 78 return (EX_TEMPFAIL);
42281a7d 79
4a4ebe09
EA
80 /*
81 ** Send the HELO command.
4672197a 82 ** My mother taught me to always introduce myself.
4a4ebe09
EA
83 */
84
85 smtpmessage("HELO %s", HostName);
86 r = reply();
3ff6d543
EA
87 if (r < 0)
88 return (EX_TEMPFAIL);
89 else if (REPLYTYPE(r) == 5)
4a4ebe09 90 return (EX_UNAVAILABLE);
4672197a 91 else if (REPLYTYPE(r) != 2)
4a4ebe09
EA
92 return (EX_TEMPFAIL);
93
8fe4fb9b
EA
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
7d7fdf93
EA
114 /*
115 ** Send the HOPS command.
116 ** This is non-standard and may give an "unknown command".
117 ** This is not an error.
118 ** It can give a "bad hop count" error if the hop
119 ** count is exceeded.
120 */
121
e6b0a75b
EA
122 /*
123 ** Send the MAIL command.
124 ** Designates the sender.
125 */
42281a7d 126
dd1fe05b 127 expand("$g", buf, &buf[sizeof buf - 1], CurEnv);
6abb7b86
EA
128 if (CurEnv->e_from.q_mailer == LocalMailer ||
129 !bitset(M_FULLSMTP, m->m_flags))
130 {
131 smtpmessage("MAIL From:<%s>", canonname(buf, 1));
132 }
133 else
134 {
135 smtpmessage("MAIL From:<@%s%c%s>", HostName,
136 buf[0] == '@' ? ',' : ':', canonname(buf, 1));
137 }
e6b0a75b 138 r = reply();
3ff6d543 139 if (r < 0 || REPLYTYPE(r) == 4)
e6b0a75b 140 return (EX_TEMPFAIL);
4672197a
EA
141 else if (r == 250)
142 return (EX_OK);
143 else if (r == 552)
144 return (EX_UNAVAILABLE);
977ccbaf 145 return (EX_PROTOCOL);
d2eb2478
EA
146}
147\f/*
4a4ebe09 148** SMTPRCPT -- designate recipient.
3c6123ce
EA
149**
150** Parameters:
e6b0a75b 151** to -- address of recipient.
3c6123ce
EA
152**
153** Returns:
e6b0a75b 154** exit status corresponding to recipient status.
3c6123ce
EA
155**
156** Side Effects:
e6b0a75b 157** Sends the mail via SMTP.
3c6123ce
EA
158*/
159
4a4ebe09 160smtprcpt(to)
e6b0a75b 161 ADDRESS *to;
3c6123ce
EA
162{
163 register int r;
2439b900 164 extern char *canonname();
3c6123ce 165
0d724131 166 smtpmessage("RCPT To:<%s>", canonname(to->q_user, 2));
e6b0a75b 167
3c6123ce 168 r = reply();
3ff6d543 169 if (r < 0 || REPLYTYPE(r) == 4)
e6b0a75b 170 return (EX_TEMPFAIL);
4672197a
EA
171 else if (REPLYTYPE(r) == 2)
172 return (EX_OK);
977ccbaf
EA
173 else if (r == 550 || r == 551 || r == 553)
174 return (EX_NOUSER);
175 else if (r == 552 || r == 554)
176 return (EX_UNAVAILABLE);
177 return (EX_PROTOCOL);
3c6123ce
EA
178}
179\f/*
e6b0a75b 180** SMTPFINISH -- finish up sending all the SMTP protocol.
d2eb2478
EA
181**
182** Parameters:
e6b0a75b 183** m -- mailer being sent to.
dd1fe05b 184** e -- the envelope for this message.
d2eb2478
EA
185**
186** Returns:
4a4ebe09 187** exit status corresponding to DATA command.
d2eb2478
EA
188**
189** Side Effects:
e6b0a75b 190** none.
d2eb2478
EA
191*/
192
dd1fe05b 193smtpfinish(m, e)
e6b0a75b 194 struct mailer *m;
dd1fe05b 195 register ENVELOPE *e;
d2eb2478
EA
196{
197 register int r;
d2eb2478 198
3c6123ce
EA
199 /*
200 ** Send the data.
201 ** Dot hiding is done here.
202 */
203
e6b0a75b 204 smtpmessage("DATA");
42281a7d 205 r = reply();
3ff6d543 206 if (r < 0 || REPLYTYPE(r) == 4)
3c6123ce 207 return (EX_TEMPFAIL);
4672197a
EA
208 else if (r == 554)
209 return (EX_UNAVAILABLE);
210 else if (r != 354)
977ccbaf 211 return (EX_PROTOCOL);
dd1fe05b
EA
212 (*e->e_puthdr)(SmtpOut, m, CurEnv);
213 fprintf(SmtpOut, "\n");
214 (*e->e_putbody)(SmtpOut, m, TRUE);
e6b0a75b 215 smtpmessage(".");
42281a7d 216 r = reply();
3ff6d543 217 if (r < 0 || REPLYTYPE(r) == 4)
3c6123ce 218 return (EX_TEMPFAIL);
4672197a
EA
219 else if (r == 250)
220 return (EX_OK);
221 else if (r == 552 || r == 554)
222 return (EX_UNAVAILABLE);
977ccbaf 223 return (EX_PROTOCOL);
d2eb2478
EA
224}
225\f/*
e6b0a75b
EA
226** SMTPQUIT -- close the SMTP connection.
227**
228** Parameters:
229** name -- name of mailer we are quitting.
230**
231** Returns:
232** none.
233**
234** Side Effects:
235** sends the final protocol and closes the connection.
236*/
237
80482eb5 238smtpquit(name)
e6b0a75b
EA
239 char *name;
240{
80482eb5 241 int i;
e6b0a75b 242
80482eb5
EA
243 if (SmtpClosing)
244 {
245 SmtpClosing = FALSE;
65a4771c 246 return;
80482eb5
EA
247 }
248
65a4771c 249 smtpmessage("QUIT");
80482eb5
EA
250 i = reply();
251 if (i != 221)
252 syserr("smtpquit %s: reply %d", name, i);
65a4771c
EA
253 (void) fclose(SmtpIn);
254 (void) fclose(SmtpOut);
255 i = endmailer(SmtpPid, name);
80482eb5
EA
256 if (i != EX_OK)
257 syserr("smtpquit %s: stat %d", name, i);
e6b0a75b
EA
258}
259\f/*
d2eb2478
EA
260** REPLY -- read arpanet reply
261**
262** Parameters:
42281a7d 263** none.
d2eb2478
EA
264**
265** Returns:
266** reply code it reads.
267**
268** Side Effects:
269** flushes the mail file.
270*/
271
42281a7d 272reply()
d2eb2478 273{
e6b0a75b 274 (void) fflush(SmtpOut);
42281a7d 275
9678c96d 276 if (tTd(18, 1))
42281a7d 277 printf("reply\n");
d2eb2478 278
37eaaadb
EA
279 /*
280 ** Read the input line, being careful not to hang.
281 */
282
d2eb2478
EA
283 for (;;)
284 {
d2eb2478 285 register int r;
37eaaadb
EA
286 register char *p;
287
37eaaadb 288 /* actually do the read */
e6f08ab1
EA
289 if (Xscript != NULL)
290 (void) fflush(Xscript); /* for debugging */
80482eb5
EA
291 if (!SmtpClosing)
292 {
293 p = sfgets(SmtpReplyBuffer, sizeof SmtpReplyBuffer, SmtpIn);
294 if (p == NULL)
295 return (-1);
296 fixcrlf(SmtpReplyBuffer, TRUE);
297 }
37eaaadb
EA
298
299 /* log the input in the transcript for future error returns */
65a4771c 300 if (Verbose && !HoldErrs)
80482eb5 301 nmessage(Arpa_Info, "%s", SmtpReplyBuffer);
e6f08ab1 302 if (Xscript != NULL)
80482eb5 303 fprintf(Xscript, "%s\n", SmtpReplyBuffer);
37eaaadb
EA
304
305 /* if continuation is required, we can go on */
80482eb5 306 if (SmtpReplyBuffer[3] == '-' || !isdigit(SmtpReplyBuffer[0]))
d2eb2478 307 continue;
37eaaadb
EA
308
309 /* decode the reply code */
80482eb5 310 r = atoi(SmtpReplyBuffer);
37eaaadb
EA
311
312 /* extra semantics: 0xx codes are "informational" */
d2eb2478
EA
313 if (r < 100)
314 continue;
37eaaadb 315
80482eb5
EA
316 /* reply code 421 is "Service Shutting Down" */
317 if (r == SMTPCLOSING && !SmtpClosing)
318 {
319 smtpquit("SMTP Shutdown");
320 SmtpClosing = TRUE;
321 }
322
d2eb2478
EA
323 return (r);
324 }
325}
42281a7d 326\f/*
e6b0a75b 327** SMTPMESSAGE -- send message to server
42281a7d
EA
328**
329** Parameters:
330** f -- format
331** a, b, c -- parameters
332**
333** Returns:
334** none.
335**
336** Side Effects:
e6b0a75b 337** writes message to SmtpOut.
42281a7d
EA
338*/
339
e6b0a75b
EA
340/*VARARGS1*/
341smtpmessage(f, a, b, c)
42281a7d
EA
342 char *f;
343{
344 char buf[100];
345
e6b0a75b 346 (void) sprintf(buf, f, a, b, c);
9678c96d 347 if (tTd(18, 1) || (Verbose && !HoldErrs))
f4aec471 348 nmessage(Arpa_Info, ">>> %s", buf);
e6f08ab1
EA
349 if (Xscript != NULL)
350 fprintf(Xscript, ">>> %s\n", buf);
80482eb5
EA
351 if (!SmtpClosing)
352 fprintf(SmtpOut, "%s\r\n", buf);
42281a7d 353}
884a20cb
EA
354
355# endif SMTP