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