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