1 /*
2  * Copyright (c) 1983 Eric P. Allman
3  * Copyright (c) 1988, 1993
4  *	The Regents of the University of California.  All rights reserved.
5  *
6  * %sccs.include.redist.c%
7  */
8 
9 #ifndef lint
10 static char sccsid[] = "@(#)collect.c	8.18 (Berkeley) 07/23/94";
11 #endif /* not lint */
12 
13 # include <errno.h>
14 # include "sendmail.h"
15 
16 /*
17 **  COLLECT -- read & parse message header & make temp file.
18 **
19 **	Creates a temporary file name and copies the standard
20 **	input to that file.  Leading UNIX-style "From" lines are
21 **	stripped off (after important information is extracted).
22 **
23 **	Parameters:
24 **		fp -- file to read.
25 **		smtpmode -- if set, we are running SMTP: give an RFC821
26 **			style message to say we are ready to collect
27 **			input, and never ignore a single dot to mean
28 **			end of message.
29 **		requeueflag -- this message will be requeued later, so
30 **			don't do final processing on it.
31 **		hdrp -- the location to stash the header.
32 **		e -- the current envelope.
33 **
34 **	Returns:
35 **		none.
36 **
37 **	Side Effects:
38 **		Temp file is created and filled.
39 **		The from person may be set.
40 */
41 
42 char	*CollectErrorMessage;
43 bool	CollectErrno;
44 
45 collect(fp, smtpmode, requeueflag, hdrp, e)
46 	FILE *fp;
47 	bool smtpmode;
48 	bool requeueflag;
49 	HDR **hdrp;
50 	register ENVELOPE *e;
51 {
52 	register FILE *tf;
53 	bool ignrdot = smtpmode ? FALSE : IgnrDot;
54 	time_t dbto = smtpmode ? TimeOuts.to_datablock : 0;
55 	register char *workbuf, *freebuf;
56 	bool inputerr = FALSE;
57 	bool headeronly = FALSE;
58 	char buf[MAXLINE], buf2[MAXLINE];
59 	extern char *hvalue();
60 	extern bool isheader(), flusheol();
61 
62 	CollectErrorMessage = NULL;
63 	CollectErrno = 0;
64 	if (hdrp == NULL)
65 		hdrp = &e->e_header;
66 	else
67 		headeronly = TRUE;
68 
69 	/*
70 	**  Create the temp file name and create the file.
71 	*/
72 
73 	if (!headeronly)
74 	{
75 		e->e_df = queuename(e, 'd');
76 		e->e_df = newstr(e->e_df);
77 		if ((tf = dfopen(e->e_df, O_WRONLY|O_CREAT|O_TRUNC, FileMode)) == NULL)
78 		{
79 			syserr("Cannot create %s", e->e_df);
80 			e->e_flags |= EF_NORETURN;
81 			finis();
82 		}
83 		HasEightBits = FALSE;
84 	}
85 
86 	/*
87 	**  Tell ARPANET to go ahead.
88 	*/
89 
90 	if (smtpmode)
91 		message("354 Enter mail, end with \".\" on a line by itself");
92 
93 	/* set global timer to monitor progress */
94 	sfgetset(dbto);
95 
96 	/*
97 	**  Try to read a UNIX-style From line
98 	*/
99 
100 	if (sfgets(buf, MAXLINE, fp, dbto, "initial message read") == NULL)
101 		goto readerr;
102 	fixcrlf(buf, FALSE);
103 # ifndef NOTUNIX
104 	if (!headeronly && !SaveFrom && strncmp(buf, "From ", 5) == 0)
105 	{
106 		if (!flusheol(buf, fp, dbto))
107 			goto readerr;
108 		eatfrom(buf, e);
109 		if (sfgets(buf, MAXLINE, fp, dbto,
110 				"message header read") == NULL)
111 			goto readerr;
112 		fixcrlf(buf, FALSE);
113 	}
114 # endif /* NOTUNIX */
115 
116 	/*
117 	**  Copy fp to temp file & do message editing.
118 	**	To keep certain mailers from getting confused,
119 	**	and to keep the output clean, lines that look
120 	**	like UNIX "From" lines are deleted in the header.
121 	*/
122 
123 	workbuf = buf;		/* `workbuf' contains a header field */
124 	freebuf = buf2;		/* `freebuf' can be used for read-ahead */
125 	for (;;)
126 	{
127 		char *curbuf;
128 		int curbuffree;
129 		register int curbuflen;
130 		char *p;
131 
132 		/* first, see if the header is over */
133 		if (!isheader(workbuf))
134 		{
135 			fixcrlf(workbuf, TRUE);
136 			break;
137 		}
138 
139 		/* if the line is too long, throw the rest away */
140 		if (!flusheol(workbuf, fp, dbto))
141 			goto readerr;
142 
143 		/* it's okay to toss '\n' now (flusheol() needed it) */
144 		fixcrlf(workbuf, TRUE);
145 
146 		curbuf = workbuf;
147 		curbuflen = strlen(curbuf);
148 		curbuffree = MAXLINE - curbuflen;
149 		p = curbuf + curbuflen;
150 
151 		/* get the rest of this field */
152 		for (;;)
153 		{
154 			int clen;
155 
156 			if (sfgets(freebuf, MAXLINE, fp, dbto,
157 					"message header read") == NULL)
158 			{
159 				freebuf[0] = '\0';
160 				break;
161 			}
162 
163 			/* is this a continuation line? */
164 			if (*freebuf != ' ' && *freebuf != '\t')
165 				break;
166 
167 			if (!flusheol(freebuf, fp, dbto))
168 				goto readerr;
169 
170 			fixcrlf(freebuf, TRUE);
171 			clen = strlen(freebuf) + 1;
172 
173 			/* if insufficient room, dynamically allocate buffer */
174 			if (clen >= curbuffree)
175 			{
176 				/* reallocate buffer */
177 				int nbuflen = ((p - curbuf) + clen) * 2;
178 				char *nbuf = xalloc(nbuflen);
179 
180 				p = nbuf + curbuflen;
181 				curbuffree = nbuflen - curbuflen;
182 				bcopy(curbuf, nbuf, curbuflen);
183 				if (curbuf != buf && curbuf != buf2)
184 					free(curbuf);
185 				curbuf = nbuf;
186 			}
187 			*p++ = '\n';
188 			bcopy(freebuf, p, clen - 1);
189 			p += clen - 1;
190 			curbuffree -= clen;
191 			curbuflen += clen;
192 		}
193 		*p++ = '\0';
194 
195 		e->e_msgsize += curbuflen;
196 
197 		/*
198 		**  The working buffer now becomes the free buffer, since
199 		**  the free buffer contains a new header field.
200 		**
201 		**  This is premature, since we still havent called
202 		**  chompheader() to process the field we just created
203 		**  (so the call to chompheader() will use `freebuf').
204 		**  This convolution is necessary so that if we break out
205 		**  of the loop due to H_EOH, `workbuf' will always be
206 		**  the next unprocessed buffer.
207 		*/
208 
209 		{
210 			register char *tmp = workbuf;
211 			workbuf = freebuf;
212 			freebuf = tmp;
213 		}
214 
215 		/*
216 		**  Snarf header away.
217 		*/
218 
219 		if (bitset(H_EOH, chompheader(curbuf, FALSE, e)))
220 			break;
221 
222 		/*
223 		**  If the buffer was dynamically allocated, free it.
224 		*/
225 
226 		if (curbuf != buf && curbuf != buf2)
227 			free(curbuf);
228 	}
229 
230 	if (tTd(30, 1))
231 		printf("EOH\n");
232 
233 	if (headeronly)
234 	{
235 		if (*workbuf != '\0')
236 			syserr("collect: lost first line of message");
237 		goto readerr;
238 	}
239 
240 	if (*workbuf == '\0')
241 	{
242 		/* throw away a blank line */
243 		if (sfgets(buf, MAXLINE, fp, dbto,
244 				"message separator read") == NULL)
245 			goto readerr;
246 	}
247 	else if (workbuf == buf2)	/* guarantee `buf' contains data */
248 		(void) strcpy(buf, buf2);
249 
250 	/*
251 	**  Collect the body of the message.
252 	*/
253 
254 	for (;;)
255 	{
256 		register char *bp = buf;
257 
258 		fixcrlf(buf, TRUE);
259 
260 		/* check for end-of-message */
261 		if (!ignrdot && buf[0] == '.' && (buf[1] == '\n' || buf[1] == '\0'))
262 			break;
263 
264 		/* check for transparent dot */
265 		if ((OpMode == MD_SMTP || OpMode == MD_DAEMON) &&
266 		    bp[0] == '.' && bp[1] == '.')
267 			bp++;
268 
269 		/*
270 		**  Figure message length, output the line to the temp
271 		**  file, and insert a newline if missing.
272 		*/
273 
274 		e->e_msgsize += strlen(bp) + 1;
275 		fputs(bp, tf);
276 		fputs("\n", tf);
277 		if (ferror(tf))
278 			tferror(tf, e);
279 		if (sfgets(buf, MAXLINE, fp, dbto, "message body read") == NULL)
280 			goto readerr;
281 	}
282 
283 readerr:
284 	if ((feof(fp) && smtpmode) || ferror(fp))
285 	{
286 		if (tTd(30, 1))
287 			printf("collect: read error\n");
288 		inputerr = TRUE;
289 	}
290 
291 	/* reset global timer */
292 	sfgetset((time_t) 0);
293 
294 	if (headeronly)
295 		return;
296 
297 	if (tf != NULL)
298 	{
299 		if (fflush(tf) != 0)
300 			tferror(tf, e);
301 		if (fsync(fileno(tf)) < 0 || fclose(tf) < 0)
302 		{
303 			tferror(tf, e);
304 			finis();
305 		}
306 	}
307 
308 	if (CollectErrorMessage != NULL && Errors <= 0)
309 	{
310 		if (CollectErrno != 0)
311 		{
312 			errno = CollectErrno;
313 			syserr(CollectErrorMessage, e->e_df);
314 			finis();
315 		}
316 		usrerr(CollectErrorMessage);
317 	}
318 	else if (inputerr && (OpMode == MD_SMTP || OpMode == MD_DAEMON))
319 	{
320 		/* An EOF when running SMTP is an error */
321 		char *host;
322 		char *problem;
323 
324 		host = RealHostName;
325 		if (host == NULL)
326 			host = "localhost";
327 
328 		if (feof(fp))
329 			problem = "unexpected close";
330 		else if (ferror(fp))
331 			problem = "I/O error";
332 		else
333 			problem = "read timeout";
334 # ifdef LOG
335 		if (LogLevel > 0 && feof(fp))
336 			syslog(LOG_NOTICE,
337 			    "collect: %s on connection from %s, sender=%s: %s\n",
338 			    problem, host, e->e_from.q_paddr, errstring(errno));
339 # endif
340 		if (feof(fp))
341 			usrerr("451 collect: %s on connection from %s, from=%s",
342 				problem, host, e->e_from.q_paddr);
343 		else
344 			syserr("451 collect: %s on connection from %s, from=%s",
345 				problem, host, e->e_from.q_paddr);
346 
347 		/* don't return an error indication */
348 		e->e_to = NULL;
349 		e->e_flags &= ~EF_FATALERRS;
350 		e->e_flags |= EF_CLRQUEUE;
351 
352 		/* and don't try to deliver the partial message either */
353 		if (InChild)
354 			ExitStat = EX_QUIT;
355 		finis();
356 	}
357 
358 	/*
359 	**  Find out some information from the headers.
360 	**	Examples are who is the from person & the date.
361 	*/
362 
363 	eatheader(e, !requeueflag);
364 
365 	/* collect statistics */
366 	if (OpMode != MD_VERIFY)
367 		markstats(e, (ADDRESS *) NULL);
368 
369 	/*
370 	**  Add an Apparently-To: line if we have no recipient lines.
371 	*/
372 
373 	if (hvalue("to", e->e_header) == NULL &&
374 	    hvalue("cc", e->e_header) == NULL &&
375 	    hvalue("bcc", e->e_header) == NULL &&
376 	    hvalue("apparently-to", e->e_header) == NULL)
377 	{
378 		register ADDRESS *q;
379 
380 		/* create an Apparently-To: field */
381 		/*    that or reject the message.... */
382 		for (q = e->e_sendqueue; q != NULL; q = q->q_next)
383 		{
384 			if (q->q_alias != NULL)
385 				continue;
386 			if (tTd(30, 3))
387 				printf("Adding Apparently-To: %s\n", q->q_paddr);
388 			addheader("Apparently-To", q->q_paddr, &e->e_header);
389 		}
390 	}
391 
392 	/* check for message too large */
393 	if (MaxMessageSize > 0 && e->e_msgsize > MaxMessageSize)
394 	{
395 		usrerr("552 Message exceeds maximum fixed size (%ld)",
396 			MaxMessageSize);
397 	}
398 
399 	/* check for illegal 8-bit data */
400 	if (HasEightBits)
401 	{
402 		e->e_flags |= EF_HAS8BIT;
403 		if (bitset(MM_MIME8BIT, MimeMode))
404 		{
405 			/* convert it to MIME */
406 			if (hvalue("MIME-Version", e->e_header) == NULL)
407 			{
408 				char mimebuf[20];
409 
410 				strcpy(mimebuf, "MIME-Version: 1.0");
411 				chompheader(mimebuf, FALSE, e);
412 			}
413 			if (e->e_bodytype == NULL)
414 				e->e_bodytype = "8BITMIME";
415 		}
416 		else if (!bitset(MM_PASS8BIT, MimeMode))
417 			usrerr("554 Eight bit data not allowed");
418 	}
419 
420 	if ((e->e_dfp = fopen(e->e_df, "r")) == NULL)
421 	{
422 		/* we haven't acked receipt yet, so just chuck this */
423 		syserr("Cannot reopen %s", e->e_df);
424 		finis();
425 	}
426 }
427 /*
428 **  FLUSHEOL -- if not at EOL, throw away rest of input line.
429 **
430 **	Parameters:
431 **		buf -- last line read in (checked for '\n'),
432 **		fp -- file to be read from.
433 **
434 **	Returns:
435 **		FALSE on error from sfgets(), TRUE otherwise.
436 **
437 **	Side Effects:
438 **		none.
439 */
440 
441 bool
442 flusheol(buf, fp, dbto)
443 	char *buf;
444 	FILE *fp;
445 	time_t dbto;
446 {
447 	register char *p = buf;
448 	char junkbuf[MAXLINE];
449 
450 	while (strchr(p, '\n') == NULL)
451 	{
452 		CollectErrorMessage = "553 header line too long";
453 		CollectErrno = 0;
454 		if (sfgets(junkbuf, MAXLINE, fp, dbto,
455 				"long line flush") == NULL)
456 			return (FALSE);
457 		p = junkbuf;
458 	}
459 
460 	return (TRUE);
461 }
462 /*
463 **  TFERROR -- signal error on writing the temporary file.
464 **
465 **	Parameters:
466 **		tf -- the file pointer for the temporary file.
467 **
468 **	Returns:
469 **		none.
470 **
471 **	Side Effects:
472 **		Gives an error message.
473 **		Arranges for following output to go elsewhere.
474 */
475 
476 tferror(tf, e)
477 	FILE *tf;
478 	register ENVELOPE *e;
479 {
480 	CollectErrno = errno;
481 	if (errno == ENOSPC)
482 	{
483 		struct stat st;
484 		long avail;
485 		long bsize;
486 
487 		e->e_flags |= EF_NORETURN;
488 		if (fstat(fileno(tf), &st) < 0)
489 			st.st_size = 0;
490 		(void) freopen(e->e_df, "w", tf);
491 		if (st.st_size <= 0)
492 			fprintf(tf, "\n*** Mail could not be accepted");
493 		else if (sizeof st.st_size > sizeof (long))
494 			fprintf(tf, "\n*** Mail of at least %qd bytes could not be accepted\n",
495 				st.st_size);
496 		else
497 			fprintf(tf, "\n*** Mail of at least %ld bytes could not be accepted\n",
498 				st.st_size);
499 		fprintf(tf, "*** at %s due to lack of disk space for temp file.\n",
500 			MyHostName);
501 		avail = freespace(QueueDir, &bsize);
502 		if (avail > 0)
503 		{
504 			if (bsize > 1024)
505 				avail *= bsize / 1024;
506 			else if (bsize < 1024)
507 				avail /= 1024 / bsize;
508 			fprintf(tf, "*** Currently, %ld kilobytes are available for mail temp files.\n",
509 				avail);
510 		}
511 		CollectErrorMessage = "452 Out of disk space for temp file";
512 	}
513 	else
514 	{
515 		CollectErrorMessage = "cannot write message body to disk (%s)";
516 	}
517 	(void) freopen("/dev/null", "w", tf);
518 }
519 /*
520 **  EATFROM -- chew up a UNIX style from line and process
521 **
522 **	This does indeed make some assumptions about the format
523 **	of UNIX messages.
524 **
525 **	Parameters:
526 **		fm -- the from line.
527 **
528 **	Returns:
529 **		none.
530 **
531 **	Side Effects:
532 **		extracts what information it can from the header,
533 **		such as the date.
534 */
535 
536 # ifndef NOTUNIX
537 
538 char	*DowList[] =
539 {
540 	"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", NULL
541 };
542 
543 char	*MonthList[] =
544 {
545 	"Jan", "Feb", "Mar", "Apr", "May", "Jun",
546 	"Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
547 	NULL
548 };
549 
550 eatfrom(fm, e)
551 	char *fm;
552 	register ENVELOPE *e;
553 {
554 	register char *p;
555 	register char **dt;
556 
557 	if (tTd(30, 2))
558 		printf("eatfrom(%s)\n", fm);
559 
560 	/* find the date part */
561 	p = fm;
562 	while (*p != '\0')
563 	{
564 		/* skip a word */
565 		while (*p != '\0' && *p != ' ')
566 			p++;
567 		while (*p == ' ')
568 			p++;
569 		if (!(isascii(*p) && isupper(*p)) ||
570 		    p[3] != ' ' || p[13] != ':' || p[16] != ':')
571 			continue;
572 
573 		/* we have a possible date */
574 		for (dt = DowList; *dt != NULL; dt++)
575 			if (strncmp(*dt, p, 3) == 0)
576 				break;
577 		if (*dt == NULL)
578 			continue;
579 
580 		for (dt = MonthList; *dt != NULL; dt++)
581 			if (strncmp(*dt, &p[4], 3) == 0)
582 				break;
583 		if (*dt != NULL)
584 			break;
585 	}
586 
587 	if (*p != '\0')
588 	{
589 		char *q;
590 		extern char *arpadate();
591 
592 		/* we have found a date */
593 		q = xalloc(25);
594 		(void) strncpy(q, p, 25);
595 		q[24] = '\0';
596 		q = arpadate(q);
597 		define('a', newstr(q), e);
598 	}
599 }
600 
601 # endif /* NOTUNIX */
602