merge a bit with Sun version
[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
2604d017 13static char sccsid[] = "@(#)usersmtp.c 8.43 (Berkeley) %G% (with SMTP)";
bee79b64 14#else
2604d017 15static char sccsid[] = "@(#)usersmtp.c 8.43 (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
2604d017 286 if (strlen(line) < (SIZE_T) 5)
3001ca78
EA
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;
eb381926 305 else if (strcasecmp(line, "x-dsn-03") == 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 */
66d16835
EA
498 if (bitset(QHASNOTIFY, to->q_flags) &&
499 bitset(QPRIMARY, to->q_flags))
82e3dc75 500 {
e1f691b3
EA
501 bool firstone = TRUE;
502
503 strcat(optbuf, " NOTIFY=");
504 if (bitset(QPINGONSUCCESS, to->q_flags))
505 {
506 strcat(optbuf, "SUCCESS");
507 firstone = FALSE;
508 }
509 if (bitset(QPINGONFAILURE, to->q_flags))
510 {
511 if (!firstone)
512 strcat(optbuf, ",");
513 strcat(optbuf, "FAILURE");
514 firstone = FALSE;
515 }
516 if (bitset(QPINGONDELAY, to->q_flags))
517 {
518 if (!firstone)
519 strcat(optbuf, ",");
520 strcat(optbuf, "DELAY");
521 firstone = FALSE;
522 }
523 if (firstone)
524 strcat(optbuf, "NEVER");
82e3dc75 525 }
82e3dc75 526
3b6c2253
EA
527 /* ORCPT= parameter */
528 if (to->q_orcpt != NULL)
529 {
530 strcat(optbuf, " ORCPT=");
531 strcat(optbuf, to->q_orcpt);
532 }
68d9129a 533 }
3c6123ce 534
68d9129a 535 smtpmessage("RCPT To:<%s>%s", m, mci, to->q_user, optbuf);
e6b0a75b 536
8e948497 537 SmtpPhase = mci->mci_phase = "client RCPT";
e62e1144 538 setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
3001ca78 539 r = reply(m, mci, e, TimeOuts.to_rcpt, NULL);
68d9129a 540 setstatus(to, SmtpReplyBuffer);
3ff6d543 541 if (r < 0 || REPLYTYPE(r) == 4)
e6b0a75b 542 return (EX_TEMPFAIL);
4672197a
EA
543 else if (REPLYTYPE(r) == 2)
544 return (EX_OK);
977ccbaf
EA
545 else if (r == 550 || r == 551 || r == 553)
546 return (EX_NOUSER);
547 else if (r == 552 || r == 554)
548 return (EX_UNAVAILABLE);
51d9cc47
EA
549
550#ifdef LOG
68f7099c 551 if (LogLevel > 1)
51d9cc47 552 {
6d3599b7
EA
553 syslog(LOG_CRIT, "%s: %s: SMTP RCPT protocol error: %s",
554 e->e_id, mci->mci_host, SmtpReplyBuffer);
51d9cc47
EA
555 }
556#endif
557
977ccbaf 558 return (EX_PROTOCOL);
3c6123ce
EA
559}
560\f/*
4db45bf1 561** SMTPDATA -- send the data and clean up the transaction.
d2eb2478
EA
562**
563** Parameters:
e6b0a75b 564** m -- mailer being sent to.
dd1fe05b 565** e -- the envelope for this message.
d2eb2478
EA
566**
567** Returns:
4a4ebe09 568** exit status corresponding to DATA command.
d2eb2478
EA
569**
570** Side Effects:
e6b0a75b 571** none.
d2eb2478
EA
572*/
573
8e5c6745 574static jmp_buf CtxDataTimeout;
ea07b2d2 575static void datatimeout();
8e5c6745 576
06771186 577smtpdata(m, mci, e)
e6b0a75b 578 struct mailer *m;
f2e44ded 579 register MCI *mci;
dd1fe05b 580 register ENVELOPE *e;
d2eb2478
EA
581{
582 register int r;
8e5c6745
EA
583 register EVENT *ev;
584 time_t timeout;
d2eb2478 585
3c6123ce
EA
586 /*
587 ** Send the data.
4db45bf1
EA
588 ** First send the command and check that it is ok.
589 ** Then send the data.
590 ** Follow it up with a dot to terminate.
591 ** Finally get the results of the transaction.
3c6123ce
EA
592 */
593
4db45bf1 594 /* send the command and check ok to proceed */
e62e1144 595 smtpmessage("DATA", m, mci);
8e948497 596 SmtpPhase = mci->mci_phase = "client DATA 354";
e62e1144 597 setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
3001ca78 598 r = reply(m, mci, e, TimeOuts.to_datainit, NULL);
3ff6d543 599 if (r < 0 || REPLYTYPE(r) == 4)
c6f2ede6
EA
600 {
601 smtpquit(m, mci, e);
3c6123ce 602 return (EX_TEMPFAIL);
c6f2ede6 603 }
4672197a 604 else if (r == 554)
c6f2ede6
EA
605 {
606 smtprset(m, mci, e);
4672197a 607 return (EX_UNAVAILABLE);
c6f2ede6 608 }
4672197a 609 else if (r != 354)
c6f2ede6 610 {
51d9cc47 611#ifdef LOG
68f7099c 612 if (LogLevel > 1)
51d9cc47 613 {
6d3599b7
EA
614 syslog(LOG_CRIT, "%s: %s: SMTP DATA-1 protocol error: %s",
615 e->e_id, mci->mci_host, SmtpReplyBuffer);
51d9cc47
EA
616 }
617#endif
c6f2ede6 618 smtprset(m, mci, e);
977ccbaf 619 return (EX_PROTOCOL);
c6f2ede6 620 }
4db45bf1 621
e532e51b
EA
622 /*
623 ** Set timeout around data writes. Make it at least large
624 ** enough for DNS timeouts on all recipients plus some fudge
625 ** factor. The main thing is that it should not be infinite.
626 */
627
8e5c6745
EA
628 if (setjmp(CtxDataTimeout) != 0)
629 {
630 mci->mci_errno = errno;
631 mci->mci_exitstat = EX_TEMPFAIL;
632 mci->mci_state = MCIS_ERROR;
8e5c6745
EA
633 syserr("451 timeout writing message to %s", mci->mci_host);
634 smtpquit(m, mci, e);
635 return EX_TEMPFAIL;
636 }
637
fd57f063
EA
638 timeout = e->e_msgsize / 16;
639 if (timeout < (time_t) 60)
640 timeout = (time_t) 60;
641 timeout += e->e_nrcpts * 90;
8e5c6745
EA
642 ev = setevent(timeout, datatimeout, 0);
643
c23930c0
EA
644 /*
645 ** Output the actual message.
646 */
647
51d448e5
EA
648 (*e->e_puthdr)(mci, e->e_header, e);
649 (*e->e_putbody)(mci, e, NULL);
4db45bf1 650
c23930c0
EA
651 /*
652 ** Cleanup after sending message.
653 */
654
8e5c6745
EA
655 clrevent(ev);
656
fe3849ea
EA
657 if (ferror(mci->mci_out))
658 {
659 /* error during processing -- don't send the dot */
660 mci->mci_errno = EIO;
661 mci->mci_exitstat = EX_IOERR;
662 mci->mci_state = MCIS_ERROR;
663 smtpquit(m, mci, e);
664 return EX_IOERR;
665 }
666
4db45bf1 667 /* terminate the message */
06771186 668 fprintf(mci->mci_out, ".%s", m->m_eol);
8e5c6745
EA
669 if (TrafficLogFile != NULL)
670 fprintf(TrafficLogFile, "%05d >>> .\n", getpid());
dc2cd20f 671 if (Verbose)
b6edea3d 672 nmessage(">>> .");
4db45bf1
EA
673
674 /* check for the results of the transaction */
8e948497 675 SmtpPhase = mci->mci_phase = "client DATA 250";
e62e1144 676 setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
3001ca78 677 r = reply(m, mci, e, TimeOuts.to_datafinal, NULL);
e62e1144 678 if (r < 0)
c6f2ede6
EA
679 {
680 smtpquit(m, mci, e);
e62e1144 681 return (EX_TEMPFAIL);
c6f2ede6 682 }
e62e1144 683 mci->mci_state = MCIS_OPEN;
614b4258 684 e->e_statmsg = newstr(&SmtpReplyBuffer[4]);
e62e1144 685 if (REPLYTYPE(r) == 4)
3c6123ce 686 return (EX_TEMPFAIL);
4672197a
EA
687 else if (r == 250)
688 return (EX_OK);
689 else if (r == 552 || r == 554)
690 return (EX_UNAVAILABLE);
51d9cc47 691#ifdef LOG
68f7099c 692 if (LogLevel > 1)
51d9cc47 693 {
6d3599b7
EA
694 syslog(LOG_CRIT, "%s: %s: SMTP DATA-2 protocol error: %s",
695 e->e_id, mci->mci_host, SmtpReplyBuffer);
51d9cc47
EA
696 }
697#endif
977ccbaf 698 return (EX_PROTOCOL);
d2eb2478 699}
8e5c6745
EA
700
701
ea07b2d2 702static void
8e5c6745
EA
703datatimeout()
704{
705 longjmp(CtxDataTimeout, 1);
706}
d2eb2478 707\f/*
e6b0a75b
EA
708** SMTPQUIT -- close the SMTP connection.
709**
710** Parameters:
e009f4f4 711** m -- a pointer to the mailer.
e6b0a75b
EA
712**
713** Returns:
714** none.
715**
716** Side Effects:
717** sends the final protocol and closes the connection.
718*/
719
e62e1144 720smtpquit(m, mci, e)
06771186 721 register MAILER *m;
f2e44ded 722 register MCI *mci;
e62e1144 723 ENVELOPE *e;
e6b0a75b 724{
90e0e4e2
EA
725 bool oldSuprErrs = SuprErrs;
726
727 /*
728 ** Suppress errors here -- we may be processing a different
729 ** job when we do the quit connection, and we don't want the
730 ** new job to be penalized for something that isn't it's
731 ** problem.
732 */
733
734 SuprErrs = TRUE;
e6b0a75b 735
f2e44ded 736 /* send the quit message if we haven't gotten I/O error */
e62e1144 737 if (mci->mci_state != MCIS_ERROR)
044ab67a 738 {
8e948497 739 SmtpPhase = "client QUIT";
e62e1144 740 smtpmessage("QUIT", m, mci);
3001ca78 741 (void) reply(m, mci, e, TimeOuts.to_quit, NULL);
90e0e4e2 742 SuprErrs = oldSuprErrs;
06771186 743 if (mci->mci_state == MCIS_CLOSED)
90e0e4e2
EA
744 {
745 SuprErrs = oldSuprErrs;
11957f11 746 return;
90e0e4e2 747 }
80482eb5
EA
748 }
749
2ae0e0ed 750 /* now actually close the connection and pick up the zombie */
c1e6eba2 751 (void) endmailer(mci, e, NULL);
90e0e4e2
EA
752
753 SuprErrs = oldSuprErrs;
e6b0a75b
EA
754}
755\f/*
f2e44ded
EA
756** SMTPRSET -- send a RSET (reset) command
757*/
758
759smtprset(m, mci, e)
760 register MAILER *m;
761 register MCI *mci;
762 ENVELOPE *e;
763{
764 int r;
765
8e948497 766 SmtpPhase = "client RSET";
f2e44ded 767 smtpmessage("RSET", m, mci);
3001ca78 768 r = reply(m, mci, e, TimeOuts.to_rset, NULL);
263c5860
EA
769 if (r < 0)
770 mci->mci_state = MCIS_ERROR;
f2e44ded 771 else if (REPLYTYPE(r) == 2)
263c5860
EA
772 {
773 mci->mci_state = MCIS_OPEN;
774 return;
775 }
776 smtpquit(m, mci, e);
f2e44ded
EA
777}
778\f/*
c428b1b2 779** SMTPPROBE -- check the connection state
f2e44ded
EA
780*/
781
c428b1b2 782smtpprobe(mci)
f2e44ded
EA
783 register MCI *mci;
784{
785 int r;
786 MAILER *m = mci->mci_mailer;
787 extern ENVELOPE BlankEnvelope;
788 ENVELOPE *e = &BlankEnvelope;
789
8e948497 790 SmtpPhase = "client probe";
c428b1b2 791 smtpmessage("RSET", m, mci);
3001ca78 792 r = reply(m, mci, e, TimeOuts.to_miscshort, NULL);
5d41b806 793 if (r < 0 || REPLYTYPE(r) != 2)
f2e44ded
EA
794 smtpquit(m, mci, e);
795 return r;
796}
797\f/*
d2eb2478
EA
798** REPLY -- read arpanet reply
799**
800** Parameters:
4db45bf1 801** m -- the mailer we are reading the reply from.
474bc899
EA
802** mci -- the mailer connection info structure.
803** e -- the current envelope.
804** timeout -- the timeout for reads.
3001ca78
EA
805** pfunc -- processing function for second and subsequent
806** lines of response -- if null, no special
807** processing is done.
d2eb2478
EA
808**
809** Returns:
810** reply code it reads.
811**
812** Side Effects:
813** flushes the mail file.
814*/
815
3001ca78 816reply(m, mci, e, timeout, pfunc)
1dbda134 817 MAILER *m;
f2e44ded 818 MCI *mci;
e62e1144 819 ENVELOPE *e;
3001ca78
EA
820 time_t timeout;
821 void (*pfunc)();
d2eb2478 822{
cb452ecf
EA
823 register char *bufp;
824 register int r;
3001ca78 825 bool firstline = TRUE;
a9bac7a9
EA
826 char junkbuf[MAXLINE];
827
474bc899
EA
828 if (mci->mci_out != NULL)
829 (void) fflush(mci->mci_out);
42281a7d 830
9678c96d 831 if (tTd(18, 1))
42281a7d 832 printf("reply\n");
d2eb2478 833
37eaaadb
EA
834 /*
835 ** Read the input line, being careful not to hang.
836 */
837
cb452ecf 838 for (bufp = SmtpReplyBuffer;; bufp = junkbuf)
d2eb2478 839 {
37eaaadb 840 register char *p;
e62e1144 841 extern time_t curtime();
37eaaadb 842
37eaaadb 843 /* actually do the read */
e62e1144
EA
844 if (e->e_xfp != NULL)
845 (void) fflush(e->e_xfp); /* for debugging */
d0729491
EA
846
847 /* if we are in the process of closing just give the code */
06771186 848 if (mci->mci_state == MCIS_CLOSED)
d0729491
EA
849 return (SMTPCLOSING);
850
bc854e30
EA
851 if (mci->mci_out != NULL)
852 fflush(mci->mci_out);
853
d0729491 854 /* get the line from the other side */
8e948497 855 p = sfgets(bufp, MAXLINE, mci->mci_in, timeout, SmtpPhase);
e62e1144
EA
856 mci->mci_lastuse = curtime();
857
d0729491 858 if (p == NULL)
ed7382d3 859 {
8e5c6745 860 bool oldholderrs;
044ab67a 861 extern char MsgBuf[]; /* err.c */
044ab67a 862
46f6ec52
EA
863 /* if the remote end closed early, fake an error */
864 if (errno == 0)
865# ifdef ECONNRESET
866 errno = ECONNRESET;
f3d8f6d6 867# else /* ECONNRESET */
46f6ec52 868 errno = EPIPE;
f3d8f6d6 869# endif /* ECONNRESET */
46f6ec52 870
474bc899 871 mci->mci_errno = errno;
6e99f903 872 mci->mci_exitstat = EX_TEMPFAIL;
8e5c6745
EA
873 oldholderrs = HoldErrs;
874 HoldErrs = TRUE;
875 usrerr("451 reply: read error from %s", mci->mci_host);
876
9759fb92
EA
877 /* if debugging, pause so we can see state */
878 if (tTd(18, 100))
879 pause();
f2e44ded 880 mci->mci_state = MCIS_ERROR;
e62e1144 881 smtpquit(m, mci, e);
8e5c6745
EA
882#ifdef XDEBUG
883 {
884 char wbuf[MAXLINE];
5e3f856e
EA
885 char *p = wbuf;
886 if (e->e_to != NULL)
887 {
888 sprintf(p, "%s... ", e->e_to);
889 p += strlen(p);
890 }
891 sprintf(p, "reply(%s) during %s",
892 mci->mci_host, SmtpPhase);
8e5c6745
EA
893 checkfd012(wbuf);
894 }
895#endif
896 HoldErrs = oldholderrs;
d0729491 897 return (-1);
ed7382d3 898 }
a9bac7a9 899 fixcrlf(bufp, TRUE);
37eaaadb 900
8e5c6745
EA
901 /* EHLO failure is not a real error */
902 if (e->e_xfp != NULL && (bufp[0] == '4' ||
903 (bufp[0] == '5' && strncmp(SmtpMsgBuffer, "EHLO", 4) != 0)))
d7dbe85b
EA
904 {
905 /* serious error -- log the previous command */
337a0030
EA
906 if (SmtpNeedIntro)
907 {
908 /* inform user who we are chatting with */
909 fprintf(CurEnv->e_xfp,
910 "... while talking to %s:\n",
911 CurHostName);
912 SmtpNeedIntro = FALSE;
913 }
cb452ecf
EA
914 if (SmtpMsgBuffer[0] != '\0')
915 fprintf(e->e_xfp, ">>> %s\n", SmtpMsgBuffer);
916 SmtpMsgBuffer[0] = '\0';
d7dbe85b
EA
917
918 /* now log the message as from the other side */
a9bac7a9 919 fprintf(e->e_xfp, "<<< %s\n", bufp);
d7dbe85b
EA
920 }
921
922 /* display the input for verbose mode */
dc2cd20f 923 if (Verbose)
09bdfe00 924 nmessage("050 %s", bufp);
37eaaadb 925
3001ca78 926 /* process the line */
0717d167
EA
927 if (pfunc != NULL)
928 (*pfunc)(bufp, firstline, m, mci, e);
3001ca78
EA
929
930 firstline = FALSE;
931
37eaaadb 932 /* if continuation is required, we can go on */
cb452ecf
EA
933 if (bufp[3] == '-')
934 continue;
935
936 /* ignore improperly formated input */
937 if (!(isascii(bufp[0]) && isdigit(bufp[0])))
d2eb2478 938 continue;
37eaaadb
EA
939
940 /* decode the reply code */
cb452ecf 941 r = atoi(bufp);
37eaaadb
EA
942
943 /* extra semantics: 0xx codes are "informational" */
cb452ecf
EA
944 if (r >= 100)
945 break;
946 }
37eaaadb 947
cb452ecf
EA
948 /*
949 ** Now look at SmtpReplyBuffer -- only care about the first
950 ** line of the response from here on out.
951 */
5d41b806 952
cb452ecf
EA
953 /* save temporary failure messages for posterity */
954 if (SmtpReplyBuffer[0] == '4' && SmtpError[0] == '\0')
955 (void) strcpy(SmtpError, SmtpReplyBuffer);
80482eb5 956
cb452ecf
EA
957 /* reply code 421 is "Service Shutting Down" */
958 if (r == SMTPCLOSING && mci->mci_state != MCIS_SSD)
959 {
960 /* send the quit protocol */
961 mci->mci_state = MCIS_SSD;
962 smtpquit(m, mci, e);
d2eb2478 963 }
cb452ecf
EA
964
965 return (r);
d2eb2478 966}
42281a7d 967\f/*
e6b0a75b 968** SMTPMESSAGE -- send message to server
42281a7d
EA
969**
970** Parameters:
971** f -- format
4db45bf1 972** m -- the mailer to control formatting.
42281a7d
EA
973** a, b, c -- parameters
974**
975** Returns:
976** none.
977**
978** Side Effects:
06771186 979** writes message to mci->mci_out.
42281a7d
EA
980*/
981
e6b0a75b 982/*VARARGS1*/
6e99f903
EA
983#ifdef __STDC__
984smtpmessage(char *f, MAILER *m, MCI *mci, ...)
985#else
986smtpmessage(f, m, mci, va_alist)
42281a7d 987 char *f;
4db45bf1 988 MAILER *m;
f2e44ded 989 MCI *mci;
6e99f903
EA
990 va_dcl
991#endif
42281a7d 992{
5229f34d
EA
993 VA_LOCAL_DECL
994
07b49560 995 VA_START(mci);
5229f34d
EA
996 (void) vsprintf(SmtpMsgBuffer, f, ap);
997 VA_END;
bc854e30 998
dc2cd20f 999 if (tTd(18, 1) || Verbose)
b6edea3d 1000 nmessage(">>> %s", SmtpMsgBuffer);
8e5c6745
EA
1001 if (TrafficLogFile != NULL)
1002 fprintf(TrafficLogFile, "%05d >>> %s\n", getpid(), SmtpMsgBuffer);
06771186 1003 if (mci->mci_out != NULL)
bc854e30 1004 {
06771186 1005 fprintf(mci->mci_out, "%s%s", SmtpMsgBuffer,
f2e44ded 1006 m == NULL ? "\r\n" : m->m_eol);
bc854e30 1007 }
80ec420e 1008 else if (tTd(18, 1))
0852beb8 1009 {
80ec420e 1010 printf("smtpmessage: NULL mci_out\n");
0852beb8 1011 }
42281a7d 1012}
884a20cb 1013
f3d8f6d6 1014# endif /* SMTP */