mirror of
https://github.com/opnsense/src.git
synced 2026-04-28 17:49:22 -04:00
pass over this and check it more closely, but my initial concern was to get it to all compile again. Some of the obsoleted files 'cvs rm'ed.
1055 lines
23 KiB
C
1055 lines
23 KiB
C
/*
|
||
* Copyright (c) 1983, 1995 Eric P. Allman
|
||
* Copyright (c) 1988, 1993
|
||
* The Regents of the University of California. All rights reserved.
|
||
*
|
||
* Redistribution and use in source and binary forms, with or without
|
||
* modification, are permitted provided that the following conditions
|
||
* are met:
|
||
* 1. Redistributions of source code must retain the above copyright
|
||
* notice, this list of conditions and the following disclaimer.
|
||
* 2. Redistributions in binary form must reproduce the above copyright
|
||
* notice, this list of conditions and the following disclaimer in the
|
||
* documentation and/or other materials provided with the distribution.
|
||
* 3. All advertising materials mentioning features or use of this software
|
||
* must display the following acknowledgement:
|
||
* This product includes software developed by the University of
|
||
* California, Berkeley and its contributors.
|
||
* 4. Neither the name of the University nor the names of its contributors
|
||
* may be used to endorse or promote products derived from this software
|
||
* without specific prior written permission.
|
||
*
|
||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||
* SUCH DAMAGE.
|
||
*/
|
||
|
||
# include "sendmail.h"
|
||
|
||
#ifndef lint
|
||
#ifdef SMTP
|
||
static char sccsid[] = "@(#)usersmtp.c 8.65 (Berkeley) 9/28/95 (with SMTP)";
|
||
#else
|
||
static char sccsid[] = "@(#)usersmtp.c 8.65 (Berkeley) 9/28/95 (without SMTP)";
|
||
#endif
|
||
#endif /* not lint */
|
||
|
||
# include <sysexits.h>
|
||
# include <errno.h>
|
||
|
||
# ifdef SMTP
|
||
|
||
/*
|
||
** USERSMTP -- run SMTP protocol from the user end.
|
||
**
|
||
** This protocol is described in RFC821.
|
||
*/
|
||
|
||
#define REPLYTYPE(r) ((r) / 100) /* first digit of reply code */
|
||
#define REPLYCLASS(r) (((r) / 10) % 10) /* second digit of reply code */
|
||
#define SMTPCLOSING 421 /* "Service Shutting Down" */
|
||
|
||
char SmtpMsgBuffer[MAXLINE]; /* buffer for commands */
|
||
char SmtpReplyBuffer[MAXLINE]; /* buffer for replies */
|
||
char SmtpError[MAXLINE] = ""; /* save failure error messages */
|
||
int SmtpPid; /* pid of mailer */
|
||
bool SmtpNeedIntro; /* need "while talking" in transcript */
|
||
|
||
extern void smtpmessage __P((char *f, MAILER *m, MCI *mci, ...));
|
||
/*
|
||
** SMTPINIT -- initialize SMTP.
|
||
**
|
||
** Opens the connection and sends the initial protocol.
|
||
**
|
||
** Parameters:
|
||
** m -- mailer to create connection to.
|
||
** pvp -- pointer to parameter vector to pass to
|
||
** the mailer.
|
||
**
|
||
** Returns:
|
||
** none.
|
||
**
|
||
** Side Effects:
|
||
** creates connection and sends initial protocol.
|
||
*/
|
||
|
||
void
|
||
smtpinit(m, mci, e)
|
||
struct mailer *m;
|
||
register MCI *mci;
|
||
ENVELOPE *e;
|
||
{
|
||
register int r;
|
||
register char *p;
|
||
extern void esmtp_check();
|
||
extern void helo_options();
|
||
|
||
if (tTd(18, 1))
|
||
{
|
||
printf("smtpinit ");
|
||
mci_dump(mci, FALSE);
|
||
}
|
||
|
||
/*
|
||
** Open the connection to the mailer.
|
||
*/
|
||
|
||
SmtpError[0] = '\0';
|
||
CurHostName = mci->mci_host; /* XXX UGLY XXX */
|
||
if (CurHostName == NULL)
|
||
CurHostName = MyHostName;
|
||
SmtpNeedIntro = TRUE;
|
||
switch (mci->mci_state)
|
||
{
|
||
case MCIS_ACTIVE:
|
||
/* need to clear old information */
|
||
smtprset(m, mci, e);
|
||
/* fall through */
|
||
|
||
case MCIS_OPEN:
|
||
return;
|
||
|
||
case MCIS_ERROR:
|
||
case MCIS_SSD:
|
||
/* shouldn't happen */
|
||
smtpquit(m, mci, e);
|
||
/* fall through */
|
||
|
||
case MCIS_CLOSED:
|
||
syserr("451 smtpinit: state CLOSED");
|
||
return;
|
||
|
||
case MCIS_OPENING:
|
||
break;
|
||
}
|
||
|
||
mci->mci_state = MCIS_OPENING;
|
||
|
||
/*
|
||
** Get the greeting message.
|
||
** This should appear spontaneously. Give it five minutes to
|
||
** happen.
|
||
*/
|
||
|
||
SmtpPhase = mci->mci_phase = "client greeting";
|
||
setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
|
||
r = reply(m, mci, e, TimeOuts.to_initial, esmtp_check);
|
||
if (r < 0 || REPLYTYPE(r) == 4)
|
||
goto tempfail1;
|
||
if (REPLYTYPE(r) != 2)
|
||
goto unavailable;
|
||
|
||
/*
|
||
** Send the HELO command.
|
||
** My mother taught me to always introduce myself.
|
||
*/
|
||
|
||
if (bitnset(M_ESMTP, m->m_flags))
|
||
mci->mci_flags |= MCIF_ESMTP;
|
||
|
||
tryhelo:
|
||
if (bitset(MCIF_ESMTP, mci->mci_flags))
|
||
{
|
||
smtpmessage("EHLO %s", m, mci, MyHostName);
|
||
SmtpPhase = mci->mci_phase = "client EHLO";
|
||
}
|
||
else
|
||
{
|
||
smtpmessage("HELO %s", m, mci, MyHostName);
|
||
SmtpPhase = mci->mci_phase = "client HELO";
|
||
}
|
||
setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
|
||
r = reply(m, mci, e, TimeOuts.to_helo, helo_options);
|
||
if (r < 0)
|
||
goto tempfail1;
|
||
else if (REPLYTYPE(r) == 5)
|
||
{
|
||
if (bitset(MCIF_ESMTP, mci->mci_flags))
|
||
{
|
||
/* try old SMTP instead */
|
||
mci->mci_flags &= ~MCIF_ESMTP;
|
||
goto tryhelo;
|
||
}
|
||
goto unavailable;
|
||
}
|
||
else if (REPLYTYPE(r) != 2)
|
||
goto tempfail1;
|
||
|
||
/*
|
||
** Check to see if we actually ended up talking to ourself.
|
||
** This means we didn't know about an alias or MX, or we managed
|
||
** to connect to an echo server.
|
||
*/
|
||
|
||
p = strchr(&SmtpReplyBuffer[4], ' ');
|
||
if (p != NULL)
|
||
*p = '\0';
|
||
if (!bitnset(M_NOLOOPCHECK, m->m_flags) &&
|
||
strcasecmp(&SmtpReplyBuffer[4], MyHostName) == 0)
|
||
{
|
||
syserr("553 %s config error: mail loops back to me (MX problem?)",
|
||
mci->mci_host);
|
||
mci->mci_exitstat = EX_CONFIG;
|
||
mci->mci_errno = 0;
|
||
smtpquit(m, mci, e);
|
||
return;
|
||
}
|
||
|
||
/*
|
||
** If this is expected to be another sendmail, send some internal
|
||
** commands.
|
||
*/
|
||
|
||
if (bitnset(M_INTERNAL, m->m_flags))
|
||
{
|
||
/* tell it to be verbose */
|
||
smtpmessage("VERB", m, mci);
|
||
r = reply(m, mci, e, TimeOuts.to_miscshort, NULL);
|
||
if (r < 0)
|
||
goto tempfail2;
|
||
}
|
||
|
||
if (mci->mci_state != MCIS_CLOSED)
|
||
{
|
||
mci->mci_state = MCIS_OPEN;
|
||
return;
|
||
}
|
||
|
||
/* got a 421 error code during startup */
|
||
|
||
tempfail1:
|
||
tempfail2:
|
||
mci->mci_exitstat = EX_TEMPFAIL;
|
||
if (mci->mci_errno == 0)
|
||
mci->mci_errno = errno;
|
||
if (mci->mci_state != MCIS_CLOSED)
|
||
smtpquit(m, mci, e);
|
||
return;
|
||
|
||
unavailable:
|
||
mci->mci_exitstat = EX_UNAVAILABLE;
|
||
mci->mci_errno = errno;
|
||
smtpquit(m, mci, e);
|
||
return;
|
||
}
|
||
/*
|
||
** ESMTP_CHECK -- check to see if this implementation likes ESMTP protocol
|
||
**
|
||
** Parameters:
|
||
** line -- the response line.
|
||
** firstline -- set if this is the first line of the reply.
|
||
** m -- the mailer.
|
||
** mci -- the mailer connection info.
|
||
** e -- the envelope.
|
||
**
|
||
** Returns:
|
||
** none.
|
||
*/
|
||
|
||
void
|
||
esmtp_check(line, firstline, m, mci, e)
|
||
char *line;
|
||
bool firstline;
|
||
MAILER *m;
|
||
register MCI *mci;
|
||
ENVELOPE *e;
|
||
{
|
||
if (strstr(line, "ESMTP") != NULL)
|
||
mci->mci_flags |= MCIF_ESMTP;
|
||
if (strstr(line, "8BIT-OK") != NULL)
|
||
mci->mci_flags |= MCIF_8BITOK;
|
||
}
|
||
/*
|
||
** HELO_OPTIONS -- process the options on a HELO line.
|
||
**
|
||
** Parameters:
|
||
** line -- the response line.
|
||
** firstline -- set if this is the first line of the reply.
|
||
** m -- the mailer.
|
||
** mci -- the mailer connection info.
|
||
** e -- the envelope.
|
||
**
|
||
** Returns:
|
||
** none.
|
||
*/
|
||
|
||
void
|
||
helo_options(line, firstline, m, mci, e)
|
||
char *line;
|
||
bool firstline;
|
||
MAILER *m;
|
||
register MCI *mci;
|
||
ENVELOPE *e;
|
||
{
|
||
register char *p;
|
||
|
||
if (firstline)
|
||
return;
|
||
|
||
if (strlen(line) < (SIZE_T) 5)
|
||
return;
|
||
line += 4;
|
||
p = strchr(line, ' ');
|
||
if (p != NULL)
|
||
*p++ = '\0';
|
||
if (strcasecmp(line, "size") == 0)
|
||
{
|
||
mci->mci_flags |= MCIF_SIZE;
|
||
if (p != NULL)
|
||
mci->mci_maxsize = atol(p);
|
||
}
|
||
else if (strcasecmp(line, "8bitmime") == 0)
|
||
{
|
||
mci->mci_flags |= MCIF_8BITMIME;
|
||
mci->mci_flags &= ~MCIF_7BIT;
|
||
}
|
||
else if (strcasecmp(line, "expn") == 0)
|
||
mci->mci_flags |= MCIF_EXPN;
|
||
else if (strcasecmp(line, "dsn") == 0)
|
||
mci->mci_flags |= MCIF_DSN;
|
||
}
|
||
/*
|
||
** SMTPMAILFROM -- send MAIL command
|
||
**
|
||
** Parameters:
|
||
** m -- the mailer.
|
||
** mci -- the mailer connection structure.
|
||
** e -- the envelope (including the sender to specify).
|
||
*/
|
||
|
||
int
|
||
smtpmailfrom(m, mci, e)
|
||
struct mailer *m;
|
||
MCI *mci;
|
||
ENVELOPE *e;
|
||
{
|
||
int r;
|
||
char *bufp;
|
||
char *bodytype;
|
||
char buf[MAXNAME + 1];
|
||
char optbuf[MAXLINE];
|
||
|
||
if (tTd(18, 2))
|
||
printf("smtpmailfrom: CurHost=%s\n", CurHostName);
|
||
|
||
/* set up appropriate options to include */
|
||
if (bitset(MCIF_SIZE, mci->mci_flags) && e->e_msgsize > 0)
|
||
sprintf(optbuf, " SIZE=%ld", e->e_msgsize);
|
||
else
|
||
strcpy(optbuf, "");
|
||
|
||
bodytype = e->e_bodytype;
|
||
if (bitset(MCIF_8BITMIME, mci->mci_flags))
|
||
{
|
||
if (bodytype == NULL &&
|
||
bitset(MM_MIME8BIT, MimeMode) &&
|
||
bitset(EF_HAS8BIT, e->e_flags) &&
|
||
!bitset(EF_DONT_MIME, e->e_flags) &&
|
||
!bitnset(M_8BITS, m->m_flags))
|
||
bodytype = "8BITMIME";
|
||
if (bodytype != NULL)
|
||
{
|
||
strcat(optbuf, " BODY=");
|
||
strcat(optbuf, bodytype);
|
||
}
|
||
}
|
||
else if (bitnset(M_8BITS, m->m_flags) ||
|
||
!bitset(EF_HAS8BIT, e->e_flags) ||
|
||
bitset(MCIF_8BITOK, mci->mci_flags))
|
||
{
|
||
/* just pass it through */
|
||
}
|
||
#if MIME8TO7
|
||
else if (bitset(MM_CVTMIME, MimeMode) &&
|
||
!bitset(EF_DONT_MIME, e->e_flags) &&
|
||
(!bitset(MM_PASS8BIT, MimeMode) ||
|
||
bitset(EF_IS_MIME, e->e_flags)))
|
||
{
|
||
/* must convert from 8bit MIME format to 7bit encoded */
|
||
mci->mci_flags |= MCIF_CVT8TO7;
|
||
}
|
||
#endif
|
||
else if (!bitset(MM_PASS8BIT, MimeMode))
|
||
{
|
||
/* cannot just send a 8-bit version */
|
||
usrerr("%s does not support 8BITMIME", mci->mci_host);
|
||
mci->mci_status = "5.6.3";
|
||
return EX_DATAERR;
|
||
}
|
||
|
||
if (bitset(MCIF_DSN, mci->mci_flags))
|
||
{
|
||
if (e->e_envid != NULL)
|
||
{
|
||
strcat(optbuf, " ENVID=");
|
||
strcat(optbuf, e->e_envid);
|
||
}
|
||
|
||
/* RET= parameter */
|
||
if (bitset(EF_RET_PARAM, e->e_flags))
|
||
{
|
||
strcat(optbuf, " RET=");
|
||
if (bitset(EF_NO_BODY_RETN, e->e_flags))
|
||
strcat(optbuf, "HDRS");
|
||
else
|
||
strcat(optbuf, "FULL");
|
||
}
|
||
}
|
||
|
||
/*
|
||
** Send the MAIL command.
|
||
** Designates the sender.
|
||
*/
|
||
|
||
mci->mci_state = MCIS_ACTIVE;
|
||
|
||
if (bitset(EF_RESPONSE, e->e_flags) &&
|
||
!bitnset(M_NO_NULL_FROM, m->m_flags))
|
||
(void) strcpy(buf, "");
|
||
else
|
||
expand("\201g", buf, sizeof buf, e);
|
||
if (buf[0] == '<')
|
||
{
|
||
/* strip off <angle brackets> (put back on below) */
|
||
bufp = &buf[strlen(buf) - 1];
|
||
if (*bufp == '>')
|
||
*bufp = '\0';
|
||
bufp = &buf[1];
|
||
}
|
||
else
|
||
bufp = buf;
|
||
if (bitnset(M_LOCALMAILER, e->e_from.q_mailer->m_flags) ||
|
||
!bitnset(M_FROMPATH, m->m_flags))
|
||
{
|
||
smtpmessage("MAIL From:<%s>%s", m, mci, bufp, optbuf);
|
||
}
|
||
else
|
||
{
|
||
smtpmessage("MAIL From:<@%s%c%s>%s", m, mci, MyHostName,
|
||
*bufp == '@' ? ',' : ':', bufp, optbuf);
|
||
}
|
||
SmtpPhase = mci->mci_phase = "client MAIL";
|
||
setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
|
||
r = reply(m, mci, e, TimeOuts.to_mail, NULL);
|
||
if (r < 0 || r == 421)
|
||
{
|
||
/* communications failure/service shutting down */
|
||
mci->mci_exitstat = EX_TEMPFAIL;
|
||
mci->mci_errno = errno;
|
||
smtpquit(m, mci, e);
|
||
return EX_TEMPFAIL;
|
||
}
|
||
else if (REPLYTYPE(r) == 4)
|
||
{
|
||
return EX_TEMPFAIL;
|
||
}
|
||
else if (REPLYTYPE(r) == 2)
|
||
{
|
||
return EX_OK;
|
||
}
|
||
else if (r == 501)
|
||
{
|
||
/* syntax error in arguments */
|
||
mci->mci_status = "5.5.2";
|
||
return EX_DATAERR;
|
||
}
|
||
else if (r == 553)
|
||
{
|
||
/* mailbox name not allowed */
|
||
mci->mci_status = "5.1.3";
|
||
return EX_DATAERR;
|
||
}
|
||
else if (r == 552)
|
||
{
|
||
/* exceeded storage allocation */
|
||
mci->mci_status = "5.2.2";
|
||
return EX_UNAVAILABLE;
|
||
}
|
||
else if (REPLYTYPE(r) == 5)
|
||
{
|
||
/* unknown error */
|
||
return EX_UNAVAILABLE;
|
||
}
|
||
|
||
#ifdef LOG
|
||
if (LogLevel > 1)
|
||
{
|
||
syslog(LOG_CRIT, "%s: %.100s: SMTP MAIL protocol error: %s",
|
||
e->e_id, mci->mci_host,
|
||
shortenstring(SmtpReplyBuffer, 403));
|
||
}
|
||
#endif
|
||
|
||
/* protocol error -- close up */
|
||
smtpquit(m, mci, e);
|
||
return EX_PROTOCOL;
|
||
}
|
||
/*
|
||
** SMTPRCPT -- designate recipient.
|
||
**
|
||
** Parameters:
|
||
** to -- address of recipient.
|
||
** m -- the mailer we are sending to.
|
||
** mci -- the connection info for this transaction.
|
||
** e -- the envelope for this transaction.
|
||
**
|
||
** Returns:
|
||
** exit status corresponding to recipient status.
|
||
**
|
||
** Side Effects:
|
||
** Sends the mail via SMTP.
|
||
*/
|
||
|
||
int
|
||
smtprcpt(to, m, mci, e)
|
||
ADDRESS *to;
|
||
register MAILER *m;
|
||
MCI *mci;
|
||
ENVELOPE *e;
|
||
{
|
||
register int r;
|
||
char optbuf[MAXLINE];
|
||
extern char *smtptodsn();
|
||
|
||
strcpy(optbuf, "");
|
||
if (bitset(MCIF_DSN, mci->mci_flags))
|
||
{
|
||
/* NOTIFY= parameter */
|
||
if (bitset(QHASNOTIFY, to->q_flags) &&
|
||
bitset(QPRIMARY, to->q_flags))
|
||
{
|
||
bool firstone = TRUE;
|
||
|
||
strcat(optbuf, " NOTIFY=");
|
||
if (bitset(QPINGONSUCCESS, to->q_flags))
|
||
{
|
||
strcat(optbuf, "SUCCESS");
|
||
firstone = FALSE;
|
||
}
|
||
if (bitset(QPINGONFAILURE, to->q_flags))
|
||
{
|
||
if (!firstone)
|
||
strcat(optbuf, ",");
|
||
strcat(optbuf, "FAILURE");
|
||
firstone = FALSE;
|
||
}
|
||
if (bitset(QPINGONDELAY, to->q_flags))
|
||
{
|
||
if (!firstone)
|
||
strcat(optbuf, ",");
|
||
strcat(optbuf, "DELAY");
|
||
firstone = FALSE;
|
||
}
|
||
if (firstone)
|
||
strcat(optbuf, "NEVER");
|
||
}
|
||
|
||
/* ORCPT= parameter */
|
||
if (to->q_orcpt != NULL)
|
||
{
|
||
strcat(optbuf, " ORCPT=");
|
||
strcat(optbuf, to->q_orcpt);
|
||
}
|
||
}
|
||
|
||
smtpmessage("RCPT To:<%s>%s", m, mci, to->q_user, optbuf);
|
||
|
||
SmtpPhase = mci->mci_phase = "client RCPT";
|
||
setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
|
||
r = reply(m, mci, e, TimeOuts.to_rcpt, NULL);
|
||
to->q_rstatus = newstr(SmtpReplyBuffer);
|
||
to->q_status = smtptodsn(r);
|
||
to->q_statmta = mci->mci_host;
|
||
if (r < 0 || REPLYTYPE(r) == 4)
|
||
return EX_TEMPFAIL;
|
||
else if (REPLYTYPE(r) == 2)
|
||
return EX_OK;
|
||
else if (r == 550 || r == 551 || r == 553)
|
||
return EX_NOUSER;
|
||
else if (REPLYTYPE(r) == 5)
|
||
return EX_UNAVAILABLE;
|
||
|
||
#ifdef LOG
|
||
if (LogLevel > 1)
|
||
{
|
||
syslog(LOG_CRIT, "%s: %.100s: SMTP RCPT protocol error: %s",
|
||
e->e_id, mci->mci_host,
|
||
shortenstring(SmtpReplyBuffer, 403));
|
||
}
|
||
#endif
|
||
|
||
return EX_PROTOCOL;
|
||
}
|
||
/*
|
||
** SMTPDATA -- send the data and clean up the transaction.
|
||
**
|
||
** Parameters:
|
||
** m -- mailer being sent to.
|
||
** e -- the envelope for this message.
|
||
**
|
||
** Returns:
|
||
** exit status corresponding to DATA command.
|
||
**
|
||
** Side Effects:
|
||
** none.
|
||
*/
|
||
|
||
static jmp_buf CtxDataTimeout;
|
||
static void datatimeout();
|
||
|
||
int
|
||
smtpdata(m, mci, e)
|
||
struct mailer *m;
|
||
register MCI *mci;
|
||
register ENVELOPE *e;
|
||
{
|
||
register int r;
|
||
register EVENT *ev;
|
||
time_t timeout;
|
||
|
||
/*
|
||
** Send the data.
|
||
** First send the command and check that it is ok.
|
||
** Then send the data.
|
||
** Follow it up with a dot to terminate.
|
||
** Finally get the results of the transaction.
|
||
*/
|
||
|
||
/* send the command and check ok to proceed */
|
||
smtpmessage("DATA", m, mci);
|
||
SmtpPhase = mci->mci_phase = "client DATA 354";
|
||
setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
|
||
r = reply(m, mci, e, TimeOuts.to_datainit, NULL);
|
||
if (r < 0 || REPLYTYPE(r) == 4)
|
||
{
|
||
smtpquit(m, mci, e);
|
||
return EX_TEMPFAIL;
|
||
}
|
||
else if (REPLYTYPE(r) == 5)
|
||
{
|
||
smtprset(m, mci, e);
|
||
return EX_UNAVAILABLE;
|
||
}
|
||
else if (r != 354)
|
||
{
|
||
#ifdef LOG
|
||
if (LogLevel > 1)
|
||
{
|
||
syslog(LOG_CRIT, "%s: %.100s: SMTP DATA-1 protocol error: %s",
|
||
e->e_id, mci->mci_host,
|
||
shortenstring(SmtpReplyBuffer, 403));
|
||
}
|
||
#endif
|
||
smtprset(m, mci, e);
|
||
return (EX_PROTOCOL);
|
||
}
|
||
|
||
/*
|
||
** Set timeout around data writes. Make it at least large
|
||
** enough for DNS timeouts on all recipients plus some fudge
|
||
** factor. The main thing is that it should not be infinite.
|
||
*/
|
||
|
||
if (setjmp(CtxDataTimeout) != 0)
|
||
{
|
||
mci->mci_errno = errno;
|
||
mci->mci_exitstat = EX_TEMPFAIL;
|
||
mci->mci_state = MCIS_ERROR;
|
||
syserr("451 timeout writing message to %s", mci->mci_host);
|
||
smtpquit(m, mci, e);
|
||
return EX_TEMPFAIL;
|
||
}
|
||
|
||
timeout = e->e_msgsize / 16;
|
||
if (timeout < (time_t) 600)
|
||
timeout = (time_t) 600;
|
||
timeout += e->e_nrcpts * 300;
|
||
ev = setevent(timeout, datatimeout, 0);
|
||
|
||
/*
|
||
** Output the actual message.
|
||
*/
|
||
|
||
(*e->e_puthdr)(mci, e->e_header, e);
|
||
(*e->e_putbody)(mci, e, NULL);
|
||
|
||
/*
|
||
** Cleanup after sending message.
|
||
*/
|
||
|
||
clrevent(ev);
|
||
|
||
if (ferror(mci->mci_out))
|
||
{
|
||
/* error during processing -- don't send the dot */
|
||
mci->mci_errno = EIO;
|
||
mci->mci_exitstat = EX_IOERR;
|
||
mci->mci_state = MCIS_ERROR;
|
||
smtpquit(m, mci, e);
|
||
return EX_IOERR;
|
||
}
|
||
|
||
/* terminate the message */
|
||
fprintf(mci->mci_out, ".%s", m->m_eol);
|
||
if (TrafficLogFile != NULL)
|
||
fprintf(TrafficLogFile, "%05d >>> .\n", getpid());
|
||
if (Verbose)
|
||
nmessage(">>> .");
|
||
|
||
/* check for the results of the transaction */
|
||
SmtpPhase = mci->mci_phase = "client DATA 250";
|
||
setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
|
||
r = reply(m, mci, e, TimeOuts.to_datafinal, NULL);
|
||
if (r < 0)
|
||
{
|
||
smtpquit(m, mci, e);
|
||
return EX_TEMPFAIL;
|
||
}
|
||
mci->mci_state = MCIS_OPEN;
|
||
e->e_statmsg = newstr(&SmtpReplyBuffer[4]);
|
||
if (REPLYTYPE(r) == 4)
|
||
return EX_TEMPFAIL;
|
||
else if (REPLYCLASS(r) != 5)
|
||
/* fall through */ ;
|
||
else if (REPLYTYPE(r) == 2)
|
||
return EX_OK;
|
||
else if (REPLYTYPE(r) == 5)
|
||
return EX_UNAVAILABLE;
|
||
#ifdef LOG
|
||
if (LogLevel > 1)
|
||
{
|
||
syslog(LOG_CRIT, "%s: %.100s: SMTP DATA-2 protocol error: %s",
|
||
e->e_id, mci->mci_host,
|
||
shortenstring(SmtpReplyBuffer, 403));
|
||
}
|
||
#endif
|
||
return EX_PROTOCOL;
|
||
}
|
||
|
||
|
||
static void
|
||
datatimeout()
|
||
{
|
||
longjmp(CtxDataTimeout, 1);
|
||
}
|
||
/*
|
||
** SMTPQUIT -- close the SMTP connection.
|
||
**
|
||
** Parameters:
|
||
** m -- a pointer to the mailer.
|
||
**
|
||
** Returns:
|
||
** none.
|
||
**
|
||
** Side Effects:
|
||
** sends the final protocol and closes the connection.
|
||
*/
|
||
|
||
void
|
||
smtpquit(m, mci, e)
|
||
register MAILER *m;
|
||
register MCI *mci;
|
||
ENVELOPE *e;
|
||
{
|
||
bool oldSuprErrs = SuprErrs;
|
||
|
||
/*
|
||
** Suppress errors here -- we may be processing a different
|
||
** job when we do the quit connection, and we don't want the
|
||
** new job to be penalized for something that isn't it's
|
||
** problem.
|
||
*/
|
||
|
||
SuprErrs = TRUE;
|
||
|
||
/* send the quit message if we haven't gotten I/O error */
|
||
if (mci->mci_state != MCIS_ERROR)
|
||
{
|
||
SmtpPhase = "client QUIT";
|
||
smtpmessage("QUIT", m, mci);
|
||
(void) reply(m, mci, e, TimeOuts.to_quit, NULL);
|
||
SuprErrs = oldSuprErrs;
|
||
if (mci->mci_state == MCIS_CLOSED)
|
||
{
|
||
SuprErrs = oldSuprErrs;
|
||
return;
|
||
}
|
||
}
|
||
|
||
/* now actually close the connection and pick up the zombie */
|
||
(void) endmailer(mci, e, NULL);
|
||
|
||
SuprErrs = oldSuprErrs;
|
||
}
|
||
/*
|
||
** SMTPRSET -- send a RSET (reset) command
|
||
*/
|
||
|
||
void
|
||
smtprset(m, mci, e)
|
||
register MAILER *m;
|
||
register MCI *mci;
|
||
ENVELOPE *e;
|
||
{
|
||
int r;
|
||
|
||
SmtpPhase = "client RSET";
|
||
smtpmessage("RSET", m, mci);
|
||
r = reply(m, mci, e, TimeOuts.to_rset, NULL);
|
||
if (r < 0)
|
||
mci->mci_state = MCIS_ERROR;
|
||
else if (REPLYTYPE(r) == 2)
|
||
{
|
||
mci->mci_state = MCIS_OPEN;
|
||
return;
|
||
}
|
||
smtpquit(m, mci, e);
|
||
}
|
||
/*
|
||
** SMTPPROBE -- check the connection state
|
||
*/
|
||
|
||
int
|
||
smtpprobe(mci)
|
||
register MCI *mci;
|
||
{
|
||
int r;
|
||
MAILER *m = mci->mci_mailer;
|
||
extern ENVELOPE BlankEnvelope;
|
||
ENVELOPE *e = &BlankEnvelope;
|
||
|
||
SmtpPhase = "client probe";
|
||
smtpmessage("RSET", m, mci);
|
||
r = reply(m, mci, e, TimeOuts.to_miscshort, NULL);
|
||
if (r < 0 || REPLYTYPE(r) != 2)
|
||
smtpquit(m, mci, e);
|
||
return r;
|
||
}
|
||
/*
|
||
** REPLY -- read arpanet reply
|
||
**
|
||
** Parameters:
|
||
** m -- the mailer we are reading the reply from.
|
||
** mci -- the mailer connection info structure.
|
||
** e -- the current envelope.
|
||
** timeout -- the timeout for reads.
|
||
** pfunc -- processing function called on each line of response.
|
||
** If null, no special processing is done.
|
||
**
|
||
** Returns:
|
||
** reply code it reads.
|
||
**
|
||
** Side Effects:
|
||
** flushes the mail file.
|
||
*/
|
||
|
||
int
|
||
reply(m, mci, e, timeout, pfunc)
|
||
MAILER *m;
|
||
MCI *mci;
|
||
ENVELOPE *e;
|
||
time_t timeout;
|
||
void (*pfunc)();
|
||
{
|
||
register char *bufp;
|
||
register int r;
|
||
bool firstline = TRUE;
|
||
char junkbuf[MAXLINE];
|
||
|
||
if (mci->mci_out != NULL)
|
||
(void) fflush(mci->mci_out);
|
||
|
||
if (tTd(18, 1))
|
||
printf("reply\n");
|
||
|
||
/*
|
||
** Read the input line, being careful not to hang.
|
||
*/
|
||
|
||
for (bufp = SmtpReplyBuffer;; bufp = junkbuf)
|
||
{
|
||
register char *p;
|
||
extern time_t curtime();
|
||
|
||
/* actually do the read */
|
||
if (e->e_xfp != NULL)
|
||
(void) fflush(e->e_xfp); /* for debugging */
|
||
|
||
/* if we are in the process of closing just give the code */
|
||
if (mci->mci_state == MCIS_CLOSED)
|
||
return (SMTPCLOSING);
|
||
|
||
if (mci->mci_out != NULL)
|
||
fflush(mci->mci_out);
|
||
|
||
/* get the line from the other side */
|
||
p = sfgets(bufp, MAXLINE, mci->mci_in, timeout, SmtpPhase);
|
||
mci->mci_lastuse = curtime();
|
||
|
||
if (p == NULL)
|
||
{
|
||
bool oldholderrs;
|
||
|
||
/* if the remote end closed early, fake an error */
|
||
if (errno == 0)
|
||
# ifdef ECONNRESET
|
||
errno = ECONNRESET;
|
||
# else /* ECONNRESET */
|
||
errno = EPIPE;
|
||
# endif /* ECONNRESET */
|
||
|
||
mci->mci_errno = errno;
|
||
mci->mci_exitstat = EX_TEMPFAIL;
|
||
oldholderrs = HoldErrs;
|
||
HoldErrs = TRUE;
|
||
usrerr("451 reply: read error from %s", mci->mci_host);
|
||
|
||
/* if debugging, pause so we can see state */
|
||
if (tTd(18, 100))
|
||
pause();
|
||
mci->mci_state = MCIS_ERROR;
|
||
smtpquit(m, mci, e);
|
||
#if XDEBUG
|
||
{
|
||
char wbuf[MAXLINE];
|
||
char *p = wbuf;
|
||
|
||
if (e->e_to != NULL)
|
||
{
|
||
sprintf(p, "%s... ",
|
||
shortenstring(e->e_to, 203));
|
||
p += strlen(p);
|
||
}
|
||
sprintf(p, "reply(%.100s) during %s",
|
||
mci->mci_host, SmtpPhase);
|
||
checkfd012(wbuf);
|
||
}
|
||
#endif
|
||
HoldErrs = oldholderrs;
|
||
return (-1);
|
||
}
|
||
fixcrlf(bufp, TRUE);
|
||
|
||
/* EHLO failure is not a real error */
|
||
if (e->e_xfp != NULL && (bufp[0] == '4' ||
|
||
(bufp[0] == '5' && strncmp(SmtpMsgBuffer, "EHLO", 4) != 0)))
|
||
{
|
||
/* serious error -- log the previous command */
|
||
if (SmtpNeedIntro)
|
||
{
|
||
/* inform user who we are chatting with */
|
||
fprintf(CurEnv->e_xfp,
|
||
"... while talking to %s:\n",
|
||
CurHostName);
|
||
SmtpNeedIntro = FALSE;
|
||
}
|
||
if (SmtpMsgBuffer[0] != '\0')
|
||
fprintf(e->e_xfp, ">>> %s\n", SmtpMsgBuffer);
|
||
SmtpMsgBuffer[0] = '\0';
|
||
|
||
/* now log the message as from the other side */
|
||
fprintf(e->e_xfp, "<<< %s\n", bufp);
|
||
}
|
||
|
||
/* display the input for verbose mode */
|
||
if (Verbose)
|
||
nmessage("050 %s", bufp);
|
||
|
||
/* process the line */
|
||
if (pfunc != NULL)
|
||
(*pfunc)(bufp, firstline, m, mci, e);
|
||
|
||
firstline = FALSE;
|
||
|
||
/* if continuation is required, we can go on */
|
||
if (bufp[3] == '-')
|
||
continue;
|
||
|
||
/* ignore improperly formated input */
|
||
if (!(isascii(bufp[0]) && isdigit(bufp[0])))
|
||
continue;
|
||
|
||
/* decode the reply code */
|
||
r = atoi(bufp);
|
||
|
||
/* extra semantics: 0xx codes are "informational" */
|
||
if (r >= 100)
|
||
break;
|
||
}
|
||
|
||
/*
|
||
** Now look at SmtpReplyBuffer -- only care about the first
|
||
** line of the response from here on out.
|
||
*/
|
||
|
||
/* save temporary failure messages for posterity */
|
||
if (SmtpReplyBuffer[0] == '4' && SmtpError[0] == '\0')
|
||
(void) strcpy(SmtpError, SmtpReplyBuffer);
|
||
|
||
/* reply code 421 is "Service Shutting Down" */
|
||
if (r == SMTPCLOSING && mci->mci_state != MCIS_SSD)
|
||
{
|
||
/* send the quit protocol */
|
||
mci->mci_state = MCIS_SSD;
|
||
smtpquit(m, mci, e);
|
||
}
|
||
|
||
return (r);
|
||
}
|
||
/*
|
||
** SMTPMESSAGE -- send message to server
|
||
**
|
||
** Parameters:
|
||
** f -- format
|
||
** m -- the mailer to control formatting.
|
||
** a, b, c -- parameters
|
||
**
|
||
** Returns:
|
||
** none.
|
||
**
|
||
** Side Effects:
|
||
** writes message to mci->mci_out.
|
||
*/
|
||
|
||
/*VARARGS1*/
|
||
void
|
||
#ifdef __STDC__
|
||
smtpmessage(char *f, MAILER *m, MCI *mci, ...)
|
||
#else
|
||
smtpmessage(f, m, mci, va_alist)
|
||
char *f;
|
||
MAILER *m;
|
||
MCI *mci;
|
||
va_dcl
|
||
#endif
|
||
{
|
||
VA_LOCAL_DECL
|
||
|
||
VA_START(mci);
|
||
(void) vsnprintf(SmtpMsgBuffer, sizeof SmtpMsgBuffer, f, ap);
|
||
VA_END;
|
||
|
||
if (tTd(18, 1) || Verbose)
|
||
nmessage(">>> %s", SmtpMsgBuffer);
|
||
if (TrafficLogFile != NULL)
|
||
fprintf(TrafficLogFile, "%05d >>> %s\n", getpid(), SmtpMsgBuffer);
|
||
if (mci->mci_out != NULL)
|
||
{
|
||
fprintf(mci->mci_out, "%s%s", SmtpMsgBuffer,
|
||
m == NULL ? "\r\n" : m->m_eol);
|
||
}
|
||
else if (tTd(18, 1))
|
||
{
|
||
printf("smtpmessage: NULL mci_out\n");
|
||
}
|
||
}
|
||
|
||
# endif /* SMTP */
|