xref: /freebsd/contrib/sendmail/src/collect.c (revision 1ed4a948e1e90356ea79bab18495d577540c7165)
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
15 static 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 
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 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 
296 bufferchar:
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 
375 nextstate:
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 
468 readerr:
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 
720 static void
721 collecttimeout(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 
748 static void
749 dferror(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 
830 static char	*DowList[] =
831 {
832 	"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", NULL
833 };
834 
835 static char	*MonthList[] =
836 {
837 	"Jan", "Feb", "Mar", "Apr", "May", "Jun",
838 	"Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
839 	NULL
840 };
841 
842 static void
843 eatfrom(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