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