collect.c revision e9af4bc0b1cc30cea75d6ad4aa2fde97d985e9be
/*
* Copyright (c) 1998-2006, 2008 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.284 2008/08/06 05:26:24 ca Exp $")
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, RSF_UNSTRUCTURED|RSF_COUNT,
3, NULL, e->e_id, NULL);
/*
** 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");
/*
** 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, true);
break;
case NRA_ADD_TO_UNDISCLOSED:
addheader("To", "undisclosed-recipients:;", 0, e, true);
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, true);
}
}
}
}
/*
** 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(false, 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.
** rsetsize -- reset e_msgsize?
**
** 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.
*/
/* 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, rsetsize)
SM_FILE_T *fp;
bool smtpmode;
HDR **hdrp;
register ENVELOPE *e;
bool rsetsize;
{
register SM_FILE_T *df;
bool ignrdot;
int dbto;
register char *bp;
int c;
bool inputerr;
bool headeronly;
char *buf;
int buflen;
int istate;
int mstate;
int hdrslen;
int numhdrs;
int afd;
unsigned char *pbp;
unsigned char peekbuf[8];
char bufbuf[MAXLINE];
df = NULL;
ignrdot = smtpmode ? false : IgnrDot;
/* timeout for I/O functions is in milliseconds */
dbto = smtpmode ? ((int) TimeOuts.to_datablock * 1000)
: SM_TIME_FOREVER;
sm_io_setinfo(fp, SM_IO_WHAT_TIMEOUT, &dbto);
set_tls_rd_tmo(TimeOuts.to_datablock);
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;
/*
** Tell ARPANET to go ahead.
*/
if (smtpmode)
message("354 Enter mail, end with \".\" on a line by itself");
/* simulate an I/O timeout when used as sink */
if (tTd(83, 101))
sleep(319);
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 (rsetsize)
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;
}
/* timeout? */
if (c == SM_IO_EOF && errno == EAGAIN
&& smtpmode)
{
/*
** Override e_message in
** usrerr() as this is the
** reason for failure that
** should be logged for
** undelivered recipients.
*/
e->e_message = NULL;
errno = 0;
inputerr = true;
goto readabort;
}
break;
}
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 (ignrdot ||
(c != '.' &&
OpMode != MD_SMTP &&
OpMode != MD_DAEMON &&
OpMode != MD_ARPAFTP))
{
SM_ASSERT(pbp < peekbuf +
sizeof(peekbuf));
*pbp++ = c;
c = '.';
}
break;
case IS_DOTCR:
if (c == '\n' && !ignrdot)
goto readerr;
else
{
/* push back the ".\rx" */
SM_ASSERT(pbp < peekbuf +
sizeof(peekbuf));
*pbp++ = c;
if (OpMode != MD_SMTP &&
OpMode != MD_DAEMON &&
OpMode != MD_ARPAFTP)
{
SM_ASSERT(pbp < peekbuf +
sizeof(peekbuf));
*pbp++ = '\r';
c = '.';
}
else
c = '\r';
}
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;
}
SM_ASSERT(mstate == MS_UFROM || mstate == MS_HEADER);
/* header -- buffer up */
if (bp >= &buf[buflen - 2])
{
char *obuf;
/* out of space for header */
obuf = buf;
if (buflen < MEMCHUNKSIZE)
buflen *= 2;
else
buflen += MEMCHUNKSIZE;
if (buflen <= 0)
{
sm_syslog(LOG_NOTICE, e->e_id,
"header overflow from %s during message collect",
CURHOSTNAME);
errno = 0;
e->e_flags |= EF_CLRQUEUE;
e->e_status = "5.6.0";
usrerrenh(e->e_status,
"552 Headers too large");
goto discard;
}
buf = xalloc(buflen);
memmove(buf, obuf, bp - obuf);
bp = &buf[bp - obuf];
if (obuf != bufbuf)
sm_free(obuf); /* XXX */
}
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);
discard:
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);
/* timeout? */
if (c == SM_IO_EOF && errno == EAGAIN
&& smtpmode)
{
/*
** Override e_message in
** usrerr() as this is the
** reason for failure that
** should be logged for
** undelivered recipients.
*/
e->e_message = NULL;
errno = 0;
inputerr = true;
goto readabort;
}
} 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;
}
SM_ASSERT(bp > buf);
/* guaranteed by isheader(buf) */
SM_ASSERT(*(bp - 1) != '\n' || bp > buf + 1);
/* 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;
}
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_NO ||
SuperSafe == SAFE_INTERACTIVE ||
(SuperSafe == SAFE_REALLY_POSTMILTER && smtpmode))
{
/* skip next few clauses */
/* EMPTY */
/* Note: updfs() is not called in this case! */
}
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)
{
dferror(df, "sm_io_getinfo", e);
flush_errors(true);
finis(true, true, ExitStat);
/* NOTREACHED */
}
else if (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, 0, 1, "collect");
}
/* An EOF when running SMTP is an error */
readabort:
if (inputerr && (OpMode == MD_SMTP || OpMode == MD_DAEMON))
{
char *host;
char *problem;
ADDRESS *q;
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("421 4.4.1 collect: %s on connection from %s, from=%s",
problem, host,
shortenstring(e->e_from.q_paddr, MAXSHORTSTR));
else
syserr("421 4.4.1 collect: %s on connection from %s, from=%s",
problem, host,
shortenstring(e->e_from.q_paddr, MAXSHORTSTR));
flush_errors(true);
/* don't return an error indication */
e->e_to = NULL;
e->e_flags &= ~EF_FATALERRS;
e->e_flags |= EF_CLRQUEUE;
/* Don't send any message notification to sender */
for (q = e->e_sendqueue; q != NULL; q = q->q_next)
{
if (QS_IS_DEAD(q->q_state))
continue;
q->q_state = QS_FATALERR;
}
(void) sm_io_close(df, SM_TIME_DEFAULT);
df = NULL;
finis(true, true, ExitStat);
/* NOTREACHED */
}
/* Log collection information. */
if (tTd(92, 2))
sm_dprintf("collect: e_id=%s, EF_LOGSENDER=%d, LogLevel=%d\n",
e->e_id, bitset(EF_LOGSENDER, e->e_flags), LogLevel);
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_B, 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;
/* collect statistics */
if (OpMode != MD_VERIFY)
{
/*
** Recalculate e_msgpriority, it is done at in eatheader()
** which is called (in 8.12) after the header is collected,
** hence e_msgsize is (most likely) incorrect.
*/
e->e_msgpriority = e->e_msgsize
- e->e_class * WkClassFact
+ e->e_nrcpts * WkRecipFact;
markstats(e, (ADDRESS *) NULL, STATS_NORMAL);
}
}
/*
** 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.
*/
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_B, 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, (int) geteuid(), (int) 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.
** e -- envelope
**
** 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 */