don't pass through NOTIFY= unless it was given in the SMTP RCPT
[unix-history] / usr / src / usr.sbin / sendmail / src / usersmtp.c
CommitLineData
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
e1f691b3 13static char sccsid[] = "@(#)usersmtp.c 8.40 (Berkeley) %G% (with SMTP)";
bee79b64 14#else
e1f691b3 15static char sccsid[] = "@(#)usersmtp.c 8.40 (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 34char SmtpMsgBuffer[MAXLINE]; /* buffer for commands */
d0729491 35char SmtpReplyBuffer[MAXLINE]; /* buffer for replies */
46f6ec52 36char SmtpError[MAXLINE] = ""; /* save failure error messages */
d0729491 37int SmtpPid; /* pid of mailer */
337a0030 38bool SmtpNeedIntro; /* need "while talking" in transcript */
16133a1c
EA
39
40#ifdef __STDC__
41extern 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 60smtpinit(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 */
39f3bc06
EA
82 if (CurHostName == NULL)
83 CurHostName = MyHostName;
337a0030 84 SmtpNeedIntro = TRUE;
f2e44ded 85 switch (mci->mci_state)
1ea752a1 86 {
f2e44ded
EA
87 case MCIS_ACTIVE:
88 /* need to clear old information */
89 smtprset(m, mci, e);
263c5860 90 /* fall through */
46f6ec52 91
f2e44ded
EA
92 case MCIS_OPEN:
93 return;
94
95 case MCIS_ERROR:
96 case MCIS_SSD:
97 /* shouldn't happen */
98 smtpquit(m, mci, e);
263c5860 99 /* fall through */
f2e44ded
EA
100
101 case MCIS_CLOSED:
b6edea3d 102 syserr("451 smtpinit: state CLOSED");
f2e44ded
EA
103 return;
104
105 case MCIS_OPENING:
106 break;
1ea752a1 107 }
d2eb2478 108
f2e44ded
EA
109 mci->mci_state = MCIS_OPENING;
110
e6b0a75b
EA
111 /*
112 ** Get the greeting message.
057a9c5d 113 ** This should appear spontaneously. Give it five minutes to
e4581b86 114 ** happen.
e6b0a75b 115 */
3c6123ce 116
8e948497 117 SmtpPhase = mci->mci_phase = "client greeting";
e62e1144 118 setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
77c24e97 119 r = reply(m, mci, e, TimeOuts.to_initial, esmtp_check);
4990fc15 120 if (r < 0 || REPLYTYPE(r) == 4)
07cb6633 121 goto tempfail1;
4990fc15
EA
122 if (REPLYTYPE(r) != 2)
123 goto unavailable;
42281a7d 124
4a4ebe09
EA
125 /*
126 ** Send the HELO command.
4672197a 127 ** My mother taught me to always introduce myself.
4a4ebe09
EA
128 */
129
3001ca78
EA
130 if (bitnset(M_ESMTP, m->m_flags))
131 mci->mci_flags |= MCIF_ESMTP;
132
133tryhelo:
134 if (bitset(MCIF_ESMTP, mci->mci_flags))
135 {
136 smtpmessage("EHLO %s", m, mci, MyHostName);
8e948497 137 SmtpPhase = mci->mci_phase = "client EHLO";
3001ca78
EA
138 }
139 else
140 {
141 smtpmessage("HELO %s", m, mci, MyHostName);
8e948497 142 SmtpPhase = mci->mci_phase = "client HELO";
3001ca78 143 }
e62e1144 144 setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
3001ca78 145 r = reply(m, mci, e, TimeOuts.to_helo, helo_options);
3ff6d543 146 if (r < 0)
07cb6633 147 goto tempfail1;
3ff6d543 148 else if (REPLYTYPE(r) == 5)
3001ca78
EA
149 {
150 if (bitset(MCIF_ESMTP, mci->mci_flags))
151 {
152 /* try old SMTP instead */
153 mci->mci_flags &= ~MCIF_ESMTP;
154 goto tryhelo;
155 }
057a9c5d 156 goto unavailable;
3001ca78 157 }
4672197a 158 else if (REPLYTYPE(r) != 2)
07cb6633 159 goto tempfail1;
4a4ebe09 160
a9bac7a9
EA
161 /*
162 ** Check to see if we actually ended up talking to ourself.
163 ** This means we didn't know about an alias or MX, or we managed
164 ** to connect to an echo server.
165 */
166
f4900dab 167 p = strchr(&SmtpReplyBuffer[4], ' ');
a9bac7a9 168 if (p != NULL)
a3934270 169 *p = '\0';
2bade550
EA
170 if (!bitnset(M_NOLOOPCHECK, m->m_flags) &&
171 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
b45fe690
EA
195 if (mci->mci_state != MCIS_CLOSED)
196 {
197 mci->mci_state = MCIS_OPEN;
198 return;
199 }
200
201 /* got a 421 error code during startup */
e62e1144
EA
202
203 tempfail1:
204 tempfail2:
205 mci->mci_exitstat = EX_TEMPFAIL;
474bc899
EA
206 if (mci->mci_errno == 0)
207 mci->mci_errno = errno;
208 if (mci->mci_state != MCIS_CLOSED)
209 smtpquit(m, mci, e);
f2e44ded 210 return;
e62e1144
EA
211
212 unavailable:
213 mci->mci_exitstat = EX_UNAVAILABLE;
214 mci->mci_errno = errno;
215 smtpquit(m, mci, e);
f2e44ded 216 return;
e62e1144 217}
3001ca78 218\f/*
77c24e97
EA
219** ESMTP_CHECK -- check to see if this implementation likes ESMTP protocol
220**
77c24e97
EA
221** Parameters:
222** line -- the response line.
0717d167 223** firstline -- set if this is the first line of the reply.
77c24e97
EA
224** m -- the mailer.
225** mci -- the mailer connection info.
226** e -- the envelope.
227**
228** Returns:
229** none.
230*/
231
232void
0717d167 233esmtp_check(line, firstline, m, mci, e)
77c24e97 234 char *line;
0717d167 235 bool firstline;
77c24e97
EA
236 MAILER *m;
237 register MCI *mci;
238 ENVELOPE *e;
239{
c7a0eaaf
EA
240 register char *l;
241
242 for (l = line; (l = strchr(++l, 'E')) != NULL; )
0717d167 243 {
c7a0eaaf 244 if (strncmp(l, "ESMTP ", 6) == 0)
0717d167
EA
245 {
246 mci->mci_flags |= MCIF_ESMTP;
247 break;
248 }
249 }
c7a0eaaf
EA
250 for (l = line; (l = strchr(++l, '8')) != NULL; )
251 {
252 if (strncmp(l, "8BIT OK", 7) == 0)
253 {
254 mci->mci_flags |= MCIF_8BITOK;
255 break;
256 }
257 }
77c24e97
EA
258}
259\f/*
3001ca78
EA
260** HELO_OPTIONS -- process the options on a HELO line.
261**
262** Parameters:
263** line -- the response line.
0717d167 264** firstline -- set if this is the first line of the reply.
3001ca78
EA
265** m -- the mailer.
266** mci -- the mailer connection info.
267** e -- the envelope.
268**
269** Returns:
270** none.
271*/
272
273void
0717d167 274helo_options(line, firstline, m, mci, e)
3001ca78 275 char *line;
0717d167 276 bool firstline;
3001ca78
EA
277 MAILER *m;
278 register MCI *mci;
279 ENVELOPE *e;
280{
281 register char *p;
282
fcbc62f9 283 if (firstline)
0717d167
EA
284 return;
285
3001ca78
EA
286 if (strlen(line) < 5)
287 return;
288 line += 4;
289 p = strchr(line, ' ');
290 if (p != NULL)
291 *p++ = '\0';
292 if (strcasecmp(line, "size") == 0)
293 {
294 mci->mci_flags |= MCIF_SIZE;
295 if (p != NULL)
296 mci->mci_maxsize = atol(p);
297 }
298 else if (strcasecmp(line, "8bitmime") == 0)
23fafb99 299 {
3001ca78 300 mci->mci_flags |= MCIF_8BITMIME;
23fafb99
EA
301 mci->mci_flags &= ~MCIF_7BIT;
302 }
3001ca78
EA
303 else if (strcasecmp(line, "expn") == 0)
304 mci->mci_flags |= MCIF_EXPN;
c7a0eaaf 305 else if (strcasecmp(line, "x-dsn-3") == 0)
68d9129a 306 mci->mci_flags |= MCIF_DSN;
3001ca78
EA
307}
308\f/*
309** SMTPMAILFROM -- send MAIL command
310**
311** Parameters:
312** m -- the mailer.
313** mci -- the mailer connection structure.
314** e -- the envelope (including the sender to specify).
315*/
e62e1144
EA
316
317smtpmailfrom(m, mci, e)
318 struct mailer *m;
f2e44ded 319 MCI *mci;
e62e1144
EA
320 ENVELOPE *e;
321{
322 int r;
61fabf2e 323 char *bufp;
7d858063 324 char *bodytype;
8446c922 325 char buf[MAXNAME + 1];
3001ca78 326 char optbuf[MAXLINE];
e62e1144 327
8e5c6745 328 if (tTd(18, 2))
322eceee
EA
329 printf("smtpmailfrom: CurHost=%s\n", CurHostName);
330
3001ca78 331 /* set up appropriate options to include */
1a139f8c 332 if (bitset(MCIF_SIZE, mci->mci_flags) && e->e_msgsize > 0)
3001ca78
EA
333 sprintf(optbuf, " SIZE=%ld", e->e_msgsize);
334 else
335 strcpy(optbuf, "");
336
7d858063
EA
337 bodytype = e->e_bodytype;
338 if (bitset(MCIF_8BITMIME, mci->mci_flags))
df04c7c2 339 {
7d858063
EA
340 if (bodytype == NULL &&
341 bitset(MM_MIME8BIT, MimeMode) &&
342 bitset(EF_HAS8BIT, e->e_flags) &&
343 !bitnset(M_8BITS, m->m_flags))
344 bodytype = "8BITMIME";
345 if (bodytype != NULL)
df04c7c2
EA
346 {
347 strcat(optbuf, " BODY=");
7d858063 348 strcat(optbuf, bodytype);
df04c7c2
EA
349 }
350 }
bb2afea8
EA
351 else if (bitnset(M_8BITS, m->m_flags) ||
352 !bitset(EF_HAS8BIT, e->e_flags) ||
353 (e->e_bodytype != NULL &&
354 strcasecmp(e->e_bodytype, "7bit") == 0))
7d858063
EA
355 {
356 /* just pass it through */
357 }
358 else if (bitset(MM_CVTMIME, MimeMode) &&
bb2afea8 359 (e->e_bodytype != NULL || !bitset(MM_PASS8BIT, MimeMode)))
7d858063
EA
360 {
361 /* must convert from 8bit MIME format to 7bit encoded */
362 mci->mci_flags |= MCIF_CVT8TO7;
363 }
364 else if (!bitset(MM_PASS8BIT, MimeMode))
365 {
366 /* cannot just send a 8-bit version */
367 usrerr("%s does not support 8BITMIME", mci->mci_host);
368 return EX_DATAERR;
369 }
df04c7c2 370
82e3dc75 371 if (bitset(MCIF_DSN, mci->mci_flags))
68d9129a 372 {
82e3dc75
EA
373 if (e->e_envid != NULL)
374 {
375 strcat(optbuf, " ENVID=");
376 strcat(optbuf, e->e_envid);
377 }
c7a0eaaf
EA
378
379 /* RET= parameter */
380 if (bitset(EF_RET_PARAM, e->e_flags))
381 {
382 strcat(optbuf, " RET=");
383 if (bitset(EF_NO_BODY_RETN, e->e_flags))
384 strcat(optbuf, "HDRS");
385 else
386 strcat(optbuf, "FULL");
387 }
68d9129a
EA
388 }
389
7d7fdf93
EA
390 /*
391 ** Send the HOPS command.
392 ** This is non-standard and may give an "unknown command".
393 ** This is not an error.
394 ** It can give a "bad hop count" error if the hop
395 ** count is exceeded.
396 */
397
e6b0a75b
EA
398 /*
399 ** Send the MAIL command.
400 ** Designates the sender.
401 */
42281a7d 402
e62e1144
EA
403 mci->mci_state = MCIS_ACTIVE;
404
2cd2111f
EA
405 if (bitset(EF_RESPONSE, e->e_flags) &&
406 !bitnset(M_NO_NULL_FROM, m->m_flags))
bc854e30
EA
407 (void) strcpy(buf, "");
408 else
832e8a27 409 expand("\201g", buf, sizeof buf, e);
61fabf2e
EA
410 if (buf[0] == '<')
411 {
412 /* strip off <angle brackets> (put back on below) */
413 bufp = &buf[strlen(buf) - 1];
414 if (*bufp == '>')
415 *bufp = '\0';
416 bufp = &buf[1];
417 }
418 else
419 bufp = buf;
2bade550 420 if (bitnset(M_LOCALMAILER, e->e_from.q_mailer->m_flags) ||
1dbda134 421 !bitnset(M_FROMPATH, m->m_flags))
6abb7b86 422 {
61fabf2e 423 smtpmessage("MAIL From:<%s>%s", m, mci, bufp, optbuf);
6abb7b86
EA
424 }
425 else
426 {
3001ca78 427 smtpmessage("MAIL From:<@%s%c%s>%s", m, mci, MyHostName,
61fabf2e 428 *bufp == '@' ? ',' : ':', bufp, optbuf);
6abb7b86 429 }
8e948497 430 SmtpPhase = mci->mci_phase = "client MAIL";
e62e1144 431 setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
3001ca78 432 r = reply(m, mci, e, TimeOuts.to_mail, NULL);
3ff6d543 433 if (r < 0 || REPLYTYPE(r) == 4)
e62e1144
EA
434 {
435 mci->mci_exitstat = EX_TEMPFAIL;
436 mci->mci_errno = errno;
437 smtpquit(m, mci, e);
e62e1144
EA
438 return EX_TEMPFAIL;
439 }
4672197a 440 else if (r == 250)
e62e1144 441 {
e62e1144
EA
442 return EX_OK;
443 }
03981add
EA
444 else if (r == 501 || r == 553)
445 {
446 /* syntax error in arguments */
447 smtpquit(m, mci, e);
448 return EX_DATAERR;
449 }
4672197a 450 else if (r == 552)
e62e1144
EA
451 {
452 /* signal service unavailable */
e62e1144 453 smtpquit(m, mci, e);
e62e1144
EA
454 return EX_UNAVAILABLE;
455 }
057a9c5d 456
51d9cc47 457#ifdef LOG
68f7099c 458 if (LogLevel > 1)
51d9cc47 459 {
6d3599b7
EA
460 syslog(LOG_CRIT, "%s: %s: SMTP MAIL protocol error: %s",
461 e->e_id, mci->mci_host, SmtpReplyBuffer);
51d9cc47
EA
462 }
463#endif
464
057a9c5d 465 /* protocol error -- close up */
e62e1144 466 smtpquit(m, mci, e);
e62e1144 467 return EX_PROTOCOL;
d2eb2478
EA
468}
469\f/*
4a4ebe09 470** SMTPRCPT -- designate recipient.
3c6123ce
EA
471**
472** Parameters:
e6b0a75b 473** to -- address of recipient.
4db45bf1 474** m -- the mailer we are sending to.
474bc899
EA
475** mci -- the connection info for this transaction.
476** e -- the envelope for this transaction.
3c6123ce
EA
477**
478** Returns:
e6b0a75b 479** exit status corresponding to recipient status.
3c6123ce
EA
480**
481** Side Effects:
e6b0a75b 482** Sends the mail via SMTP.
3c6123ce
EA
483*/
484
e62e1144 485smtprcpt(to, m, mci, e)
e6b0a75b 486 ADDRESS *to;
4db45bf1 487 register MAILER *m;
f2e44ded 488 MCI *mci;
e62e1144 489 ENVELOPE *e;
3c6123ce
EA
490{
491 register int r;
68d9129a
EA
492 char optbuf[MAXLINE];
493
494 strcpy(optbuf, "");
495 if (bitset(MCIF_DSN, mci->mci_flags))
496 {
3b6c2253 497 /* NOTIFY= parameter */
e1f691b3 498 if (bitset(QHASNOTIFY, to->q_flags))
82e3dc75 499 {
e1f691b3
EA
500 bool firstone = TRUE;
501
502 strcat(optbuf, " NOTIFY=");
503 if (bitset(QPINGONSUCCESS, to->q_flags))
504 {
505 strcat(optbuf, "SUCCESS");
506 firstone = FALSE;
507 }
508 if (bitset(QPINGONFAILURE, to->q_flags))
509 {
510 if (!firstone)
511 strcat(optbuf, ",");
512 strcat(optbuf, "FAILURE");
513 firstone = FALSE;
514 }
515 if (bitset(QPINGONDELAY, to->q_flags))
516 {
517 if (!firstone)
518 strcat(optbuf, ",");
519 strcat(optbuf, "DELAY");
520 firstone = FALSE;
521 }
522 if (firstone)
523 strcat(optbuf, "NEVER");
82e3dc75 524 }
82e3dc75 525
3b6c2253
EA
526 /* ORCPT= parameter */
527 if (to->q_orcpt != NULL)
528 {
529 strcat(optbuf, " ORCPT=");
530 strcat(optbuf, to->q_orcpt);
531 }
68d9129a 532 }
3c6123ce 533
68d9129a 534 smtpmessage("RCPT To:<%s>%s", m, mci, to->q_user, optbuf);
e6b0a75b 535
8e948497 536 SmtpPhase = mci->mci_phase = "client RCPT";
e62e1144 537 setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
3001ca78 538 r = reply(m, mci, e, TimeOuts.to_rcpt, NULL);
68d9129a 539 setstatus(to, SmtpReplyBuffer);
3ff6d543 540 if (r < 0 || REPLYTYPE(r) == 4)
e6b0a75b 541 return (EX_TEMPFAIL);
4672197a
EA
542 else if (REPLYTYPE(r) == 2)
543 return (EX_OK);
977ccbaf
EA
544 else if (r == 550 || r == 551 || r == 553)
545 return (EX_NOUSER);
546 else if (r == 552 || r == 554)
547 return (EX_UNAVAILABLE);
51d9cc47
EA
548
549#ifdef LOG
68f7099c 550 if (LogLevel > 1)
51d9cc47 551 {
6d3599b7
EA
552 syslog(LOG_CRIT, "%s: %s: SMTP RCPT protocol error: %s",
553 e->e_id, mci->mci_host, SmtpReplyBuffer);
51d9cc47
EA
554 }
555#endif
556
977ccbaf 557 return (EX_PROTOCOL);
3c6123ce
EA
558}
559\f/*
4db45bf1 560** SMTPDATA -- send the data and clean up the transaction.
d2eb2478
EA
561**
562** Parameters:
e6b0a75b 563** m -- mailer being sent to.
dd1fe05b 564** e -- the envelope for this message.
d2eb2478
EA
565**
566** Returns:
4a4ebe09 567** exit status corresponding to DATA command.
d2eb2478
EA
568**
569** Side Effects:
e6b0a75b 570** none.
d2eb2478
EA
571*/
572
8e5c6745 573static jmp_buf CtxDataTimeout;
ea07b2d2 574static void datatimeout();
8e5c6745 575
06771186 576smtpdata(m, mci, e)
e6b0a75b 577 struct mailer *m;
f2e44ded 578 register MCI *mci;
dd1fe05b 579 register ENVELOPE *e;
d2eb2478
EA
580{
581 register int r;
8e5c6745
EA
582 register EVENT *ev;
583 time_t timeout;
d2eb2478 584
3c6123ce
EA
585 /*
586 ** Send the data.
4db45bf1
EA
587 ** First send the command and check that it is ok.
588 ** Then send the data.
589 ** Follow it up with a dot to terminate.
590 ** Finally get the results of the transaction.
3c6123ce
EA
591 */
592
4db45bf1 593 /* send the command and check ok to proceed */
e62e1144 594 smtpmessage("DATA", m, mci);
8e948497 595 SmtpPhase = mci->mci_phase = "client DATA 354";
e62e1144 596 setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
3001ca78 597 r = reply(m, mci, e, TimeOuts.to_datainit, NULL);
3ff6d543 598 if (r < 0 || REPLYTYPE(r) == 4)
c6f2ede6
EA
599 {
600 smtpquit(m, mci, e);
3c6123ce 601 return (EX_TEMPFAIL);
c6f2ede6 602 }
4672197a 603 else if (r == 554)
c6f2ede6
EA
604 {
605 smtprset(m, mci, e);
4672197a 606 return (EX_UNAVAILABLE);
c6f2ede6 607 }
4672197a 608 else if (r != 354)
c6f2ede6 609 {
51d9cc47 610#ifdef LOG
68f7099c 611 if (LogLevel > 1)
51d9cc47 612 {
6d3599b7
EA
613 syslog(LOG_CRIT, "%s: %s: SMTP DATA-1 protocol error: %s",
614 e->e_id, mci->mci_host, SmtpReplyBuffer);
51d9cc47
EA
615 }
616#endif
c6f2ede6 617 smtprset(m, mci, e);
977ccbaf 618 return (EX_PROTOCOL);
c6f2ede6 619 }
4db45bf1 620
e532e51b
EA
621 /*
622 ** Set timeout around data writes. Make it at least large
623 ** enough for DNS timeouts on all recipients plus some fudge
624 ** factor. The main thing is that it should not be infinite.
625 */
626
8e5c6745
EA
627 if (setjmp(CtxDataTimeout) != 0)
628 {
629 mci->mci_errno = errno;
630 mci->mci_exitstat = EX_TEMPFAIL;
631 mci->mci_state = MCIS_ERROR;
8e5c6745
EA
632 syserr("451 timeout writing message to %s", mci->mci_host);
633 smtpquit(m, mci, e);
634 return EX_TEMPFAIL;
635 }
636
fd57f063
EA
637 timeout = e->e_msgsize / 16;
638 if (timeout < (time_t) 60)
639 timeout = (time_t) 60;
640 timeout += e->e_nrcpts * 90;
8e5c6745
EA
641 ev = setevent(timeout, datatimeout, 0);
642
c23930c0
EA
643 /*
644 ** Output the actual message.
645 */
646
51d448e5
EA
647 (*e->e_puthdr)(mci, e->e_header, e);
648 (*e->e_putbody)(mci, e, NULL);
4db45bf1 649
c23930c0
EA
650 /*
651 ** Cleanup after sending message.
652 */
653
8e5c6745
EA
654 clrevent(ev);
655
fe3849ea
EA
656 if (ferror(mci->mci_out))
657 {
658 /* error during processing -- don't send the dot */
659 mci->mci_errno = EIO;
660 mci->mci_exitstat = EX_IOERR;
661 mci->mci_state = MCIS_ERROR;
662 smtpquit(m, mci, e);
663 return EX_IOERR;
664 }
665
4db45bf1 666 /* terminate the message */
06771186 667 fprintf(mci->mci_out, ".%s", m->m_eol);
8e5c6745
EA
668 if (TrafficLogFile != NULL)
669 fprintf(TrafficLogFile, "%05d >>> .\n", getpid());
dc2cd20f 670 if (Verbose)
b6edea3d 671 nmessage(">>> .");
4db45bf1
EA
672
673 /* check for the results of the transaction */
8e948497 674 SmtpPhase = mci->mci_phase = "client DATA 250";
e62e1144 675 setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
3001ca78 676 r = reply(m, mci, e, TimeOuts.to_datafinal, NULL);
e62e1144 677 if (r < 0)
c6f2ede6
EA
678 {
679 smtpquit(m, mci, e);
e62e1144 680 return (EX_TEMPFAIL);
c6f2ede6 681 }
e62e1144 682 mci->mci_state = MCIS_OPEN;
614b4258 683 e->e_statmsg = newstr(&SmtpReplyBuffer[4]);
e62e1144 684 if (REPLYTYPE(r) == 4)
3c6123ce 685 return (EX_TEMPFAIL);
4672197a
EA
686 else if (r == 250)
687 return (EX_OK);
688 else if (r == 552 || r == 554)
689 return (EX_UNAVAILABLE);
51d9cc47 690#ifdef LOG
68f7099c 691 if (LogLevel > 1)
51d9cc47 692 {
6d3599b7
EA
693 syslog(LOG_CRIT, "%s: %s: SMTP DATA-2 protocol error: %s",
694 e->e_id, mci->mci_host, SmtpReplyBuffer);
51d9cc47
EA
695 }
696#endif
977ccbaf 697 return (EX_PROTOCOL);
d2eb2478 698}
8e5c6745
EA
699
700
ea07b2d2 701static void
8e5c6745
EA
702datatimeout()
703{
704 longjmp(CtxDataTimeout, 1);
705}
d2eb2478 706\f/*
e6b0a75b
EA
707** SMTPQUIT -- close the SMTP connection.
708**
709** Parameters:
e009f4f4 710** m -- a pointer to the mailer.
e6b0a75b
EA
711**
712** Returns:
713** none.
714**
715** Side Effects:
716** sends the final protocol and closes the connection.
717*/
718
e62e1144 719smtpquit(m, mci, e)
06771186 720 register MAILER *m;
f2e44ded 721 register MCI *mci;
e62e1144 722 ENVELOPE *e;
e6b0a75b 723{
90e0e4e2
EA
724 bool oldSuprErrs = SuprErrs;
725
726 /*
727 ** Suppress errors here -- we may be processing a different
728 ** job when we do the quit connection, and we don't want the
729 ** new job to be penalized for something that isn't it's
730 ** problem.
731 */
732
733 SuprErrs = TRUE;
e6b0a75b 734
f2e44ded 735 /* send the quit message if we haven't gotten I/O error */
e62e1144 736 if (mci->mci_state != MCIS_ERROR)
044ab67a 737 {
8e948497 738 SmtpPhase = "client QUIT";
e62e1144 739 smtpmessage("QUIT", m, mci);
3001ca78 740 (void) reply(m, mci, e, TimeOuts.to_quit, NULL);
90e0e4e2 741 SuprErrs = oldSuprErrs;
06771186 742 if (mci->mci_state == MCIS_CLOSED)
90e0e4e2
EA
743 {
744 SuprErrs = oldSuprErrs;
11957f11 745 return;
90e0e4e2 746 }
80482eb5
EA
747 }
748
2ae0e0ed 749 /* now actually close the connection and pick up the zombie */
c1e6eba2 750 (void) endmailer(mci, e, NULL);
90e0e4e2
EA
751
752 SuprErrs = oldSuprErrs;
e6b0a75b
EA
753}
754\f/*
f2e44ded
EA
755** SMTPRSET -- send a RSET (reset) command
756*/
757
758smtprset(m, mci, e)
759 register MAILER *m;
760 register MCI *mci;
761 ENVELOPE *e;
762{
763 int r;
764
8e948497 765 SmtpPhase = "client RSET";
f2e44ded 766 smtpmessage("RSET", m, mci);
3001ca78 767 r = reply(m, mci, e, TimeOuts.to_rset, NULL);
263c5860
EA
768 if (r < 0)
769 mci->mci_state = MCIS_ERROR;
f2e44ded 770 else if (REPLYTYPE(r) == 2)
263c5860
EA
771 {
772 mci->mci_state = MCIS_OPEN;
773 return;
774 }
775 smtpquit(m, mci, e);
f2e44ded
EA
776}
777\f/*
c428b1b2 778** SMTPPROBE -- check the connection state
f2e44ded
EA
779*/
780
c428b1b2 781smtpprobe(mci)
f2e44ded
EA
782 register MCI *mci;
783{
784 int r;
785 MAILER *m = mci->mci_mailer;
786 extern ENVELOPE BlankEnvelope;
787 ENVELOPE *e = &BlankEnvelope;
788
8e948497 789 SmtpPhase = "client probe";
c428b1b2 790 smtpmessage("RSET", m, mci);
3001ca78 791 r = reply(m, mci, e, TimeOuts.to_miscshort, NULL);
5d41b806 792 if (r < 0 || REPLYTYPE(r) != 2)
f2e44ded
EA
793 smtpquit(m, mci, e);
794 return r;
795}
796\f/*
d2eb2478
EA
797** REPLY -- read arpanet reply
798**
799** Parameters:
4db45bf1 800** m -- the mailer we are reading the reply from.
474bc899
EA
801** mci -- the mailer connection info structure.
802** e -- the current envelope.
803** timeout -- the timeout for reads.
3001ca78
EA
804** pfunc -- processing function for second and subsequent
805** lines of response -- if null, no special
806** processing is done.
d2eb2478
EA
807**
808** Returns:
809** reply code it reads.
810**
811** Side Effects:
812** flushes the mail file.
813*/
814
3001ca78 815reply(m, mci, e, timeout, pfunc)
1dbda134 816 MAILER *m;
f2e44ded 817 MCI *mci;
e62e1144 818 ENVELOPE *e;
3001ca78
EA
819 time_t timeout;
820 void (*pfunc)();
d2eb2478 821{
cb452ecf
EA
822 register char *bufp;
823 register int r;
3001ca78 824 bool firstline = TRUE;
a9bac7a9
EA
825 char junkbuf[MAXLINE];
826
474bc899
EA
827 if (mci->mci_out != NULL)
828 (void) fflush(mci->mci_out);
42281a7d 829
9678c96d 830 if (tTd(18, 1))
42281a7d 831 printf("reply\n");
d2eb2478 832
37eaaadb
EA
833 /*
834 ** Read the input line, being careful not to hang.
835 */
836
cb452ecf 837 for (bufp = SmtpReplyBuffer;; bufp = junkbuf)
d2eb2478 838 {
37eaaadb 839 register char *p;
e62e1144 840 extern time_t curtime();
37eaaadb 841
37eaaadb 842 /* actually do the read */
e62e1144
EA
843 if (e->e_xfp != NULL)
844 (void) fflush(e->e_xfp); /* for debugging */
d0729491
EA
845
846 /* if we are in the process of closing just give the code */
06771186 847 if (mci->mci_state == MCIS_CLOSED)
d0729491
EA
848 return (SMTPCLOSING);
849
bc854e30
EA
850 if (mci->mci_out != NULL)
851 fflush(mci->mci_out);
852
d0729491 853 /* get the line from the other side */
8e948497 854 p = sfgets(bufp, MAXLINE, mci->mci_in, timeout, SmtpPhase);
e62e1144
EA
855 mci->mci_lastuse = curtime();
856
d0729491 857 if (p == NULL)
ed7382d3 858 {
8e5c6745 859 bool oldholderrs;
044ab67a 860 extern char MsgBuf[]; /* err.c */
044ab67a 861
46f6ec52
EA
862 /* if the remote end closed early, fake an error */
863 if (errno == 0)
864# ifdef ECONNRESET
865 errno = ECONNRESET;
f3d8f6d6 866# else /* ECONNRESET */
46f6ec52 867 errno = EPIPE;
f3d8f6d6 868# endif /* ECONNRESET */
46f6ec52 869
474bc899 870 mci->mci_errno = errno;
6e99f903 871 mci->mci_exitstat = EX_TEMPFAIL;
8e5c6745
EA
872 oldholderrs = HoldErrs;
873 HoldErrs = TRUE;
874 usrerr("451 reply: read error from %s", mci->mci_host);
875
9759fb92
EA
876 /* if debugging, pause so we can see state */
877 if (tTd(18, 100))
878 pause();
f2e44ded 879 mci->mci_state = MCIS_ERROR;
e62e1144 880 smtpquit(m, mci, e);
8e5c6745
EA
881#ifdef XDEBUG
882 {
883 char wbuf[MAXLINE];
5e3f856e
EA
884 char *p = wbuf;
885 if (e->e_to != NULL)
886 {
887 sprintf(p, "%s... ", e->e_to);
888 p += strlen(p);
889 }
890 sprintf(p, "reply(%s) during %s",
891 mci->mci_host, SmtpPhase);
8e5c6745
EA
892 checkfd012(wbuf);
893 }
894#endif
895 HoldErrs = oldholderrs;
d0729491 896 return (-1);
ed7382d3 897 }
a9bac7a9 898 fixcrlf(bufp, TRUE);
37eaaadb 899
8e5c6745
EA
900 /* EHLO failure is not a real error */
901 if (e->e_xfp != NULL && (bufp[0] == '4' ||
902 (bufp[0] == '5' && strncmp(SmtpMsgBuffer, "EHLO", 4) != 0)))
d7dbe85b
EA
903 {
904 /* serious error -- log the previous command */
337a0030
EA
905 if (SmtpNeedIntro)
906 {
907 /* inform user who we are chatting with */
908 fprintf(CurEnv->e_xfp,
909 "... while talking to %s:\n",
910 CurHostName);
911 SmtpNeedIntro = FALSE;
912 }
cb452ecf
EA
913 if (SmtpMsgBuffer[0] != '\0')
914 fprintf(e->e_xfp, ">>> %s\n", SmtpMsgBuffer);
915 SmtpMsgBuffer[0] = '\0';
d7dbe85b
EA
916
917 /* now log the message as from the other side */
a9bac7a9 918 fprintf(e->e_xfp, "<<< %s\n", bufp);
d7dbe85b
EA
919 }
920
921 /* display the input for verbose mode */
dc2cd20f 922 if (Verbose)
09bdfe00 923 nmessage("050 %s", bufp);
37eaaadb 924
3001ca78 925 /* process the line */
0717d167
EA
926 if (pfunc != NULL)
927 (*pfunc)(bufp, firstline, m, mci, e);
3001ca78
EA
928
929 firstline = FALSE;
930
37eaaadb 931 /* if continuation is required, we can go on */
cb452ecf
EA
932 if (bufp[3] == '-')
933 continue;
934
935 /* ignore improperly formated input */
936 if (!(isascii(bufp[0]) && isdigit(bufp[0])))
d2eb2478 937 continue;
37eaaadb
EA
938
939 /* decode the reply code */
cb452ecf 940 r = atoi(bufp);
37eaaadb
EA
941
942 /* extra semantics: 0xx codes are "informational" */
cb452ecf
EA
943 if (r >= 100)
944 break;
945 }
37eaaadb 946
cb452ecf
EA
947 /*
948 ** Now look at SmtpReplyBuffer -- only care about the first
949 ** line of the response from here on out.
950 */
5d41b806 951
cb452ecf
EA
952 /* save temporary failure messages for posterity */
953 if (SmtpReplyBuffer[0] == '4' && SmtpError[0] == '\0')
954 (void) strcpy(SmtpError, SmtpReplyBuffer);
80482eb5 955
cb452ecf
EA
956 /* reply code 421 is "Service Shutting Down" */
957 if (r == SMTPCLOSING && mci->mci_state != MCIS_SSD)
958 {
959 /* send the quit protocol */
960 mci->mci_state = MCIS_SSD;
961 smtpquit(m, mci, e);
d2eb2478 962 }
cb452ecf
EA
963
964 return (r);
d2eb2478 965}
42281a7d 966\f/*
e6b0a75b 967** SMTPMESSAGE -- send message to server
42281a7d
EA
968**
969** Parameters:
970** f -- format
4db45bf1 971** m -- the mailer to control formatting.
42281a7d
EA
972** a, b, c -- parameters
973**
974** Returns:
975** none.
976**
977** Side Effects:
06771186 978** writes message to mci->mci_out.
42281a7d
EA
979*/
980
e6b0a75b 981/*VARARGS1*/
6e99f903
EA
982#ifdef __STDC__
983smtpmessage(char *f, MAILER *m, MCI *mci, ...)
984#else
985smtpmessage(f, m, mci, va_alist)
42281a7d 986 char *f;
4db45bf1 987 MAILER *m;
f2e44ded 988 MCI *mci;
6e99f903
EA
989 va_dcl
990#endif
42281a7d 991{
5229f34d
EA
992 VA_LOCAL_DECL
993
07b49560 994 VA_START(mci);
5229f34d
EA
995 (void) vsprintf(SmtpMsgBuffer, f, ap);
996 VA_END;
bc854e30 997
dc2cd20f 998 if (tTd(18, 1) || Verbose)
b6edea3d 999 nmessage(">>> %s", SmtpMsgBuffer);
8e5c6745
EA
1000 if (TrafficLogFile != NULL)
1001 fprintf(TrafficLogFile, "%05d >>> %s\n", getpid(), SmtpMsgBuffer);
06771186 1002 if (mci->mci_out != NULL)
bc854e30 1003 {
06771186 1004 fprintf(mci->mci_out, "%s%s", SmtpMsgBuffer,
f2e44ded 1005 m == NULL ? "\r\n" : m->m_eol);
bc854e30 1006 }
80ec420e 1007 else if (tTd(18, 1))
0852beb8 1008 {
80ec420e 1009 printf("smtpmessage: NULL mci_out\n");
0852beb8 1010 }
42281a7d 1011}
884a20cb 1012
f3d8f6d6 1013# endif /* SMTP */