This commit was generated by cvs2svn to track changes on a CVS vendor
[unix-history] / usr.sbin / sendmail / src / collect.c
CommitLineData
15637ed4
RG
1/*
2 * Copyright (c) 1983 Eric P. Allman
6f14531a
RG
3 * Copyright (c) 1988, 1993
4 * The Regents of the University of California. All rights reserved.
15637ed4
RG
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. All advertising materials mentioning features or use of this software
15 * must display the following acknowledgement:
16 * This product includes software developed by the University of
17 * California, Berkeley and its contributors.
18 * 4. Neither the name of the University nor the names of its contributors
19 * may be used to endorse or promote products derived from this software
20 * without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 */
34
35#ifndef lint
69fc843f 36static char sccsid[] = "@(#)collect.c 8.6 (Berkeley) 10/27/93";
15637ed4
RG
37#endif /* not lint */
38
39# include <errno.h>
40# include "sendmail.h"
41
42/*
43** COLLECT -- read & parse message header & make temp file.
44**
45** Creates a temporary file name and copies the standard
46** input to that file. Leading UNIX-style "From" lines are
47** stripped off (after important information is extracted).
48**
49** Parameters:
6f14531a
RG
50** smtpmode -- if set, we are running SMTP: give an RFC821
51** style message to say we are ready to collect
52** input, and never ignore a single dot to mean
53** end of message.
54** requeueflag -- this message will be requeued later, so
55** don't do final processing on it.
56** e -- the current envelope.
15637ed4
RG
57**
58** Returns:
59** none.
60**
61** Side Effects:
62** Temp file is created and filled.
63** The from person may be set.
64*/
65
6f14531a
RG
66collect(smtpmode, requeueflag, e)
67 bool smtpmode;
68 bool requeueflag;
69 register ENVELOPE *e;
15637ed4
RG
70{
71 register FILE *tf;
6f14531a
RG
72 bool ignrdot = smtpmode ? FALSE : IgnrDot;
73 char buf[MAXLINE], buf2[MAXLINE];
15637ed4 74 register char *workbuf, *freebuf;
d747e748 75 bool inputerr = FALSE;
15637ed4
RG
76 extern char *hvalue();
77 extern bool isheader(), flusheol();
78
79 /*
80 ** Create the temp file name and create the file.
81 */
82
d747e748
JH
83 e->e_df = queuename(e, 'd');
84 e->e_df = newstr(e->e_df);
6f14531a 85 if ((tf = dfopen(e->e_df, O_WRONLY|O_CREAT, FileMode)) == NULL)
15637ed4 86 {
6f14531a 87 syserr("Cannot create %s", e->e_df);
15637ed4
RG
88 NoReturn = TRUE;
89 finis();
90 }
15637ed4
RG
91
92 /*
93 ** Tell ARPANET to go ahead.
94 */
95
6f14531a
RG
96 if (smtpmode)
97 message("354 Enter mail, end with \".\" on a line by itself");
15637ed4
RG
98
99 /*
100 ** Try to read a UNIX-style From line
101 */
102
6f14531a
RG
103 if (sfgets(buf, MAXLINE, InChannel, TimeOuts.to_datablock,
104 "initial message read") == NULL)
15637ed4
RG
105 goto readerr;
106 fixcrlf(buf, FALSE);
107# ifndef NOTUNIX
108 if (!SaveFrom && strncmp(buf, "From ", 5) == 0)
109 {
110 if (!flusheol(buf, InChannel))
111 goto readerr;
6f14531a
RG
112 eatfrom(buf, e);
113 if (sfgets(buf, MAXLINE, InChannel, TimeOuts.to_datablock,
114 "message header read") == NULL)
15637ed4
RG
115 goto readerr;
116 fixcrlf(buf, FALSE);
117 }
6f14531a 118# endif /* NOTUNIX */
15637ed4
RG
119
120 /*
121 ** Copy InChannel to temp file & do message editing.
122 ** To keep certain mailers from getting confused,
123 ** and to keep the output clean, lines that look
124 ** like UNIX "From" lines are deleted in the header.
125 */
126
127 workbuf = buf; /* `workbuf' contains a header field */
128 freebuf = buf2; /* `freebuf' can be used for read-ahead */
129 for (;;)
130 {
6f14531a
RG
131 char *curbuf;
132 int curbuffree;
133 register int curbuflen;
134 char *p;
135
15637ed4
RG
136 /* first, see if the header is over */
137 if (!isheader(workbuf))
138 {
139 fixcrlf(workbuf, TRUE);
140 break;
141 }
142
143 /* if the line is too long, throw the rest away */
144 if (!flusheol(workbuf, InChannel))
145 goto readerr;
146
147 /* it's okay to toss '\n' now (flusheol() needed it) */
148 fixcrlf(workbuf, TRUE);
149
6f14531a
RG
150 curbuf = workbuf;
151 curbuflen = strlen(curbuf);
152 curbuffree = MAXLINE - curbuflen;
153 p = curbuf + curbuflen;
15637ed4
RG
154
155 /* get the rest of this field */
156 for (;;)
157 {
6f14531a
RG
158 int clen;
159
160 if (sfgets(freebuf, MAXLINE, InChannel,
161 TimeOuts.to_datablock,
162 "message header read") == NULL)
15637ed4
RG
163 goto readerr;
164
165 /* is this a continuation line? */
166 if (*freebuf != ' ' && *freebuf != '\t')
167 break;
168
169 if (!flusheol(freebuf, InChannel))
170 goto readerr;
171
6f14531a
RG
172 fixcrlf(freebuf, TRUE);
173 clen = strlen(freebuf) + 1;
174
175 /* if insufficient room, dynamically allocate buffer */
176 if (clen >= curbuffree)
15637ed4 177 {
6f14531a
RG
178 /* reallocate buffer */
179 int nbuflen = ((p - curbuf) + clen) * 2;
180 char *nbuf = xalloc(nbuflen);
181
182 p = nbuf + curbuflen;
183 curbuffree = nbuflen - curbuflen;
184 bcopy(curbuf, nbuf, curbuflen);
185 if (curbuf != buf && curbuf != buf2)
186 free(curbuf);
187 curbuf = nbuf;
15637ed4 188 }
6f14531a
RG
189 *p++ = '\n';
190 bcopy(freebuf, p, clen - 1);
191 p += clen - 1;
192 curbuffree -= clen;
193 curbuflen += clen;
15637ed4 194 }
6f14531a 195 *p++ = '\0';
15637ed4 196
6f14531a 197 e->e_msgsize += curbuflen;
15637ed4
RG
198
199 /*
200 ** The working buffer now becomes the free buffer, since
201 ** the free buffer contains a new header field.
202 **
203 ** This is premature, since we still havent called
204 ** chompheader() to process the field we just created
205 ** (so the call to chompheader() will use `freebuf').
206 ** This convolution is necessary so that if we break out
207 ** of the loop due to H_EOH, `workbuf' will always be
208 ** the next unprocessed buffer.
209 */
210
211 {
212 register char *tmp = workbuf;
213 workbuf = freebuf;
214 freebuf = tmp;
215 }
216
217 /*
218 ** Snarf header away.
219 */
220
6f14531a 221 if (bitset(H_EOH, chompheader(curbuf, FALSE, e)))
15637ed4 222 break;
6f14531a
RG
223
224 /*
225 ** If the buffer was dynamically allocated, free it.
226 */
227
228 if (curbuf != buf && curbuf != buf2)
229 free(curbuf);
15637ed4
RG
230 }
231
232 if (tTd(30, 1))
233 printf("EOH\n");
234
235 if (*workbuf == '\0')
236 {
237 /* throw away a blank line */
6f14531a
RG
238 if (sfgets(buf, MAXLINE, InChannel, TimeOuts.to_datablock,
239 "message separator read") == NULL)
15637ed4
RG
240 goto readerr;
241 }
242 else if (workbuf == buf2) /* guarantee `buf' contains data */
243 (void) strcpy(buf, buf2);
244
245 /*
246 ** Collect the body of the message.
247 */
248
d747e748 249 for (;;)
15637ed4
RG
250 {
251 register char *bp = buf;
252
253 fixcrlf(buf, TRUE);
254
255 /* check for end-of-message */
6f14531a 256 if (!ignrdot && buf[0] == '.' && (buf[1] == '\n' || buf[1] == '\0'))
15637ed4
RG
257 break;
258
259 /* check for transparent dot */
6f14531a 260 if (OpMode == MD_SMTP && bp[0] == '.' && bp[1] == '.')
15637ed4
RG
261 bp++;
262
263 /*
264 ** Figure message length, output the line to the temp
265 ** file, and insert a newline if missing.
266 */
267
6f14531a 268 e->e_msgsize += strlen(bp) + 1;
15637ed4
RG
269 fputs(bp, tf);
270 fputs("\n", tf);
271 if (ferror(tf))
6f14531a 272 tferror(tf, e);
d747e748
JH
273 if (sfgets(buf, MAXLINE, InChannel, TimeOuts.to_datablock,
274 "message body read") == NULL)
275 goto readerr;
276 }
15637ed4 277
d747e748
JH
278 if (feof(InChannel) || ferror(InChannel))
279 {
15637ed4 280readerr:
d747e748
JH
281 inputerr = TRUE;
282 }
283
15637ed4 284 if (fflush(tf) != 0)
6f14531a 285 tferror(tf, e);
69fc843f
AM
286 if (fsync(fileno(tf)) < 0 || fclose(tf) < 0)
287 {
288 syserr("cannot sync message data to disk (%s)", e->e_df);
289 finis();
290 }
15637ed4
RG
291
292 /* An EOF when running SMTP is an error */
d747e748 293 if (inputerr && OpMode == MD_SMTP)
15637ed4 294 {
6f14531a 295 char *host;
d747e748 296 char *problem;
6f14531a
RG
297
298 host = RealHostName;
299 if (host == NULL)
300 host = "localhost";
301
d747e748
JH
302 if (feof(InChannel))
303 problem = "unexpected close";
304 else if (ferror(InChannel))
305 problem = "I/O error";
306 else
307 problem = "read timeout";
15637ed4 308# ifdef LOG
6f14531a 309 if (LogLevel > 0 && feof(InChannel))
15637ed4 310 syslog(LOG_NOTICE,
d747e748
JH
311 "collect: %s on connection from %s, sender=%s: %m\n",
312 problem, host, e->e_from.q_paddr);
15637ed4 313# endif
6f14531a 314 (feof(InChannel) ? usrerr : syserr)
d747e748
JH
315 ("451 collect: %s on connection from %s, from=%s",
316 problem, host, e->e_from.q_paddr);
15637ed4
RG
317
318 /* don't return an error indication */
6f14531a
RG
319 e->e_to = NULL;
320 e->e_flags &= ~EF_FATALERRS;
d747e748 321 e->e_flags |= EF_CLRQUEUE;
15637ed4
RG
322
323 /* and don't try to deliver the partial message either */
d747e748
JH
324 if (InChild)
325 ExitStat = EX_QUIT;
15637ed4
RG
326 finis();
327 }
328
329 /*
330 ** Find out some information from the headers.
331 ** Examples are who is the from person & the date.
332 */
333
6f14531a 334 eatheader(e, !requeueflag);
15637ed4 335
d747e748
JH
336 /* collect statistics */
337 if (OpMode != MD_VERIFY)
338 markstats(e, (ADDRESS *) NULL);
339
15637ed4
RG
340 /*
341 ** Add an Apparently-To: line if we have no recipient lines.
342 */
343
6f14531a
RG
344 if (hvalue("to", e) == NULL && hvalue("cc", e) == NULL &&
345 hvalue("bcc", e) == NULL && hvalue("apparently-to", e) == NULL)
15637ed4
RG
346 {
347 register ADDRESS *q;
348
349 /* create an Apparently-To: field */
350 /* that or reject the message.... */
6f14531a 351 for (q = e->e_sendqueue; q != NULL; q = q->q_next)
15637ed4
RG
352 {
353 if (q->q_alias != NULL)
354 continue;
355 if (tTd(30, 3))
356 printf("Adding Apparently-To: %s\n", q->q_paddr);
6f14531a 357 addheader("Apparently-To", q->q_paddr, e);
15637ed4
RG
358 }
359 }
360
6f14531a
RG
361 /* check for message too large */
362 if (MaxMessageSize > 0 && e->e_msgsize > MaxMessageSize)
363 {
364 usrerr("552 Message exceeds maximum fixed size (%ld)",
365 MaxMessageSize);
366 }
367
368 if ((e->e_dfp = fopen(e->e_df, "r")) == NULL)
369 {
370 /* we haven't acked receipt yet, so just chuck this */
371 syserr("Cannot reopen %s", e->e_df);
372 finis();
373 }
15637ed4
RG
374}
375\f/*
376** FLUSHEOL -- if not at EOL, throw away rest of input line.
377**
378** Parameters:
379** buf -- last line read in (checked for '\n'),
380** fp -- file to be read from.
381**
382** Returns:
383** FALSE on error from sfgets(), TRUE otherwise.
384**
385** Side Effects:
386** none.
387*/
388
389bool
390flusheol(buf, fp)
391 char *buf;
392 FILE *fp;
393{
15637ed4 394 register char *p = buf;
6f14531a
RG
395 bool printmsg = TRUE;
396 char junkbuf[MAXLINE];
15637ed4 397
6f14531a
RG
398 while (strchr(p, '\n') == NULL)
399 {
400 if (printmsg)
401 usrerr("553 header line too long");
402 printmsg = FALSE;
403 if (sfgets(junkbuf, MAXLINE, fp, TimeOuts.to_datablock,
404 "long line flush") == NULL)
405 return (FALSE);
15637ed4
RG
406 p = junkbuf;
407 }
408
6f14531a 409 return (TRUE);
15637ed4
RG
410}
411\f/*
412** TFERROR -- signal error on writing the temporary file.
413**
414** Parameters:
415** tf -- the file pointer for the temporary file.
416**
417** Returns:
418** none.
419**
420** Side Effects:
421** Gives an error message.
422** Arranges for following output to go elsewhere.
423*/
424
6f14531a 425tferror(tf, e)
15637ed4 426 FILE *tf;
6f14531a 427 register ENVELOPE *e;
15637ed4
RG
428{
429 if (errno == ENOSPC)
430 {
6f14531a 431 (void) freopen(e->e_df, "w", tf);
15637ed4
RG
432 fputs("\nMAIL DELETED BECAUSE OF LACK OF DISK SPACE\n\n", tf);
433 usrerr("452 Out of disk space for temp file");
434 }
435 else
6f14531a 436 syserr("collect: Cannot write %s", e->e_df);
15637ed4
RG
437 (void) freopen("/dev/null", "w", tf);
438}
439\f/*
440** EATFROM -- chew up a UNIX style from line and process
441**
442** This does indeed make some assumptions about the format
443** of UNIX messages.
444**
445** Parameters:
446** fm -- the from line.
447**
448** Returns:
449** none.
450**
451** Side Effects:
452** extracts what information it can from the header,
453** such as the date.
454*/
455
456# ifndef NOTUNIX
457
458char *DowList[] =
459{
460 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", NULL
461};
462
463char *MonthList[] =
464{
465 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
466 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
467 NULL
468};
469
6f14531a 470eatfrom(fm, e)
15637ed4 471 char *fm;
6f14531a 472 register ENVELOPE *e;
15637ed4
RG
473{
474 register char *p;
475 register char **dt;
476
477 if (tTd(30, 2))
478 printf("eatfrom(%s)\n", fm);
479
480 /* find the date part */
481 p = fm;
482 while (*p != '\0')
483 {
484 /* skip a word */
485 while (*p != '\0' && *p != ' ')
486 p++;
487 while (*p == ' ')
488 p++;
6f14531a
RG
489 if (!(isascii(*p) && isupper(*p)) ||
490 p[3] != ' ' || p[13] != ':' || p[16] != ':')
15637ed4
RG
491 continue;
492
493 /* we have a possible date */
494 for (dt = DowList; *dt != NULL; dt++)
495 if (strncmp(*dt, p, 3) == 0)
496 break;
497 if (*dt == NULL)
498 continue;
499
500 for (dt = MonthList; *dt != NULL; dt++)
501 if (strncmp(*dt, &p[4], 3) == 0)
502 break;
503 if (*dt != NULL)
504 break;
505 }
506
6f14531a 507 if (*p != '\0')
15637ed4
RG
508 {
509 char *q;
510 extern char *arpadate();
511
512 /* we have found a date */
513 q = xalloc(25);
514 (void) strncpy(q, p, 25);
515 q[24] = '\0';
15637ed4 516 q = arpadate(q);
6f14531a 517 define('a', newstr(q), e);
15637ed4
RG
518 }
519}
520
6f14531a 521# endif /* NOTUNIX */