/* * Copyright (c) 1998-2004 Sendmail, Inc. and its suppliers. * All rights reserved. * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved. * Copyright (c) 1988, 1993 * The Regents of the University of California. All rights reserved. * * By using this file, you agree to the terms and conditions set * forth in the LICENSE file which can be found at the top level of * the sendmail distribution. * */ #include SM_RCSID("@(#)$Id: collect.c,v 8.261 2005/02/16 23:38:51 ca Exp $") static void collecttimeout __P((int)); static void eatfrom __P((char *volatile, ENVELOPE *)); static void collect_doheader __P((ENVELOPE *)); static SM_FILE_T *collect_dfopen __P((ENVELOPE *)); static SM_FILE_T *collect_eoh __P((ENVELOPE *, int, int)); /* ** COLLECT_EOH -- end-of-header processing in collect() ** ** Called by collect() when it encounters the blank line ** separating the header from the message body, or when it ** encounters EOF in a message that contains only a header. ** ** Parameters: ** e -- envelope ** numhdrs -- number of headers ** hdrslen -- length of headers ** ** Results: ** NULL, or handle to open data file ** ** Side Effects: ** end-of-header check ruleset is invoked. ** envelope state is updated. ** headers may be added and deleted. ** selects the queue. ** opens the data file. */ static SM_FILE_T * collect_eoh(e, numhdrs, hdrslen) ENVELOPE *e; int numhdrs; int hdrslen; { char hnum[16]; char hsize[16]; /* call the end-of-header check ruleset */ (void) sm_snprintf(hnum, sizeof hnum, "%d", numhdrs); (void) sm_snprintf(hsize, sizeof hsize, "%d", hdrslen); if (tTd(30, 10)) sm_dprintf("collect: rscheck(\"check_eoh\", \"%s $| %s\")\n", hnum, hsize); (void) rscheck("check_eoh", hnum, hsize, e, RSF_UNSTRUCTURED|RSF_COUNT, 3, NULL, e->e_id); /* ** Process the header, ** select the queue, open the data file. */ collect_doheader(e); return collect_dfopen(e); } /* ** COLLECT_DOHEADER -- process header in collect() ** ** Called by collect() after it has finished parsing the header, ** but before it selects the queue and creates the data file. ** The results of processing the header will affect queue selection. ** ** Parameters: ** e -- envelope ** ** Results: ** none. ** ** Side Effects: ** envelope state is updated. ** headers may be added and deleted. */ static void collect_doheader(e) ENVELOPE *e; { /* ** Find out some information from the headers. ** Examples are who is the from person & the date. */ eatheader(e, true, false); if (GrabTo && e->e_sendqueue == NULL) usrerr("No recipient addresses found in header"); /* ** If we have a Return-Receipt-To:, turn it into a DSN. */ if (RrtImpliesDsn && hvalue("return-receipt-to", e->e_header) != NULL) { ADDRESS *q; for (q = e->e_sendqueue; q != NULL; q = q->q_next) if (!bitset(QHASNOTIFY, q->q_flags)) q->q_flags |= QHASNOTIFY|QPINGONSUCCESS; } /* ** Add an appropriate recipient line if we have none. */ if (hvalue("to", e->e_header) != NULL || hvalue("cc", e->e_header) != NULL || hvalue("apparently-to", e->e_header) != NULL) { /* have a valid recipient header -- delete Bcc: headers */ e->e_flags |= EF_DELETE_BCC; } else if (hvalue("bcc", e->e_header) == NULL) { /* no valid recipient headers */ register ADDRESS *q; char *hdr = NULL; /* create a recipient field */ switch (NoRecipientAction) { case NRA_ADD_APPARENTLY_TO: hdr = "Apparently-To"; break; case NRA_ADD_TO: hdr = "To"; break; case NRA_ADD_BCC: addheader("Bcc", " ", 0, e); break; case NRA_ADD_TO_UNDISCLOSED: addheader("To", "undisclosed-recipients:;", 0, e); break; } if (hdr != NULL) { for (q = e->e_sendqueue; q != NULL; q = q->q_next) { if (q->q_alias != NULL) continue; if (tTd(30, 3)) sm_dprintf("Adding %s: %s\n", hdr, q->q_paddr); addheader(hdr, q->q_paddr, 0, e); } } } } /* ** COLLECT_DFOPEN -- open the message data file ** ** Called by collect() after it has finished processing the header. ** Queue selection occurs at this point, possibly based on the ** envelope's recipient list and on header information. ** ** Parameters: ** e -- envelope ** ** Results: ** NULL, or a pointer to an open data file, ** into which the message body will be written by collect(). ** ** Side Effects: ** Calls syserr, sets EF_FATALERRS and returns NULL ** if there is insufficient disk space. ** Aborts process if data file could not be opened. ** Otherwise, the queue is selected, ** e->e_{dfino,dfdev,msgsize,flags} are updated, ** and a pointer to an open data file is returned. */ static SM_FILE_T * collect_dfopen(e) ENVELOPE *e; { MODE_T oldumask = 0; int dfd; struct stat stbuf; SM_FILE_T *df; char *dfname; if (!setnewqueue(e)) return NULL; dfname = queuename(e, DATAFL_LETTER); if (bitset(S_IWGRP, QueueFileMode)) oldumask = umask(002); df = bfopen(dfname, QueueFileMode, DataFileBufferSize, SFF_OPENASROOT); if (bitset(S_IWGRP, QueueFileMode)) (void) umask(oldumask); if (df == NULL) { syserr("@Cannot create %s", dfname); e->e_flags |= EF_NO_BODY_RETN; flush_errors(true); finis(false, true, ExitStat); /* NOTREACHED */ } dfd = sm_io_getinfo(df, SM_IO_WHAT_FD, NULL); if (dfd < 0 || fstat(dfd, &stbuf) < 0) e->e_dfino = -1; else { e->e_dfdev = stbuf.st_dev; e->e_dfino = stbuf.st_ino; } e->e_flags |= EF_HAS_DF; return df; } /* ** COLLECT -- read & parse message header & make temp file. ** ** Creates a temporary file name and copies the standard ** input to that file. Leading UNIX-style "From" lines are ** stripped off (after important information is extracted). ** ** Parameters: ** fp -- file to read. ** smtpmode -- if set, we are running SMTP: give an RFC821 ** style message to say we are ready to collect ** input, and never ignore a single dot to mean ** end of message. ** hdrp -- the location to stash the header. ** e -- the current envelope. ** rsetsize -- reset e_msgsize? ** ** Returns: ** none. ** ** Side Effects: ** If successful, ** - Data file is created and filled, and e->e_dfp is set. ** - The from person may be set. ** If the "enough disk space" check fails, ** - syserr is called. ** - e->e_dfp is NULL. ** - e->e_flags & EF_FATALERRS is set. ** - collect() returns. ** If data file cannot be created, the process is terminated. */ static jmp_buf CtxCollectTimeout; static bool volatile CollectProgress; static SM_EVENT *volatile CollectTimeout = NULL; /* values for input state machine */ #define IS_NORM 0 /* middle of line */ #define IS_BOL 1 /* beginning of line */ #define IS_DOT 2 /* read a dot at beginning of line */ #define IS_DOTCR 3 /* read ".\r" at beginning of line */ #define IS_CR 4 /* read a carriage return */ /* values for message state machine */ #define MS_UFROM 0 /* reading Unix from line */ #define MS_HEADER 1 /* reading message header */ #define MS_BODY 2 /* reading message body */ #define MS_DISCARD 3 /* discarding rest of message */ void collect(fp, smtpmode, hdrp, e, rsetsize) SM_FILE_T *fp; bool smtpmode; HDR **hdrp; register ENVELOPE *e; bool rsetsize; { register SM_FILE_T *volatile df; volatile bool ignrdot; volatile int dbto; register char *volatile bp; volatile int c; volatile bool inputerr; bool headeronly; char *volatile buf; volatile int buflen; volatile int istate; volatile int mstate; volatile int hdrslen; volatile int numhdrs; volatile int afd; unsigned char *volatile pbp; unsigned char peekbuf[8]; char bufbuf[MAXLINE]; df = NULL; ignrdot = smtpmode ? false : IgnrDot; dbto = smtpmode ? (int) TimeOuts.to_datablock : 0; c = SM_IO_EOF; inputerr = false; headeronly = hdrp != NULL; hdrslen = 0; numhdrs = 0; HasEightBits = false; buf = bp = bufbuf; buflen = sizeof bufbuf; pbp = peekbuf; istate = IS_BOL; mstate = SaveFrom ? MS_HEADER : MS_UFROM; CollectProgress = false; /* ** Tell ARPANET to go ahead. */ if (smtpmode) message("354 Enter mail, end with \".\" on a line by itself"); if (tTd(30, 2)) sm_dprintf("collect\n"); /* ** Read the message. ** ** This is done using two interleaved state machines. ** The input state machine is looking for things like ** hidden dots; the message state machine is handling ** the larger picture (e.g., header versus body). */ if (dbto != 0) { /* handle possible input timeout */ if (setjmp(CtxCollectTimeout) != 0) { if (LogLevel > 2) sm_syslog(LOG_NOTICE, e->e_id, "timeout waiting for input from %s during message collect", CURHOSTNAME); errno = 0; if (smtpmode) { /* ** Override e_message in usrerr() as this ** is the reason for failure that should ** be logged for undelivered recipients. */ e->e_message = NULL; } usrerr("451 4.4.1 timeout waiting for input during message collect"); goto readerr; } CollectTimeout = sm_setevent(dbto, collecttimeout, dbto); } if (rsetsize) e->e_msgsize = 0; for (;;) { if (tTd(30, 35)) sm_dprintf("top, istate=%d, mstate=%d\n", istate, mstate); for (;;) { if (pbp > peekbuf) c = *--pbp; else { while (!sm_io_eof(fp) && !sm_io_error(fp)) { errno = 0; c = sm_io_getc(fp, SM_TIME_DEFAULT); if (c == SM_IO_EOF && errno == EINTR) { /* Interrupted, retry */ sm_io_clearerr(fp); continue; } break; } CollectProgress = true; if (TrafficLogFile != NULL && !headeronly) { if (istate == IS_BOL) (void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT, "%05d <<< ", (int) CurrentPid); if (c == SM_IO_EOF) (void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT, "[EOF]\n"); else (void) sm_io_putc(TrafficLogFile, SM_TIME_DEFAULT, c); } if (c == SM_IO_EOF) goto readerr; if (SevenBitInput) c &= 0x7f; else HasEightBits |= bitset(0x80, c); } if (tTd(30, 94)) sm_dprintf("istate=%d, c=%c (0x%x)\n", istate, (char) c, c); switch (istate) { case IS_BOL: if (c == '.') { istate = IS_DOT; continue; } break; case IS_DOT: if (c == '\n' && !ignrdot && !bitset(EF_NL_NOT_EOL, e->e_flags)) goto readerr; else if (c == '\r' && !bitset(EF_CRLF_NOT_EOL, e->e_flags)) { istate = IS_DOTCR; continue; } else if (ignrdot || (c != '.' && OpMode != MD_SMTP && OpMode != MD_DAEMON && OpMode != MD_ARPAFTP)) { SM_ASSERT(pbp < peekbuf + sizeof(peekbuf)); *pbp++ = c; c = '.'; } break; case IS_DOTCR: if (c == '\n' && !ignrdot) goto readerr; else { /* push back the ".\rx" */ SM_ASSERT(pbp < peekbuf + sizeof(peekbuf)); *pbp++ = c; if (OpMode != MD_SMTP && OpMode != MD_DAEMON && OpMode != MD_ARPAFTP) { SM_ASSERT(pbp < peekbuf + sizeof(peekbuf)); *pbp++ = '\r'; c = '.'; } else c = '\r'; } break; case IS_CR: if (c == '\n') istate = IS_BOL; else { (void) sm_io_ungetc(fp, SM_TIME_DEFAULT, c); c = '\r'; istate = IS_NORM; } goto bufferchar; } if (c == '\r' && !bitset(EF_CRLF_NOT_EOL, e->e_flags)) { istate = IS_CR; continue; } else if (c == '\n' && !bitset(EF_NL_NOT_EOL, e->e_flags)) istate = IS_BOL; else istate = IS_NORM; bufferchar: if (!headeronly) { /* no overflow? */ if (e->e_msgsize >= 0) { e->e_msgsize++; if (MaxMessageSize > 0 && !bitset(EF_TOOBIG, e->e_flags) && e->e_msgsize > MaxMessageSize) e->e_flags |= EF_TOOBIG; } } switch (mstate) { case MS_BODY: /* just put the character out */ if (!bitset(EF_TOOBIG, e->e_flags)) (void) sm_io_putc(df, SM_TIME_DEFAULT, c); /* FALLTHROUGH */ case MS_DISCARD: continue; } SM_ASSERT(mstate == MS_UFROM || mstate == MS_HEADER); /* header -- buffer up */ if (bp >= &buf[buflen - 2]) { char *obuf; /* out of space for header */ obuf = buf; if (buflen < MEMCHUNKSIZE) buflen *= 2; else buflen += MEMCHUNKSIZE; buf = xalloc(buflen); memmove(buf, obuf, bp - obuf); bp = &buf[bp - obuf]; if (obuf != bufbuf) sm_free(obuf); /* XXX */ } /* ** XXX Notice: the logic here is broken. ** An input to sendmail that doesn't contain a ** header but starts immediately with the body whose ** first line contain characters which match the ** following "if" will cause problems: those ** characters will NOT appear in the output... ** Do we care? */ if (c >= 0200 && c <= 0237) { #if 0 /* causes complaints -- figure out something for 8.n+1 */ usrerr("Illegal character 0x%x in header", c); #else /* 0 */ /* EMPTY */ #endif /* 0 */ } else if (c != '\0') { *bp++ = c; ++hdrslen; if (!headeronly && MaxHeadersLength > 0 && hdrslen > MaxHeadersLength) { sm_syslog(LOG_NOTICE, e->e_id, "headers too large (%d max) from %s during message collect", MaxHeadersLength, CURHOSTNAME); errno = 0; e->e_flags |= EF_CLRQUEUE; e->e_status = "5.6.0"; usrerrenh(e->e_status, "552 Headers too large (%d max)", MaxHeadersLength); mstate = MS_DISCARD; } } if (istate == IS_BOL) break; } *bp = '\0'; nextstate: if (tTd(30, 35)) sm_dprintf("nextstate, istate=%d, mstate=%d, line = \"%s\"\n", istate, mstate, buf); switch (mstate) { case MS_UFROM: mstate = MS_HEADER; #ifndef NOTUNIX if (strncmp(buf, "From ", 5) == 0) { bp = buf; eatfrom(buf, e); continue; } #endif /* ! NOTUNIX */ /* FALLTHROUGH */ case MS_HEADER: if (!isheader(buf)) { mstate = MS_BODY; goto nextstate; } /* check for possible continuation line */ do { sm_io_clearerr(fp); errno = 0; c = sm_io_getc(fp, SM_TIME_DEFAULT); } while (c == SM_IO_EOF && errno == EINTR); if (c != SM_IO_EOF) (void) sm_io_ungetc(fp, SM_TIME_DEFAULT, c); if (c == ' ' || c == '\t') { /* yep -- defer this */ continue; } /* trim off trailing CRLF or NL */ SM_ASSERT(bp > buf); if (*--bp != '\n' || *--bp != '\r') bp++; *bp = '\0'; if (bitset(H_EOH, chompheader(buf, CHHDR_CHECK | CHHDR_USER, hdrp, e))) { mstate = MS_BODY; goto nextstate; } numhdrs++; break; case MS_BODY: if (tTd(30, 1)) sm_dprintf("EOH\n"); if (headeronly) goto readerr; df = collect_eoh(e, numhdrs, hdrslen); if (df == NULL) e->e_flags |= EF_TOOBIG; bp = buf; /* toss blank line */ if ((!bitset(EF_CRLF_NOT_EOL, e->e_flags) && bp[0] == '\r' && bp[1] == '\n') || (!bitset(EF_NL_NOT_EOL, e->e_flags) && bp[0] == '\n')) { break; } /* if not a blank separator, write it out */ if (!bitset(EF_TOOBIG, e->e_flags)) { while (*bp != '\0') (void) sm_io_putc(df, SM_TIME_DEFAULT, *bp++); } break; } bp = buf; } readerr: if ((sm_io_eof(fp) && smtpmode) || sm_io_error(fp)) { const char *errmsg; if (sm_io_eof(fp)) errmsg = "unexpected close"; else errmsg = sm_errstring(errno); if (tTd(30, 1)) sm_dprintf("collect: premature EOM: %s\n", errmsg); if (LogLevel > 1) sm_syslog(LOG_WARNING, e->e_id, "collect: premature EOM: %s", errmsg); inputerr = true; } /* reset global timer */ if (CollectTimeout != NULL) sm_clrevent(CollectTimeout); if (headeronly) return; if (mstate != MS_BODY) { /* no body or discard, so we never opened the data file */ SM_ASSERT(df == NULL); df = collect_eoh(e, numhdrs, hdrslen); } if (df == NULL) { /* skip next few clauses */ /* EMPTY */ } else if (sm_io_flush(df, SM_TIME_DEFAULT) != 0 || sm_io_error(df)) { dferror(df, "sm_io_flush||sm_io_error", e); flush_errors(true); finis(true, true, ExitStat); /* NOTREACHED */ } else if (SuperSafe == SAFE_NO || SuperSafe == SAFE_INTERACTIVE || (SuperSafe == SAFE_REALLY_POSTMILTER && smtpmode)) { /* skip next few clauses */ /* EMPTY */ /* Note: updfs() is not called in this case! */ } else if (sm_io_setinfo(df, SM_BF_COMMIT, NULL) < 0 && errno != EINVAL) { int save_errno = errno; if (save_errno == EEXIST) { char *dfile; struct stat st; int dfd; dfile = queuename(e, DATAFL_LETTER); if (stat(dfile, &st) < 0) st.st_size = -1; errno = EEXIST; syserr("@collect: bfcommit(%s): already on disk, size=%ld", dfile, (long) st.st_size); dfd = sm_io_getinfo(df, SM_IO_WHAT_FD, NULL); if (dfd >= 0) dumpfd(dfd, true, true); } errno = save_errno; dferror(df, "bfcommit", e); flush_errors(true); finis(save_errno != EEXIST, true, ExitStat); } else if ((afd = sm_io_getinfo(df, SM_IO_WHAT_FD, NULL)) < 0) { dferror(df, "sm_io_getinfo", e); flush_errors(true); finis(true, true, ExitStat); /* NOTREACHED */ } else if (fsync(afd) < 0) { dferror(df, "fsync", e); flush_errors(true); finis(true, true, ExitStat); /* NOTREACHED */ } else if (sm_io_close(df, SM_TIME_DEFAULT) < 0) { dferror(df, "sm_io_close", e); flush_errors(true); finis(true, true, ExitStat); /* NOTREACHED */ } else { /* everything is happily flushed to disk */ df = NULL; /* remove from available space in filesystem */ updfs(e, 0, 1, "collect"); } /* An EOF when running SMTP is an error */ if (inputerr && (OpMode == MD_SMTP || OpMode == MD_DAEMON)) { char *host; char *problem; ADDRESS *q; host = RealHostName; if (host == NULL) host = "localhost"; if (sm_io_eof(fp)) problem = "unexpected close"; else if (sm_io_error(fp)) problem = "I/O error"; else problem = "read timeout"; if (LogLevel > 0 && sm_io_eof(fp)) sm_syslog(LOG_NOTICE, e->e_id, "collect: %s on connection from %.100s, sender=%s", problem, host, shortenstring(e->e_from.q_paddr, MAXSHORTSTR)); if (sm_io_eof(fp)) usrerr("451 4.4.1 collect: %s on connection from %s, from=%s", problem, host, shortenstring(e->e_from.q_paddr, MAXSHORTSTR)); else syserr("451 4.4.1 collect: %s on connection from %s, from=%s", problem, host, shortenstring(e->e_from.q_paddr, MAXSHORTSTR)); /* don't return an error indication */ e->e_to = NULL; e->e_flags &= ~EF_FATALERRS; e->e_flags |= EF_CLRQUEUE; /* Don't send any message notification to sender */ for (q = e->e_sendqueue; q != NULL; q = q->q_next) { if (QS_IS_DEAD(q->q_state)) continue; q->q_state = QS_FATALERR; } finis(true, true, ExitStat); /* NOTREACHED */ } /* Log collection information. */ if (bitset(EF_LOGSENDER, e->e_flags) && LogLevel > 4) { logsender(e, e->e_msgid); e->e_flags &= ~EF_LOGSENDER; } /* check for message too large */ if (bitset(EF_TOOBIG, e->e_flags)) { e->e_flags |= EF_NO_BODY_RETN|EF_CLRQUEUE; if (!bitset(EF_FATALERRS, e->e_flags)) { e->e_status = "5.2.3"; usrerrenh(e->e_status, "552 Message exceeds maximum fixed size (%ld)", MaxMessageSize); if (LogLevel > 6) sm_syslog(LOG_NOTICE, e->e_id, "message size (%ld) exceeds maximum (%ld)", e->e_msgsize, MaxMessageSize); } } /* check for illegal 8-bit data */ if (HasEightBits) { e->e_flags |= EF_HAS8BIT; if (!bitset(MM_PASS8BIT|MM_MIME8BIT, MimeMode) && !bitset(EF_IS_MIME, e->e_flags)) { e->e_status = "5.6.1"; usrerrenh(e->e_status, "554 Eight bit data not allowed"); } } else { /* if it claimed to be 8 bits, well, it lied.... */ if (e->e_bodytype != NULL && sm_strcasecmp(e->e_bodytype, "8BITMIME") == 0) e->e_bodytype = "7BIT"; } if (SuperSafe == SAFE_REALLY && !bitset(EF_FATALERRS, e->e_flags)) { char *dfname = queuename(e, DATAFL_LETTER); if ((e->e_dfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, dfname, SM_IO_RDONLY_B, NULL)) == NULL) { /* we haven't acked receipt yet, so just chuck this */ syserr("@Cannot reopen %s", dfname); finis(true, true, ExitStat); /* NOTREACHED */ } } else e->e_dfp = df; /* collect statistics */ if (OpMode != MD_VERIFY) { /* ** Recalculate e_msgpriority, it is done at in eatheader() ** which is called (in 8.12) after the header is collected, ** hence e_msgsize is (most likely) incorrect. */ e->e_msgpriority = e->e_msgsize - e->e_class * WkClassFact + e->e_nrcpts * WkRecipFact; markstats(e, (ADDRESS *) NULL, STATS_NORMAL); } } static void collecttimeout(timeout) int timeout; { int save_errno = errno; /* ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE ** DOING. */ if (CollectProgress) { /* reset the timeout */ CollectTimeout = sm_sigsafe_setevent(timeout, collecttimeout, timeout); CollectProgress = false; } else { /* event is done */ CollectTimeout = NULL; } /* if no progress was made or problem resetting event, die now */ if (CollectTimeout == NULL) { errno = ETIMEDOUT; longjmp(CtxCollectTimeout, 1); } errno = save_errno; } /* ** DFERROR -- signal error on writing the data file. ** ** Called by collect(). Collect() always terminates the process ** immediately after calling dferror(), which means that the SMTP ** session will be terminated, which means that any error message ** issued by dferror must be a 421 error, as per RFC 821. ** ** Parameters: ** df -- the file pointer for the data file. ** msg -- detailed message. ** e -- the current envelope. ** ** Returns: ** none. ** ** Side Effects: ** Gives an error message. ** Arranges for following output to go elsewhere. */ void dferror(df, msg, e) SM_FILE_T *volatile df; char *msg; register ENVELOPE *e; { char *dfname; dfname = queuename(e, DATAFL_LETTER); setstat(EX_IOERR); if (errno == ENOSPC) { #if STAT64 > 0 struct stat64 st; #else /* STAT64 > 0 */ struct stat st; #endif /* STAT64 > 0 */ long avail; long bsize; e->e_flags |= EF_NO_BODY_RETN; if ( #if STAT64 > 0 fstat64(sm_io_getinfo(df, SM_IO_WHAT_FD, NULL), &st) #else /* STAT64 > 0 */ fstat(sm_io_getinfo(df, SM_IO_WHAT_FD, NULL), &st) #endif /* STAT64 > 0 */ < 0) st.st_size = 0; (void) sm_io_reopen(SmFtStdio, SM_TIME_DEFAULT, dfname, SM_IO_WRONLY_B, NULL, df); if (st.st_size <= 0) (void) sm_io_fprintf(df, SM_TIME_DEFAULT, "\n*** Mail could not be accepted"); else (void) sm_io_fprintf(df, SM_TIME_DEFAULT, "\n*** Mail of at least %llu bytes could not be accepted\n", (ULONGLONG_T) st.st_size); (void) sm_io_fprintf(df, SM_TIME_DEFAULT, "*** at %s due to lack of disk space for temp file.\n", MyHostName); avail = freediskspace(qid_printqueue(e->e_qgrp, e->e_qdir), &bsize); if (avail > 0) { if (bsize > 1024) avail *= bsize / 1024; else if (bsize < 1024) avail /= 1024 / bsize; (void) sm_io_fprintf(df, SM_TIME_DEFAULT, "*** Currently, %ld kilobytes are available for mail temp files.\n", avail); } #if 0 /* Wrong response code; should be 421. */ e->e_status = "4.3.1"; usrerrenh(e->e_status, "452 Out of disk space for temp file"); #else /* 0 */ syserr("421 4.3.1 Out of disk space for temp file"); #endif /* 0 */ } else syserr("421 4.3.0 collect: Cannot write %s (%s, uid=%d, gid=%d)", dfname, msg, (int) geteuid(), (int) getegid()); if (sm_io_reopen(SmFtStdio, SM_TIME_DEFAULT, SM_PATH_DEVNULL, SM_IO_WRONLY, NULL, df) == NULL) sm_syslog(LOG_ERR, e->e_id, "dferror: sm_io_reopen(\"/dev/null\") failed: %s", sm_errstring(errno)); } /* ** EATFROM -- chew up a UNIX style from line and process ** ** This does indeed make some assumptions about the format ** of UNIX messages. ** ** Parameters: ** fm -- the from line. ** e -- envelope ** ** Returns: ** none. ** ** Side Effects: ** extracts what information it can from the header, ** such as the date. */ #ifndef NOTUNIX static char *DowList[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", NULL }; static char *MonthList[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", NULL }; static void eatfrom(fm, e) char *volatile fm; register ENVELOPE *e; { register char *p; register char **dt; if (tTd(30, 2)) sm_dprintf("eatfrom(%s)\n", fm); /* find the date part */ p = fm; while (*p != '\0') { /* skip a word */ while (*p != '\0' && *p != ' ') p++; while (*p == ' ') p++; if (strlen(p) < 17) { /* no room for the date */ return; } if (!(isascii(*p) && isupper(*p)) || p[3] != ' ' || p[13] != ':' || p[16] != ':') continue; /* we have a possible date */ for (dt = DowList; *dt != NULL; dt++) if (strncmp(*dt, p, 3) == 0) break; if (*dt == NULL) continue; for (dt = MonthList; *dt != NULL; dt++) { if (strncmp(*dt, &p[4], 3) == 0) break; } if (*dt != NULL) break; } if (*p != '\0') { char *q, buf[25]; /* we have found a date */ (void) sm_strlcpy(buf, p, sizeof(buf)); q = arpadate(buf); macdefine(&e->e_macro, A_TEMP, 'a', q); } } #endif /* ! NOTUNIX */