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