2 * Copyright (c) 1998 Sendmail, Inc. All rights reserved.
3 * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.
4 * Copyright (c) 1988, 1993
5 * The Regents of the University of California. All rights reserved.
7 * By using this file, you agree to the terms and conditions set
8 * forth in the LICENSE file which can be found at the top level of
9 * the sendmail distribution.
14 static char sccsid[] = "@(#)collect.c 8.93 (Berkeley) 1/26/1999";
18 # include "sendmail.h"
21 ** COLLECT -- read & parse message header & make temp file.
23 ** Creates a temporary file name and copies the standard
24 ** input to that file. Leading UNIX-style "From" lines are
25 ** stripped off (after important information is extracted).
28 ** fp -- file to read.
29 ** smtpmode -- if set, we are running SMTP: give an RFC821
30 ** style message to say we are ready to collect
31 ** input, and never ignore a single dot to mean
33 ** hdrp -- the location to stash the header.
34 ** e -- the current envelope.
40 ** Temp file is created and filled.
41 ** The from person may be set.
44 static jmp_buf CtxCollectTimeout;
45 static void collecttimeout __P((time_t));
46 static bool CollectProgress;
47 static EVENT *CollectTimeout;
49 /* values for input state machine */
50 #define IS_NORM 0 /* middle of line */
51 #define IS_BOL 1 /* beginning of line */
52 #define IS_DOT 2 /* read a dot at beginning of line */
53 #define IS_DOTCR 3 /* read ".\r" at beginning of line */
54 #define IS_CR 4 /* read a carriage return */
56 /* values for message state machine */
57 #define MS_UFROM 0 /* reading Unix from line */
58 #define MS_HEADER 1 /* reading message header */
59 #define MS_BODY 2 /* reading message body */
60 #define MS_DISCARD 3 /* discarding rest of message */
63 collect(fp, smtpmode, hdrp, e)
69 register FILE *volatile tf;
70 volatile bool ignrdot = smtpmode ? FALSE : IgnrDot;
71 volatile time_t dbto = smtpmode ? TimeOuts.to_datablock : 0;
72 register char *volatile bp;
74 volatile bool inputerr = FALSE;
83 char dfname[MAXQFNAME];
85 extern bool isheader __P((char *));
86 extern void tferror __P((FILE *volatile, ENVELOPE *));
88 headeronly = hdrp != NULL;
91 ** Create the temp file name and create the file.
99 strcpy(dfname, queuename(e, 'd'));
100 tfd = dfopen(dfname, O_WRONLY|O_CREAT|O_TRUNC, FileMode, SFF_ANYFILE);
101 if (tfd < 0 || (tf = fdopen(tfd, "w")) == NULL)
103 syserr("Cannot create %s", dfname);
104 e->e_flags |= EF_NO_BODY_RETN;
105 finis(TRUE, ExitStat);
107 if (fstat(fileno(tf), &stbuf) < 0)
111 e->e_dfdev = stbuf.st_dev;
112 e->e_dfino = stbuf.st_ino;
114 HasEightBits = FALSE;
116 e->e_flags |= EF_HAS_DF;
120 ** Tell ARPANET to go ahead.
124 message("354 Enter mail, end with \".\" on a line by itself");
132 ** This is done using two interleaved state machines.
133 ** The input state machine is looking for things like
134 ** hidden dots; the message state machine is handling
135 ** the larger picture (e.g., header versus body).
139 buflen = sizeof bufbuf;
142 mstate = SaveFrom ? MS_HEADER : MS_UFROM;
143 CollectProgress = FALSE;
147 /* handle possible input timeout */
148 if (setjmp(CtxCollectTimeout) != 0)
151 sm_syslog(LOG_NOTICE, e->e_id,
152 "timeout waiting for input from %s during message collect",
153 CurHostName ? CurHostName : "<local machine>");
155 usrerr("451 timeout waiting for input during message collect");
158 CollectTimeout = setevent(dbto, collecttimeout, dbto);
164 printf("top, istate=%d, mstate=%d\n", istate, mstate);
171 while (!feof(fp) && !ferror(fp))
179 CollectProgress = TRUE;
180 if (TrafficLogFile != NULL && !headeronly)
182 if (istate == IS_BOL)
183 fprintf(TrafficLogFile, "%05d <<< ",
186 fprintf(TrafficLogFile, "[EOF]\n");
188 putc(c, TrafficLogFile);
195 HasEightBits |= bitset(0x80, c);
198 printf("istate=%d, c=%c (0x%x)\n",
211 if (c == '\n' && !ignrdot &&
212 !bitset(EF_NL_NOT_EOL, e->e_flags))
214 else if (c == '\r' &&
215 !bitset(EF_CRLF_NOT_EOL, e->e_flags))
221 (OpMode != MD_SMTP &&
222 OpMode != MD_DAEMON &&
223 OpMode != MD_ARPAFTP))
231 if (c == '\n' && !ignrdot)
235 /* push back the ".\rx" */
254 if (c == '\r' && !bitset(EF_CRLF_NOT_EOL, e->e_flags))
259 else if (c == '\n' && !bitset(EF_NL_NOT_EOL, e->e_flags))
270 /* just put the character out */
271 if (MaxMessageSize <= 0 ||
272 e->e_msgsize <= MaxMessageSize)
281 /* header -- buffer up */
282 if (bp >= &buf[buflen - 2])
286 if (mstate != MS_HEADER)
289 /* out of space for header */
291 if (buflen < MEMCHUNKSIZE)
294 buflen += MEMCHUNKSIZE;
295 buf = xalloc(buflen);
296 bcopy(obuf, buf, bp - obuf);
297 bp = &buf[bp - obuf];
301 if (c >= 0200 && c <= 0237)
303 #if 0 /* causes complaints -- figure out something for 8.9 */
304 usrerr("Illegal character 0x%x in header", c);
310 if (MaxHeadersLength > 0 &&
311 ++hdrslen > MaxHeadersLength)
313 sm_syslog(LOG_NOTICE, e->e_id,
314 "headers too large (%d max) from %s during message collect",
316 CurHostName != NULL ? CurHostName : "localhost");
318 e->e_flags |= EF_CLRQUEUE;
319 e->e_status = "5.6.0";
320 usrerr("552 Headers too large (%d max)",
325 if (istate == IS_BOL)
332 printf("nextstate, istate=%d, mstate=%d, line = \"%s\"\n",
333 istate, mstate, buf);
339 if (strncmp(buf, "From ", 5) == 0)
341 extern void eatfrom __P((char *volatile, ENVELOPE *));
357 /* check for possible continuation line */
363 } while (errno == EINTR);
366 if (c == ' ' || c == '\t')
368 /* yep -- defer this */
372 /* trim off trailing CRLF or NL */
373 if (*--bp != '\n' || *--bp != '\r')
377 if (bitset(H_EOH, chompheader(buf, FALSE, hdrp, e)))
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) &&
400 /* if not a blank separator, write it out */
401 if (MaxMessageSize <= 0 ||
402 e->e_msgsize <= MaxMessageSize)
413 if ((feof(fp) && smtpmode) || ferror(fp))
415 const char *errmsg = errstring(errno);
418 printf("collect: premature EOM: %s\n", errmsg);
420 sm_syslog(LOG_WARNING, e->e_id,
421 "collect: premature EOM: %s", errmsg);
425 /* reset global timer */
426 clrevent(CollectTimeout);
432 (fflush(tf) != 0 || ferror(tf) ||
433 (SuperSafe && fsync(fileno(tf)) < 0) ||
438 finis(TRUE, ExitStat);
441 /* An EOF when running SMTP is an error */
442 if (inputerr && (OpMode == MD_SMTP || OpMode == MD_DAEMON))
452 problem = "unexpected close";
454 problem = "I/O error";
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",
461 shortenstring(e->e_from.q_paddr, MAXSHORTSTR),
464 usrerr("451 collect: %s on connection from %s, from=%s",
466 shortenstring(e->e_from.q_paddr, MAXSHORTSTR));
468 syserr("451 collect: %s on connection from %s, from=%s",
470 shortenstring(e->e_from.q_paddr, MAXSHORTSTR));
472 /* don't return an error indication */
474 e->e_flags &= ~EF_FATALERRS;
475 e->e_flags |= EF_CLRQUEUE;
477 /* and don't try to deliver the partial message either */
480 finis(TRUE, ExitStat);
484 ** Find out some information from the headers.
485 ** Examples are who is the from person & the date.
490 if (GrabTo && e->e_sendqueue == NULL)
491 usrerr("No recipient addresses found in header");
493 /* collect statistics */
494 if (OpMode != MD_VERIFY)
495 markstats(e, (ADDRESS *) NULL, FALSE);
497 #if _FFR_DSN_RRT_OPTION
499 ** If we have a Return-Receipt-To:, turn it into a DSN.
502 if (RrtImpliesDsn && hvalue("return-receipt-to", e->e_header) != NULL)
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;
513 ** Add an Apparently-To: line if we have no recipient lines.
516 if (hvalue("to", e->e_header) != NULL ||
517 hvalue("cc", e->e_header) != NULL ||
518 hvalue("apparently-to", e->e_header) != NULL)
520 /* have a valid recipient header -- delete Bcc: headers */
521 e->e_flags |= EF_DELETE_BCC;
523 else if (hvalue("bcc", e->e_header) == NULL)
525 /* no valid recipient headers */
529 /* create an Apparently-To: field */
530 /* that or reject the message.... */
531 switch (NoRecipientAction)
533 case NRA_ADD_APPARENTLY_TO:
534 hdr = "Apparently-To";
542 addheader("Bcc", " ", &e->e_header);
545 case NRA_ADD_TO_UNDISCLOSED:
546 addheader("To", "undisclosed-recipients:;", &e->e_header);
552 for (q = e->e_sendqueue; q != NULL; q = q->q_next)
554 if (q->q_alias != NULL)
557 printf("Adding %s: %s\n",
559 addheader(hdr, q->q_paddr, &e->e_header);
564 /* check for message too large */
565 if (MaxMessageSize > 0 && e->e_msgsize > MaxMessageSize)
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)",
572 sm_syslog(LOG_NOTICE, e->e_id,
573 "message size (%ld) exceeds maximum (%ld)",
574 e->e_msgsize, MaxMessageSize);
577 /* check for illegal 8-bit data */
580 e->e_flags |= EF_HAS8BIT;
581 if (!bitset(MM_PASS8BIT|MM_MIME8BIT, MimeMode) &&
582 !bitset(EF_IS_MIME, e->e_flags))
584 e->e_status = "5.6.1";
585 usrerr("554 Eight bit data not allowed");
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";
596 if ((e->e_dfp = fopen(dfname, "r")) == NULL)
598 /* we haven't acked receipt yet, so just chuck this */
599 syserr("Cannot reopen %s", dfname);
600 finis(TRUE, ExitStat);
606 collecttimeout(timeout)
609 /* if no progress was made, die now */
610 if (!CollectProgress)
611 longjmp(CtxCollectTimeout, 1);
613 /* otherwise reset the timeout */
614 CollectTimeout = setevent(timeout, collecttimeout, timeout);
615 CollectProgress = FALSE;
618 ** TFERROR -- signal error on writing the temporary file.
621 ** tf -- the file pointer for the temporary file.
622 ** e -- the current envelope.
628 ** Gives an error message.
629 ** Arranges for following output to go elsewhere.
635 register ENVELOPE *e;
647 extern long freediskspace __P((char *, long *));
649 e->e_flags |= EF_NO_BODY_RETN;
653 fstat64(fileno(tf), &st)
655 fstat(fileno(tf), &st)
659 (void) freopen(queuename(e, 'd'), "w", tf);
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));
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",
670 avail = freediskspace(QueueDir, &bsize);
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",
680 e->e_status = "4.3.1";
681 usrerr("452 Out of disk space for temp file");
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",
691 ** EATFROM -- chew up a UNIX style from line and process
693 ** This does indeed make some assumptions about the format
697 ** fm -- the from line.
703 ** extracts what information it can from the header,
711 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", NULL
716 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
717 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
724 register ENVELOPE *e;
730 printf("eatfrom(%s)\n", fm);
732 /* find the date part */
737 while (*p != '\0' && *p != ' ')
741 if (!(isascii(*p) && isupper(*p)) ||
742 p[3] != ' ' || p[13] != ':' || p[16] != ':')
745 /* we have a possible date */
746 for (dt = DowList; *dt != NULL; dt++)
747 if (strncmp(*dt, p, 3) == 0)
752 for (dt = MonthList; *dt != NULL; dt++)
753 if (strncmp(*dt, &p[4], 3) == 0)
763 /* we have found a date */
765 (void) strncpy(q, p, 25);
768 define('a', newstr(q), e);
772 # endif /* NOTUNIX */