122716Sdist /*
268839Seric * Copyright (c) 1983, 1995 Eric P. Allman
362532Sbostic * Copyright (c) 1988, 1993
462532Sbostic * The Regents of the University of California. All rights reserved.
533731Sbostic *
642831Sbostic * %sccs.include.redist.c%
733731Sbostic */
822716Sdist
933731Sbostic # include "sendmail.h"
1022716Sdist
1133731Sbostic #ifndef lint
1233731Sbostic #ifdef SMTP
13*69928Seric static char sccsid[] = "@(#)usersmtp.c 8.57 (Berkeley) 06/19/95 (with SMTP)";
1433731Sbostic #else
15*69928Seric static char sccsid[] = "@(#)usersmtp.c 8.57 (Berkeley) 06/19/95 (without SMTP)";
1633731Sbostic #endif
1733731Sbostic #endif /* not lint */
1833731Sbostic
194684Seric # include <sysexits.h>
2021065Seric # include <errno.h>
214684Seric
2233731Sbostic # ifdef SMTP
234684Seric
244684Seric /*
259391Seric ** USERSMTP -- run SMTP protocol from the user end.
269391Seric **
279391Seric ** This protocol is described in RFC821.
289391Seric */
299391Seric
309391Seric #define REPLYTYPE(r) ((r) / 100) /* first digit of reply code */
319391Seric #define REPLYCLASS(r) (((r) / 10) % 10) /* second digit of reply code */
329391Seric #define SMTPCLOSING 421 /* "Service Shutting Down" */
339391Seric
3414900Seric char SmtpMsgBuffer[MAXLINE]; /* buffer for commands */
3510054Seric char SmtpReplyBuffer[MAXLINE]; /* buffer for replies */
3621065Seric char SmtpError[MAXLINE] = ""; /* save failure error messages */
3710054Seric int SmtpPid; /* pid of mailer */
3864071Seric bool SmtpNeedIntro; /* need "while talking" in transcript */
3958671Seric
4069748Seric extern void smtpmessage __P((char *f, MAILER *m, MCI *mci, ...));
419391Seric /*
424865Seric ** SMTPINIT -- initialize SMTP.
434684Seric **
444865Seric ** Opens the connection and sends the initial protocol.
454684Seric **
464684Seric ** Parameters:
474865Seric ** m -- mailer to create connection to.
484865Seric ** pvp -- pointer to parameter vector to pass to
494865Seric ** the mailer.
504684Seric **
514684Seric ** Returns:
5254967Seric ** none.
534684Seric **
544684Seric ** Side Effects:
554865Seric ** creates connection and sends initial protocol.
564684Seric */
574684Seric
5869748Seric void
smtpinit(m,mci,e)5954967Seric smtpinit(m, mci, e)
604865Seric struct mailer *m;
6154967Seric register MCI *mci;
6253751Seric ENVELOPE *e;
634684Seric {
644865Seric register int r;
6558957Seric register char *p;
6660210Seric extern void esmtp_check();
6759285Seric extern void helo_options();
684684Seric
6963753Seric if (tTd(18, 1))
7057379Seric {
7157379Seric printf("smtpinit ");
7264731Seric mci_dump(mci, FALSE);
7357379Seric }
7457379Seric
754865Seric /*
764865Seric ** Open the connection to the mailer.
774865Seric */
784684Seric
7921065Seric SmtpError[0] = '\0';
8057379Seric CurHostName = mci->mci_host; /* XXX UGLY XXX */
8168100Seric if (CurHostName == NULL)
8268100Seric CurHostName = MyHostName;
8364071Seric SmtpNeedIntro = TRUE;
8454967Seric switch (mci->mci_state)
856051Seric {
8654967Seric case MCIS_ACTIVE:
8754967Seric /* need to clear old information */
8854967Seric smtprset(m, mci, e);
8957734Seric /* fall through */
9015139Seric
9154967Seric case MCIS_OPEN:
9254967Seric return;
9354967Seric
9454967Seric case MCIS_ERROR:
9554967Seric case MCIS_SSD:
9654967Seric /* shouldn't happen */
9754967Seric smtpquit(m, mci, e);
9857734Seric /* fall through */
9954967Seric
10054967Seric case MCIS_CLOSED:
10158151Seric syserr("451 smtpinit: state CLOSED");
10254967Seric return;
10354967Seric
10454967Seric case MCIS_OPENING:
10554967Seric break;
1066051Seric }
1074796Seric
10854967Seric mci->mci_state = MCIS_OPENING;
10954967Seric
1104865Seric /*
1114865Seric ** Get the greeting message.
11214913Seric ** This should appear spontaneously. Give it five minutes to
11314886Seric ** happen.
1144865Seric */
1154797Seric
11661093Seric SmtpPhase = mci->mci_phase = "client greeting";
11753751Seric setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
11860210Seric r = reply(m, mci, e, TimeOuts.to_initial, esmtp_check);
11964750Seric if (r < 0 || REPLYTYPE(r) == 4)
12052104Seric goto tempfail1;
12164750Seric if (REPLYTYPE(r) != 2)
12264750Seric goto unavailable;
1234684Seric
1244865Seric /*
1254976Seric ** Send the HELO command.
1267963Seric ** My mother taught me to always introduce myself.
1274976Seric */
1284976Seric
12959285Seric if (bitnset(M_ESMTP, m->m_flags))
13059285Seric mci->mci_flags |= MCIF_ESMTP;
13159285Seric
13259285Seric tryhelo:
13359285Seric if (bitset(MCIF_ESMTP, mci->mci_flags))
13459285Seric {
13559285Seric smtpmessage("EHLO %s", m, mci, MyHostName);
13661093Seric SmtpPhase = mci->mci_phase = "client EHLO";
13759285Seric }
13859285Seric else
13959285Seric {
14059285Seric smtpmessage("HELO %s", m, mci, MyHostName);
14161093Seric SmtpPhase = mci->mci_phase = "client HELO";
14259285Seric }
14353751Seric setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
14459285Seric r = reply(m, mci, e, TimeOuts.to_helo, helo_options);
1458005Seric if (r < 0)
14652104Seric goto tempfail1;
1478005Seric else if (REPLYTYPE(r) == 5)
14859285Seric {
14959285Seric if (bitset(MCIF_ESMTP, mci->mci_flags))
15059285Seric {
15159285Seric /* try old SMTP instead */
15259285Seric mci->mci_flags &= ~MCIF_ESMTP;
15359285Seric goto tryhelo;
15459285Seric }
15514913Seric goto unavailable;
15659285Seric }
1577963Seric else if (REPLYTYPE(r) != 2)
15852104Seric goto tempfail1;
1594976Seric
1604976Seric /*
16158957Seric ** Check to see if we actually ended up talking to ourself.
16258957Seric ** This means we didn't know about an alias or MX, or we managed
16358957Seric ** to connect to an echo server.
16458957Seric */
16558957Seric
16659026Seric p = strchr(&SmtpReplyBuffer[4], ' ');
16758957Seric if (p != NULL)
16861707Seric *p = '\0';
16967472Seric if (!bitnset(M_NOLOOPCHECK, m->m_flags) &&
17067472Seric strcasecmp(&SmtpReplyBuffer[4], MyHostName) == 0)
17158957Seric {
17258957Seric syserr("553 %s config error: mail loops back to myself",
17358957Seric MyHostName);
17458957Seric mci->mci_exitstat = EX_CONFIG;
17558957Seric mci->mci_errno = 0;
17658957Seric smtpquit(m, mci, e);
17758957Seric return;
17858957Seric }
17958957Seric
18058957Seric /*
1819315Seric ** If this is expected to be another sendmail, send some internal
1829315Seric ** commands.
1839315Seric */
1849315Seric
18510688Seric if (bitnset(M_INTERNAL, m->m_flags))
1869315Seric {
1879315Seric /* tell it to be verbose */
18853751Seric smtpmessage("VERB", m, mci);
18959285Seric r = reply(m, mci, e, TimeOuts.to_miscshort, NULL);
1909315Seric if (r < 0)
19152104Seric goto tempfail2;
1929315Seric }
1939315Seric
19465057Seric if (mci->mci_state != MCIS_CLOSED)
19565057Seric {
19665057Seric mci->mci_state = MCIS_OPEN;
19765057Seric return;
19865057Seric }
19953751Seric
20065057Seric /* got a 421 error code during startup */
20165057Seric
20253751Seric tempfail1:
20353751Seric tempfail2:
20453751Seric mci->mci_exitstat = EX_TEMPFAIL;
20557379Seric if (mci->mci_errno == 0)
20657379Seric mci->mci_errno = errno;
20757379Seric if (mci->mci_state != MCIS_CLOSED)
20857379Seric smtpquit(m, mci, e);
20954967Seric return;
21053751Seric
21153751Seric unavailable:
21253751Seric mci->mci_exitstat = EX_UNAVAILABLE;
21353751Seric mci->mci_errno = errno;
21453751Seric smtpquit(m, mci, e);
21554967Seric return;
21653751Seric }
21759285Seric /*
21860210Seric ** ESMTP_CHECK -- check to see if this implementation likes ESMTP protocol
21960210Seric **
22060210Seric ** Parameters:
22160210Seric ** line -- the response line.
22267893Seric ** firstline -- set if this is the first line of the reply.
22360210Seric ** m -- the mailer.
22460210Seric ** mci -- the mailer connection info.
22560210Seric ** e -- the envelope.
22660210Seric **
22760210Seric ** Returns:
22860210Seric ** none.
22960210Seric */
23060210Seric
23160210Seric void
esmtp_check(line,firstline,m,mci,e)23267893Seric esmtp_check(line, firstline, m, mci, e)
23360210Seric char *line;
23467893Seric bool firstline;
23560210Seric MAILER *m;
23660210Seric register MCI *mci;
23760210Seric ENVELOPE *e;
23860210Seric {
23969650Seric if (strstr(line, "ESMTP ") != NULL)
24069650Seric mci->mci_flags |= MCIF_ESMTP;
241*69928Seric if (strstr(line, "8BIT-OK") != NULL)
24269650Seric mci->mci_flags |= MCIF_8BITOK;
24360210Seric }
24460210Seric /*
24559285Seric ** HELO_OPTIONS -- process the options on a HELO line.
24659285Seric **
24759285Seric ** Parameters:
24859285Seric ** line -- the response line.
24967893Seric ** firstline -- set if this is the first line of the reply.
25059285Seric ** m -- the mailer.
25159285Seric ** mci -- the mailer connection info.
25259285Seric ** e -- the envelope.
25359285Seric **
25459285Seric ** Returns:
25559285Seric ** none.
25659285Seric */
25753751Seric
25859285Seric void
helo_options(line,firstline,m,mci,e)25967893Seric helo_options(line, firstline, m, mci, e)
26059285Seric char *line;
26167893Seric bool firstline;
26259285Seric MAILER *m;
26359285Seric register MCI *mci;
26459285Seric ENVELOPE *e;
26559285Seric {
26659285Seric register char *p;
26759285Seric
26867971Seric if (firstline)
26967893Seric return;
27067893Seric
27168706Seric if (strlen(line) < (SIZE_T) 5)
27259285Seric return;
27359285Seric line += 4;
27459285Seric p = strchr(line, ' ');
27559285Seric if (p != NULL)
27659285Seric *p++ = '\0';
27759285Seric if (strcasecmp(line, "size") == 0)
27859285Seric {
27959285Seric mci->mci_flags |= MCIF_SIZE;
28059285Seric if (p != NULL)
28159285Seric mci->mci_maxsize = atol(p);
28259285Seric }
28359285Seric else if (strcasecmp(line, "8bitmime") == 0)
28465870Seric {
28559285Seric mci->mci_flags |= MCIF_8BITMIME;
28665870Seric mci->mci_flags &= ~MCIF_7BIT;
28765870Seric }
28859285Seric else if (strcasecmp(line, "expn") == 0)
28959285Seric mci->mci_flags |= MCIF_EXPN;
29068606Seric else if (strcasecmp(line, "x-dsn-03") == 0)
29167880Seric mci->mci_flags |= MCIF_DSN;
29259285Seric }
29359285Seric /*
29459285Seric ** SMTPMAILFROM -- send MAIL command
29559285Seric **
29659285Seric ** Parameters:
29759285Seric ** m -- the mailer.
29859285Seric ** mci -- the mailer connection structure.
29959285Seric ** e -- the envelope (including the sender to specify).
30059285Seric */
30159285Seric
30269748Seric int
smtpmailfrom(m,mci,e)30353751Seric smtpmailfrom(m, mci, e)
30453751Seric struct mailer *m;
30554967Seric MCI *mci;
30653751Seric ENVELOPE *e;
30753751Seric {
30853751Seric int r;
30965494Seric char *bufp;
31067887Seric char *bodytype;
31168528Seric char buf[MAXNAME + 1];
31259285Seric char optbuf[MAXLINE];
31353751Seric
31463753Seric if (tTd(18, 2))
31557943Seric printf("smtpmailfrom: CurHost=%s\n", CurHostName);
31657943Seric
31759285Seric /* set up appropriate options to include */
31864254Seric if (bitset(MCIF_SIZE, mci->mci_flags) && e->e_msgsize > 0)
31959285Seric sprintf(optbuf, " SIZE=%ld", e->e_msgsize);
32059285Seric else
32159285Seric strcpy(optbuf, "");
32259285Seric
32367887Seric bodytype = e->e_bodytype;
32467887Seric if (bitset(MCIF_8BITMIME, mci->mci_flags))
32567417Seric {
32667887Seric if (bodytype == NULL &&
32767887Seric bitset(MM_MIME8BIT, MimeMode) &&
32867887Seric bitset(EF_HAS8BIT, e->e_flags) &&
32969922Seric !bitset(EF_DONT_MIME, e->e_flags) &&
33067887Seric !bitnset(M_8BITS, m->m_flags))
33167887Seric bodytype = "8BITMIME";
33267887Seric if (bodytype != NULL)
33367417Seric {
33467417Seric strcat(optbuf, " BODY=");
33567887Seric strcat(optbuf, bodytype);
33667417Seric }
33767417Seric }
33867995Seric else if (bitnset(M_8BITS, m->m_flags) ||
339*69928Seric !bitset(EF_HAS8BIT, e->e_flags) ||
340*69928Seric bitset(MCIF_8BITOK, mci->mci_flags))
34167887Seric {
34267887Seric /* just pass it through */
34367887Seric }
34469480Seric #if MIME8TO7
34567887Seric else if (bitset(MM_CVTMIME, MimeMode) &&
34669922Seric !bitset(EF_DONT_MIME, e->e_flags) &&
34768884Seric (!bitset(MM_PASS8BIT, MimeMode) ||
34868884Seric bitset(EF_IS_MIME, e->e_flags)))
34967887Seric {
35067887Seric /* must convert from 8bit MIME format to 7bit encoded */
35167887Seric mci->mci_flags |= MCIF_CVT8TO7;
35267887Seric }
35369480Seric #endif
35467887Seric else if (!bitset(MM_PASS8BIT, MimeMode))
35567887Seric {
35667887Seric /* cannot just send a 8-bit version */
35767887Seric usrerr("%s does not support 8BITMIME", mci->mci_host);
35868857Seric mci->mci_status = "5.6.3";
35967887Seric return EX_DATAERR;
36067887Seric }
36167417Seric
36267963Seric if (bitset(MCIF_DSN, mci->mci_flags))
36367880Seric {
36467963Seric if (e->e_envid != NULL)
36567963Seric {
36667963Seric strcat(optbuf, " ENVID=");
36767963Seric strcat(optbuf, e->e_envid);
36867963Seric }
36968559Seric
37068559Seric /* RET= parameter */
37168559Seric if (bitset(EF_RET_PARAM, e->e_flags))
37268559Seric {
37368559Seric strcat(optbuf, " RET=");
37468559Seric if (bitset(EF_NO_BODY_RETN, e->e_flags))
37568559Seric strcat(optbuf, "HDRS");
37668559Seric else
37768559Seric strcat(optbuf, "FULL");
37868559Seric }
37967880Seric }
38067880Seric
3819315Seric /*
3824865Seric ** Send the MAIL command.
3834865Seric ** Designates the sender.
3844865Seric */
3854796Seric
38653751Seric mci->mci_state = MCIS_ACTIVE;
38753751Seric
38859540Seric if (bitset(EF_RESPONSE, e->e_flags) &&
38959540Seric !bitnset(M_NO_NULL_FROM, m->m_flags))
39058680Seric (void) strcpy(buf, "");
39158680Seric else
39268529Seric expand("\201g", buf, sizeof buf, e);
39365494Seric if (buf[0] == '<')
39465494Seric {
39565494Seric /* strip off <angle brackets> (put back on below) */
39665494Seric bufp = &buf[strlen(buf) - 1];
39765494Seric if (*bufp == '>')
39865494Seric *bufp = '\0';
39965494Seric bufp = &buf[1];
40065494Seric }
40165494Seric else
40265494Seric bufp = buf;
40367472Seric if (bitnset(M_LOCALMAILER, e->e_from.q_mailer->m_flags) ||
40410688Seric !bitnset(M_FROMPATH, m->m_flags))
4058436Seric {
40665494Seric smtpmessage("MAIL From:<%s>%s", m, mci, bufp, optbuf);
4078436Seric }
4088436Seric else
4098436Seric {
41059285Seric smtpmessage("MAIL From:<@%s%c%s>%s", m, mci, MyHostName,
41165494Seric *bufp == '@' ? ',' : ':', bufp, optbuf);
4128436Seric }
41361093Seric SmtpPhase = mci->mci_phase = "client MAIL";
41453751Seric setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
41559285Seric r = reply(m, mci, e, TimeOuts.to_mail, NULL);
41668811Seric if (r < 0 || r == 421)
41753751Seric {
41868811Seric /* communications failure/service shutting down */
41953751Seric mci->mci_exitstat = EX_TEMPFAIL;
42053751Seric mci->mci_errno = errno;
42153751Seric smtpquit(m, mci, e);
42253751Seric return EX_TEMPFAIL;
42353751Seric }
42468811Seric else if (REPLYTYPE(r) == 4)
42568811Seric {
42668811Seric return EX_TEMPFAIL;
42768811Seric }
4287963Seric else if (r == 250)
42953751Seric {
43053751Seric return EX_OK;
43153751Seric }
43268857Seric else if (r == 501)
43368075Seric {
43468857Seric /* syntax error in arguments */
43568857Seric mci->mci_status = "5.5.2";
43668075Seric return EX_DATAERR;
43768075Seric }
43868857Seric else if (r == 553)
43968857Seric {
44068857Seric /* mailbox name not allowed */
44168857Seric mci->mci_status = "5.1.3";
44268857Seric return EX_DATAERR;
44368857Seric }
4447963Seric else if (r == 552)
44553751Seric {
44668811Seric /* exceeded storage allocation */
44768857Seric mci->mci_status = "5.2.2";
44853751Seric return EX_UNAVAILABLE;
44953751Seric }
45014913Seric
45158008Seric #ifdef LOG
45258020Seric if (LogLevel > 1)
45358008Seric {
45467860Seric syslog(LOG_CRIT, "%s: %s: SMTP MAIL protocol error: %s",
45567860Seric e->e_id, mci->mci_host, SmtpReplyBuffer);
45658008Seric }
45758008Seric #endif
45858008Seric
45914913Seric /* protocol error -- close up */
46053751Seric smtpquit(m, mci, e);
46153751Seric return EX_PROTOCOL;
4624684Seric }
4634684Seric /*
4644976Seric ** SMTPRCPT -- designate recipient.
4654797Seric **
4664797Seric ** Parameters:
4674865Seric ** to -- address of recipient.
46810175Seric ** m -- the mailer we are sending to.
46957379Seric ** mci -- the connection info for this transaction.
47057379Seric ** e -- the envelope for this transaction.
4714797Seric **
4724797Seric ** Returns:
4734865Seric ** exit status corresponding to recipient status.
4744797Seric **
4754797Seric ** Side Effects:
4764865Seric ** Sends the mail via SMTP.
4774797Seric */
4784797Seric
47969748Seric int
smtprcpt(to,m,mci,e)48053751Seric smtprcpt(to, m, mci, e)
4814865Seric ADDRESS *to;
48210175Seric register MAILER *m;
48354967Seric MCI *mci;
48453751Seric ENVELOPE *e;
4854797Seric {
4864797Seric register int r;
48767880Seric char optbuf[MAXLINE];
48868857Seric extern char *smtptodsn();
4894797Seric
49067880Seric strcpy(optbuf, "");
49167880Seric if (bitset(MCIF_DSN, mci->mci_flags))
49267880Seric {
49367987Seric /* NOTIFY= parameter */
49468603Seric if (bitset(QHASNOTIFY, to->q_flags) &&
49568603Seric bitset(QPRIMARY, to->q_flags))
49667880Seric {
49768595Seric bool firstone = TRUE;
49868595Seric
49968595Seric strcat(optbuf, " NOTIFY=");
50068595Seric if (bitset(QPINGONSUCCESS, to->q_flags))
50168595Seric {
50268595Seric strcat(optbuf, "SUCCESS");
50368595Seric firstone = FALSE;
50468595Seric }
50568595Seric if (bitset(QPINGONFAILURE, to->q_flags))
50668595Seric {
50768595Seric if (!firstone)
50868595Seric strcat(optbuf, ",");
50968595Seric strcat(optbuf, "FAILURE");
51068595Seric firstone = FALSE;
51168595Seric }
51268595Seric if (bitset(QPINGONDELAY, to->q_flags))
51368595Seric {
51468595Seric if (!firstone)
51568595Seric strcat(optbuf, ",");
51668595Seric strcat(optbuf, "DELAY");
51768595Seric firstone = FALSE;
51868595Seric }
51968595Seric if (firstone)
52068595Seric strcat(optbuf, "NEVER");
52167880Seric }
52267963Seric
52367987Seric /* ORCPT= parameter */
52467987Seric if (to->q_orcpt != NULL)
52567987Seric {
52667987Seric strcat(optbuf, " ORCPT=");
52767987Seric strcat(optbuf, to->q_orcpt);
52867987Seric }
52967880Seric }
5304865Seric
53167880Seric smtpmessage("RCPT To:<%s>%s", m, mci, to->q_user, optbuf);
53267880Seric
53361093Seric SmtpPhase = mci->mci_phase = "client RCPT";
53453751Seric setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
53559285Seric r = reply(m, mci, e, TimeOuts.to_rcpt, NULL);
53668867Seric to->q_rstatus = newstr(SmtpReplyBuffer);
53768857Seric to->q_status = smtptodsn(r);
5388005Seric if (r < 0 || REPLYTYPE(r) == 4)
53968857Seric return EX_TEMPFAIL;
5407963Seric else if (REPLYTYPE(r) == 2)
54168857Seric return EX_OK;
5427964Seric else if (r == 550 || r == 551 || r == 553)
54368857Seric return EX_NOUSER;
5447964Seric else if (r == 552 || r == 554)
54568857Seric return EX_UNAVAILABLE;
54658008Seric
54758008Seric #ifdef LOG
54858020Seric if (LogLevel > 1)
54958008Seric {
55067860Seric syslog(LOG_CRIT, "%s: %s: SMTP RCPT protocol error: %s",
55167860Seric e->e_id, mci->mci_host, SmtpReplyBuffer);
55258008Seric }
55358008Seric #endif
55458008Seric
5557964Seric return (EX_PROTOCOL);
5564797Seric }
5574797Seric /*
55810175Seric ** SMTPDATA -- send the data and clean up the transaction.
5594684Seric **
5604684Seric ** Parameters:
5614865Seric ** m -- mailer being sent to.
5626980Seric ** e -- the envelope for this message.
5634684Seric **
5644684Seric ** Returns:
5654976Seric ** exit status corresponding to DATA command.
5664684Seric **
5674684Seric ** Side Effects:
5684865Seric ** none.
5694684Seric */
5704684Seric
57163753Seric static jmp_buf CtxDataTimeout;
57268433Seric static void datatimeout();
57363753Seric
57469748Seric int
smtpdata(m,mci,e)57553740Seric smtpdata(m, mci, e)
5764865Seric struct mailer *m;
57754967Seric register MCI *mci;
5786980Seric register ENVELOPE *e;
5794684Seric {
5804684Seric register int r;
58163753Seric register EVENT *ev;
58263753Seric time_t timeout;
5834684Seric
5844797Seric /*
5854797Seric ** Send the data.
58610175Seric ** First send the command and check that it is ok.
58710175Seric ** Then send the data.
58810175Seric ** Follow it up with a dot to terminate.
58910175Seric ** Finally get the results of the transaction.
5904797Seric */
5914797Seric
59210175Seric /* send the command and check ok to proceed */
59353751Seric smtpmessage("DATA", m, mci);
59461093Seric SmtpPhase = mci->mci_phase = "client DATA 354";
59553751Seric setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
59659285Seric r = reply(m, mci, e, TimeOuts.to_datainit, NULL);
5978005Seric if (r < 0 || REPLYTYPE(r) == 4)
59857990Seric {
59957990Seric smtpquit(m, mci, e);
6004797Seric return (EX_TEMPFAIL);
60157990Seric }
6027963Seric else if (r == 554)
60357990Seric {
60457990Seric smtprset(m, mci, e);
6057963Seric return (EX_UNAVAILABLE);
60657990Seric }
6077963Seric else if (r != 354)
60857990Seric {
60958008Seric #ifdef LOG
61058020Seric if (LogLevel > 1)
61158008Seric {
61267860Seric syslog(LOG_CRIT, "%s: %s: SMTP DATA-1 protocol error: %s",
61367860Seric e->e_id, mci->mci_host, SmtpReplyBuffer);
61458008Seric }
61558008Seric #endif
61657990Seric smtprset(m, mci, e);
6177964Seric return (EX_PROTOCOL);
61857990Seric }
61910175Seric
62063757Seric /*
62163757Seric ** Set timeout around data writes. Make it at least large
62263757Seric ** enough for DNS timeouts on all recipients plus some fudge
62363757Seric ** factor. The main thing is that it should not be infinite.
62463757Seric */
62563757Seric
62663753Seric if (setjmp(CtxDataTimeout) != 0)
62763753Seric {
62863753Seric mci->mci_errno = errno;
62963753Seric mci->mci_exitstat = EX_TEMPFAIL;
63063753Seric mci->mci_state = MCIS_ERROR;
63163753Seric syserr("451 timeout writing message to %s", mci->mci_host);
63263753Seric smtpquit(m, mci, e);
63363753Seric return EX_TEMPFAIL;
63463753Seric }
63563753Seric
63663787Seric timeout = e->e_msgsize / 16;
63768740Seric if (timeout < (time_t) 600)
63868740Seric timeout = (time_t) 600;
63968740Seric timeout += e->e_nrcpts * 300;
64063753Seric ev = setevent(timeout, datatimeout, 0);
64163753Seric
64267546Seric /*
64367546Seric ** Output the actual message.
64467546Seric */
64567546Seric
64668228Seric (*e->e_puthdr)(mci, e->e_header, e);
64768228Seric (*e->e_putbody)(mci, e, NULL);
64810175Seric
64967546Seric /*
65067546Seric ** Cleanup after sending message.
65167546Seric */
65267546Seric
65363753Seric clrevent(ev);
65463753Seric
65564718Seric if (ferror(mci->mci_out))
65664718Seric {
65764718Seric /* error during processing -- don't send the dot */
65864718Seric mci->mci_errno = EIO;
65964718Seric mci->mci_exitstat = EX_IOERR;
66064718Seric mci->mci_state = MCIS_ERROR;
66164718Seric smtpquit(m, mci, e);
66264718Seric return EX_IOERR;
66364718Seric }
66464718Seric
66510175Seric /* terminate the message */
66653740Seric fprintf(mci->mci_out, ".%s", m->m_eol);
66763753Seric if (TrafficLogFile != NULL)
66863753Seric fprintf(TrafficLogFile, "%05d >>> .\n", getpid());
66958120Seric if (Verbose)
67058151Seric nmessage(">>> .");
67110175Seric
67210175Seric /* check for the results of the transaction */
67361093Seric SmtpPhase = mci->mci_phase = "client DATA 250";
67453751Seric setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
67559285Seric r = reply(m, mci, e, TimeOuts.to_datafinal, NULL);
67653751Seric if (r < 0)
67757990Seric {
67857990Seric smtpquit(m, mci, e);
6794797Seric return (EX_TEMPFAIL);
68057990Seric }
68153751Seric mci->mci_state = MCIS_OPEN;
68258917Seric e->e_statmsg = newstr(&SmtpReplyBuffer[4]);
68353751Seric if (REPLYTYPE(r) == 4)
68453751Seric return (EX_TEMPFAIL);
6857963Seric else if (r == 250)
6867963Seric return (EX_OK);
6877963Seric else if (r == 552 || r == 554)
6887963Seric return (EX_UNAVAILABLE);
68958008Seric #ifdef LOG
69058020Seric if (LogLevel > 1)
69158008Seric {
69267860Seric syslog(LOG_CRIT, "%s: %s: SMTP DATA-2 protocol error: %s",
69367860Seric e->e_id, mci->mci_host, SmtpReplyBuffer);
69458008Seric }
69558008Seric #endif
6967964Seric return (EX_PROTOCOL);
6974684Seric }
69863753Seric
69963753Seric
70068433Seric static void
datatimeout()70163753Seric datatimeout()
70263753Seric {
70363753Seric longjmp(CtxDataTimeout, 1);
70463753Seric }
7054684Seric /*
7064865Seric ** SMTPQUIT -- close the SMTP connection.
7074865Seric **
7084865Seric ** Parameters:
70915535Seric ** m -- a pointer to the mailer.
7104865Seric **
7114865Seric ** Returns:
7124865Seric ** none.
7134865Seric **
7144865Seric ** Side Effects:
7154865Seric ** sends the final protocol and closes the connection.
7164865Seric */
7174865Seric
71869748Seric void
smtpquit(m,mci,e)71953751Seric smtpquit(m, mci, e)
72053751Seric register MAILER *m;
72154967Seric register MCI *mci;
72253751Seric ENVELOPE *e;
7234865Seric {
72464822Seric bool oldSuprErrs = SuprErrs;
7254865Seric
72664822Seric /*
72764822Seric ** Suppress errors here -- we may be processing a different
72864822Seric ** job when we do the quit connection, and we don't want the
72964822Seric ** new job to be penalized for something that isn't it's
73064822Seric ** problem.
73164822Seric */
73264822Seric
73364822Seric SuprErrs = TRUE;
73464822Seric
73554967Seric /* send the quit message if we haven't gotten I/O error */
73653751Seric if (mci->mci_state != MCIS_ERROR)
7379391Seric {
73861093Seric SmtpPhase = "client QUIT";
73953751Seric smtpmessage("QUIT", m, mci);
74059285Seric (void) reply(m, mci, e, TimeOuts.to_quit, NULL);
74164822Seric SuprErrs = oldSuprErrs;
74253740Seric if (mci->mci_state == MCIS_CLOSED)
74364822Seric {
74464822Seric SuprErrs = oldSuprErrs;
74510159Seric return;
74664822Seric }
7479391Seric }
7489391Seric
74952676Seric /* now actually close the connection and pick up the zombie */
75065194Seric (void) endmailer(mci, e, NULL);
75164822Seric
75264822Seric SuprErrs = oldSuprErrs;
7534865Seric }
7544865Seric /*
75554967Seric ** SMTPRSET -- send a RSET (reset) command
75654967Seric */
75754967Seric
75869748Seric void
smtprset(m,mci,e)75954967Seric smtprset(m, mci, e)
76054967Seric register MAILER *m;
76154967Seric register MCI *mci;
76254967Seric ENVELOPE *e;
76354967Seric {
76454967Seric int r;
76554967Seric
76661093Seric SmtpPhase = "client RSET";
76754967Seric smtpmessage("RSET", m, mci);
76859285Seric r = reply(m, mci, e, TimeOuts.to_rset, NULL);
76957734Seric if (r < 0)
77057734Seric mci->mci_state = MCIS_ERROR;
77154967Seric else if (REPLYTYPE(r) == 2)
77257734Seric {
77357734Seric mci->mci_state = MCIS_OPEN;
77457734Seric return;
77557734Seric }
77657734Seric smtpquit(m, mci, e);
77754967Seric }
77854967Seric /*
77958867Seric ** SMTPPROBE -- check the connection state
78054967Seric */
78154967Seric
78269748Seric int
smtpprobe(mci)78358867Seric smtpprobe(mci)
78454967Seric register MCI *mci;
78554967Seric {
78654967Seric int r;
78754967Seric MAILER *m = mci->mci_mailer;
78854967Seric extern ENVELOPE BlankEnvelope;
78954967Seric ENVELOPE *e = &BlankEnvelope;
79054967Seric
79161093Seric SmtpPhase = "client probe";
79258867Seric smtpmessage("RSET", m, mci);
79359285Seric r = reply(m, mci, e, TimeOuts.to_miscshort, NULL);
79458061Seric if (r < 0 || REPLYTYPE(r) != 2)
79554967Seric smtpquit(m, mci, e);
79654967Seric return r;
79754967Seric }
79854967Seric /*
7994684Seric ** REPLY -- read arpanet reply
8004684Seric **
8014684Seric ** Parameters:
80210175Seric ** m -- the mailer we are reading the reply from.
80357379Seric ** mci -- the mailer connection info structure.
80457379Seric ** e -- the current envelope.
80557379Seric ** timeout -- the timeout for reads.
80669107Seric ** pfunc -- processing function called on each line of response.
80769107Seric ** If null, no special processing is done.
8084684Seric **
8094684Seric ** Returns:
8104684Seric ** reply code it reads.
8114684Seric **
8124684Seric ** Side Effects:
8134684Seric ** flushes the mail file.
8144684Seric */
8154684Seric
81669748Seric int
reply(m,mci,e,timeout,pfunc)81759285Seric reply(m, mci, e, timeout, pfunc)
81853751Seric MAILER *m;
81954967Seric MCI *mci;
82053751Seric ENVELOPE *e;
82159285Seric time_t timeout;
82259285Seric void (*pfunc)();
8234684Seric {
82459014Seric register char *bufp;
82559014Seric register int r;
82659285Seric bool firstline = TRUE;
82758957Seric char junkbuf[MAXLINE];
82858957Seric
82957379Seric if (mci->mci_out != NULL)
83057379Seric (void) fflush(mci->mci_out);
8314684Seric
8327677Seric if (tTd(18, 1))
8334796Seric printf("reply\n");
8344796Seric
8357356Seric /*
8367356Seric ** Read the input line, being careful not to hang.
8377356Seric */
8387356Seric
83959014Seric for (bufp = SmtpReplyBuffer;; bufp = junkbuf)
8404684Seric {
8417356Seric register char *p;
84253751Seric extern time_t curtime();
8434684Seric
8447685Seric /* actually do the read */
84553751Seric if (e->e_xfp != NULL)
84653751Seric (void) fflush(e->e_xfp); /* for debugging */
8477356Seric
84810054Seric /* if we are in the process of closing just give the code */
84953740Seric if (mci->mci_state == MCIS_CLOSED)
85010054Seric return (SMTPCLOSING);
85110054Seric
85258680Seric if (mci->mci_out != NULL)
85358680Seric fflush(mci->mci_out);
85458680Seric
85510054Seric /* get the line from the other side */
85661093Seric p = sfgets(bufp, MAXLINE, mci->mci_in, timeout, SmtpPhase);
85753751Seric mci->mci_lastuse = curtime();
85853751Seric
85910054Seric if (p == NULL)
86010131Seric {
86163753Seric bool oldholderrs;
86210148Seric
86321065Seric /* if the remote end closed early, fake an error */
86421065Seric if (errno == 0)
86521065Seric # ifdef ECONNRESET
86621065Seric errno = ECONNRESET;
86756795Seric # else /* ECONNRESET */
86821065Seric errno = EPIPE;
86956795Seric # endif /* ECONNRESET */
87021065Seric
87157379Seric mci->mci_errno = errno;
87257642Seric mci->mci_exitstat = EX_TEMPFAIL;
87363753Seric oldholderrs = HoldErrs;
87463753Seric HoldErrs = TRUE;
87563753Seric usrerr("451 reply: read error from %s", mci->mci_host);
87663753Seric
87710420Seric /* if debugging, pause so we can see state */
87810420Seric if (tTd(18, 100))
87910420Seric pause();
88054967Seric mci->mci_state = MCIS_ERROR;
88153751Seric smtpquit(m, mci, e);
88269881Seric #if XDEBUG
88363753Seric {
88463753Seric char wbuf[MAXLINE];
88564082Seric char *p = wbuf;
88664082Seric if (e->e_to != NULL)
88764082Seric {
88864082Seric sprintf(p, "%s... ", e->e_to);
88964082Seric p += strlen(p);
89064082Seric }
89164082Seric sprintf(p, "reply(%s) during %s",
89264082Seric mci->mci_host, SmtpPhase);
89363753Seric checkfd012(wbuf);
89463753Seric }
89563753Seric #endif
89663753Seric HoldErrs = oldholderrs;
89710054Seric return (-1);
89810131Seric }
89958957Seric fixcrlf(bufp, TRUE);
90010054Seric
90163753Seric /* EHLO failure is not a real error */
90263753Seric if (e->e_xfp != NULL && (bufp[0] == '4' ||
90363753Seric (bufp[0] == '5' && strncmp(SmtpMsgBuffer, "EHLO", 4) != 0)))
90414900Seric {
90514900Seric /* serious error -- log the previous command */
90664071Seric if (SmtpNeedIntro)
90764071Seric {
90864071Seric /* inform user who we are chatting with */
90964071Seric fprintf(CurEnv->e_xfp,
91064071Seric "... while talking to %s:\n",
91164071Seric CurHostName);
91264071Seric SmtpNeedIntro = FALSE;
91364071Seric }
91459014Seric if (SmtpMsgBuffer[0] != '\0')
91559014Seric fprintf(e->e_xfp, ">>> %s\n", SmtpMsgBuffer);
91659014Seric SmtpMsgBuffer[0] = '\0';
91714900Seric
91814900Seric /* now log the message as from the other side */
91958957Seric fprintf(e->e_xfp, "<<< %s\n", bufp);
92014900Seric }
92114900Seric
92214900Seric /* display the input for verbose mode */
92358120Seric if (Verbose)
92459956Seric nmessage("050 %s", bufp);
9257356Seric
92659285Seric /* process the line */
92767893Seric if (pfunc != NULL)
92867893Seric (*pfunc)(bufp, firstline, m, mci, e);
92959285Seric
93059285Seric firstline = FALSE;
93159285Seric
9327356Seric /* if continuation is required, we can go on */
93359014Seric if (bufp[3] == '-')
9344684Seric continue;
9357356Seric
93659014Seric /* ignore improperly formated input */
93759014Seric if (!(isascii(bufp[0]) && isdigit(bufp[0])))
93859014Seric continue;
93959014Seric
9407356Seric /* decode the reply code */
94159014Seric r = atoi(bufp);
9427356Seric
9437356Seric /* extra semantics: 0xx codes are "informational" */
94459014Seric if (r >= 100)
94559014Seric break;
94659014Seric }
9477356Seric
94859014Seric /*
94959014Seric ** Now look at SmtpReplyBuffer -- only care about the first
95059014Seric ** line of the response from here on out.
95159014Seric */
95258061Seric
95359014Seric /* save temporary failure messages for posterity */
95459014Seric if (SmtpReplyBuffer[0] == '4' && SmtpError[0] == '\0')
95559014Seric (void) strcpy(SmtpError, SmtpReplyBuffer);
9569391Seric
95759014Seric /* reply code 421 is "Service Shutting Down" */
95859014Seric if (r == SMTPCLOSING && mci->mci_state != MCIS_SSD)
95959014Seric {
96059014Seric /* send the quit protocol */
96159014Seric mci->mci_state = MCIS_SSD;
96259014Seric smtpquit(m, mci, e);
9634684Seric }
96459014Seric
96559014Seric return (r);
9664684Seric }
9674796Seric /*
9684865Seric ** SMTPMESSAGE -- send message to server
9694796Seric **
9704796Seric ** Parameters:
9714796Seric ** f -- format
97210175Seric ** m -- the mailer to control formatting.
9734796Seric ** a, b, c -- parameters
9744796Seric **
9754796Seric ** Returns:
9764796Seric ** none.
9774796Seric **
9784796Seric ** Side Effects:
97953740Seric ** writes message to mci->mci_out.
9804796Seric */
9814796Seric
9824865Seric /*VARARGS1*/
98369748Seric void
98457642Seric #ifdef __STDC__
smtpmessage(char * f,MAILER * m,MCI * mci,...)98557642Seric smtpmessage(char *f, MAILER *m, MCI *mci, ...)
98657642Seric #else
98757642Seric smtpmessage(f, m, mci, va_alist)
9884796Seric char *f;
98910175Seric MAILER *m;
99054967Seric MCI *mci;
99157642Seric va_dcl
99257642Seric #endif
9934796Seric {
99456852Seric VA_LOCAL_DECL
99556852Seric
99657135Seric VA_START(mci);
99756852Seric (void) vsprintf(SmtpMsgBuffer, f, ap);
99856852Seric VA_END;
99958680Seric
100058120Seric if (tTd(18, 1) || Verbose)
100158151Seric nmessage(">>> %s", SmtpMsgBuffer);
100263753Seric if (TrafficLogFile != NULL)
100363753Seric fprintf(TrafficLogFile, "%05d >>> %s\n", getpid(), SmtpMsgBuffer);
100453740Seric if (mci->mci_out != NULL)
100558680Seric {
100653740Seric fprintf(mci->mci_out, "%s%s", SmtpMsgBuffer,
100754967Seric m == NULL ? "\r\n" : m->m_eol);
100858680Seric }
100959149Seric else if (tTd(18, 1))
101058725Seric {
101159149Seric printf("smtpmessage: NULL mci_out\n");
101258725Seric }
10134796Seric }
10145182Seric
101556795Seric # endif /* SMTP */
1016