📄 collect.c
字号:
/*
* Copyright (c) 1998-2000 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.
*
*/
#ifndef lint
static char id[] = "@(#)$Id: collect.c,v 8.136.4.6 2000/09/21 21:52:16 ca Exp $";
#endif /* ! lint */
#include <sendmail.h>
static void collecttimeout __P((time_t));
static void dferror __P((FILE *volatile, char *, ENVELOPE *));
static void eatfrom __P((char *volatile, ENVELOPE *));
/*
** 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:
** Temp file is created and filled.
** The from person may be set.
*/
static jmp_buf CtxCollectTimeout;
static bool CollectProgress;
static EVENT *CollectTimeout;
/* 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)
FILE *fp;
bool smtpmode;
HDR **hdrp;
register ENVELOPE *e;
{
register FILE *volatile df;
volatile bool ignrdot = smtpmode ? FALSE : IgnrDot;
volatile time_t dbto = smtpmode ? TimeOuts.to_datablock : 0;
register char *volatile bp;
volatile int c = EOF;
volatile bool inputerr = FALSE;
bool headeronly;
char *volatile buf;
volatile int buflen;
volatile int istate;
volatile int mstate;
volatile int hdrslen = 0;
volatile int numhdrs = 0;
volatile int dfd;
volatile int afd;
volatile int rstat = EX_OK;
u_char *volatile pbp;
u_char peekbuf[8];
char hsize[16];
char hnum[16];
char dfname[MAXPATHLEN];
char bufbuf[MAXLINE];
headeronly = hdrp != NULL;
/*
** Create the temp file name and create the file.
*/
if (!headeronly)
{
struct stat stbuf;
(void) strlcpy(dfname, queuename(e, 'd'), sizeof dfname);
#if _FFR_QUEUE_FILE_MODE
{
MODE_T oldumask;
if (bitset(S_IWGRP, QueueFileMode))
oldumask = umask(002);
df = bfopen(dfname, QueueFileMode, DataFileBufferSize,
SFF_OPENASROOT);
if (bitset(S_IWGRP, QueueFileMode))
(void) umask(oldumask);
}
#else /* _FFR_QUEUE_FILE_MODE */
df = bfopen(dfname, FileMode, DataFileBufferSize,
SFF_OPENASROOT);
#endif /* _FFR_QUEUE_FILE_MODE */
if (df == NULL)
{
syserr("Cannot create %s", dfname);
e->e_flags |= EF_NO_BODY_RETN;
finis(TRUE, ExitStat);
/* NOTREACHED */
}
dfd = fileno(df);
if (dfd < 0 || fstat(dfd, &stbuf) < 0)
e->e_dfino = -1;
else
{
e->e_dfdev = stbuf.st_dev;
e->e_dfino = stbuf.st_ino;
}
HasEightBits = FALSE;
e->e_msgsize = 0;
e->e_flags |= EF_HAS_DF;
}
/*
** Tell ARPANET to go ahead.
*/
if (smtpmode)
message("354 Enter mail, end with \".\" on a line by itself");
if (tTd(30, 2))
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).
*/
buf = bp = bufbuf;
buflen = sizeof bufbuf;
pbp = peekbuf;
istate = IS_BOL;
mstate = SaveFrom ? MS_HEADER : MS_UFROM;
CollectProgress = FALSE;
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 ? CurHostName : "<local machine>");
errno = 0;
usrerr("451 4.4.1 timeout waiting for input during message collect");
goto readerr;
}
CollectTimeout = setevent(dbto, collecttimeout, dbto);
}
for (;;)
{
if (tTd(30, 35))
dprintf("top, istate=%d, mstate=%d\n", istate, mstate);
for (;;)
{
if (pbp > peekbuf)
c = *--pbp;
else
{
while (!feof(fp) && !ferror(fp))
{
errno = 0;
c = getc(fp);
if (c == EOF && errno == EINTR)
{
/* Interrupted, retry */
clearerr(fp);
continue;
}
break;
}
CollectProgress = TRUE;
if (TrafficLogFile != NULL && !headeronly)
{
if (istate == IS_BOL)
(void) fprintf(TrafficLogFile, "%05d <<< ",
(int) getpid());
if (c == EOF)
(void) fprintf(TrafficLogFile, "[EOF]\n");
else
(void) putc(c, TrafficLogFile);
}
if (c == EOF)
goto readerr;
if (SevenBitInput)
c &= 0x7f;
else
HasEightBits |= bitset(0x80, c);
}
if (tTd(30, 94))
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) ungetc(c, fp);
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) putc(c, df);
/* 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)
free(obuf);
}
if (c >= 0200 && c <= 0237)
{
#if 0 /* causes complaints -- figure out something for 8.11 */
usrerr("Illegal character 0x%x in header", c);
#else /* 0 */
/* EMPTY */
#endif /* 0 */
}
else if (c != '\0')
{
*bp++ = c;
if (MaxHeadersLength > 0 &&
++hdrslen > MaxHeadersLength)
{
sm_syslog(LOG_NOTICE, e->e_id,
"headers too large (%d max) from %s during message collect",
MaxHeadersLength,
CurHostName != NULL ? CurHostName : "localhost");
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))
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
{
clearerr(fp);
errno = 0;
c = getc(fp);
} while (c == EOF && errno == EINTR);
if (c != EOF)
(void) ungetc(c, fp);
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))
dprintf("EOH\n");
if (headeronly)
goto readerr;
/* call the end-of-header check ruleset */
snprintf(hnum, sizeof hnum, "%d", numhdrs);
snprintf(hsize, sizeof hsize, "%d", hdrslen);
if (tTd(30, 10))
dprintf("collect: rscheck(\"check_eoh\", \"%s $| %s\")\n",
hnum, hsize);
rstat = rscheck("check_eoh", hnum, hsize, e, FALSE,
TRUE, 4);
bp = buf;
/* toss blank line */
if ((!bitset(EF_CRLF_NOT_EOL, e->e_flags) &&
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -