diff options
Diffstat (limited to 'contrib/sendmail/src/collect.c')
-rw-r--r-- | contrib/sendmail/src/collect.c | 1105 |
1 files changed, 1105 insertions, 0 deletions
diff --git a/contrib/sendmail/src/collect.c b/contrib/sendmail/src/collect.c new file mode 100644 index 0000000..f5d7247 --- /dev/null +++ b/contrib/sendmail/src/collect.c @@ -0,0 +1,1105 @@ +/* + * Copyright (c) 1998-2006, 2008 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 <sendmail.h> + +SM_RCSID("@(#)$Id: collect.c,v 8.284 2008/08/06 05:26:24 ca Exp $") + +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, NULL); + + /* + ** 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, true); + break; + + case NRA_ADD_TO_UNDISCLOSED: + addheader("To", "undisclosed-recipients:;", 0, e, true); + 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, true); + } + } + } +} + +/* +** 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. +*/ + +/* 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 *df; + bool ignrdot; + int dbto; + register char *bp; + int c; + bool inputerr; + bool headeronly; + char *buf; + int buflen; + int istate; + int mstate; + int hdrslen; + int numhdrs; + int afd; + unsigned char *pbp; + unsigned char peekbuf[8]; + char bufbuf[MAXLINE]; + + df = NULL; + ignrdot = smtpmode ? false : IgnrDot; + + /* timeout for I/O functions is in milliseconds */ + dbto = smtpmode ? ((int) TimeOuts.to_datablock * 1000) + : SM_TIME_FOREVER; + sm_io_setinfo(fp, SM_IO_WHAT_TIMEOUT, &dbto); + set_tls_rd_tmo(TimeOuts.to_datablock); + 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; + + /* + ** Tell ARPANET to go ahead. + */ + + if (smtpmode) + message("354 Enter mail, end with \".\" on a line by itself"); + + /* simulate an I/O timeout when used as sink */ + if (tTd(83, 101)) + sleep(319); + + 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 (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; + } + + /* timeout? */ + if (c == SM_IO_EOF && errno == EAGAIN + && smtpmode) + { + /* + ** Override e_message in + ** usrerr() as this is the + ** reason for failure that + ** should be logged for + ** undelivered recipients. + */ + + e->e_message = NULL; + errno = 0; + inputerr = true; + goto readabort; + } + break; + } + 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; + if (buflen <= 0) + { + sm_syslog(LOG_NOTICE, e->e_id, + "header overflow from %s during message collect", + CURHOSTNAME); + errno = 0; + e->e_flags |= EF_CLRQUEUE; + e->e_status = "5.6.0"; + usrerrenh(e->e_status, + "552 Headers too large"); + goto discard; + } + buf = xalloc(buflen); + memmove(buf, obuf, bp - obuf); + bp = &buf[bp - obuf]; + if (obuf != bufbuf) + sm_free(obuf); /* XXX */ + } + + 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); + discard: + 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); + + /* timeout? */ + if (c == SM_IO_EOF && errno == EAGAIN + && smtpmode) + { + /* + ** Override e_message in + ** usrerr() as this is the + ** reason for failure that + ** should be logged for + ** undelivered recipients. + */ + + e->e_message = NULL; + errno = 0; + inputerr = true; + goto readabort; + } + } 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; + } + + SM_ASSERT(bp > buf); + + /* guaranteed by isheader(buf) */ + SM_ASSERT(*(bp - 1) != '\n' || bp > buf + 1); + + /* trim off trailing CRLF or NL */ + 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; + } + + 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 */ + readabort: + 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("421 4.4.1 collect: %s on connection from %s, from=%s", + problem, host, + shortenstring(e->e_from.q_paddr, MAXSHORTSTR)); + else + syserr("421 4.4.1 collect: %s on connection from %s, from=%s", + problem, host, + shortenstring(e->e_from.q_paddr, MAXSHORTSTR)); + flush_errors(true); + + /* 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; + } + + (void) sm_io_close(df, SM_TIME_DEFAULT); + df = NULL; + finis(true, true, ExitStat); + /* NOTREACHED */ + } + + /* Log collection information. */ + if (tTd(92, 2)) + sm_dprintf("collect: e_id=%s, EF_LOGSENDER=%d, LogLevel=%d\n", + e->e_id, bitset(EF_LOGSENDER, e->e_flags), LogLevel); + 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); + } +} + +/* +** 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 */ |