int numheaders = 0;
collect(fp, smtpmode, hdrp, e)	FILE *fp;	bool smtpmode;	HDR **hdrp;	register ENVELOPE *e;{	register FILE *volatile tf;	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;	u_char *volatile pbp;	u_char peekbuf[8];	char dfname[MAXQFNAME];	char bufbuf[MAXLINE];	int numheaders = 0;	extern bool isheader __P((char *));	extern void tferror __P((FILE *volatile, ENVELOPE *));	headeronly = hdrp != NULL;	/*	**  Create the temp file name and create the file.	*/	if (!headeronly)	{		int tfd;		struct stat stbuf;		strcpy(dfname, queuename(e, 'd'));		tfd = dfopen(dfname, O_WRONLY|O_CREAT|O_TRUNC, FileMode, SFF_ANYFILE);		if (tfd < 0 || (tf = fdopen(tfd, "w")) == NULL)		{			syserr("Cannot create %s", dfname);			e->e_flags |= EF_NO_BODY_RETN;			finis(TRUE, ExitStat);		}		if (fstat(fileno(tf), &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))		printf("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 timeout waiting for input during message collect");			goto readerr;		}		CollectTimeout = setevent(dbto, collecttimeout, dbto);	}	for (;;)	{		if (tTd(30, 35))			printf("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 (errno != EINTR)						break;					clearerr(fp);				}				CollectProgress = TRUE;				if (TrafficLogFile != NULL && !headeronly)				{					if (istate == IS_BOL)						fprintf(TrafficLogFile, "%05d <<< ",							(int) getpid());					if (c == EOF)						fprintf(TrafficLogFile, "[EOF]\n");					else						putc(c, TrafficLogFile);				}				if (c == EOF)					goto readerr;				if (SevenBitInput)					c &= 0x7f;				else					HasEightBits |= bitset(0x80, c);			}			if (tTd(30, 94))				printf("istate=%d, c=%c (0x%x)\n",					istate, 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				{					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)				e->e_msgsize++;			if (mstate == MS_BODY)			{				/* just put the character out */				if (MaxMessageSize <= 0 ||				    e->e_msgsize <= MaxMessageSize)					putc(c, tf);				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);				bcopy(obuf, buf, 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.9 */				usrerr("Illegal character 0x%x in header", c);#endif			}			else if (c != '\0')				*bp++ = c;			if (istate == IS_BOL)				break;		}		*bp = '\0';nextstate:		if (tTd(30, 35))			printf("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)			{				extern void eatfrom __P((char *volatile, ENVELOPE *));				bp = buf;				eatfrom(buf, e);				continue;			}#endif			/* fall through */		  case MS_HEADER:			if (!isheader(buf))			{				mstate = MS_BODY;				goto nextstate;			}			/* check for unreasonable number of headers */			if (numheaders++ > MAXHDRLINES)			{				sm_syslog(LOG_NOTICE, e->e_id,				    "excessive headers from %s during message collect",				    CurHostName ? CurHostName : "<local machine>");				errno = 0;				usrerr("451 Excessive number of mail headers.");				goto readerr;			}			/* check for possible continuation line */			do			{				clearerr(fp);				errno = 0;				c = getc(fp);			} while (errno == EINTR);			if (c != EOF)				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, FALSE, hdrp, e)))			{				mstate = MS_BODY;				goto nextstate;			}			break;		  case MS_BODY:			if (tTd(30, 1))				printf("EOH\n");			if (headeronly)				goto readerr;			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 (MaxMessageSize <= 0 ||			    e->e_msgsize <= MaxMessageSize)			{				while (*bp != '\0')					putc(*bp++, tf);			}			break;		}		bp = buf;	}readerr:	if ((feof(fp) && smtpmode) || ferror(fp))	{		const char *errmsg = errstring(errno);		if (tTd(30, 1))			printf("collect: premature EOM: %s\n", errmsg);		if (LogLevel >= 2)			sm_syslog(LOG_WARNING, e->e_id,				"collect: premature EOM: %s", errmsg);		inputerr = TRUE;	}	/* reset global timer */	clrevent(CollectTimeout);	if (headeronly)		return;	if (tf != NULL &&	    (fflush(tf) != 0 || ferror(tf) ||	     (SuperSafe && fsync(fileno(tf)) < 0) ||	     fclose(tf) < 0))	{		tferror(tf, e);		flush_errors(TRUE);		finis(TRUE, ExitStat);	}	/* 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 (feof(fp))			problem = "unexpected close";		else if (ferror(fp))			problem = "I/O error";		else			problem = "read timeout";		if (LogLevel > 0 && feof(fp))			sm_syslog(LOG_NOTICE, e->e_id,			    "collect: %s on connection from %.100s, sender=%s: %s",			    problem, host,			    shortenstring(e->e_from.q_paddr, MAXSHORTSTR),			    errstring(errno));		if (feof(fp))			usrerr("451 collect: %s on connection from %s, from=%s",				problem, host,				shortenstring(e->e_from.q_paddr, MAXSHORTSTR));		else			syserr("451 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;		/* and don't try to deliver the partial message either */		if (InChild)			ExitStat = EX_QUIT;		finis(TRUE, ExitStat);	}	/*	**  Find out some information from the headers.	**	Examples are who is the from person & the date.	*/	eatheader(e, TRUE);	if (GrabTo && e->e_sendqueue == NULL)		usrerr("No recipient addresses found in header");	/* collect statistics */	if (OpMode != MD_VERIFY)		markstats(e, (ADDRESS *) NULL, FALSE);#if _FFR_DSN_RRT_OPTION	/*	**  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;	}#endif	/*	**  Add an Apparently-To: line if we have no recipient lines.	*/	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 an Apparently-To: field */		/*    that or reject the message.... */		switch (NoRecipientAction)		{		  case NRA_ADD_APPARENTLY_TO:			hdr = "Apparently-To";			break;		  case NRA_ADD_TO:			hdr = "To";			break;		  case NRA_ADD_BCC:			addheader("Bcc", " ", &e->e_header);			break;		  case NRA_ADD_TO_UNDISCLOSED:			addheader("To", "undisclosed-recipients:;", &e->e_header);			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))					printf("Adding %s: %s\n",						hdr, q->q_paddr);				addheader(hdr, q->q_paddr, &e->e_header);			}		}	}	/* check for message too large */	if (MaxMessageSize > 0 && e->e_msgsize > MaxMessageSize)	{		e->e_flags |= EF_NO_BODY_RETN|EF_CLRQUEUE;		e->e_status = "5.2.3";		usrerr("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";			usrerr("554 Eight bit data not allowed");		}	}	else	{		/* if it claimed to be 8 bits, well, it lied.... */		if (e->e_bodytype != NULL &&		    strcasecmp(e->e_bodytype, "8BITMIME") == 0)			e->e_bodytype = "7BIT";	}	if ((e->e_dfp = fopen(dfname, "r")) == NULL)	{		/* we haven't acked receipt yet, so just chuck this */		syserr("Cannot reopen %s", dfname);		finis(TRUE, ExitStat);	}}