Commit | Line | Data |
---|---|---|
b2a81223 | 1 | /* |
dc45ba8c | 2 | * Copyright (c) 1983 Eric P. Allman |
24634489 KB |
3 | * Copyright (c) 1988, 1993 |
4 | * The Regents of the University of California. All rights reserved. | |
bee79b64 | 5 | * |
0d77f9d8 | 6 | * %sccs.include.redist.c% |
bee79b64 | 7 | */ |
b2a81223 | 8 | |
e6b0a75b | 9 | # include "sendmail.h" |
d2eb2478 | 10 | |
bee79b64 KB |
11 | #ifndef lint |
12 | #ifdef SMTP | |
4990fc15 | 13 | static char sccsid[] = "@(#)usersmtp.c 8.13 (Berkeley) %G% (with SMTP)"; |
bee79b64 | 14 | #else |
4990fc15 | 15 | static char sccsid[] = "@(#)usersmtp.c 8.13 (Berkeley) %G% (without SMTP)"; |
bee79b64 KB |
16 | #endif |
17 | #endif /* not lint */ | |
80482eb5 | 18 | |
bee79b64 KB |
19 | # include <sysexits.h> |
20 | # include <errno.h> | |
80482eb5 | 21 | |
bee79b64 | 22 | # ifdef SMTP |
d2eb2478 EA |
23 | |
24 | /* | |
80482eb5 EA |
25 | ** USERSMTP -- run SMTP protocol from the user end. |
26 | ** | |
27 | ** This protocol is described in RFC821. | |
28 | */ | |
29 | ||
30 | #define REPLYTYPE(r) ((r) / 100) /* first digit of reply code */ | |
31 | #define REPLYCLASS(r) (((r) / 10) % 10) /* second digit of reply code */ | |
32 | #define SMTPCLOSING 421 /* "Service Shutting Down" */ | |
33 | ||
d7dbe85b | 34 | char SmtpMsgBuffer[MAXLINE]; /* buffer for commands */ |
d0729491 | 35 | char SmtpReplyBuffer[MAXLINE]; /* buffer for replies */ |
46f6ec52 | 36 | char SmtpError[MAXLINE] = ""; /* save failure error messages */ |
d0729491 | 37 | int SmtpPid; /* pid of mailer */ |
337a0030 | 38 | bool SmtpNeedIntro; /* need "while talking" in transcript */ |
16133a1c EA |
39 | |
40 | #ifdef __STDC__ | |
41 | extern smtpmessage(char *f, MAILER *m, MCI *mci, ...); | |
42 | #endif | |
80482eb5 | 43 | \f/* |
e6b0a75b | 44 | ** SMTPINIT -- initialize SMTP. |
d2eb2478 | 45 | ** |
e6b0a75b | 46 | ** Opens the connection and sends the initial protocol. |
d2eb2478 EA |
47 | ** |
48 | ** Parameters: | |
e6b0a75b EA |
49 | ** m -- mailer to create connection to. |
50 | ** pvp -- pointer to parameter vector to pass to | |
51 | ** the mailer. | |
d2eb2478 EA |
52 | ** |
53 | ** Returns: | |
f2e44ded | 54 | ** none. |
d2eb2478 EA |
55 | ** |
56 | ** Side Effects: | |
e6b0a75b | 57 | ** creates connection and sends initial protocol. |
d2eb2478 EA |
58 | */ |
59 | ||
f2e44ded | 60 | smtpinit(m, mci, e) |
e6b0a75b | 61 | struct mailer *m; |
f2e44ded | 62 | register MCI *mci; |
e62e1144 | 63 | ENVELOPE *e; |
d2eb2478 | 64 | { |
e6b0a75b | 65 | register int r; |
a9bac7a9 | 66 | register char *p; |
77c24e97 | 67 | extern void esmtp_check(); |
3001ca78 | 68 | extern void helo_options(); |
d2eb2478 | 69 | |
8e5c6745 | 70 | if (tTd(18, 1)) |
474bc899 EA |
71 | { |
72 | printf("smtpinit "); | |
deff97fd | 73 | mci_dump(mci, FALSE); |
474bc899 EA |
74 | } |
75 | ||
e6b0a75b EA |
76 | /* |
77 | ** Open the connection to the mailer. | |
78 | */ | |
d2eb2478 | 79 | |
46f6ec52 | 80 | SmtpError[0] = '\0'; |
474bc899 | 81 | CurHostName = mci->mci_host; /* XXX UGLY XXX */ |
337a0030 | 82 | SmtpNeedIntro = TRUE; |
f2e44ded | 83 | switch (mci->mci_state) |
1ea752a1 | 84 | { |
f2e44ded EA |
85 | case MCIS_ACTIVE: |
86 | /* need to clear old information */ | |
87 | smtprset(m, mci, e); | |
263c5860 | 88 | /* fall through */ |
46f6ec52 | 89 | |
f2e44ded EA |
90 | case MCIS_OPEN: |
91 | return; | |
92 | ||
93 | case MCIS_ERROR: | |
94 | case MCIS_SSD: | |
95 | /* shouldn't happen */ | |
96 | smtpquit(m, mci, e); | |
263c5860 | 97 | /* fall through */ |
f2e44ded EA |
98 | |
99 | case MCIS_CLOSED: | |
b6edea3d | 100 | syserr("451 smtpinit: state CLOSED"); |
f2e44ded EA |
101 | return; |
102 | ||
103 | case MCIS_OPENING: | |
104 | break; | |
1ea752a1 | 105 | } |
d2eb2478 | 106 | |
f2e44ded EA |
107 | mci->mci_state = MCIS_OPENING; |
108 | ||
e6b0a75b EA |
109 | /* |
110 | ** Get the greeting message. | |
057a9c5d | 111 | ** This should appear spontaneously. Give it five minutes to |
e4581b86 | 112 | ** happen. |
e6b0a75b | 113 | */ |
3c6123ce | 114 | |
8e948497 | 115 | SmtpPhase = mci->mci_phase = "client greeting"; |
e62e1144 | 116 | setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); |
77c24e97 | 117 | r = reply(m, mci, e, TimeOuts.to_initial, esmtp_check); |
4990fc15 | 118 | if (r < 0 || REPLYTYPE(r) == 4) |
07cb6633 | 119 | goto tempfail1; |
4990fc15 EA |
120 | if (REPLYTYPE(r) != 2) |
121 | goto unavailable; | |
42281a7d | 122 | |
4a4ebe09 EA |
123 | /* |
124 | ** Send the HELO command. | |
4672197a | 125 | ** My mother taught me to always introduce myself. |
4a4ebe09 EA |
126 | */ |
127 | ||
3001ca78 EA |
128 | if (bitnset(M_ESMTP, m->m_flags)) |
129 | mci->mci_flags |= MCIF_ESMTP; | |
130 | ||
131 | tryhelo: | |
132 | if (bitset(MCIF_ESMTP, mci->mci_flags)) | |
133 | { | |
134 | smtpmessage("EHLO %s", m, mci, MyHostName); | |
8e948497 | 135 | SmtpPhase = mci->mci_phase = "client EHLO"; |
3001ca78 EA |
136 | } |
137 | else | |
138 | { | |
139 | smtpmessage("HELO %s", m, mci, MyHostName); | |
8e948497 | 140 | SmtpPhase = mci->mci_phase = "client HELO"; |
3001ca78 | 141 | } |
e62e1144 | 142 | setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); |
3001ca78 | 143 | r = reply(m, mci, e, TimeOuts.to_helo, helo_options); |
3ff6d543 | 144 | if (r < 0) |
07cb6633 | 145 | goto tempfail1; |
3ff6d543 | 146 | else if (REPLYTYPE(r) == 5) |
3001ca78 EA |
147 | { |
148 | if (bitset(MCIF_ESMTP, mci->mci_flags)) | |
149 | { | |
150 | /* try old SMTP instead */ | |
151 | mci->mci_flags &= ~MCIF_ESMTP; | |
152 | goto tryhelo; | |
153 | } | |
057a9c5d | 154 | goto unavailable; |
3001ca78 | 155 | } |
4672197a | 156 | else if (REPLYTYPE(r) != 2) |
07cb6633 | 157 | goto tempfail1; |
4a4ebe09 | 158 | |
a9bac7a9 EA |
159 | /* |
160 | ** Check to see if we actually ended up talking to ourself. | |
161 | ** This means we didn't know about an alias or MX, or we managed | |
162 | ** to connect to an echo server. | |
28b7d7e1 EA |
163 | ** |
164 | ** If this code remains at all, "CheckLoopBack" should be | |
165 | ** a mailer flag. This is a MAYBENEXTRELEASE feature. | |
a9bac7a9 EA |
166 | */ |
167 | ||
f4900dab | 168 | p = strchr(&SmtpReplyBuffer[4], ' '); |
a9bac7a9 | 169 | if (p != NULL) |
a3934270 | 170 | *p = '\0'; |
76c07648 | 171 | if (CheckLoopBack && strcasecmp(&SmtpReplyBuffer[4], MyHostName) == 0) |
a9bac7a9 EA |
172 | { |
173 | syserr("553 %s config error: mail loops back to myself", | |
174 | MyHostName); | |
175 | mci->mci_exitstat = EX_CONFIG; | |
176 | mci->mci_errno = 0; | |
177 | smtpquit(m, mci, e); | |
178 | return; | |
179 | } | |
180 | ||
8fe4fb9b EA |
181 | /* |
182 | ** If this is expected to be another sendmail, send some internal | |
183 | ** commands. | |
184 | */ | |
185 | ||
1dbda134 | 186 | if (bitnset(M_INTERNAL, m->m_flags)) |
8fe4fb9b EA |
187 | { |
188 | /* tell it to be verbose */ | |
e62e1144 | 189 | smtpmessage("VERB", m, mci); |
3001ca78 | 190 | r = reply(m, mci, e, TimeOuts.to_miscshort, NULL); |
8fe4fb9b | 191 | if (r < 0) |
07cb6633 | 192 | goto tempfail2; |
8fe4fb9b EA |
193 | } |
194 | ||
e62e1144 | 195 | mci->mci_state = MCIS_OPEN; |
f2e44ded | 196 | return; |
e62e1144 EA |
197 | |
198 | tempfail1: | |
199 | tempfail2: | |
200 | mci->mci_exitstat = EX_TEMPFAIL; | |
474bc899 EA |
201 | if (mci->mci_errno == 0) |
202 | mci->mci_errno = errno; | |
203 | if (mci->mci_state != MCIS_CLOSED) | |
204 | smtpquit(m, mci, e); | |
f2e44ded | 205 | return; |
e62e1144 EA |
206 | |
207 | unavailable: | |
208 | mci->mci_exitstat = EX_UNAVAILABLE; | |
209 | mci->mci_errno = errno; | |
210 | smtpquit(m, mci, e); | |
f2e44ded | 211 | return; |
e62e1144 | 212 | } |
3001ca78 | 213 | \f/* |
77c24e97 EA |
214 | ** ESMTP_CHECK -- check to see if this implementation likes ESMTP protocol |
215 | ** | |
216 | ** | |
217 | ** Parameters: | |
218 | ** line -- the response line. | |
219 | ** m -- the mailer. | |
220 | ** mci -- the mailer connection info. | |
221 | ** e -- the envelope. | |
222 | ** | |
223 | ** Returns: | |
224 | ** none. | |
225 | */ | |
226 | ||
227 | void | |
228 | esmtp_check(line, m, mci, e) | |
229 | char *line; | |
230 | MAILER *m; | |
231 | register MCI *mci; | |
232 | ENVELOPE *e; | |
233 | { | |
234 | if (strlen(line) < 5) | |
235 | return; | |
236 | line += 4; | |
237 | if (strncmp(line, "ESMTP ", 6) == 0) | |
238 | mci->mci_flags |= MCIF_ESMTP; | |
239 | } | |
240 | \f/* | |
3001ca78 EA |
241 | ** HELO_OPTIONS -- process the options on a HELO line. |
242 | ** | |
243 | ** Parameters: | |
244 | ** line -- the response line. | |
245 | ** m -- the mailer. | |
246 | ** mci -- the mailer connection info. | |
247 | ** e -- the envelope. | |
248 | ** | |
249 | ** Returns: | |
250 | ** none. | |
251 | */ | |
252 | ||
253 | void | |
254 | helo_options(line, m, mci, e) | |
255 | char *line; | |
256 | MAILER *m; | |
257 | register MCI *mci; | |
258 | ENVELOPE *e; | |
259 | { | |
260 | register char *p; | |
261 | ||
262 | if (strlen(line) < 5) | |
263 | return; | |
264 | line += 4; | |
265 | p = strchr(line, ' '); | |
266 | if (p != NULL) | |
267 | *p++ = '\0'; | |
268 | if (strcasecmp(line, "size") == 0) | |
269 | { | |
270 | mci->mci_flags |= MCIF_SIZE; | |
271 | if (p != NULL) | |
272 | mci->mci_maxsize = atol(p); | |
273 | } | |
274 | else if (strcasecmp(line, "8bitmime") == 0) | |
275 | mci->mci_flags |= MCIF_8BITMIME; | |
276 | else if (strcasecmp(line, "expn") == 0) | |
277 | mci->mci_flags |= MCIF_EXPN; | |
278 | } | |
279 | \f/* | |
280 | ** SMTPMAILFROM -- send MAIL command | |
281 | ** | |
282 | ** Parameters: | |
283 | ** m -- the mailer. | |
284 | ** mci -- the mailer connection structure. | |
285 | ** e -- the envelope (including the sender to specify). | |
286 | */ | |
e62e1144 EA |
287 | |
288 | smtpmailfrom(m, mci, e) | |
289 | struct mailer *m; | |
f2e44ded | 290 | MCI *mci; |
e62e1144 EA |
291 | ENVELOPE *e; |
292 | { | |
293 | int r; | |
294 | char buf[MAXNAME]; | |
3001ca78 | 295 | char optbuf[MAXLINE]; |
e62e1144 | 296 | |
8e5c6745 | 297 | if (tTd(18, 2)) |
322eceee EA |
298 | printf("smtpmailfrom: CurHost=%s\n", CurHostName); |
299 | ||
3001ca78 | 300 | /* set up appropriate options to include */ |
1a139f8c | 301 | if (bitset(MCIF_SIZE, mci->mci_flags) && e->e_msgsize > 0) |
3001ca78 EA |
302 | sprintf(optbuf, " SIZE=%ld", e->e_msgsize); |
303 | else | |
304 | strcpy(optbuf, ""); | |
305 | ||
7d7fdf93 EA |
306 | /* |
307 | ** Send the HOPS command. | |
308 | ** This is non-standard and may give an "unknown command". | |
309 | ** This is not an error. | |
310 | ** It can give a "bad hop count" error if the hop | |
311 | ** count is exceeded. | |
312 | */ | |
313 | ||
e6b0a75b EA |
314 | /* |
315 | ** Send the MAIL command. | |
316 | ** Designates the sender. | |
317 | */ | |
42281a7d | 318 | |
e62e1144 EA |
319 | mci->mci_state = MCIS_ACTIVE; |
320 | ||
2cd2111f EA |
321 | if (bitset(EF_RESPONSE, e->e_flags) && |
322 | !bitnset(M_NO_NULL_FROM, m->m_flags)) | |
bc854e30 EA |
323 | (void) strcpy(buf, ""); |
324 | else | |
325 | expand("\201g", buf, &buf[sizeof buf - 1], e); | |
e62e1144 | 326 | if (e->e_from.q_mailer == LocalMailer || |
1dbda134 | 327 | !bitnset(M_FROMPATH, m->m_flags)) |
6abb7b86 | 328 | { |
3001ca78 | 329 | smtpmessage("MAIL From:<%s>%s", m, mci, buf, optbuf); |
6abb7b86 EA |
330 | } |
331 | else | |
332 | { | |
3001ca78 EA |
333 | smtpmessage("MAIL From:<@%s%c%s>%s", m, mci, MyHostName, |
334 | buf[0] == '@' ? ',' : ':', buf, optbuf); | |
6abb7b86 | 335 | } |
8e948497 | 336 | SmtpPhase = mci->mci_phase = "client MAIL"; |
e62e1144 | 337 | setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); |
3001ca78 | 338 | r = reply(m, mci, e, TimeOuts.to_mail, NULL); |
3ff6d543 | 339 | if (r < 0 || REPLYTYPE(r) == 4) |
e62e1144 EA |
340 | { |
341 | mci->mci_exitstat = EX_TEMPFAIL; | |
342 | mci->mci_errno = errno; | |
343 | smtpquit(m, mci, e); | |
e62e1144 EA |
344 | return EX_TEMPFAIL; |
345 | } | |
4672197a | 346 | else if (r == 250) |
e62e1144 EA |
347 | { |
348 | mci->mci_exitstat = EX_OK; | |
349 | return EX_OK; | |
350 | } | |
4672197a | 351 | else if (r == 552) |
e62e1144 EA |
352 | { |
353 | /* signal service unavailable */ | |
354 | mci->mci_exitstat = EX_UNAVAILABLE; | |
355 | smtpquit(m, mci, e); | |
e62e1144 EA |
356 | return EX_UNAVAILABLE; |
357 | } | |
057a9c5d | 358 | |
51d9cc47 | 359 | #ifdef LOG |
68f7099c | 360 | if (LogLevel > 1) |
51d9cc47 EA |
361 | { |
362 | syslog(LOG_CRIT, "%s: SMTP MAIL protocol error: %s", | |
363 | e->e_id, SmtpReplyBuffer); | |
364 | } | |
365 | #endif | |
366 | ||
057a9c5d | 367 | /* protocol error -- close up */ |
e62e1144 EA |
368 | smtpquit(m, mci, e); |
369 | mci->mci_exitstat = EX_PROTOCOL; | |
e62e1144 | 370 | return EX_PROTOCOL; |
d2eb2478 EA |
371 | } |
372 | \f/* | |
4a4ebe09 | 373 | ** SMTPRCPT -- designate recipient. |
3c6123ce EA |
374 | ** |
375 | ** Parameters: | |
e6b0a75b | 376 | ** to -- address of recipient. |
4db45bf1 | 377 | ** m -- the mailer we are sending to. |
474bc899 EA |
378 | ** mci -- the connection info for this transaction. |
379 | ** e -- the envelope for this transaction. | |
3c6123ce EA |
380 | ** |
381 | ** Returns: | |
e6b0a75b | 382 | ** exit status corresponding to recipient status. |
3c6123ce EA |
383 | ** |
384 | ** Side Effects: | |
e6b0a75b | 385 | ** Sends the mail via SMTP. |
3c6123ce EA |
386 | */ |
387 | ||
e62e1144 | 388 | smtprcpt(to, m, mci, e) |
e6b0a75b | 389 | ADDRESS *to; |
4db45bf1 | 390 | register MAILER *m; |
f2e44ded | 391 | MCI *mci; |
e62e1144 | 392 | ENVELOPE *e; |
3c6123ce EA |
393 | { |
394 | register int r; | |
395 | ||
e62e1144 | 396 | smtpmessage("RCPT To:<%s>", m, mci, to->q_user); |
e6b0a75b | 397 | |
8e948497 | 398 | SmtpPhase = mci->mci_phase = "client RCPT"; |
e62e1144 | 399 | setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); |
3001ca78 | 400 | r = reply(m, mci, e, TimeOuts.to_rcpt, NULL); |
3ff6d543 | 401 | if (r < 0 || REPLYTYPE(r) == 4) |
e6b0a75b | 402 | return (EX_TEMPFAIL); |
4672197a EA |
403 | else if (REPLYTYPE(r) == 2) |
404 | return (EX_OK); | |
977ccbaf EA |
405 | else if (r == 550 || r == 551 || r == 553) |
406 | return (EX_NOUSER); | |
407 | else if (r == 552 || r == 554) | |
408 | return (EX_UNAVAILABLE); | |
51d9cc47 EA |
409 | |
410 | #ifdef LOG | |
68f7099c | 411 | if (LogLevel > 1) |
51d9cc47 EA |
412 | { |
413 | syslog(LOG_CRIT, "%s: SMTP RCPT protocol error: %s", | |
414 | e->e_id, SmtpReplyBuffer); | |
415 | } | |
416 | #endif | |
417 | ||
977ccbaf | 418 | return (EX_PROTOCOL); |
3c6123ce EA |
419 | } |
420 | \f/* | |
4db45bf1 | 421 | ** SMTPDATA -- send the data and clean up the transaction. |
d2eb2478 EA |
422 | ** |
423 | ** Parameters: | |
e6b0a75b | 424 | ** m -- mailer being sent to. |
dd1fe05b | 425 | ** e -- the envelope for this message. |
d2eb2478 EA |
426 | ** |
427 | ** Returns: | |
4a4ebe09 | 428 | ** exit status corresponding to DATA command. |
d2eb2478 EA |
429 | ** |
430 | ** Side Effects: | |
e6b0a75b | 431 | ** none. |
d2eb2478 EA |
432 | */ |
433 | ||
8e5c6745 | 434 | static jmp_buf CtxDataTimeout; |
e6cb9fc4 | 435 | static int datatimeout(); |
8e5c6745 | 436 | |
06771186 | 437 | smtpdata(m, mci, e) |
e6b0a75b | 438 | struct mailer *m; |
f2e44ded | 439 | register MCI *mci; |
dd1fe05b | 440 | register ENVELOPE *e; |
d2eb2478 EA |
441 | { |
442 | register int r; | |
8e5c6745 EA |
443 | register EVENT *ev; |
444 | time_t timeout; | |
d2eb2478 | 445 | |
3c6123ce EA |
446 | /* |
447 | ** Send the data. | |
4db45bf1 EA |
448 | ** First send the command and check that it is ok. |
449 | ** Then send the data. | |
450 | ** Follow it up with a dot to terminate. | |
451 | ** Finally get the results of the transaction. | |
3c6123ce EA |
452 | */ |
453 | ||
4db45bf1 | 454 | /* send the command and check ok to proceed */ |
e62e1144 | 455 | smtpmessage("DATA", m, mci); |
8e948497 | 456 | SmtpPhase = mci->mci_phase = "client DATA 354"; |
e62e1144 | 457 | setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); |
3001ca78 | 458 | r = reply(m, mci, e, TimeOuts.to_datainit, NULL); |
3ff6d543 | 459 | if (r < 0 || REPLYTYPE(r) == 4) |
c6f2ede6 EA |
460 | { |
461 | smtpquit(m, mci, e); | |
3c6123ce | 462 | return (EX_TEMPFAIL); |
c6f2ede6 | 463 | } |
4672197a | 464 | else if (r == 554) |
c6f2ede6 EA |
465 | { |
466 | smtprset(m, mci, e); | |
4672197a | 467 | return (EX_UNAVAILABLE); |
c6f2ede6 | 468 | } |
4672197a | 469 | else if (r != 354) |
c6f2ede6 | 470 | { |
51d9cc47 | 471 | #ifdef LOG |
68f7099c | 472 | if (LogLevel > 1) |
51d9cc47 EA |
473 | { |
474 | syslog(LOG_CRIT, "%s: SMTP DATA-1 protocol error: %s", | |
475 | e->e_id, SmtpReplyBuffer); | |
476 | } | |
477 | #endif | |
c6f2ede6 | 478 | smtprset(m, mci, e); |
977ccbaf | 479 | return (EX_PROTOCOL); |
c6f2ede6 | 480 | } |
4db45bf1 | 481 | |
e532e51b EA |
482 | /* |
483 | ** Set timeout around data writes. Make it at least large | |
484 | ** enough for DNS timeouts on all recipients plus some fudge | |
485 | ** factor. The main thing is that it should not be infinite. | |
486 | */ | |
487 | ||
8e5c6745 EA |
488 | if (setjmp(CtxDataTimeout) != 0) |
489 | { | |
490 | mci->mci_errno = errno; | |
491 | mci->mci_exitstat = EX_TEMPFAIL; | |
492 | mci->mci_state = MCIS_ERROR; | |
8e5c6745 EA |
493 | syserr("451 timeout writing message to %s", mci->mci_host); |
494 | smtpquit(m, mci, e); | |
495 | return EX_TEMPFAIL; | |
496 | } | |
497 | ||
fd57f063 EA |
498 | timeout = e->e_msgsize / 16; |
499 | if (timeout < (time_t) 60) | |
500 | timeout = (time_t) 60; | |
501 | timeout += e->e_nrcpts * 90; | |
8e5c6745 EA |
502 | ev = setevent(timeout, datatimeout, 0); |
503 | ||
4db45bf1 | 504 | /* now output the actual message */ |
e62e1144 | 505 | (*e->e_puthdr)(mci->mci_out, m, e); |
06771186 | 506 | putline("\n", mci->mci_out, m); |
2f6a8a78 | 507 | (*e->e_putbody)(mci->mci_out, m, e, NULL); |
4db45bf1 | 508 | |
8e5c6745 EA |
509 | clrevent(ev); |
510 | ||
fe3849ea EA |
511 | if (ferror(mci->mci_out)) |
512 | { | |
513 | /* error during processing -- don't send the dot */ | |
514 | mci->mci_errno = EIO; | |
515 | mci->mci_exitstat = EX_IOERR; | |
516 | mci->mci_state = MCIS_ERROR; | |
517 | smtpquit(m, mci, e); | |
518 | return EX_IOERR; | |
519 | } | |
520 | ||
4db45bf1 | 521 | /* terminate the message */ |
06771186 | 522 | fprintf(mci->mci_out, ".%s", m->m_eol); |
8e5c6745 EA |
523 | if (TrafficLogFile != NULL) |
524 | fprintf(TrafficLogFile, "%05d >>> .\n", getpid()); | |
dc2cd20f | 525 | if (Verbose) |
b6edea3d | 526 | nmessage(">>> ."); |
4db45bf1 EA |
527 | |
528 | /* check for the results of the transaction */ | |
8e948497 | 529 | SmtpPhase = mci->mci_phase = "client DATA 250"; |
e62e1144 | 530 | setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); |
3001ca78 | 531 | r = reply(m, mci, e, TimeOuts.to_datafinal, NULL); |
e62e1144 | 532 | if (r < 0) |
c6f2ede6 EA |
533 | { |
534 | smtpquit(m, mci, e); | |
e62e1144 | 535 | return (EX_TEMPFAIL); |
c6f2ede6 | 536 | } |
e62e1144 | 537 | mci->mci_state = MCIS_OPEN; |
614b4258 | 538 | e->e_statmsg = newstr(&SmtpReplyBuffer[4]); |
e62e1144 | 539 | if (REPLYTYPE(r) == 4) |
3c6123ce | 540 | return (EX_TEMPFAIL); |
4672197a EA |
541 | else if (r == 250) |
542 | return (EX_OK); | |
543 | else if (r == 552 || r == 554) | |
544 | return (EX_UNAVAILABLE); | |
51d9cc47 | 545 | #ifdef LOG |
68f7099c | 546 | if (LogLevel > 1) |
51d9cc47 EA |
547 | { |
548 | syslog(LOG_CRIT, "%s: SMTP DATA-2 protocol error: %s", | |
549 | e->e_id, SmtpReplyBuffer); | |
550 | } | |
551 | #endif | |
977ccbaf | 552 | return (EX_PROTOCOL); |
d2eb2478 | 553 | } |
8e5c6745 EA |
554 | |
555 | ||
556 | static int | |
557 | datatimeout() | |
558 | { | |
559 | longjmp(CtxDataTimeout, 1); | |
560 | } | |
d2eb2478 | 561 | \f/* |
e6b0a75b EA |
562 | ** SMTPQUIT -- close the SMTP connection. |
563 | ** | |
564 | ** Parameters: | |
e009f4f4 | 565 | ** m -- a pointer to the mailer. |
e6b0a75b EA |
566 | ** |
567 | ** Returns: | |
568 | ** none. | |
569 | ** | |
570 | ** Side Effects: | |
571 | ** sends the final protocol and closes the connection. | |
572 | */ | |
573 | ||
e62e1144 | 574 | smtpquit(m, mci, e) |
06771186 | 575 | register MAILER *m; |
f2e44ded | 576 | register MCI *mci; |
e62e1144 | 577 | ENVELOPE *e; |
e6b0a75b | 578 | { |
80482eb5 | 579 | int i; |
e6b0a75b | 580 | |
f2e44ded | 581 | /* send the quit message if we haven't gotten I/O error */ |
e62e1144 | 582 | if (mci->mci_state != MCIS_ERROR) |
044ab67a | 583 | { |
8e948497 | 584 | SmtpPhase = "client QUIT"; |
e62e1144 | 585 | smtpmessage("QUIT", m, mci); |
3001ca78 | 586 | (void) reply(m, mci, e, TimeOuts.to_quit, NULL); |
06771186 | 587 | if (mci->mci_state == MCIS_CLOSED) |
11957f11 | 588 | return; |
80482eb5 EA |
589 | } |
590 | ||
2ae0e0ed | 591 | /* now actually close the connection and pick up the zombie */ |
ffc1c1a0 | 592 | i = endmailer(mci, e, m->m_argv); |
80482eb5 | 593 | if (i != EX_OK) |
b6edea3d | 594 | syserr("451 smtpquit %s: stat %d", m->m_argv[0], i); |
e6b0a75b EA |
595 | } |
596 | \f/* | |
f2e44ded EA |
597 | ** SMTPRSET -- send a RSET (reset) command |
598 | */ | |
599 | ||
600 | smtprset(m, mci, e) | |
601 | register MAILER *m; | |
602 | register MCI *mci; | |
603 | ENVELOPE *e; | |
604 | { | |
605 | int r; | |
606 | ||
8e948497 | 607 | SmtpPhase = "client RSET"; |
f2e44ded | 608 | smtpmessage("RSET", m, mci); |
3001ca78 | 609 | r = reply(m, mci, e, TimeOuts.to_rset, NULL); |
263c5860 EA |
610 | if (r < 0) |
611 | mci->mci_state = MCIS_ERROR; | |
f2e44ded | 612 | else if (REPLYTYPE(r) == 2) |
263c5860 EA |
613 | { |
614 | mci->mci_state = MCIS_OPEN; | |
615 | return; | |
616 | } | |
617 | smtpquit(m, mci, e); | |
f2e44ded EA |
618 | } |
619 | \f/* | |
c428b1b2 | 620 | ** SMTPPROBE -- check the connection state |
f2e44ded EA |
621 | */ |
622 | ||
c428b1b2 | 623 | smtpprobe(mci) |
f2e44ded EA |
624 | register MCI *mci; |
625 | { | |
626 | int r; | |
627 | MAILER *m = mci->mci_mailer; | |
628 | extern ENVELOPE BlankEnvelope; | |
629 | ENVELOPE *e = &BlankEnvelope; | |
630 | ||
8e948497 | 631 | SmtpPhase = "client probe"; |
c428b1b2 | 632 | smtpmessage("RSET", m, mci); |
3001ca78 | 633 | r = reply(m, mci, e, TimeOuts.to_miscshort, NULL); |
5d41b806 | 634 | if (r < 0 || REPLYTYPE(r) != 2) |
f2e44ded EA |
635 | smtpquit(m, mci, e); |
636 | return r; | |
637 | } | |
638 | \f/* | |
d2eb2478 EA |
639 | ** REPLY -- read arpanet reply |
640 | ** | |
641 | ** Parameters: | |
4db45bf1 | 642 | ** m -- the mailer we are reading the reply from. |
474bc899 EA |
643 | ** mci -- the mailer connection info structure. |
644 | ** e -- the current envelope. | |
645 | ** timeout -- the timeout for reads. | |
3001ca78 EA |
646 | ** pfunc -- processing function for second and subsequent |
647 | ** lines of response -- if null, no special | |
648 | ** processing is done. | |
d2eb2478 EA |
649 | ** |
650 | ** Returns: | |
651 | ** reply code it reads. | |
652 | ** | |
653 | ** Side Effects: | |
654 | ** flushes the mail file. | |
655 | */ | |
656 | ||
3001ca78 | 657 | reply(m, mci, e, timeout, pfunc) |
1dbda134 | 658 | MAILER *m; |
f2e44ded | 659 | MCI *mci; |
e62e1144 | 660 | ENVELOPE *e; |
3001ca78 EA |
661 | time_t timeout; |
662 | void (*pfunc)(); | |
d2eb2478 | 663 | { |
cb452ecf EA |
664 | register char *bufp; |
665 | register int r; | |
3001ca78 | 666 | bool firstline = TRUE; |
a9bac7a9 EA |
667 | char junkbuf[MAXLINE]; |
668 | ||
474bc899 EA |
669 | if (mci->mci_out != NULL) |
670 | (void) fflush(mci->mci_out); | |
42281a7d | 671 | |
9678c96d | 672 | if (tTd(18, 1)) |
42281a7d | 673 | printf("reply\n"); |
d2eb2478 | 674 | |
37eaaadb EA |
675 | /* |
676 | ** Read the input line, being careful not to hang. | |
677 | */ | |
678 | ||
cb452ecf | 679 | for (bufp = SmtpReplyBuffer;; bufp = junkbuf) |
d2eb2478 | 680 | { |
37eaaadb | 681 | register char *p; |
e62e1144 | 682 | extern time_t curtime(); |
37eaaadb | 683 | |
37eaaadb | 684 | /* actually do the read */ |
e62e1144 EA |
685 | if (e->e_xfp != NULL) |
686 | (void) fflush(e->e_xfp); /* for debugging */ | |
d0729491 EA |
687 | |
688 | /* if we are in the process of closing just give the code */ | |
06771186 | 689 | if (mci->mci_state == MCIS_CLOSED) |
d0729491 EA |
690 | return (SMTPCLOSING); |
691 | ||
bc854e30 EA |
692 | if (mci->mci_out != NULL) |
693 | fflush(mci->mci_out); | |
694 | ||
d0729491 | 695 | /* get the line from the other side */ |
8e948497 | 696 | p = sfgets(bufp, MAXLINE, mci->mci_in, timeout, SmtpPhase); |
e62e1144 EA |
697 | mci->mci_lastuse = curtime(); |
698 | ||
d0729491 | 699 | if (p == NULL) |
ed7382d3 | 700 | { |
8e5c6745 | 701 | bool oldholderrs; |
044ab67a | 702 | extern char MsgBuf[]; /* err.c */ |
044ab67a | 703 | |
46f6ec52 EA |
704 | /* if the remote end closed early, fake an error */ |
705 | if (errno == 0) | |
706 | # ifdef ECONNRESET | |
707 | errno = ECONNRESET; | |
f3d8f6d6 | 708 | # else /* ECONNRESET */ |
46f6ec52 | 709 | errno = EPIPE; |
f3d8f6d6 | 710 | # endif /* ECONNRESET */ |
46f6ec52 | 711 | |
474bc899 | 712 | mci->mci_errno = errno; |
6e99f903 | 713 | mci->mci_exitstat = EX_TEMPFAIL; |
8e5c6745 EA |
714 | oldholderrs = HoldErrs; |
715 | HoldErrs = TRUE; | |
716 | usrerr("451 reply: read error from %s", mci->mci_host); | |
717 | ||
9759fb92 EA |
718 | /* if debugging, pause so we can see state */ |
719 | if (tTd(18, 100)) | |
720 | pause(); | |
f2e44ded | 721 | mci->mci_state = MCIS_ERROR; |
e62e1144 | 722 | smtpquit(m, mci, e); |
8e5c6745 EA |
723 | #ifdef XDEBUG |
724 | { | |
725 | char wbuf[MAXLINE]; | |
5e3f856e EA |
726 | char *p = wbuf; |
727 | if (e->e_to != NULL) | |
728 | { | |
729 | sprintf(p, "%s... ", e->e_to); | |
730 | p += strlen(p); | |
731 | } | |
732 | sprintf(p, "reply(%s) during %s", | |
733 | mci->mci_host, SmtpPhase); | |
8e5c6745 EA |
734 | checkfd012(wbuf); |
735 | } | |
736 | #endif | |
737 | HoldErrs = oldholderrs; | |
d0729491 | 738 | return (-1); |
ed7382d3 | 739 | } |
a9bac7a9 | 740 | fixcrlf(bufp, TRUE); |
37eaaadb | 741 | |
8e5c6745 EA |
742 | /* EHLO failure is not a real error */ |
743 | if (e->e_xfp != NULL && (bufp[0] == '4' || | |
744 | (bufp[0] == '5' && strncmp(SmtpMsgBuffer, "EHLO", 4) != 0))) | |
d7dbe85b EA |
745 | { |
746 | /* serious error -- log the previous command */ | |
337a0030 EA |
747 | if (SmtpNeedIntro) |
748 | { | |
749 | /* inform user who we are chatting with */ | |
750 | fprintf(CurEnv->e_xfp, | |
751 | "... while talking to %s:\n", | |
752 | CurHostName); | |
753 | SmtpNeedIntro = FALSE; | |
754 | } | |
cb452ecf EA |
755 | if (SmtpMsgBuffer[0] != '\0') |
756 | fprintf(e->e_xfp, ">>> %s\n", SmtpMsgBuffer); | |
757 | SmtpMsgBuffer[0] = '\0'; | |
d7dbe85b EA |
758 | |
759 | /* now log the message as from the other side */ | |
a9bac7a9 | 760 | fprintf(e->e_xfp, "<<< %s\n", bufp); |
d7dbe85b EA |
761 | } |
762 | ||
763 | /* display the input for verbose mode */ | |
dc2cd20f | 764 | if (Verbose) |
09bdfe00 | 765 | nmessage("050 %s", bufp); |
37eaaadb | 766 | |
3001ca78 EA |
767 | /* process the line */ |
768 | if (pfunc != NULL && !firstline) | |
769 | (*pfunc)(bufp, m, mci, e); | |
770 | ||
771 | firstline = FALSE; | |
772 | ||
37eaaadb | 773 | /* if continuation is required, we can go on */ |
cb452ecf EA |
774 | if (bufp[3] == '-') |
775 | continue; | |
776 | ||
777 | /* ignore improperly formated input */ | |
778 | if (!(isascii(bufp[0]) && isdigit(bufp[0]))) | |
d2eb2478 | 779 | continue; |
37eaaadb EA |
780 | |
781 | /* decode the reply code */ | |
cb452ecf | 782 | r = atoi(bufp); |
37eaaadb EA |
783 | |
784 | /* extra semantics: 0xx codes are "informational" */ | |
cb452ecf EA |
785 | if (r >= 100) |
786 | break; | |
787 | } | |
37eaaadb | 788 | |
cb452ecf EA |
789 | /* |
790 | ** Now look at SmtpReplyBuffer -- only care about the first | |
791 | ** line of the response from here on out. | |
792 | */ | |
5d41b806 | 793 | |
cb452ecf EA |
794 | /* save temporary failure messages for posterity */ |
795 | if (SmtpReplyBuffer[0] == '4' && SmtpError[0] == '\0') | |
796 | (void) strcpy(SmtpError, SmtpReplyBuffer); | |
80482eb5 | 797 | |
cb452ecf EA |
798 | /* reply code 421 is "Service Shutting Down" */ |
799 | if (r == SMTPCLOSING && mci->mci_state != MCIS_SSD) | |
800 | { | |
801 | /* send the quit protocol */ | |
802 | mci->mci_state = MCIS_SSD; | |
803 | smtpquit(m, mci, e); | |
d2eb2478 | 804 | } |
cb452ecf EA |
805 | |
806 | return (r); | |
d2eb2478 | 807 | } |
42281a7d | 808 | \f/* |
e6b0a75b | 809 | ** SMTPMESSAGE -- send message to server |
42281a7d EA |
810 | ** |
811 | ** Parameters: | |
812 | ** f -- format | |
4db45bf1 | 813 | ** m -- the mailer to control formatting. |
42281a7d EA |
814 | ** a, b, c -- parameters |
815 | ** | |
816 | ** Returns: | |
817 | ** none. | |
818 | ** | |
819 | ** Side Effects: | |
06771186 | 820 | ** writes message to mci->mci_out. |
42281a7d EA |
821 | */ |
822 | ||
e6b0a75b | 823 | /*VARARGS1*/ |
6e99f903 EA |
824 | #ifdef __STDC__ |
825 | smtpmessage(char *f, MAILER *m, MCI *mci, ...) | |
826 | #else | |
827 | smtpmessage(f, m, mci, va_alist) | |
42281a7d | 828 | char *f; |
4db45bf1 | 829 | MAILER *m; |
f2e44ded | 830 | MCI *mci; |
6e99f903 EA |
831 | va_dcl |
832 | #endif | |
42281a7d | 833 | { |
5229f34d EA |
834 | VA_LOCAL_DECL |
835 | ||
07b49560 | 836 | VA_START(mci); |
5229f34d EA |
837 | (void) vsprintf(SmtpMsgBuffer, f, ap); |
838 | VA_END; | |
bc854e30 | 839 | |
dc2cd20f | 840 | if (tTd(18, 1) || Verbose) |
b6edea3d | 841 | nmessage(">>> %s", SmtpMsgBuffer); |
8e5c6745 EA |
842 | if (TrafficLogFile != NULL) |
843 | fprintf(TrafficLogFile, "%05d >>> %s\n", getpid(), SmtpMsgBuffer); | |
06771186 | 844 | if (mci->mci_out != NULL) |
bc854e30 | 845 | { |
06771186 | 846 | fprintf(mci->mci_out, "%s%s", SmtpMsgBuffer, |
f2e44ded | 847 | m == NULL ? "\r\n" : m->m_eol); |
bc854e30 | 848 | } |
80ec420e | 849 | else if (tTd(18, 1)) |
0852beb8 | 850 | { |
80ec420e | 851 | printf("smtpmessage: NULL mci_out\n"); |
0852beb8 | 852 | } |
42281a7d | 853 | } |
884a20cb | 854 | |
f3d8f6d6 | 855 | # endif /* SMTP */ |