1068 lines
24 KiB
C
1068 lines
24 KiB
C
/*
|
|
* Copyright (c) 1998-2001 Sendmail, Inc. and its suppliers.
|
|
* All rights reserved.
|
|
* Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.
|
|
* Copyright (c) 1988, 1993
|
|
* The Regents of the University of California. All rights reserved.
|
|
*
|
|
* By using this file, you agree to the terms and conditions set
|
|
* forth in the LICENSE file which can be found at the top level of
|
|
* the sendmail distribution.
|
|
*
|
|
*/
|
|
|
|
#include <sendmail.h>
|
|
|
|
SM_RCSID("@(#)$Id: collect.c,v 8.237 2001/12/10 19:56:03 ca Exp $")
|
|
|
|
static void collecttimeout __P((time_t));
|
|
static void dferror __P((SM_FILE_T *volatile, char *, ENVELOPE *));
|
|
static void eatfrom __P((char *volatile, ENVELOPE *));
|
|
static void collect_doheader __P((ENVELOPE *));
|
|
static SM_FILE_T *collect_dfopen __P((ENVELOPE *));
|
|
static SM_FILE_T *collect_eoh __P((ENVELOPE *, int, int));
|
|
|
|
/*
|
|
** COLLECT_EOH -- end-of-header processing in collect()
|
|
**
|
|
** Called by collect() when it encounters the blank line
|
|
** separating the header from the message body, or when it
|
|
** encounters EOF in a message that contains only a header.
|
|
**
|
|
** Parameters:
|
|
** e -- envelope
|
|
** numhdrs -- number of headers
|
|
** hdrslen -- length of headers
|
|
**
|
|
** Results:
|
|
** NULL, or handle to open data file
|
|
**
|
|
** Side Effects:
|
|
** end-of-header check ruleset is invoked.
|
|
** envelope state is updated.
|
|
** headers may be added and deleted.
|
|
** selects the queue.
|
|
** opens the data file.
|
|
*/
|
|
|
|
static SM_FILE_T *
|
|
collect_eoh(e, numhdrs, hdrslen)
|
|
ENVELOPE *e;
|
|
int numhdrs;
|
|
int hdrslen;
|
|
{
|
|
char hnum[16];
|
|
char hsize[16];
|
|
|
|
/* call the end-of-header check ruleset */
|
|
(void) sm_snprintf(hnum, sizeof hnum, "%d", numhdrs);
|
|
(void) sm_snprintf(hsize, sizeof hsize, "%d", hdrslen);
|
|
if (tTd(30, 10))
|
|
sm_dprintf("collect: rscheck(\"check_eoh\", \"%s $| %s\")\n",
|
|
hnum, hsize);
|
|
(void) rscheck("check_eoh", hnum, hsize, e, false, true, 3, NULL,
|
|
e->e_id);
|
|
|
|
/*
|
|
** Process the header,
|
|
** select the queue, open the data file.
|
|
*/
|
|
|
|
collect_doheader(e);
|
|
return collect_dfopen(e);
|
|
}
|
|
|
|
/*
|
|
** COLLECT_DOHEADER -- process header in collect()
|
|
**
|
|
** Called by collect() after it has finished parsing the header,
|
|
** but before it selects the queue and creates the data file.
|
|
** The results of processing the header will affect queue selection.
|
|
**
|
|
** Parameters:
|
|
** e -- envelope
|
|
**
|
|
** Results:
|
|
** none.
|
|
**
|
|
** Side Effects:
|
|
** envelope state is updated.
|
|
** headers may be added and deleted.
|
|
*/
|
|
|
|
static void
|
|
collect_doheader(e)
|
|
ENVELOPE *e;
|
|
{
|
|
/*
|
|
** Find out some information from the headers.
|
|
** Examples are who is the from person & the date.
|
|
*/
|
|
|
|
eatheader(e, true, false);
|
|
|
|
if (GrabTo && e->e_sendqueue == NULL)
|
|
usrerr("No recipient addresses found in header");
|
|
|
|
/* collect statistics */
|
|
if (OpMode != MD_VERIFY)
|
|
markstats(e, (ADDRESS *) NULL, STATS_NORMAL);
|
|
|
|
/*
|
|
** If we have a Return-Receipt-To:, turn it into a DSN.
|
|
*/
|
|
|
|
if (RrtImpliesDsn && hvalue("return-receipt-to", e->e_header) != NULL)
|
|
{
|
|
ADDRESS *q;
|
|
|
|
for (q = e->e_sendqueue; q != NULL; q = q->q_next)
|
|
if (!bitset(QHASNOTIFY, q->q_flags))
|
|
q->q_flags |= QHASNOTIFY|QPINGONSUCCESS;
|
|
}
|
|
|
|
/*
|
|
** Add an appropriate recipient line if we have none.
|
|
*/
|
|
|
|
if (hvalue("to", e->e_header) != NULL ||
|
|
hvalue("cc", e->e_header) != NULL ||
|
|
hvalue("apparently-to", e->e_header) != NULL)
|
|
{
|
|
/* have a valid recipient header -- delete Bcc: headers */
|
|
e->e_flags |= EF_DELETE_BCC;
|
|
}
|
|
else if (hvalue("bcc", e->e_header) == NULL)
|
|
{
|
|
/* no valid recipient headers */
|
|
register ADDRESS *q;
|
|
char *hdr = NULL;
|
|
|
|
/* create a recipient field */
|
|
switch (NoRecipientAction)
|
|
{
|
|
case NRA_ADD_APPARENTLY_TO:
|
|
hdr = "Apparently-To";
|
|
break;
|
|
|
|
case NRA_ADD_TO:
|
|
hdr = "To";
|
|
break;
|
|
|
|
case NRA_ADD_BCC:
|
|
addheader("Bcc", " ", 0, e);
|
|
break;
|
|
|
|
case NRA_ADD_TO_UNDISCLOSED:
|
|
addheader("To", "undisclosed-recipients:;", 0, e);
|
|
break;
|
|
}
|
|
|
|
if (hdr != NULL)
|
|
{
|
|
for (q = e->e_sendqueue; q != NULL; q = q->q_next)
|
|
{
|
|
if (q->q_alias != NULL)
|
|
continue;
|
|
if (tTd(30, 3))
|
|
sm_dprintf("Adding %s: %s\n",
|
|
hdr, q->q_paddr);
|
|
addheader(hdr, q->q_paddr, 0, e);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
** COLLECT_DFOPEN -- open the message data file
|
|
**
|
|
** Called by collect() after it has finished processing the header.
|
|
** Queue selection occurs at this point, possibly based on the
|
|
** envelope's recipient list and on header information.
|
|
**
|
|
** Parameters:
|
|
** e -- envelope
|
|
**
|
|
** Results:
|
|
** NULL, or a pointer to an open data file,
|
|
** into which the message body will be written by collect().
|
|
**
|
|
** Side Effects:
|
|
** Calls syserr, sets EF_FATALERRS and returns NULL
|
|
** if there is insufficient disk space.
|
|
** Aborts process if data file could not be opened.
|
|
** Otherwise, the queue is selected,
|
|
** e->e_{dfino,dfdev,msgsize,flags} are updated,
|
|
** and a pointer to an open data file is returned.
|
|
*/
|
|
|
|
static SM_FILE_T *
|
|
collect_dfopen(e)
|
|
ENVELOPE *e;
|
|
{
|
|
MODE_T oldumask = 0;
|
|
int dfd;
|
|
struct stat stbuf;
|
|
SM_FILE_T *df;
|
|
char *dfname;
|
|
|
|
if (!setnewqueue(e))
|
|
return NULL;
|
|
|
|
dfname = queuename(e, DATAFL_LETTER);
|
|
if (bitset(S_IWGRP, QueueFileMode))
|
|
oldumask = umask(002);
|
|
df = bfopen(dfname, QueueFileMode, DataFileBufferSize,
|
|
SFF_OPENASROOT);
|
|
if (bitset(S_IWGRP, QueueFileMode))
|
|
(void) umask(oldumask);
|
|
if (df == NULL)
|
|
{
|
|
syserr("@Cannot create %s", dfname);
|
|
e->e_flags |= EF_NO_BODY_RETN;
|
|
flush_errors(true);
|
|
finis(true, true, ExitStat);
|
|
/* NOTREACHED */
|
|
}
|
|
dfd = sm_io_getinfo(df, SM_IO_WHAT_FD, NULL);
|
|
if (dfd < 0 || fstat(dfd, &stbuf) < 0)
|
|
e->e_dfino = -1;
|
|
else
|
|
{
|
|
e->e_dfdev = stbuf.st_dev;
|
|
e->e_dfino = stbuf.st_ino;
|
|
}
|
|
e->e_flags |= EF_HAS_DF;
|
|
return df;
|
|
}
|
|
|
|
/*
|
|
** COLLECT -- read & parse message header & make temp file.
|
|
**
|
|
** Creates a temporary file name and copies the standard
|
|
** input to that file. Leading UNIX-style "From" lines are
|
|
** stripped off (after important information is extracted).
|
|
**
|
|
** Parameters:
|
|
** fp -- file to read.
|
|
** smtpmode -- if set, we are running SMTP: give an RFC821
|
|
** style message to say we are ready to collect
|
|
** input, and never ignore a single dot to mean
|
|
** end of message.
|
|
** hdrp -- the location to stash the header.
|
|
** e -- the current envelope.
|
|
**
|
|
** Returns:
|
|
** none.
|
|
**
|
|
** Side Effects:
|
|
** If successful,
|
|
** - Data file is created and filled, and e->e_dfp is set.
|
|
** - The from person may be set.
|
|
** If the "enough disk space" check fails,
|
|
** - syserr is called.
|
|
** - e->e_dfp is NULL.
|
|
** - e->e_flags & EF_FATALERRS is set.
|
|
** - collect() returns.
|
|
** If data file cannot be created, the process is terminated.
|
|
*/
|
|
|
|
static jmp_buf CtxCollectTimeout;
|
|
static bool volatile CollectProgress;
|
|
static SM_EVENT *volatile CollectTimeout = NULL;
|
|
|
|
/* values for input state machine */
|
|
#define IS_NORM 0 /* middle of line */
|
|
#define IS_BOL 1 /* beginning of line */
|
|
#define IS_DOT 2 /* read a dot at beginning of line */
|
|
#define IS_DOTCR 3 /* read ".\r" at beginning of line */
|
|
#define IS_CR 4 /* read a carriage return */
|
|
|
|
/* values for message state machine */
|
|
#define MS_UFROM 0 /* reading Unix from line */
|
|
#define MS_HEADER 1 /* reading message header */
|
|
#define MS_BODY 2 /* reading message body */
|
|
#define MS_DISCARD 3 /* discarding rest of message */
|
|
|
|
void
|
|
collect(fp, smtpmode, hdrp, e)
|
|
SM_FILE_T *fp;
|
|
bool smtpmode;
|
|
HDR **hdrp;
|
|
register ENVELOPE *e;
|
|
{
|
|
register SM_FILE_T *volatile df;
|
|
volatile bool ignrdot;
|
|
volatile time_t dbto;
|
|
register char *volatile bp;
|
|
volatile int c;
|
|
volatile bool inputerr;
|
|
bool headeronly;
|
|
char *volatile buf;
|
|
volatile int buflen;
|
|
volatile int istate;
|
|
volatile int mstate;
|
|
volatile int hdrslen;
|
|
volatile int numhdrs;
|
|
volatile int afd;
|
|
unsigned char *volatile pbp;
|
|
unsigned char peekbuf[8];
|
|
char bufbuf[MAXLINE];
|
|
|
|
df = NULL;
|
|
ignrdot = smtpmode ? false : IgnrDot;
|
|
dbto = smtpmode ? TimeOuts.to_datablock : 0;
|
|
c = SM_IO_EOF;
|
|
inputerr = false;
|
|
headeronly = hdrp != NULL;
|
|
hdrslen = 0;
|
|
numhdrs = 0;
|
|
HasEightBits = false;
|
|
buf = bp = bufbuf;
|
|
buflen = sizeof bufbuf;
|
|
pbp = peekbuf;
|
|
istate = IS_BOL;
|
|
mstate = SaveFrom ? MS_HEADER : MS_UFROM;
|
|
CollectProgress = false;
|
|
|
|
/*
|
|
** Tell ARPANET to go ahead.
|
|
*/
|
|
|
|
if (smtpmode)
|
|
message("354 Enter mail, end with \".\" on a line by itself");
|
|
|
|
if (tTd(30, 2))
|
|
sm_dprintf("collect\n");
|
|
|
|
/*
|
|
** Read the message.
|
|
**
|
|
** This is done using two interleaved state machines.
|
|
** The input state machine is looking for things like
|
|
** hidden dots; the message state machine is handling
|
|
** the larger picture (e.g., header versus body).
|
|
*/
|
|
|
|
if (dbto != 0)
|
|
{
|
|
/* handle possible input timeout */
|
|
if (setjmp(CtxCollectTimeout) != 0)
|
|
{
|
|
if (LogLevel > 2)
|
|
sm_syslog(LOG_NOTICE, e->e_id,
|
|
"timeout waiting for input from %s during message collect",
|
|
CURHOSTNAME);
|
|
errno = 0;
|
|
usrerr("451 4.4.1 timeout waiting for input during message collect");
|
|
goto readerr;
|
|
}
|
|
CollectTimeout = sm_setevent(dbto, collecttimeout, dbto);
|
|
}
|
|
|
|
e->e_msgsize = 0;
|
|
for (;;)
|
|
{
|
|
if (tTd(30, 35))
|
|
sm_dprintf("top, istate=%d, mstate=%d\n", istate,
|
|
mstate);
|
|
for (;;)
|
|
{
|
|
if (pbp > peekbuf)
|
|
c = *--pbp;
|
|
else
|
|
{
|
|
while (!sm_io_eof(fp) && !sm_io_error(fp))
|
|
{
|
|
errno = 0;
|
|
c = sm_io_getc(fp, SM_TIME_DEFAULT);
|
|
if (c == SM_IO_EOF && errno == EINTR)
|
|
{
|
|
/* Interrupted, retry */
|
|
sm_io_clearerr(fp);
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
CollectProgress = true;
|
|
if (TrafficLogFile != NULL && !headeronly)
|
|
{
|
|
if (istate == IS_BOL)
|
|
(void) sm_io_fprintf(TrafficLogFile,
|
|
SM_TIME_DEFAULT,
|
|
"%05d <<< ",
|
|
(int) CurrentPid);
|
|
if (c == SM_IO_EOF)
|
|
(void) sm_io_fprintf(TrafficLogFile,
|
|
SM_TIME_DEFAULT,
|
|
"[EOF]\n");
|
|
else
|
|
(void) sm_io_putc(TrafficLogFile,
|
|
SM_TIME_DEFAULT,
|
|
c);
|
|
}
|
|
if (c == SM_IO_EOF)
|
|
goto readerr;
|
|
if (SevenBitInput)
|
|
c &= 0x7f;
|
|
else
|
|
HasEightBits |= bitset(0x80, c);
|
|
}
|
|
if (tTd(30, 94))
|
|
sm_dprintf("istate=%d, c=%c (0x%x)\n",
|
|
istate, (char) c, c);
|
|
switch (istate)
|
|
{
|
|
case IS_BOL:
|
|
if (c == '.')
|
|
{
|
|
istate = IS_DOT;
|
|
continue;
|
|
}
|
|
break;
|
|
|
|
case IS_DOT:
|
|
if (c == '\n' && !ignrdot &&
|
|
!bitset(EF_NL_NOT_EOL, e->e_flags))
|
|
goto readerr;
|
|
else if (c == '\r' &&
|
|
!bitset(EF_CRLF_NOT_EOL, e->e_flags))
|
|
{
|
|
istate = IS_DOTCR;
|
|
continue;
|
|
}
|
|
else if (c != '.' ||
|
|
(OpMode != MD_SMTP &&
|
|
OpMode != MD_DAEMON &&
|
|
OpMode != MD_ARPAFTP))
|
|
{
|
|
*pbp++ = c;
|
|
c = '.';
|
|
}
|
|
break;
|
|
|
|
case IS_DOTCR:
|
|
if (c == '\n' && !ignrdot)
|
|
goto readerr;
|
|
else
|
|
{
|
|
/* push back the ".\rx" */
|
|
*pbp++ = c;
|
|
*pbp++ = '\r';
|
|
c = '.';
|
|
}
|
|
break;
|
|
|
|
case IS_CR:
|
|
if (c == '\n')
|
|
istate = IS_BOL;
|
|
else
|
|
{
|
|
(void) sm_io_ungetc(fp, SM_TIME_DEFAULT,
|
|
c);
|
|
c = '\r';
|
|
istate = IS_NORM;
|
|
}
|
|
goto bufferchar;
|
|
}
|
|
|
|
if (c == '\r' && !bitset(EF_CRLF_NOT_EOL, e->e_flags))
|
|
{
|
|
istate = IS_CR;
|
|
continue;
|
|
}
|
|
else if (c == '\n' && !bitset(EF_NL_NOT_EOL,
|
|
e->e_flags))
|
|
istate = IS_BOL;
|
|
else
|
|
istate = IS_NORM;
|
|
|
|
bufferchar:
|
|
if (!headeronly)
|
|
{
|
|
/* no overflow? */
|
|
if (e->e_msgsize >= 0)
|
|
{
|
|
e->e_msgsize++;
|
|
if (MaxMessageSize > 0 &&
|
|
!bitset(EF_TOOBIG, e->e_flags) &&
|
|
e->e_msgsize > MaxMessageSize)
|
|
e->e_flags |= EF_TOOBIG;
|
|
}
|
|
}
|
|
switch (mstate)
|
|
{
|
|
case MS_BODY:
|
|
/* just put the character out */
|
|
if (!bitset(EF_TOOBIG, e->e_flags))
|
|
(void) sm_io_putc(df, SM_TIME_DEFAULT,
|
|
c);
|
|
|
|
/* FALLTHROUGH */
|
|
|
|
case MS_DISCARD:
|
|
continue;
|
|
}
|
|
|
|
/* header -- buffer up */
|
|
if (bp >= &buf[buflen - 2])
|
|
{
|
|
char *obuf;
|
|
|
|
if (mstate != MS_HEADER)
|
|
break;
|
|
|
|
/* out of space for header */
|
|
obuf = buf;
|
|
if (buflen < MEMCHUNKSIZE)
|
|
buflen *= 2;
|
|
else
|
|
buflen += MEMCHUNKSIZE;
|
|
buf = xalloc(buflen);
|
|
memmove(buf, obuf, bp - obuf);
|
|
bp = &buf[bp - obuf];
|
|
if (obuf != bufbuf)
|
|
sm_free(obuf); /* XXX */
|
|
}
|
|
|
|
/*
|
|
** XXX Notice: the logic here is broken.
|
|
** An input to sendmail that doesn't contain a
|
|
** header but starts immediately with the body whose
|
|
** first line contain characters which match the
|
|
** following "if" will cause problems: those
|
|
** characters will NOT appear in the output...
|
|
** Do we care?
|
|
*/
|
|
|
|
if (c >= 0200 && c <= 0237)
|
|
{
|
|
#if 0 /* causes complaints -- figure out something for 8.n+1 */
|
|
usrerr("Illegal character 0x%x in header", c);
|
|
#else /* 0 */
|
|
/* EMPTY */
|
|
#endif /* 0 */
|
|
}
|
|
else if (c != '\0')
|
|
{
|
|
*bp++ = c;
|
|
++hdrslen;
|
|
if (!headeronly &&
|
|
MaxHeadersLength > 0 &&
|
|
hdrslen > MaxHeadersLength)
|
|
{
|
|
sm_syslog(LOG_NOTICE, e->e_id,
|
|
"headers too large (%d max) from %s during message collect",
|
|
MaxHeadersLength,
|
|
CURHOSTNAME);
|
|
errno = 0;
|
|
e->e_flags |= EF_CLRQUEUE;
|
|
e->e_status = "5.6.0";
|
|
usrerrenh(e->e_status,
|
|
"552 Headers too large (%d max)",
|
|
MaxHeadersLength);
|
|
mstate = MS_DISCARD;
|
|
}
|
|
}
|
|
if (istate == IS_BOL)
|
|
break;
|
|
}
|
|
*bp = '\0';
|
|
|
|
nextstate:
|
|
if (tTd(30, 35))
|
|
sm_dprintf("nextstate, istate=%d, mstate=%d, line = \"%s\"\n",
|
|
istate, mstate, buf);
|
|
switch (mstate)
|
|
{
|
|
case MS_UFROM:
|
|
mstate = MS_HEADER;
|
|
#ifndef NOTUNIX
|
|
if (strncmp(buf, "From ", 5) == 0)
|
|
{
|
|
bp = buf;
|
|
eatfrom(buf, e);
|
|
continue;
|
|
}
|
|
#endif /* ! NOTUNIX */
|
|
/* FALLTHROUGH */
|
|
|
|
case MS_HEADER:
|
|
if (!isheader(buf))
|
|
{
|
|
mstate = MS_BODY;
|
|
goto nextstate;
|
|
}
|
|
|
|
/* check for possible continuation line */
|
|
do
|
|
{
|
|
sm_io_clearerr(fp);
|
|
errno = 0;
|
|
c = sm_io_getc(fp, SM_TIME_DEFAULT);
|
|
} while (c == SM_IO_EOF && errno == EINTR);
|
|
if (c != SM_IO_EOF)
|
|
(void) sm_io_ungetc(fp, SM_TIME_DEFAULT, c);
|
|
if (c == ' ' || c == '\t')
|
|
{
|
|
/* yep -- defer this */
|
|
continue;
|
|
}
|
|
|
|
/* trim off trailing CRLF or NL */
|
|
if (*--bp != '\n' || *--bp != '\r')
|
|
bp++;
|
|
*bp = '\0';
|
|
|
|
if (bitset(H_EOH, chompheader(buf,
|
|
CHHDR_CHECK | CHHDR_USER,
|
|
hdrp, e)))
|
|
{
|
|
mstate = MS_BODY;
|
|
goto nextstate;
|
|
}
|
|
numhdrs++;
|
|
break;
|
|
|
|
case MS_BODY:
|
|
if (tTd(30, 1))
|
|
sm_dprintf("EOH\n");
|
|
|
|
if (headeronly)
|
|
goto readerr;
|
|
|
|
df = collect_eoh(e, numhdrs, hdrslen);
|
|
if (df == NULL)
|
|
e->e_flags |= EF_TOOBIG;
|
|
|
|
bp = buf;
|
|
|
|
/* toss blank line */
|
|
if ((!bitset(EF_CRLF_NOT_EOL, e->e_flags) &&
|
|
bp[0] == '\r' && bp[1] == '\n') ||
|
|
(!bitset(EF_NL_NOT_EOL, e->e_flags) &&
|
|
bp[0] == '\n'))
|
|
{
|
|
break;
|
|
}
|
|
|
|
/* if not a blank separator, write it out */
|
|
if (!bitset(EF_TOOBIG, e->e_flags))
|
|
{
|
|
while (*bp != '\0')
|
|
(void) sm_io_putc(df, SM_TIME_DEFAULT,
|
|
*bp++);
|
|
}
|
|
break;
|
|
}
|
|
bp = buf;
|
|
}
|
|
|
|
readerr:
|
|
if ((sm_io_eof(fp) && smtpmode) || sm_io_error(fp))
|
|
{
|
|
const char *errmsg;
|
|
|
|
if (sm_io_eof(fp))
|
|
errmsg = "unexpected close";
|
|
else
|
|
errmsg = sm_errstring(errno);
|
|
if (tTd(30, 1))
|
|
sm_dprintf("collect: premature EOM: %s\n", errmsg);
|
|
if (LogLevel > 1)
|
|
sm_syslog(LOG_WARNING, e->e_id,
|
|
"collect: premature EOM: %s", errmsg);
|
|
inputerr = true;
|
|
}
|
|
|
|
/* reset global timer */
|
|
if (CollectTimeout != NULL)
|
|
sm_clrevent(CollectTimeout);
|
|
|
|
if (headeronly)
|
|
return;
|
|
|
|
if (mstate != MS_BODY)
|
|
{
|
|
/* no body or discard, so we never opened the data file */
|
|
SM_ASSERT(df == NULL);
|
|
df = collect_eoh(e, numhdrs, hdrslen);
|
|
}
|
|
|
|
if (df == NULL)
|
|
{
|
|
/* skip next few clauses */
|
|
/* EMPTY */
|
|
}
|
|
else if (sm_io_flush(df, SM_TIME_DEFAULT) != 0 || sm_io_error(df))
|
|
{
|
|
dferror(df, "sm_io_flush||sm_io_error", e);
|
|
flush_errors(true);
|
|
finis(true, true, ExitStat);
|
|
/* NOTREACHED */
|
|
}
|
|
else if (SuperSafe != SAFE_REALLY)
|
|
{
|
|
/* skip next few clauses */
|
|
/* EMPTY */
|
|
}
|
|
else if (sm_io_setinfo(df, SM_BF_COMMIT, NULL) < 0 && errno != EINVAL)
|
|
{
|
|
int save_errno = errno;
|
|
|
|
if (save_errno == EEXIST)
|
|
{
|
|
char *dfile;
|
|
struct stat st;
|
|
int dfd;
|
|
|
|
dfile = queuename(e, DATAFL_LETTER);
|
|
if (stat(dfile, &st) < 0)
|
|
st.st_size = -1;
|
|
errno = EEXIST;
|
|
syserr("@collect: bfcommit(%s): already on disk, size = %ld",
|
|
dfile, (long) st.st_size);
|
|
dfd = sm_io_getinfo(df, SM_IO_WHAT_FD, NULL);
|
|
if (dfd >= 0)
|
|
dumpfd(dfd, true, true);
|
|
}
|
|
errno = save_errno;
|
|
dferror(df, "bfcommit", e);
|
|
flush_errors(true);
|
|
finis(save_errno != EEXIST, true, ExitStat);
|
|
}
|
|
else if ((afd = sm_io_getinfo(df, SM_IO_WHAT_FD, NULL)) >= 0 &&
|
|
fsync(afd) < 0)
|
|
{
|
|
dferror(df, "fsync", e);
|
|
flush_errors(true);
|
|
finis(true, true, ExitStat);
|
|
/* NOTREACHED */
|
|
}
|
|
else if (sm_io_close(df, SM_TIME_DEFAULT) < 0)
|
|
{
|
|
dferror(df, "sm_io_close", e);
|
|
flush_errors(true);
|
|
finis(true, true, ExitStat);
|
|
/* NOTREACHED */
|
|
}
|
|
else
|
|
{
|
|
/* everything is happily flushed to disk */
|
|
df = NULL;
|
|
|
|
/* remove from available space in filesystem */
|
|
updfs(e, false, true);
|
|
}
|
|
|
|
/* An EOF when running SMTP is an error */
|
|
if (inputerr && (OpMode == MD_SMTP || OpMode == MD_DAEMON))
|
|
{
|
|
char *host;
|
|
char *problem;
|
|
|
|
host = RealHostName;
|
|
if (host == NULL)
|
|
host = "localhost";
|
|
|
|
if (sm_io_eof(fp))
|
|
problem = "unexpected close";
|
|
else if (sm_io_error(fp))
|
|
problem = "I/O error";
|
|
else
|
|
problem = "read timeout";
|
|
if (LogLevel > 0 && sm_io_eof(fp))
|
|
sm_syslog(LOG_NOTICE, e->e_id,
|
|
"collect: %s on connection from %.100s, sender=%s",
|
|
problem, host,
|
|
shortenstring(e->e_from.q_paddr, MAXSHORTSTR));
|
|
if (sm_io_eof(fp))
|
|
usrerr("451 4.4.1 collect: %s on connection from %s, from=%s",
|
|
problem, host,
|
|
shortenstring(e->e_from.q_paddr, MAXSHORTSTR));
|
|
else
|
|
syserr("451 4.4.1 collect: %s on connection from %s, from=%s",
|
|
problem, host,
|
|
shortenstring(e->e_from.q_paddr, MAXSHORTSTR));
|
|
|
|
/* don't return an error indication */
|
|
e->e_to = NULL;
|
|
e->e_flags &= ~EF_FATALERRS;
|
|
e->e_flags |= EF_CLRQUEUE;
|
|
|
|
finis(true, true, ExitStat);
|
|
/* NOTREACHED */
|
|
}
|
|
|
|
/* Log collection information. */
|
|
if (bitset(EF_LOGSENDER, e->e_flags) && LogLevel > 4)
|
|
{
|
|
logsender(e, e->e_msgid);
|
|
e->e_flags &= ~EF_LOGSENDER;
|
|
}
|
|
|
|
/* check for message too large */
|
|
if (bitset(EF_TOOBIG, e->e_flags))
|
|
{
|
|
e->e_flags |= EF_NO_BODY_RETN|EF_CLRQUEUE;
|
|
if (!bitset(EF_FATALERRS, e->e_flags))
|
|
{
|
|
e->e_status = "5.2.3";
|
|
usrerrenh(e->e_status,
|
|
"552 Message exceeds maximum fixed size (%ld)",
|
|
MaxMessageSize);
|
|
if (LogLevel > 6)
|
|
sm_syslog(LOG_NOTICE, e->e_id,
|
|
"message size (%ld) exceeds maximum (%ld)",
|
|
e->e_msgsize, MaxMessageSize);
|
|
}
|
|
}
|
|
|
|
/* check for illegal 8-bit data */
|
|
if (HasEightBits)
|
|
{
|
|
e->e_flags |= EF_HAS8BIT;
|
|
if (!bitset(MM_PASS8BIT|MM_MIME8BIT, MimeMode) &&
|
|
!bitset(EF_IS_MIME, e->e_flags))
|
|
{
|
|
e->e_status = "5.6.1";
|
|
usrerrenh(e->e_status, "554 Eight bit data not allowed");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* if it claimed to be 8 bits, well, it lied.... */
|
|
if (e->e_bodytype != NULL &&
|
|
sm_strcasecmp(e->e_bodytype, "8BITMIME") == 0)
|
|
e->e_bodytype = "7BIT";
|
|
}
|
|
|
|
if (SuperSafe == SAFE_REALLY && !bitset(EF_FATALERRS, e->e_flags))
|
|
{
|
|
char *dfname = queuename(e, DATAFL_LETTER);
|
|
if ((e->e_dfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, dfname,
|
|
SM_IO_RDONLY, NULL)) == NULL)
|
|
{
|
|
/* we haven't acked receipt yet, so just chuck this */
|
|
syserr("@Cannot reopen %s", dfname);
|
|
finis(true, true, ExitStat);
|
|
/* NOTREACHED */
|
|
}
|
|
}
|
|
else
|
|
e->e_dfp = df;
|
|
}
|
|
|
|
static void
|
|
collecttimeout(timeout)
|
|
time_t timeout;
|
|
{
|
|
int save_errno = errno;
|
|
|
|
/*
|
|
** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
|
|
** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
|
|
** DOING.
|
|
*/
|
|
|
|
if (CollectProgress)
|
|
{
|
|
/* reset the timeout */
|
|
CollectTimeout = sm_sigsafe_setevent(timeout, collecttimeout,
|
|
timeout);
|
|
CollectProgress = false;
|
|
}
|
|
else
|
|
{
|
|
/* event is done */
|
|
CollectTimeout = NULL;
|
|
}
|
|
|
|
/* if no progress was made or problem resetting event, die now */
|
|
if (CollectTimeout == NULL)
|
|
{
|
|
errno = ETIMEDOUT;
|
|
longjmp(CtxCollectTimeout, 1);
|
|
}
|
|
errno = save_errno;
|
|
}
|
|
/*
|
|
** DFERROR -- signal error on writing the data file.
|
|
**
|
|
** Called by collect(). Collect() always terminates the process
|
|
** immediately after calling dferror(), which means that the SMTP
|
|
** session will be terminated, which means that any error message
|
|
** issued by dferror must be a 421 error, as per RFC 821.
|
|
**
|
|
** Parameters:
|
|
** df -- the file pointer for the data file.
|
|
** msg -- detailed message.
|
|
** e -- the current envelope.
|
|
**
|
|
** Returns:
|
|
** none.
|
|
**
|
|
** Side Effects:
|
|
** Gives an error message.
|
|
** Arranges for following output to go elsewhere.
|
|
*/
|
|
|
|
static void
|
|
dferror(df, msg, e)
|
|
SM_FILE_T *volatile df;
|
|
char *msg;
|
|
register ENVELOPE *e;
|
|
{
|
|
char *dfname;
|
|
|
|
dfname = queuename(e, DATAFL_LETTER);
|
|
setstat(EX_IOERR);
|
|
if (errno == ENOSPC)
|
|
{
|
|
#if STAT64 > 0
|
|
struct stat64 st;
|
|
#else /* STAT64 > 0 */
|
|
struct stat st;
|
|
#endif /* STAT64 > 0 */
|
|
long avail;
|
|
long bsize;
|
|
|
|
e->e_flags |= EF_NO_BODY_RETN;
|
|
|
|
if (
|
|
#if STAT64 > 0
|
|
fstat64(sm_io_getinfo(df, SM_IO_WHAT_FD, NULL), &st)
|
|
#else /* STAT64 > 0 */
|
|
fstat(sm_io_getinfo(df, SM_IO_WHAT_FD, NULL), &st)
|
|
#endif /* STAT64 > 0 */
|
|
< 0)
|
|
st.st_size = 0;
|
|
(void) sm_io_reopen(SmFtStdio, SM_TIME_DEFAULT, dfname,
|
|
SM_IO_WRONLY, NULL, df);
|
|
if (st.st_size <= 0)
|
|
(void) sm_io_fprintf(df, SM_TIME_DEFAULT,
|
|
"\n*** Mail could not be accepted");
|
|
else
|
|
(void) sm_io_fprintf(df, SM_TIME_DEFAULT,
|
|
"\n*** Mail of at least %llu bytes could not be accepted\n",
|
|
(ULONGLONG_T) st.st_size);
|
|
(void) sm_io_fprintf(df, SM_TIME_DEFAULT,
|
|
"*** at %s due to lack of disk space for temp file.\n",
|
|
MyHostName);
|
|
avail = freediskspace(qid_printqueue(e->e_qgrp, e->e_qdir),
|
|
&bsize);
|
|
if (avail > 0)
|
|
{
|
|
if (bsize > 1024)
|
|
avail *= bsize / 1024;
|
|
else if (bsize < 1024)
|
|
avail /= 1024 / bsize;
|
|
(void) sm_io_fprintf(df, SM_TIME_DEFAULT,
|
|
"*** Currently, %ld kilobytes are available for mail temp files.\n",
|
|
avail);
|
|
}
|
|
#if 0
|
|
/* Wrong response code; should be 421. */
|
|
e->e_status = "4.3.1";
|
|
usrerrenh(e->e_status, "452 Out of disk space for temp file");
|
|
#else /* 0 */
|
|
syserr("421 4.3.1 Out of disk space for temp file");
|
|
#endif /* 0 */
|
|
}
|
|
else
|
|
syserr("421 4.3.0 collect: Cannot write %s (%s, uid=%d, gid=%d)",
|
|
dfname, msg, geteuid(), getegid());
|
|
if (sm_io_reopen(SmFtStdio, SM_TIME_DEFAULT, SM_PATH_DEVNULL,
|
|
SM_IO_WRONLY, NULL, df) == NULL)
|
|
sm_syslog(LOG_ERR, e->e_id,
|
|
"dferror: sm_io_reopen(\"/dev/null\") failed: %s",
|
|
sm_errstring(errno));
|
|
}
|
|
/*
|
|
** EATFROM -- chew up a UNIX style from line and process
|
|
**
|
|
** This does indeed make some assumptions about the format
|
|
** of UNIX messages.
|
|
**
|
|
** Parameters:
|
|
** fm -- the from line.
|
|
**
|
|
** Returns:
|
|
** none.
|
|
**
|
|
** Side Effects:
|
|
** extracts what information it can from the header,
|
|
** such as the date.
|
|
*/
|
|
|
|
#ifndef NOTUNIX
|
|
|
|
static char *DowList[] =
|
|
{
|
|
"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", NULL
|
|
};
|
|
|
|
static char *MonthList[] =
|
|
{
|
|
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
|
|
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
|
|
NULL
|
|
};
|
|
|
|
static void
|
|
eatfrom(fm, e)
|
|
char *volatile fm;
|
|
register ENVELOPE *e;
|
|
{
|
|
register char *p;
|
|
register char **dt;
|
|
|
|
if (tTd(30, 2))
|
|
sm_dprintf("eatfrom(%s)\n", fm);
|
|
|
|
/* find the date part */
|
|
p = fm;
|
|
while (*p != '\0')
|
|
{
|
|
/* skip a word */
|
|
while (*p != '\0' && *p != ' ')
|
|
p++;
|
|
while (*p == ' ')
|
|
p++;
|
|
if (strlen(p) < 17)
|
|
{
|
|
/* no room for the date */
|
|
return;
|
|
}
|
|
if (!(isascii(*p) && isupper(*p)) ||
|
|
p[3] != ' ' || p[13] != ':' || p[16] != ':')
|
|
continue;
|
|
|
|
/* we have a possible date */
|
|
for (dt = DowList; *dt != NULL; dt++)
|
|
if (strncmp(*dt, p, 3) == 0)
|
|
break;
|
|
if (*dt == NULL)
|
|
continue;
|
|
|
|
for (dt = MonthList; *dt != NULL; dt++)
|
|
{
|
|
if (strncmp(*dt, &p[4], 3) == 0)
|
|
break;
|
|
}
|
|
if (*dt != NULL)
|
|
break;
|
|
}
|
|
|
|
if (*p != '\0')
|
|
{
|
|
char *q, buf[25];
|
|
|
|
/* we have found a date */
|
|
(void) sm_strlcpy(buf, p, sizeof(buf));
|
|
q = arpadate(buf);
|
|
macdefine(&e->e_macro, A_TEMP, 'a', q);
|
|
}
|
|
}
|
|
#endif /* ! NOTUNIX */
|