collect.c revision 43730
1191005Sdelphij/*
2204977Simp * Copyright (c) 1998 Sendmail, Inc.  All rights reserved.
331899Ssef * Copyright (c) 1983, 1995-1997 Eric P. Allman.  All rights reserved.
431899Ssef * Copyright (c) 1988, 1993
531899Ssef *	The Regents of the University of California.  All rights reserved.
631899Ssef *
731899Ssef * By using this file, you agree to the terms and conditions set
831899Ssef * forth in the LICENSE file which can be found at the top level of
931899Ssef * the sendmail distribution.
1031899Ssef *
1131899Ssef */
1231899Ssef
1331899Ssef#ifndef lint
1431899Ssefstatic char sccsid[] = "@(#)collect.c	8.93 (Berkeley) 1/26/1999";
1531899Ssef#endif /* not lint */
1631899Ssef
1731899Ssef# include <errno.h>
1831899Ssef# include "sendmail.h"
1931899Ssef
2031899Ssef/*
2131899Ssef**  COLLECT -- read & parse message header & make temp file.
2231899Ssef**
2331899Ssef**	Creates a temporary file name and copies the standard
2431899Ssef**	input to that file.  Leading UNIX-style "From" lines are
2531899Ssef**	stripped off (after important information is extracted).
2631899Ssef**
2731899Ssef**	Parameters:
2831899Ssef**		fp -- file to read.
2931899Ssef**		smtpmode -- if set, we are running SMTP: give an RFC821
3031899Ssef**			style message to say we are ready to collect
3131899Ssef**			input, and never ignore a single dot to mean
32119852Scharnier**			end of message.
33119852Scharnier**		hdrp -- the location to stash the header.
3432275Scharnier**		e -- the current envelope.
3531899Ssef**
36222103Sbcr**	Returns:
3731567Ssef**		none.
3831567Ssef**
3931567Ssef**	Side Effects:
4031567Ssef**		Temp file is created and filled.
4185301Sdes**		The from person may be set.
42123916Scracauer*/
43104581Smike
44123916Scracauerstatic jmp_buf	CtxCollectTimeout;
45168569Sdelphijstatic void	collecttimeout __P((time_t));
46191005Sdelphijstatic bool	CollectProgress;
4785301Sdesstatic EVENT	*CollectTimeout;
4832275Scharnier
49200462Sdelphij/* values for input state machine */
50200462Sdelphij#define IS_NORM		0	/* middle of line */
5132275Scharnier#define IS_BOL		1	/* beginning of line */
5231567Ssef#define IS_DOT		2	/* read a dot at beginning of line */
5331567Ssef#define IS_DOTCR	3	/* read ".\r" at beginning of line */
5431567Ssef#define IS_CR		4	/* read a carriage return */
55101423Smdodd
5631579Speter/* values for message state machine */
5731567Ssef#define MS_UFROM	0	/* reading Unix from line */
58101282Smdodd#define MS_HEADER	1	/* reading message header */
5987703Smarkm#define MS_BODY		2	/* reading message body */
60192025Sdds#define MS_DISCARD	3	/* discarding rest of message */
6131567Ssef
62240005Szontvoid
6331567Ssefcollect(fp, smtpmode, hdrp, e)
64144177Salfred	FILE *fp;
6532275Scharnier	bool smtpmode;
6632275Scharnier	HDR **hdrp;
67144177Salfred	register ENVELOPE *e;
68192025Sdds{
69192025Sdds	register FILE *volatile tf;
70144177Salfred	volatile bool ignrdot = smtpmode ? FALSE : IgnrDot;
7131567Ssef	volatile time_t dbto = smtpmode ? TimeOuts.to_datablock : 0;
7231567Ssef	register char *volatile bp;
7338897Ssef	volatile int c = EOF;
7438897Ssef	volatile bool inputerr = FALSE;
7538897Ssef	bool headeronly;
7638897Ssef	char *volatile buf;
77228396Sed	volatile int buflen;
78144177Salfred	volatile int istate;
79144177Salfred	volatile int mstate;
80144177Salfred	u_char *volatile pbp;
8131567Ssef	int hdrslen = 0;
82130394Sdwmalone	u_char peekbuf[8];
83144177Salfred	char dfname[MAXQFNAME];
84179051Sjhb	char bufbuf[MAXLINE];
85179051Sjhb	extern bool isheader __P((char *));
86130394Sdwmalone	extern void tferror __P((FILE *volatile, ENVELOPE *));
8739908Ssef
88144177Salfred	headeronly = hdrp != NULL;
89144177Salfred
90144177Salfred	/*
91144177Salfred	**  Create the temp file name and create the file.
9239908Ssef	*/
93106716Smarcel
94144177Salfred	if (!headeronly)
95106716Smarcel	{
96154047Sgrehan		int tfd;
97154047Sgrehan		struct stat stbuf;
98154047Sgrehan
99211935Snwhitehorn		strcpy(dfname, queuename(e, 'd'));
100211935Snwhitehorn		tfd = dfopen(dfname, O_WRONLY|O_CREAT|O_TRUNC, FileMode, SFF_ANYFILE);
101154047Sgrehan		if (tfd < 0 || (tf = fdopen(tfd, "w")) == NULL)
102211935Snwhitehorn		{
103101320Sjake			syserr("Cannot create %s", dfname);
104144177Salfred			e->e_flags |= EF_NO_BODY_RETN;
105101320Sjake			finis(TRUE, ExitStat);
106188628Simp		}
107188628Simp		if (fstat(fileno(tf), &stbuf) < 0)
108188628Simp			e->e_dfino = -1;
109188628Simp		else
110188628Simp		{
111144177Salfred			e->e_dfdev = stbuf.st_dev;
11231567Ssef			e->e_dfino = stbuf.st_ino;
11331567Ssef		}
11431567Ssef		HasEightBits = FALSE;
11531567Ssef		e->e_msgsize = 0;
116240005Szont		e->e_flags |= EF_HAS_DF;
11731567Ssef	}
11831567Ssef
11931567Ssef	/*
120144177Salfred	**  Tell ARPANET to go ahead.
121144177Salfred	*/
122144177Salfred
123240005Szont	if (smtpmode)
124240005Szont		message("354 Enter mail, end with \".\" on a line by itself");
125240005Szont
126144177Salfred	if (tTd(30, 2))
12731567Ssef		printf("collect\n");
128240005Szont
129168569Sdelphij	/*
130168569Sdelphij	**  Read the message.
131168569Sdelphij	**
132168569Sdelphij	**	This is done using two interleaved state machines.
133168569Sdelphij	**	The input state machine is looking for things like
134168569Sdelphij	**	hidden dots; the message state machine is handling
135168569Sdelphij	**	the larger picture (e.g., header versus body).
13631567Ssef	*/
137144177Salfred
138240005Szont	buf = bp = bufbuf;
139144177Salfred	buflen = sizeof bufbuf;
14031567Ssef	pbp = peekbuf;
141144177Salfred	istate = IS_BOL;
142144177Salfred	mstate = SaveFrom ? MS_HEADER : MS_UFROM;
143144177Salfred	CollectProgress = FALSE;
144144177Salfred
145144177Salfred	if (dbto != 0)
146144177Salfred	{
14731567Ssef		/* handle possible input timeout */
14831567Ssef		if (setjmp(CtxCollectTimeout) != 0)
149132306Salfred		{
150132306Salfred			if (LogLevel > 2)
151132306Salfred				sm_syslog(LOG_NOTICE, e->e_id,
152132306Salfred				    "timeout waiting for input from %s during message collect",
153132306Salfred				    CurHostName ? CurHostName : "<local machine>");
154132306Salfred			errno = 0;
155132306Salfred			usrerr("451 timeout waiting for input during message collect");
156218285Sjilles			goto readerr;
157132306Salfred		}
158132306Salfred		CollectTimeout = setevent(dbto, collecttimeout, dbto);
159132306Salfred	}
160132306Salfred
161132306Salfred	for (;;)
162132306Salfred	{
16332275Scharnier		if (tTd(30, 35))
164144177Salfred			printf("top, istate=%d, mstate=%d\n", istate, mstate);
165144177Salfred		for (;;)
166144177Salfred		{
167240005Szont			if (pbp > peekbuf)
168144178Salfred				c = *--pbp;
169144177Salfred			else
170240005Szont			{
171240005Szont				while (!feof(fp) && !ferror(fp))
172240005Szont				{
17331567Ssef					errno = 0;
174144178Salfred					c = getc(fp);
175144178Salfred					if (errno != EINTR)
176144178Salfred						break;
177144177Salfred					clearerr(fp);
178192153Sdelphij				}
179144177Salfred				CollectProgress = TRUE;
180192153Sdelphij				if (TrafficLogFile != NULL && !headeronly)
181192153Sdelphij				{
182144177Salfred					if (istate == IS_BOL)
183153963Sbrian						fprintf(TrafficLogFile, "%05d <<< ",
184168569Sdelphij							(int) getpid());
185168569Sdelphij					if (c == EOF)
186168569Sdelphij						fprintf(TrafficLogFile, "[EOF]\n");
187192025Sdds					else
188144177Salfred						putc(c, TrafficLogFile);
189144177Salfred				}
190144177Salfred				if (c == EOF)
191171055Sdelphij					goto readerr;
192240005Szont				if (SevenBitInput)
193171055Sdelphij					c &= 0x7f;
194171055Sdelphij				else
195171055Sdelphij					HasEightBits |= bitset(0x80, c);
196144177Salfred			}
197144177Salfred			if (tTd(30, 94))
198144177Salfred				printf("istate=%d, c=%c (0x%x)\n",
199144177Salfred					istate, c, c);
200144177Salfred			switch (istate)
201144177Salfred			{
202144177Salfred			  case IS_BOL:
203192025Sdds				if (c == '.')
204192025Sdds				{
205192025Sdds					istate = IS_DOT;
206144177Salfred					continue;
207144177Salfred				}
208144177Salfred				break;
209144177Salfred
210144177Salfred			  case IS_DOT:
211144177Salfred				if (c == '\n' && !ignrdot &&
212144177Salfred				    !bitset(EF_NL_NOT_EOL, e->e_flags))
213144177Salfred					goto readerr;
214144177Salfred				else if (c == '\r' &&
215144177Salfred					 !bitset(EF_CRLF_NOT_EOL, e->e_flags))
216144177Salfred				{
217144177Salfred					istate = IS_DOTCR;
218153963Sbrian					continue;
219153963Sbrian				}
220153963Sbrian				else if (c != '.' ||
221240005Szont					 (OpMode != MD_SMTP &&
222144177Salfred					  OpMode != MD_DAEMON &&
223144177Salfred					  OpMode != MD_ARPAFTP))
224144177Salfred				{
225144177Salfred					*pbp++ = c;
226144177Salfred					c = '.';
227144177Salfred				}
22831567Ssef				break;
229144177Salfred
230144177Salfred			  case IS_DOTCR:
231144177Salfred				if (c == '\n' && !ignrdot)
232144177Salfred					goto readerr;
23331567Ssef				else
234144177Salfred				{
235144177Salfred					/* push back the ".\rx" */
236144177Salfred					*pbp++ = c;
237215235Sjh					*pbp++ = '\r';
238215235Sjh					c = '.';
239215235Sjh				}
240215235Sjh				break;
241215235Sjh
242215235Sjh			  case IS_CR:
243215235Sjh				if (c == '\n')
244144177Salfred					istate = IS_BOL;
24532275Scharnier				else
246144177Salfred				{
247144177Salfred					ungetc(c, fp);
248144177Salfred					c = '\r';
249144177Salfred					istate = IS_NORM;
250144177Salfred				}
251144177Salfred				goto bufferchar;
25231567Ssef			}
253144177Salfred
254144177Salfred			if (c == '\r' && !bitset(EF_CRLF_NOT_EOL, e->e_flags))
255144177Salfred			{
256144177Salfred				istate = IS_CR;
257144177Salfred				continue;
258144177Salfred			}
259144177Salfred			else if (c == '\n' && !bitset(EF_NL_NOT_EOL, e->e_flags))
260168569Sdelphij				istate = IS_BOL;
261144177Salfred			else
262144177Salfred				istate = IS_NORM;
263144177Salfred
264144177Salfredbufferchar:
26531567Ssef			if (!headeronly)
26631567Ssef				e->e_msgsize++;
267144177Salfred			switch (mstate)
268144177Salfred			{
269144177Salfred			  case MS_BODY:
270144177Salfred				/* just put the character out */
27131567Ssef				if (MaxMessageSize <= 0 ||
272101283Smdodd				    e->e_msgsize <= MaxMessageSize)
273168569Sdelphij					putc(c, tf);
274168569Sdelphij
275144178Salfred				/* fall through */
276144177Salfred
277144177Salfred			  case MS_DISCARD:
278144177Salfred				continue;
279144177Salfred			}
280144177Salfred
28131567Ssef			/* header -- buffer up */
282144177Salfred			if (bp >= &buf[buflen - 2])
283101285Smdodd			{
284144177Salfred				char *obuf;
285158630Spav
286168569Sdelphij				if (mstate != MS_HEADER)
28731567Ssef					break;
288240005Szont
289168569Sdelphij				/* out of space for header */
290168569Sdelphij				obuf = buf;
291168569Sdelphij				if (buflen < MEMCHUNKSIZE)
292168569Sdelphij					buflen *= 2;
293168569Sdelphij				else
294168569Sdelphij					buflen += MEMCHUNKSIZE;
295168569Sdelphij				buf = xalloc(buflen);
296168569Sdelphij				bcopy(obuf, buf, bp - obuf);
29731567Ssef				bp = &buf[bp - obuf];
298168569Sdelphij				if (obuf != bufbuf)
299168569Sdelphij					free(obuf);
300168569Sdelphij			}
301240005Szont			if (c >= 0200 && c <= 0237)
302240005Szont			{
303101283Smdodd#if 0	/* causes complaints -- figure out something for 8.9 */
304168569Sdelphij				usrerr("Illegal character 0x%x in header", c);
305168569Sdelphij#endif
306168569Sdelphij			}
307168569Sdelphij			else if (c != '\0')
308168569Sdelphij			{
309168569Sdelphij				*bp++ = c;
310168569Sdelphij				if (MaxHeadersLength > 0 &&
311168569Sdelphij				    ++hdrslen > MaxHeadersLength)
312168569Sdelphij				{
313144177Salfred					sm_syslog(LOG_NOTICE, e->e_id,
314144177Salfred						  "headers too large (%d max) from %s during message collect",
315168569Sdelphij						  MaxHeadersLength,
316168569Sdelphij						  CurHostName != NULL ? CurHostName : "localhost");
317168569Sdelphij					errno = 0;
318168569Sdelphij					e->e_flags |= EF_CLRQUEUE;
319168569Sdelphij					e->e_status = "5.6.0";
320144177Salfred					usrerr("552 Headers too large (%d max)",
321168569Sdelphij						MaxHeadersLength);
322168569Sdelphij					mstate = MS_DISCARD;
323168569Sdelphij				}
324168569Sdelphij			}
325168569Sdelphij			if (istate == IS_BOL)
326168569Sdelphij				break;
327168569Sdelphij		}
328168569Sdelphij		*bp = '\0';
329168569Sdelphij
330144177Salfrednextstate:
331168569Sdelphij		if (tTd(30, 35))
332168569Sdelphij			printf("nextstate, istate=%d, mstate=%d, line = \"%s\"\n",
333168569Sdelphij				istate, mstate, buf);
334168569Sdelphij		switch (mstate)
335168569Sdelphij		{
336168569Sdelphij		  case MS_UFROM:
337168569Sdelphij			mstate = MS_HEADER;
338168569Sdelphij#ifndef NOTUNIX
339168569Sdelphij			if (strncmp(buf, "From ", 5) == 0)
340168569Sdelphij			{
341168569Sdelphij				extern void eatfrom __P((char *volatile, ENVELOPE *));
342168569Sdelphij
343168569Sdelphij				bp = buf;
344168569Sdelphij				eatfrom(buf, e);
345192025Sdds				continue;
346192025Sdds			}
347168569Sdelphij#endif
348168569Sdelphij			/* fall through */
349168569Sdelphij
350168569Sdelphij		  case MS_HEADER:
351168569Sdelphij			if (!isheader(buf))
352168569Sdelphij			{
353168569Sdelphij				mstate = MS_BODY;
354168569Sdelphij				goto nextstate;
355168569Sdelphij			}
356168569Sdelphij
357168569Sdelphij			/* check for possible continuation line */
358240005Szont			do
359240005Szont			{
360240005Szont				clearerr(fp);
361240005Szont				errno = 0;
362168569Sdelphij				c = getc(fp);
363168569Sdelphij			} while (errno == EINTR);
364168569Sdelphij			if (c != EOF)
365168569Sdelphij				ungetc(c, fp);
366168569Sdelphij			if (c == ' ' || c == '\t')
367168569Sdelphij			{
368144177Salfred				/* yep -- defer this */
369168569Sdelphij				continue;
370191005Sdelphij			}
371240005Szont
372191005Sdelphij			/* trim off trailing CRLF or NL */
373191005Sdelphij			if (*--bp != '\n' || *--bp != '\r')
374191005Sdelphij				bp++;
375240005Szont			*bp = '\0';
376191005Sdelphij
377240005Szont			if (bitset(H_EOH, chompheader(buf, FALSE, hdrp, e)))
378240005Szont			{
379192025Sdds				mstate = MS_BODY;
380192025Sdds				goto nextstate;
381192025Sdds			}
382144177Salfred			break;
38331567Ssef
384		  case MS_BODY:
385			if (tTd(30, 1))
386				printf("EOH\n");
387			if (headeronly)
388				goto readerr;
389			bp = buf;
390
391			/* toss blank line */
392			if ((!bitset(EF_CRLF_NOT_EOL, e->e_flags) &&
393				bp[0] == '\r' && bp[1] == '\n') ||
394			    (!bitset(EF_NL_NOT_EOL, e->e_flags) &&
395				bp[0] == '\n'))
396			{
397				break;
398			}
399
400			/* if not a blank separator, write it out */
401			if (MaxMessageSize <= 0 ||
402			    e->e_msgsize <= MaxMessageSize)
403			{
404				while (*bp != '\0')
405					putc(*bp++, tf);
406			}
407			break;
408		}
409		bp = buf;
410	}
411
412readerr:
413	if ((feof(fp) && smtpmode) || ferror(fp))
414	{
415		const char *errmsg = errstring(errno);
416
417		if (tTd(30, 1))
418			printf("collect: premature EOM: %s\n", errmsg);
419		if (LogLevel >= 2)
420			sm_syslog(LOG_WARNING, e->e_id,
421				"collect: premature EOM: %s", errmsg);
422		inputerr = TRUE;
423	}
424
425	/* reset global timer */
426	clrevent(CollectTimeout);
427
428	if (headeronly)
429		return;
430
431	if (tf != NULL &&
432	    (fflush(tf) != 0 || ferror(tf) ||
433	     (SuperSafe && fsync(fileno(tf)) < 0) ||
434	     fclose(tf) < 0))
435	{
436		tferror(tf, e);
437		flush_errors(TRUE);
438		finis(TRUE, ExitStat);
439	}
440
441	/* An EOF when running SMTP is an error */
442	if (inputerr && (OpMode == MD_SMTP || OpMode == MD_DAEMON))
443	{
444		char *host;
445		char *problem;
446
447		host = RealHostName;
448		if (host == NULL)
449			host = "localhost";
450
451		if (feof(fp))
452			problem = "unexpected close";
453		else if (ferror(fp))
454			problem = "I/O error";
455		else
456			problem = "read timeout";
457		if (LogLevel > 0 && feof(fp))
458			sm_syslog(LOG_NOTICE, e->e_id,
459			    "collect: %s on connection from %.100s, sender=%s: %s",
460			    problem, host,
461			    shortenstring(e->e_from.q_paddr, MAXSHORTSTR),
462			    errstring(errno));
463		if (feof(fp))
464			usrerr("451 collect: %s on connection from %s, from=%s",
465				problem, host,
466				shortenstring(e->e_from.q_paddr, MAXSHORTSTR));
467		else
468			syserr("451 collect: %s on connection from %s, from=%s",
469				problem, host,
470				shortenstring(e->e_from.q_paddr, MAXSHORTSTR));
471
472		/* don't return an error indication */
473		e->e_to = NULL;
474		e->e_flags &= ~EF_FATALERRS;
475		e->e_flags |= EF_CLRQUEUE;
476
477		/* and don't try to deliver the partial message either */
478		if (InChild)
479			ExitStat = EX_QUIT;
480		finis(TRUE, ExitStat);
481	}
482
483	/*
484	**  Find out some information from the headers.
485	**	Examples are who is the from person & the date.
486	*/
487
488	eatheader(e, TRUE);
489
490	if (GrabTo && e->e_sendqueue == NULL)
491		usrerr("No recipient addresses found in header");
492
493	/* collect statistics */
494	if (OpMode != MD_VERIFY)
495		markstats(e, (ADDRESS *) NULL, FALSE);
496
497#if _FFR_DSN_RRT_OPTION
498	/*
499	**  If we have a Return-Receipt-To:, turn it into a DSN.
500	*/
501
502	if (RrtImpliesDsn && hvalue("return-receipt-to", e->e_header) != NULL)
503	{
504		ADDRESS *q;
505
506		for (q = e->e_sendqueue; q != NULL; q = q->q_next)
507			if (!bitset(QHASNOTIFY, q->q_flags))
508				q->q_flags |= QHASNOTIFY|QPINGONSUCCESS;
509	}
510#endif
511
512	/*
513	**  Add an Apparently-To: line if we have no recipient lines.
514	*/
515
516	if (hvalue("to", e->e_header) != NULL ||
517	    hvalue("cc", e->e_header) != NULL ||
518	    hvalue("apparently-to", e->e_header) != NULL)
519	{
520		/* have a valid recipient header -- delete Bcc: headers */
521		e->e_flags |= EF_DELETE_BCC;
522	}
523	else if (hvalue("bcc", e->e_header) == NULL)
524	{
525		/* no valid recipient headers */
526		register ADDRESS *q;
527		char *hdr = NULL;
528
529		/* create an Apparently-To: field */
530		/*    that or reject the message.... */
531		switch (NoRecipientAction)
532		{
533		  case NRA_ADD_APPARENTLY_TO:
534			hdr = "Apparently-To";
535			break;
536
537		  case NRA_ADD_TO:
538			hdr = "To";
539			break;
540
541		  case NRA_ADD_BCC:
542			addheader("Bcc", " ", &e->e_header);
543			break;
544
545		  case NRA_ADD_TO_UNDISCLOSED:
546			addheader("To", "undisclosed-recipients:;", &e->e_header);
547			break;
548		}
549
550		if (hdr != NULL)
551		{
552			for (q = e->e_sendqueue; q != NULL; q = q->q_next)
553			{
554				if (q->q_alias != NULL)
555					continue;
556				if (tTd(30, 3))
557					printf("Adding %s: %s\n",
558						hdr, q->q_paddr);
559				addheader(hdr, q->q_paddr, &e->e_header);
560			}
561		}
562	}
563
564	/* check for message too large */
565	if (MaxMessageSize > 0 && e->e_msgsize > MaxMessageSize)
566	{
567		e->e_flags |= EF_NO_BODY_RETN|EF_CLRQUEUE;
568		e->e_status = "5.2.3";
569		usrerr("552 Message exceeds maximum fixed size (%ld)",
570			MaxMessageSize);
571		if (LogLevel > 6)
572			sm_syslog(LOG_NOTICE, e->e_id,
573				"message size (%ld) exceeds maximum (%ld)",
574				e->e_msgsize, MaxMessageSize);
575	}
576
577	/* check for illegal 8-bit data */
578	if (HasEightBits)
579	{
580		e->e_flags |= EF_HAS8BIT;
581		if (!bitset(MM_PASS8BIT|MM_MIME8BIT, MimeMode) &&
582		    !bitset(EF_IS_MIME, e->e_flags))
583		{
584			e->e_status = "5.6.1";
585			usrerr("554 Eight bit data not allowed");
586		}
587	}
588	else
589	{
590		/* if it claimed to be 8 bits, well, it lied.... */
591		if (e->e_bodytype != NULL &&
592		    strcasecmp(e->e_bodytype, "8BITMIME") == 0)
593			e->e_bodytype = "7BIT";
594	}
595
596	if ((e->e_dfp = fopen(dfname, "r")) == NULL)
597	{
598		/* we haven't acked receipt yet, so just chuck this */
599		syserr("Cannot reopen %s", dfname);
600		finis(TRUE, ExitStat);
601	}
602}
603
604
605static void
606collecttimeout(timeout)
607	time_t timeout;
608{
609	/* if no progress was made, die now */
610	if (!CollectProgress)
611		longjmp(CtxCollectTimeout, 1);
612
613	/* otherwise reset the timeout */
614	CollectTimeout = setevent(timeout, collecttimeout, timeout);
615	CollectProgress = FALSE;
616}
617/*
618**  TFERROR -- signal error on writing the temporary file.
619**
620**	Parameters:
621**		tf -- the file pointer for the temporary file.
622**		e -- the current envelope.
623**
624**	Returns:
625**		none.
626**
627**	Side Effects:
628**		Gives an error message.
629**		Arranges for following output to go elsewhere.
630*/
631
632void
633tferror(tf, e)
634	FILE *volatile tf;
635	register ENVELOPE *e;
636{
637	setstat(EX_IOERR);
638	if (errno == ENOSPC)
639	{
640#if STAT64 > 0
641		struct stat64 st;
642#else
643		struct stat st;
644#endif
645		long avail;
646		long bsize;
647		extern long freediskspace __P((char *, long *));
648
649		e->e_flags |= EF_NO_BODY_RETN;
650
651		if (
652#if STAT64 > 0
653		    fstat64(fileno(tf), &st)
654#else
655		    fstat(fileno(tf), &st)
656#endif
657		    < 0)
658		  st.st_size = 0;
659		(void) freopen(queuename(e, 'd'), "w", tf);
660		if (st.st_size <= 0)
661			fprintf(tf, "\n*** Mail could not be accepted");
662		else if (sizeof st.st_size > sizeof (long))
663			fprintf(tf, "\n*** Mail of at least %s bytes could not be accepted\n",
664				quad_to_string(st.st_size));
665		else
666			fprintf(tf, "\n*** Mail of at least %lu bytes could not be accepted\n",
667				(unsigned long) st.st_size);
668		fprintf(tf, "*** at %s due to lack of disk space for temp file.\n",
669			MyHostName);
670		avail = freediskspace(QueueDir, &bsize);
671		if (avail > 0)
672		{
673			if (bsize > 1024)
674				avail *= bsize / 1024;
675			else if (bsize < 1024)
676				avail /= 1024 / bsize;
677			fprintf(tf, "*** Currently, %ld kilobytes are available for mail temp files.\n",
678				avail);
679		}
680		e->e_status = "4.3.1";
681		usrerr("452 Out of disk space for temp file");
682	}
683	else
684		syserr("collect: Cannot write tf%s", e->e_id);
685	if (freopen("/dev/null", "w", tf) == NULL)
686		sm_syslog(LOG_ERR, e->e_id,
687			  "tferror: freopen(\"/dev/null\") failed: %s",
688			  errstring(errno));
689}
690/*
691**  EATFROM -- chew up a UNIX style from line and process
692**
693**	This does indeed make some assumptions about the format
694**	of UNIX messages.
695**
696**	Parameters:
697**		fm -- the from line.
698**
699**	Returns:
700**		none.
701**
702**	Side Effects:
703**		extracts what information it can from the header,
704**		such as the date.
705*/
706
707# ifndef NOTUNIX
708
709char	*DowList[] =
710{
711	"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", NULL
712};
713
714char	*MonthList[] =
715{
716	"Jan", "Feb", "Mar", "Apr", "May", "Jun",
717	"Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
718	NULL
719};
720
721void
722eatfrom(fm, e)
723	char *volatile fm;
724	register ENVELOPE *e;
725{
726	register char *p;
727	register char **dt;
728
729	if (tTd(30, 2))
730		printf("eatfrom(%s)\n", fm);
731
732	/* find the date part */
733	p = fm;
734	while (*p != '\0')
735	{
736		/* skip a word */
737		while (*p != '\0' && *p != ' ')
738			p++;
739		while (*p == ' ')
740			p++;
741		if (!(isascii(*p) && isupper(*p)) ||
742		    p[3] != ' ' || p[13] != ':' || p[16] != ':')
743			continue;
744
745		/* we have a possible date */
746		for (dt = DowList; *dt != NULL; dt++)
747			if (strncmp(*dt, p, 3) == 0)
748				break;
749		if (*dt == NULL)
750			continue;
751
752		for (dt = MonthList; *dt != NULL; dt++)
753			if (strncmp(*dt, &p[4], 3) == 0)
754				break;
755		if (*dt != NULL)
756			break;
757	}
758
759	if (*p != '\0')
760	{
761		char *q;
762
763		/* we have found a date */
764		q = xalloc(25);
765		(void) strncpy(q, p, 25);
766		q[24] = '\0';
767		q = arpadate(q);
768		define('a', newstr(q), e);
769	}
770}
771
772# endif /* NOTUNIX */
773