collect.c revision 73188
1/*
2 * Copyright (c) 1998-2001 Sendmail, Inc. and its suppliers.
3 *	All rights reserved.
4 * Copyright (c) 1983, 1995-1997 Eric P. Allman.  All rights reserved.
5 * Copyright (c) 1988, 1993
6 *	The Regents of the University of California.  All rights reserved.
7 *
8 * By using this file, you agree to the terms and conditions set
9 * forth in the LICENSE file which can be found at the top level of
10 * the sendmail distribution.
11 *
12 */
13
14#ifndef lint
15static char id[] = "@(#)$Id: collect.c,v 8.136.4.15 2001/02/21 01:05:59 gshapiro Exp $";
16#endif /* ! lint */
17
18#include <sendmail.h>
19
20
21static void	collecttimeout __P((time_t));
22static void	dferror __P((FILE *volatile, char *, ENVELOPE *));
23static void	eatfrom __P((char *volatile, ENVELOPE *));
24
25/*
26**  COLLECT -- read & parse message header & make temp file.
27**
28**	Creates a temporary file name and copies the standard
29**	input to that file.  Leading UNIX-style "From" lines are
30**	stripped off (after important information is extracted).
31**
32**	Parameters:
33**		fp -- file to read.
34**		smtpmode -- if set, we are running SMTP: give an RFC821
35**			style message to say we are ready to collect
36**			input, and never ignore a single dot to mean
37**			end of message.
38**		hdrp -- the location to stash the header.
39**		e -- the current envelope.
40**
41**	Returns:
42**		none.
43**
44**	Side Effects:
45**		Temp file is created and filled.
46**		The from person may be set.
47*/
48
49static jmp_buf	CtxCollectTimeout;
50static bool	CollectProgress;
51static EVENT	*CollectTimeout;
52
53/* values for input state machine */
54#define IS_NORM		0	/* middle of line */
55#define IS_BOL		1	/* beginning of line */
56#define IS_DOT		2	/* read a dot at beginning of line */
57#define IS_DOTCR	3	/* read ".\r" at beginning of line */
58#define IS_CR		4	/* read a carriage return */
59
60/* values for message state machine */
61#define MS_UFROM	0	/* reading Unix from line */
62#define MS_HEADER	1	/* reading message header */
63#define MS_BODY		2	/* reading message body */
64#define MS_DISCARD	3	/* discarding rest of message */
65
66void
67collect(fp, smtpmode, hdrp, e)
68	FILE *fp;
69	bool smtpmode;
70	HDR **hdrp;
71	register ENVELOPE *e;
72{
73	register FILE *volatile df;
74	volatile bool ignrdot = smtpmode ? FALSE : IgnrDot;
75	volatile time_t dbto = smtpmode ? TimeOuts.to_datablock : 0;
76	register char *volatile bp;
77	volatile int c = EOF;
78	volatile bool inputerr = FALSE;
79	bool headeronly;
80	char *volatile buf;
81	volatile int buflen;
82	volatile int istate;
83	volatile int mstate;
84	volatile int hdrslen = 0;
85	volatile int numhdrs = 0;
86	volatile int dfd;
87	volatile int rstat = EX_OK;
88	u_char *volatile pbp;
89	u_char peekbuf[8];
90	char hsize[16];
91	char hnum[16];
92	char dfname[MAXPATHLEN];
93	char bufbuf[MAXLINE];
94
95	headeronly = hdrp != NULL;
96
97	/*
98	**  Create the temp file name and create the file.
99	*/
100
101	if (!headeronly)
102	{
103		struct stat stbuf;
104		long sff = SFF_OPENASROOT;
105
106
107		(void) strlcpy(dfname, queuename(e, 'd'), sizeof dfname);
108#if _FFR_QUEUE_FILE_MODE
109		{
110			MODE_T oldumask;
111
112			if (bitset(S_IWGRP, QueueFileMode))
113				oldumask = umask(002);
114			df = bfopen(dfname, QueueFileMode,
115				    DataFileBufferSize, sff);
116			if (bitset(S_IWGRP, QueueFileMode))
117				(void) umask(oldumask);
118		}
119#else /* _FFR_QUEUE_FILE_MODE */
120		df = bfopen(dfname, FileMode, DataFileBufferSize, sff);
121#endif /* _FFR_QUEUE_FILE_MODE */
122		if (df == NULL)
123		{
124			HoldErrs = FALSE;
125			if (smtpmode)
126				syserr("421 4.3.5 Unable to create data file");
127			else
128				syserr("Cannot create %s", dfname);
129			e->e_flags |= EF_NO_BODY_RETN;
130			finis(TRUE, ExitStat);
131			/* NOTREACHED */
132		}
133		dfd = fileno(df);
134		if (dfd < 0 || fstat(dfd, &stbuf) < 0)
135			e->e_dfino = -1;
136		else
137		{
138			e->e_dfdev = stbuf.st_dev;
139			e->e_dfino = stbuf.st_ino;
140		}
141		HasEightBits = FALSE;
142		e->e_msgsize = 0;
143		e->e_flags |= EF_HAS_DF;
144	}
145
146	/*
147	**  Tell ARPANET to go ahead.
148	*/
149
150	if (smtpmode)
151		message("354 Enter mail, end with \".\" on a line by itself");
152
153	if (tTd(30, 2))
154		dprintf("collect\n");
155
156	/*
157	**  Read the message.
158	**
159	**	This is done using two interleaved state machines.
160	**	The input state machine is looking for things like
161	**	hidden dots; the message state machine is handling
162	**	the larger picture (e.g., header versus body).
163	*/
164
165	buf = bp = bufbuf;
166	buflen = sizeof bufbuf;
167	pbp = peekbuf;
168	istate = IS_BOL;
169	mstate = SaveFrom ? MS_HEADER : MS_UFROM;
170	CollectProgress = FALSE;
171
172	if (dbto != 0)
173	{
174		/* handle possible input timeout */
175		if (setjmp(CtxCollectTimeout) != 0)
176		{
177			if (LogLevel > 2)
178				sm_syslog(LOG_NOTICE, e->e_id,
179				    "timeout waiting for input from %s during message collect",
180				    CurHostName ? CurHostName : "<local machine>");
181			errno = 0;
182			usrerr("451 4.4.1 timeout waiting for input during message collect");
183			goto readerr;
184		}
185		CollectTimeout = setevent(dbto, collecttimeout, dbto);
186	}
187
188	for (;;)
189	{
190		if (tTd(30, 35))
191			dprintf("top, istate=%d, mstate=%d\n", istate, mstate);
192		for (;;)
193		{
194			if (pbp > peekbuf)
195				c = *--pbp;
196			else
197			{
198				while (!feof(fp) && !ferror(fp))
199				{
200					errno = 0;
201					c = getc(fp);
202
203					if (c == EOF && errno == EINTR)
204					{
205						/* Interrupted, retry */
206						clearerr(fp);
207						continue;
208					}
209					break;
210				}
211				CollectProgress = TRUE;
212				if (TrafficLogFile != NULL && !headeronly)
213				{
214					if (istate == IS_BOL)
215						(void) fprintf(TrafficLogFile, "%05d <<< ",
216							(int) getpid());
217					if (c == EOF)
218						(void) fprintf(TrafficLogFile, "[EOF]\n");
219					else
220						(void) putc(c, TrafficLogFile);
221				}
222				if (c == EOF)
223					goto readerr;
224				if (SevenBitInput)
225					c &= 0x7f;
226				else
227					HasEightBits |= bitset(0x80, c);
228			}
229			if (tTd(30, 94))
230				dprintf("istate=%d, c=%c (0x%x)\n",
231					istate, (char) c, c);
232			switch (istate)
233			{
234			  case IS_BOL:
235				if (c == '.')
236				{
237					istate = IS_DOT;
238					continue;
239				}
240				break;
241
242			  case IS_DOT:
243				if (c == '\n' && !ignrdot &&
244				    !bitset(EF_NL_NOT_EOL, e->e_flags))
245					goto readerr;
246				else if (c == '\r' &&
247					 !bitset(EF_CRLF_NOT_EOL, e->e_flags))
248				{
249					istate = IS_DOTCR;
250					continue;
251				}
252				else if (c != '.' ||
253					 (OpMode != MD_SMTP &&
254					  OpMode != MD_DAEMON &&
255					  OpMode != MD_ARPAFTP))
256				{
257					*pbp++ = c;
258					c = '.';
259				}
260				break;
261
262			  case IS_DOTCR:
263				if (c == '\n' && !ignrdot)
264					goto readerr;
265				else
266				{
267					/* push back the ".\rx" */
268					*pbp++ = c;
269					*pbp++ = '\r';
270					c = '.';
271				}
272				break;
273
274			  case IS_CR:
275				if (c == '\n')
276					istate = IS_BOL;
277				else
278				{
279					(void) ungetc(c, fp);
280					c = '\r';
281					istate = IS_NORM;
282				}
283				goto bufferchar;
284			}
285
286			if (c == '\r' && !bitset(EF_CRLF_NOT_EOL, e->e_flags))
287			{
288				istate = IS_CR;
289				continue;
290			}
291			else if (c == '\n' && !bitset(EF_NL_NOT_EOL, e->e_flags))
292				istate = IS_BOL;
293			else
294				istate = IS_NORM;
295
296bufferchar:
297			if (!headeronly)
298			{
299				/* no overflow? */
300				if (e->e_msgsize >= 0)
301				{
302					e->e_msgsize++;
303					if (MaxMessageSize > 0 &&
304					    !bitset(EF_TOOBIG, e->e_flags) &&
305					    e->e_msgsize > MaxMessageSize)
306						 e->e_flags |= EF_TOOBIG;
307				}
308			}
309			switch (mstate)
310			{
311			  case MS_BODY:
312				/* just put the character out */
313				if (!bitset(EF_TOOBIG, e->e_flags))
314					(void) putc(c, df);
315
316				/* FALLTHROUGH */
317
318			  case MS_DISCARD:
319				continue;
320			}
321
322			/* header -- buffer up */
323			if (bp >= &buf[buflen - 2])
324			{
325				char *obuf;
326
327				if (mstate != MS_HEADER)
328					break;
329
330				/* out of space for header */
331				obuf = buf;
332				if (buflen < MEMCHUNKSIZE)
333					buflen *= 2;
334				else
335					buflen += MEMCHUNKSIZE;
336				buf = xalloc(buflen);
337				memmove(buf, obuf, bp - obuf);
338				bp = &buf[bp - obuf];
339				if (obuf != bufbuf)
340					free(obuf);
341			}
342			if (c >= 0200 && c <= 0237)
343			{
344#if 0	/* causes complaints -- figure out something for 8.11 */
345				usrerr("Illegal character 0x%x in header", c);
346#else /* 0 */
347				/* EMPTY */
348#endif /* 0 */
349			}
350			else if (c != '\0')
351			{
352				*bp++ = c;
353				hdrslen++;
354				if (MaxHeadersLength > 0 &&
355				    hdrslen > MaxHeadersLength)
356				{
357					sm_syslog(LOG_NOTICE, e->e_id,
358						  "headers too large (%d max) from %s during message collect",
359						  MaxHeadersLength,
360						  CurHostName != NULL ? CurHostName : "localhost");
361					errno = 0;
362					e->e_flags |= EF_CLRQUEUE;
363					e->e_status = "5.6.0";
364					usrerrenh(e->e_status,
365						  "552 Headers too large (%d max)",
366						  MaxHeadersLength);
367					mstate = MS_DISCARD;
368				}
369			}
370			if (istate == IS_BOL)
371				break;
372		}
373		*bp = '\0';
374
375nextstate:
376		if (tTd(30, 35))
377			dprintf("nextstate, istate=%d, mstate=%d, line = \"%s\"\n",
378				istate, mstate, buf);
379		switch (mstate)
380		{
381		  case MS_UFROM:
382			mstate = MS_HEADER;
383#ifndef NOTUNIX
384			if (strncmp(buf, "From ", 5) == 0)
385			{
386				bp = buf;
387				eatfrom(buf, e);
388				continue;
389			}
390#endif /* ! NOTUNIX */
391			/* FALLTHROUGH */
392
393		  case MS_HEADER:
394			if (!isheader(buf))
395			{
396				mstate = MS_BODY;
397				goto nextstate;
398			}
399
400			/* check for possible continuation line */
401			do
402			{
403				clearerr(fp);
404				errno = 0;
405				c = getc(fp);
406			} while (c == EOF && errno == EINTR);
407			if (c != EOF)
408				(void) ungetc(c, fp);
409			if (c == ' ' || c == '\t')
410			{
411				/* yep -- defer this */
412				continue;
413			}
414
415			/* trim off trailing CRLF or NL */
416			if (*--bp != '\n' || *--bp != '\r')
417				bp++;
418			*bp = '\0';
419
420			if (bitset(H_EOH, chompheader(buf,
421						      CHHDR_CHECK | CHHDR_USER,
422						      hdrp, e)))
423			{
424				mstate = MS_BODY;
425				goto nextstate;
426			}
427			numhdrs++;
428			break;
429
430		  case MS_BODY:
431			if (tTd(30, 1))
432				dprintf("EOH\n");
433
434			if (headeronly)
435				goto readerr;
436
437			/* call the end-of-header check ruleset */
438			snprintf(hnum, sizeof hnum, "%d", numhdrs);
439			snprintf(hsize, sizeof hsize, "%d", hdrslen);
440			if (tTd(30, 10))
441				dprintf("collect: rscheck(\"check_eoh\", \"%s $| %s\")\n",
442					hnum, hsize);
443			rstat = rscheck("check_eoh", hnum, hsize, e, FALSE,
444					TRUE, 4, NULL);
445
446			bp = buf;
447
448			/* toss blank line */
449			if ((!bitset(EF_CRLF_NOT_EOL, e->e_flags) &&
450				bp[0] == '\r' && bp[1] == '\n') ||
451			    (!bitset(EF_NL_NOT_EOL, e->e_flags) &&
452				bp[0] == '\n'))
453			{
454				break;
455			}
456
457			/* if not a blank separator, write it out */
458			if (!bitset(EF_TOOBIG, e->e_flags))
459			{
460				while (*bp != '\0')
461					(void) putc(*bp++, df);
462			}
463			break;
464		}
465		bp = buf;
466	}
467
468readerr:
469	if ((feof(fp) && smtpmode) || ferror(fp))
470	{
471		const char *errmsg = errstring(errno);
472
473		if (tTd(30, 1))
474			dprintf("collect: premature EOM: %s\n", errmsg);
475		if (LogLevel >= 2)
476			sm_syslog(LOG_WARNING, e->e_id,
477				"collect: premature EOM: %s", errmsg);
478		inputerr = TRUE;
479	}
480
481	/* reset global timer */
482	clrevent(CollectTimeout);
483
484	if (headeronly)
485		return;
486
487	if (df == NULL)
488	{
489		/* skip next few clauses */
490		/* EMPTY */
491	}
492	else if (fflush(df) != 0 || ferror(df))
493	{
494		dferror(df, "fflush||ferror", e);
495		flush_errors(TRUE);
496		finis(TRUE, ExitStat);
497		/* NOTREACHED */
498	}
499	else if (!SuperSafe)
500	{
501		/* skip next few clauses */
502		/* EMPTY */
503	}
504	else if (bfcommit(df) < 0)
505	{
506		int save_errno = errno;
507
508		if (save_errno == EEXIST)
509		{
510			char *dfile;
511			struct stat st;
512
513			dfile = queuename(e, 'd');
514			if (stat(dfile, &st) < 0)
515				st.st_size = -1;
516			errno = EEXIST;
517			syserr("collect: bfcommit(%s): already on disk, size = %ld",
518			       dfile, (long) st.st_size);
519			dfd = fileno(df);
520			if (dfd >= 0)
521				dumpfd(dfd, TRUE, TRUE);
522		}
523		errno = save_errno;
524		dferror(df, "bfcommit", e);
525		flush_errors(TRUE);
526		finis(save_errno != EEXIST, ExitStat);
527	}
528	else if (bffsync(df) < 0)
529	{
530		dferror(df, "bffsync", e);
531		flush_errors(TRUE);
532		finis(TRUE, ExitStat);
533		/* NOTREACHED */
534	}
535	else if (bfclose(df) < 0)
536	{
537		dferror(df, "bfclose", e);
538		flush_errors(TRUE);
539		finis(TRUE, ExitStat);
540		/* NOTREACHED */
541	}
542	else
543	{
544		/* everything is happily flushed to disk */
545		df = NULL;
546	}
547
548	/* An EOF when running SMTP is an error */
549	if (inputerr && (OpMode == MD_SMTP || OpMode == MD_DAEMON))
550	{
551		char *host;
552		char *problem;
553
554		host = RealHostName;
555		if (host == NULL)
556			host = "localhost";
557
558		if (feof(fp))
559			problem = "unexpected close";
560		else if (ferror(fp))
561			problem = "I/O error";
562		else
563			problem = "read timeout";
564		if (LogLevel > 0 && feof(fp))
565			sm_syslog(LOG_NOTICE, e->e_id,
566			    "collect: %s on connection from %.100s, sender=%s: %s",
567			    problem, host,
568			    shortenstring(e->e_from.q_paddr, MAXSHORTSTR),
569			    errstring(errno));
570		if (feof(fp))
571			usrerr("451 4.4.1 collect: %s on connection from %s, from=%s",
572				problem, host,
573				shortenstring(e->e_from.q_paddr, MAXSHORTSTR));
574		else
575			syserr("451 4.4.1 collect: %s on connection from %s, from=%s",
576				problem, host,
577				shortenstring(e->e_from.q_paddr, MAXSHORTSTR));
578
579		/* don't return an error indication */
580		e->e_to = NULL;
581		e->e_flags &= ~EF_FATALERRS;
582		e->e_flags |= EF_CLRQUEUE;
583
584		/* and don't try to deliver the partial message either */
585		if (InChild)
586			ExitStat = EX_QUIT;
587		finis(TRUE, ExitStat);
588		/* NOTREACHED */
589	}
590
591	/*
592	**  Find out some information from the headers.
593	**	Examples are who is the from person & the date.
594	*/
595
596	eatheader(e, TRUE);
597
598	if (GrabTo && e->e_sendqueue == NULL)
599		usrerr("No recipient addresses found in header");
600
601	/* collect statistics */
602	if (OpMode != MD_VERIFY)
603		markstats(e, (ADDRESS *) NULL, FALSE);
604
605	/*
606	**  If we have a Return-Receipt-To:, turn it into a DSN.
607	*/
608
609	if (RrtImpliesDsn && hvalue("return-receipt-to", e->e_header) != NULL)
610	{
611		ADDRESS *q;
612
613		for (q = e->e_sendqueue; q != NULL; q = q->q_next)
614			if (!bitset(QHASNOTIFY, q->q_flags))
615				q->q_flags |= QHASNOTIFY|QPINGONSUCCESS;
616	}
617
618	/*
619	**  Add an Apparently-To: line if we have no recipient lines.
620	*/
621
622	if (hvalue("to", e->e_header) != NULL ||
623	    hvalue("cc", e->e_header) != NULL ||
624	    hvalue("apparently-to", e->e_header) != NULL)
625	{
626		/* have a valid recipient header -- delete Bcc: headers */
627		e->e_flags |= EF_DELETE_BCC;
628	}
629	else if (hvalue("bcc", e->e_header) == NULL)
630	{
631		/* no valid recipient headers */
632		register ADDRESS *q;
633		char *hdr = NULL;
634
635		/* create an Apparently-To: field */
636		/*    that or reject the message.... */
637		switch (NoRecipientAction)
638		{
639		  case NRA_ADD_APPARENTLY_TO:
640			hdr = "Apparently-To";
641			break;
642
643		  case NRA_ADD_TO:
644			hdr = "To";
645			break;
646
647		  case NRA_ADD_BCC:
648			addheader("Bcc", " ", 0, &e->e_header);
649			break;
650
651		  case NRA_ADD_TO_UNDISCLOSED:
652			addheader("To", "undisclosed-recipients:;", 0, &e->e_header);
653			break;
654		}
655
656		if (hdr != NULL)
657		{
658			for (q = e->e_sendqueue; q != NULL; q = q->q_next)
659			{
660				if (q->q_alias != NULL)
661					continue;
662				if (tTd(30, 3))
663					dprintf("Adding %s: %s\n",
664						hdr, q->q_paddr);
665				addheader(hdr, q->q_paddr, 0, &e->e_header);
666			}
667		}
668	}
669
670	/* check for message too large */
671	if (bitset(EF_TOOBIG, e->e_flags))
672	{
673		e->e_flags |= EF_NO_BODY_RETN|EF_CLRQUEUE;
674		e->e_status = "5.2.3";
675		usrerrenh(e->e_status,
676			  "552 Message exceeds maximum fixed size (%ld)",
677			  MaxMessageSize);
678		if (LogLevel > 6)
679			sm_syslog(LOG_NOTICE, e->e_id,
680				"message size (%ld) exceeds maximum (%ld)",
681				e->e_msgsize, MaxMessageSize);
682	}
683
684	/* check for illegal 8-bit data */
685	if (HasEightBits)
686	{
687		e->e_flags |= EF_HAS8BIT;
688		if (!bitset(MM_PASS8BIT|MM_MIME8BIT, MimeMode) &&
689		    !bitset(EF_IS_MIME, e->e_flags))
690		{
691			e->e_status = "5.6.1";
692			usrerrenh(e->e_status, "554 Eight bit data not allowed");
693		}
694	}
695	else
696	{
697		/* if it claimed to be 8 bits, well, it lied.... */
698		if (e->e_bodytype != NULL &&
699		    strcasecmp(e->e_bodytype, "8BITMIME") == 0)
700			e->e_bodytype = "7BIT";
701	}
702
703	if (SuperSafe)
704	{
705		if ((e->e_dfp = fopen(dfname, "r")) == NULL)
706		{
707			/* we haven't acked receipt yet, so just chuck this */
708			syserr("Cannot reopen %s", dfname);
709			finis(TRUE, ExitStat);
710			/* NOTREACHED */
711		}
712	}
713	else
714		e->e_dfp = df;
715	if (e->e_dfp == NULL)
716		syserr("!collect: no e_dfp");
717}
718
719
720static void
721collecttimeout(timeout)
722	time_t timeout;
723{
724	/* if no progress was made, die now */
725	if (!CollectProgress)
726		longjmp(CtxCollectTimeout, 1);
727
728	/* otherwise reset the timeout */
729	CollectTimeout = setevent(timeout, collecttimeout, timeout);
730	CollectProgress = FALSE;
731}
732/*
733**  DFERROR -- signal error on writing the data file.
734**
735**	Parameters:
736**		df -- the file pointer for the data file.
737**		msg -- detailed message.
738**		e -- the current envelope.
739**
740**	Returns:
741**		none.
742**
743**	Side Effects:
744**		Gives an error message.
745**		Arranges for following output to go elsewhere.
746*/
747
748static void
749dferror(df, msg, e)
750	FILE *volatile df;
751	char *msg;
752	register ENVELOPE *e;
753{
754	char *dfname;
755
756	dfname = queuename(e, 'd');
757	setstat(EX_IOERR);
758	if (errno == ENOSPC)
759	{
760#if STAT64 > 0
761		struct stat64 st;
762#else /* STAT64 > 0 */
763		struct stat st;
764#endif /* STAT64 > 0 */
765		long avail;
766		long bsize;
767
768		e->e_flags |= EF_NO_BODY_RETN;
769
770		if (
771#if STAT64 > 0
772		    fstat64(fileno(df), &st)
773#else /* STAT64 > 0 */
774		    fstat(fileno(df), &st)
775#endif /* STAT64 > 0 */
776		    < 0)
777		  st.st_size = 0;
778		(void) freopen(dfname, "w", df);
779		if (st.st_size <= 0)
780			fprintf(df, "\n*** Mail could not be accepted");
781		/*CONSTCOND*/
782		else if (sizeof st.st_size > sizeof (long))
783			fprintf(df, "\n*** Mail of at least %s bytes could not be accepted\n",
784				quad_to_string(st.st_size));
785		else
786			fprintf(df, "\n*** Mail of at least %lu bytes could not be accepted\n",
787				(unsigned long) st.st_size);
788		fprintf(df, "*** at %s due to lack of disk space for temp file.\n",
789			MyHostName);
790		avail = freediskspace(qid_printqueue(e->e_queuedir), &bsize);
791		if (avail > 0)
792		{
793			if (bsize > 1024)
794				avail *= bsize / 1024;
795			else if (bsize < 1024)
796				avail /= 1024 / bsize;
797			fprintf(df, "*** Currently, %ld kilobytes are available for mail temp files.\n",
798				avail);
799		}
800		e->e_status = "4.3.1";
801		usrerrenh(e->e_status, "452 Out of disk space for temp file");
802	}
803	else
804		syserr("collect: Cannot write %s (%s, uid=%d)",
805			dfname, msg, geteuid());
806	if (freopen("/dev/null", "w", df) == NULL)
807		sm_syslog(LOG_ERR, e->e_id,
808			  "dferror: freopen(\"/dev/null\") failed: %s",
809			  errstring(errno));
810}
811/*
812**  EATFROM -- chew up a UNIX style from line and process
813**
814**	This does indeed make some assumptions about the format
815**	of UNIX messages.
816**
817**	Parameters:
818**		fm -- the from line.
819**
820**	Returns:
821**		none.
822**
823**	Side Effects:
824**		extracts what information it can from the header,
825**		such as the date.
826*/
827
828#ifndef NOTUNIX
829
830static char	*DowList[] =
831{
832	"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", NULL
833};
834
835static char	*MonthList[] =
836{
837	"Jan", "Feb", "Mar", "Apr", "May", "Jun",
838	"Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
839	NULL
840};
841
842static void
843eatfrom(fm, e)
844	char *volatile fm;
845	register ENVELOPE *e;
846{
847	register char *p;
848	register char **dt;
849
850	if (tTd(30, 2))
851		dprintf("eatfrom(%s)\n", fm);
852
853	/* find the date part */
854	p = fm;
855	while (*p != '\0')
856	{
857		/* skip a word */
858		while (*p != '\0' && *p != ' ')
859			p++;
860		while (*p == ' ')
861			p++;
862		if (strlen(p) < 17)
863		{
864			/* no room for the date */
865			return;
866		}
867		if (!(isascii(*p) && isupper(*p)) ||
868		    p[3] != ' ' || p[13] != ':' || p[16] != ':')
869			continue;
870
871		/* we have a possible date */
872		for (dt = DowList; *dt != NULL; dt++)
873			if (strncmp(*dt, p, 3) == 0)
874				break;
875		if (*dt == NULL)
876			continue;
877
878		for (dt = MonthList; *dt != NULL; dt++)
879		{
880			if (strncmp(*dt, &p[4], 3) == 0)
881				break;
882		}
883		if (*dt != NULL)
884			break;
885	}
886
887	if (*p != '\0')
888	{
889		char *q;
890
891		/* we have found a date */
892		q = xalloc(25);
893		(void) strlcpy(q, p, 25);
894		q = arpadate(q);
895		define('a', newstr(q), e);
896	}
897}
898#endif /* ! NOTUNIX */
899