* Copyright (c) 1983 Eric P. Allman
* Copyright (c) 1988, 1993
* The Regents of the University of California. All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
static char sccsid
[] = "@(#)collect.c 8.14 (Berkeley) 4/18/94";
** 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).
** 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
** requeueflag -- this message will be requeued later, so
** don't do final processing on it.
** e -- the current envelope.
** Temp file is created and filled.
** The from person may be set.
char *CollectErrorMessage
;
collect(smtpmode
, requeueflag
, e
)
bool ignrdot
= smtpmode
? FALSE
: IgnrDot
;
char buf
[MAXLINE
], buf2
[MAXLINE
];
register char *workbuf
, *freebuf
;
extern bool isheader(), flusheol();
CollectErrorMessage
= NULL
;
** Create the temp file name and create the file.
e
->e_df
= queuename(e
, 'd');
e
->e_df
= newstr(e
->e_df
);
if ((tf
= dfopen(e
->e_df
, O_WRONLY
|O_CREAT
|O_TRUNC
, FileMode
)) == NULL
)
syserr("Cannot create %s", e
->e_df
);
** Tell ARPANET to go ahead.
message("354 Enter mail, end with \".\" on a line by itself");
/* set global timer to monitor progress */
sfgetset(TimeOuts
.to_datablock
);
** Try to read a UNIX-style From line
if (sfgets(buf
, MAXLINE
, InChannel
, TimeOuts
.to_datablock
,
"initial message read") == NULL
)
if (!SaveFrom
&& strncmp(buf
, "From ", 5) == 0)
if (!flusheol(buf
, InChannel
))
if (sfgets(buf
, MAXLINE
, InChannel
, TimeOuts
.to_datablock
,
"message header read") == NULL
)
** Copy InChannel to temp file & do message editing.
** To keep certain mailers from getting confused,
** and to keep the output clean, lines that look
** like UNIX "From" lines are deleted in the header.
workbuf
= buf
; /* `workbuf' contains a header field */
freebuf
= buf2
; /* `freebuf' can be used for read-ahead */
/* first, see if the header is over */
/* if the line is too long, throw the rest away */
if (!flusheol(workbuf
, InChannel
))
/* it's okay to toss '\n' now (flusheol() needed it) */
curbuflen
= strlen(curbuf
);
curbuffree
= MAXLINE
- curbuflen
;
/* get the rest of this field */
if (sfgets(freebuf
, MAXLINE
, InChannel
,
"message header read") == NULL
)
/* is this a continuation line? */
if (*freebuf
!= ' ' && *freebuf
!= '\t')
if (!flusheol(freebuf
, InChannel
))
clen
= strlen(freebuf
) + 1;
/* if insufficient room, dynamically allocate buffer */
int nbuflen
= ((p
- curbuf
) + clen
) * 2;
char *nbuf
= xalloc(nbuflen
);
curbuffree
= nbuflen
- curbuflen
;
bcopy(curbuf
, nbuf
, curbuflen
);
if (curbuf
!= buf
&& curbuf
!= buf2
)
bcopy(freebuf
, p
, clen
- 1);
e
->e_msgsize
+= curbuflen
;
** The working buffer now becomes the free buffer, since
** the free buffer contains a new header field.
** This is premature, since we still havent called
** chompheader() to process the field we just created
** (so the call to chompheader() will use `freebuf').
** This convolution is necessary so that if we break out
** of the loop due to H_EOH, `workbuf' will always be
** the next unprocessed buffer.
register char *tmp
= workbuf
;
if (bitset(H_EOH
, chompheader(curbuf
, FALSE
, e
)))
** If the buffer was dynamically allocated, free it.
if (curbuf
!= buf
&& curbuf
!= buf2
)
/* throw away a blank line */
if (sfgets(buf
, MAXLINE
, InChannel
, TimeOuts
.to_datablock
,
"message separator read") == NULL
)
else if (workbuf
== buf2
) /* guarantee `buf' contains data */
(void) strcpy(buf
, buf2
);
** Collect the body of the message.
/* check for end-of-message */
if (!ignrdot
&& buf
[0] == '.' && (buf
[1] == '\n' || buf
[1] == '\0'))
/* check for transparent dot */
if ((OpMode
== MD_SMTP
|| OpMode
== MD_DAEMON
) &&
bp
[0] == '.' && bp
[1] == '.')
** Figure message length, output the line to the temp
** file, and insert a newline if missing.
e
->e_msgsize
+= strlen(bp
) + 1;
if (sfgets(buf
, MAXLINE
, InChannel
, TimeOuts
.to_datablock
,
"message body read") == NULL
)
if (feof(InChannel
) || ferror(InChannel
))
printf("collect: read error\n");
if (fsync(fileno(tf
)) < 0 || fclose(tf
) < 0)
if (CollectErrorMessage
!= NULL
&& Errors
<= 0)
syserr(CollectErrorMessage
, e
->e_df
);
usrerr(CollectErrorMessage
);
else if (inputerr
&& (OpMode
== MD_SMTP
|| OpMode
== MD_DAEMON
))
/* An EOF when running SMTP is an error */
problem
= "unexpected close";
else if (ferror(InChannel
))
problem
= "read timeout";
if (LogLevel
> 0 && feof(InChannel
))
"collect: %s on connection from %s, sender=%s: %s\n",
problem
, host
, e
->e_from
.q_paddr
, errstring(errno
));
usrerr("451 collect: %s on connection from %s, from=%s",
problem
, host
, e
->e_from
.q_paddr
);
syserr("451 collect: %s on connection from %s, from=%s",
problem
, host
, e
->e_from
.q_paddr
);
/* don't return an error indication */
e
->e_flags
&= ~EF_FATALERRS
;
e
->e_flags
|= EF_CLRQUEUE
;
/* and don't try to deliver the partial message either */
** Find out some information from the headers.
** Examples are who is the from person & the date.
eatheader(e
, !requeueflag
);
markstats(e
, (ADDRESS
*) NULL
);
** Add an Apparently-To: line if we have no recipient lines.
if (hvalue("to", e
) == NULL
&& hvalue("cc", e
) == NULL
&&
hvalue("bcc", e
) == NULL
&& hvalue("apparently-to", e
) == NULL
)
/* create an Apparently-To: field */
/* that or reject the message.... */
for (q
= e
->e_sendqueue
; q
!= NULL
; q
= q
->q_next
)
printf("Adding Apparently-To: %s\n", q
->q_paddr
);
addheader("Apparently-To", q
->q_paddr
, e
);
/* check for message too large */
if (MaxMessageSize
> 0 && e
->e_msgsize
> MaxMessageSize
)
usrerr("552 Message exceeds maximum fixed size (%ld)",
if ((e
->e_dfp
= fopen(e
->e_df
, "r")) == NULL
)
/* we haven't acked receipt yet, so just chuck this */
syserr("Cannot reopen %s", e
->e_df
);
** FLUSHEOL -- if not at EOL, throw away rest of input line.
** buf -- last line read in (checked for '\n'),
** fp -- file to be read from.
** FALSE on error from sfgets(), TRUE otherwise.
while (strchr(p
, '\n') == NULL
)
CollectErrorMessage
= "553 header line too long";
if (sfgets(junkbuf
, MAXLINE
, fp
, TimeOuts
.to_datablock
,
"long line flush") == NULL
)
** TFERROR -- signal error on writing the temporary file.
** tf -- the file pointer for the temporary file.
** Gives an error message.
** Arranges for following output to go elsewhere.
if (fstat(fileno(tf
), &st
) < 0)
(void) freopen(e
->e_df
, "w", tf
);
fprintf(tf
, "\n*** Mail could not be accepted");
else if (sizeof st
.st_size
> sizeof (long))
fprintf(tf
, "\n*** Mail of at least %qd bytes could not be accepted\n",
fprintf(tf
, "\n*** Mail of at least %ld bytes could not be accepted\n",
fprintf(tf
, "*** at %s due to lack of disk space for temp file.\n",
avail
= freespace(QueueDir
, &bsize
);
fprintf(tf
, "*** Currently, %ld kilobytes are available for mail temp files.\n",
CollectErrorMessage
= "452 Out of disk space for temp file";
CollectErrorMessage
= "cannot write message body to disk (%s)";
(void) freopen("/dev/null", "w", tf
);
** EATFROM -- chew up a UNIX style from line and process
** This does indeed make some assumptions about the format
** extracts what information it can from the header,
"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", NULL
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
printf("eatfrom(%s)\n", fm
);
while (*p
!= '\0' && *p
!= ' ')
if (!(isascii(*p
) && isupper(*p
)) ||
p
[3] != ' ' || p
[13] != ':' || p
[16] != ':')
/* we have a possible date */
for (dt
= DowList
; *dt
!= NULL
; dt
++)
if (strncmp(*dt
, p
, 3) == 0)
for (dt
= MonthList
; *dt
!= NULL
; dt
++)
if (strncmp(*dt
, &p
[4], 3) == 0)
/* we have found a date */
(void) strncpy(q
, p
, 25);
define('a', newstr(q
), e
);