don't close seq maps -- this causes double-closes
[unix-history] / usr / src / usr.sbin / sendmail / src / collect.c
CommitLineData
d185cb11 1/*
dc45ba8c 2 * Copyright (c) 1983 Eric P. Allman
31de980b
KB
3 * Copyright (c) 1988, 1993
4 * The Regents of the University of California. All rights reserved.
bee79b64 5 *
417f7a11 6 * %sccs.include.redist.c%
bee79b64 7 */
d185cb11
DF
8
9#ifndef lint
7d858063 10static char sccsid[] = "@(#)collect.c 8.26 (Berkeley) %G%";
bee79b64 11#endif /* not lint */
d185cb11 12
b8020f57 13# include <errno.h>
96faada8 14# include "sendmail.h"
cb590f52 15
cb590f52 16/*
a530c75f 17** COLLECT -- read & parse message header & make temp file.
cb590f52
EA
18**
19** Creates a temporary file name and copies the standard
7338e3d4
EA
20** input to that file. Leading UNIX-style "From" lines are
21** stripped off (after important information is extracted).
cb590f52
EA
22**
23** Parameters:
c23930c0 24** fp -- file to read.
17df0fcb
EA
25** from -- the person we think it may be from. If
26** there is a "From" line, we will replace
27** the name of the person by this. If NULL,
28** do no such replacement.
cb590f52
EA
29**
30** Returns:
17df0fcb
EA
31** Name of the "from" person extracted from the
32** arpanet header.
cb590f52
EA
33**
34** Side Effects:
35** Temp file is created and filled.
dc39c568 36** The from person may be set.
cb590f52
EA
37*/
38
fd540fcc
EA
39char *CollectErrorMessage;
40bool CollectErrno;
41
0d721ffb
EA
42static jmp_buf CtxCollectTimeout;
43static int collecttimeout();
44static bool CollectProgress;
45static EVENT *CollectTimeout;
46
47/* values for input state machine */
48#define IS_NORM 0 /* middle of line */
49#define IS_BOL 1 /* beginning of line */
50#define IS_DOT 2 /* read a dot at beginning of line */
51#define IS_DOTCR 3 /* read ".\r" at beginning of line */
52#define IS_CR 4 /* read a carriage return */
53
54/* values for message state machine */
55#define MS_UFROM 0 /* reading Unix from line */
56#define MS_HEADER 1 /* reading message header */
57#define MS_BODY 2 /* reading message body */
58
59
17df0fcb
EA
60maketemp(from)
61 char *from;
cb590f52
EA
62{
63 register FILE *tf;
6f121a53 64 bool ignrdot = smtpmode ? FALSE : IgnrDot;
77ca787d 65 time_t dbto = smtpmode ? TimeOuts.to_datablock : 0;
0d721ffb
EA
66 register char *bp;
67 register int c;
fe3849ea 68 bool inputerr = FALSE;
c23930c0 69 bool headeronly = FALSE;
0d721ffb
EA
70 char *buf;
71 int buflen;
72 int istate;
73 int mstate;
74 char *pbp;
75 char peekbuf[8];
76 char bufbuf[MAXLINE];
0d721ffb 77 extern bool isheader();
17df0fcb 78 extern char *index();
cb590f52 79
fd540fcc
EA
80 CollectErrorMessage = NULL;
81 CollectErrno = 0;
c23930c0
EA
82 if (hdrp == NULL)
83 hdrp = &e->e_header;
84 else
85 headeronly = TRUE;
fd540fcc 86
cb590f52
EA
87 /*
88 ** Create the temp file name and create the file.
89 */
90
c23930c0 91 if (!headeronly)
cb590f52 92 {
f009d24b
EA
93 struct stat stbuf;
94
c23930c0
EA
95 e->e_df = queuename(e, 'd');
96 e->e_df = newstr(e->e_df);
97 if ((tf = dfopen(e->e_df, O_WRONLY|O_CREAT|O_TRUNC, FileMode)) == NULL)
98 {
99 syserr("Cannot create %s", e->e_df);
100 e->e_flags |= EF_NORETURN;
101 finis();
102 }
f009d24b
EA
103 if (fstat(fileno(tf), &stbuf) < 0)
104 e->e_dfino = -1;
105 else
bb4fde22
EA
106 {
107 e->e_dfdev = stbuf.st_dev;
f009d24b 108 e->e_dfino = stbuf.st_ino;
bb4fde22 109 }
c23930c0 110 HasEightBits = FALSE;
cb590f52
EA
111 }
112
d6b27179
EA
113 /*
114 ** Tell ARPANET to go ahead.
115 */
116
6f121a53 117 if (smtpmode)
b6edea3d 118 message("354 Enter mail, end with \".\" on a line by itself");
d6b27179 119
74c5fe7c 120 /*
0d721ffb
EA
121 ** Read the message.
122 **
123 ** This is done using two interleaved state machines.
124 ** The input state machine is looking for things like
125 ** hidden dots; the message state machine is handling
126 ** the larger picture (e.g., header versus body).
74c5fe7c
EA
127 */
128
0d721ffb
EA
129 buf = bp = bufbuf;
130 buflen = sizeof bufbuf;
131 pbp = peekbuf;
132 istate = IS_BOL;
133 mstate = SaveFrom ? MS_HEADER : MS_UFROM;
134 CollectProgress = FALSE;
1a12c7d6 135
0d721ffb
EA
136 /* if transmitting binary, don't map NL to EOL */
137 if (e->e_bodytype != NULL && strcasecmp(e->e_bodytype, "8BITMIME") == 0)
138 e->e_flags |= EF_NL_NOT_EOL;
cb590f52 139
0d721ffb 140 if (dbto != 0)
cb590f52 141 {
0d721ffb
EA
142 /* handle possible input timeout */
143 if (setjmp(CtxCollectTimeout) != 0)
a909e430 144 {
0d721ffb
EA
145#ifdef LOG
146 syslog(LOG_NOTICE,
147 "timeout waiting for input from %s during message collect",
148 CurHostName ? CurHostName : "<local machine>");
149#endif
150 errno = 0;
151 usrerr("451 timeout waiting for input during message collect");
a909e430 152 goto readerr;
0d721ffb
EA
153 }
154 CollectTimeout = setevent(dbto, collecttimeout, dbto);
155 }
76f46f56 156
0d721ffb
EA
157 for (;;)
158 {
159 if (tTd(30, 35))
160 printf("top, istate=%d, mstate=%d\n", istate, mstate);
a909e430 161 for (;;)
cb590f52 162 {
0d721ffb
EA
163 if (pbp > peekbuf)
164 c = *--pbp;
165 else
7378b013 166 {
0d721ffb
EA
167 while (!feof(InChannel) && !ferror(InChannel))
168 {
169 errno = 0;
170 c = fgetc(InChannel);
171 if (errno != EINTR)
172 break;
173 clearerr(InChannel);
174 }
175 CollectProgress = TRUE;
176 if (TrafficLogFile != NULL)
177 {
178 if (istate == IS_BOL)
179 fprintf(TrafficLogFile, "%05d <<< ",
180 getpid());
181 if (c == EOF)
182 fprintf(TrafficLogFile, "[EOF]\n");
183 else
184 fputc(c, TrafficLogFile);
185 }
186 if (c == EOF)
187 goto readerr;
188 if (SevenBitInput)
189 c &= 0x7f;
190 else
191 HasEightBits |= bitset(0x80, c);
192 e->e_msgsize++;
7378b013 193 }
0d721ffb
EA
194 if (tTd(30, 94))
195 printf("istate=%d, c=%c (0x%x)\n",
196 istate, c, c);
197 switch (istate)
198 {
199 case IS_BOL:
200 if (c == '.')
201 {
202 istate = IS_DOT;
203 continue;
204 }
205 break;
a909e430 206
0d721ffb
EA
207 case IS_DOT:
208 if (c == '\n' && !ignrdot &&
209 !bitset(EF_NL_NOT_EOL, e->e_flags))
210 goto readerr;
211 else if (c == '\r' &&
212 !bitset(EF_CRLF_NOT_EOL, e->e_flags))
213 {
214 istate = IS_DOTCR;
215 continue;
216 }
217 else if (c != '.' ||
218 (OpMode != MD_SMTP &&
219 OpMode != MD_DAEMON &&
220 OpMode != MD_ARPAFTP))
221 {
222 *pbp++ = c;
223 c = '.';
224 }
1a12c7d6 225 break;
a909e430 226
0d721ffb
EA
227 case IS_DOTCR:
228 if (c == '\n')
229 goto readerr;
230 else
231 {
232 /* push back the ".\rx" */
233 *pbp++ = c;
234 *pbp++ = '\r';
235 c = '.';
236 }
237 break;
a909e430 238
0d721ffb 239 case IS_CR:
8eea9dcb
EA
240 if (c == '\n')
241 istate = IS_BOL;
242 else
0d721ffb
EA
243 {
244 ungetc(c, InChannel);
245 c = '\r';
8eea9dcb 246 istate = IS_NORM;
0d721ffb 247 }
8eea9dcb 248 goto bufferchar;
0d721ffb 249 }
f9e990ef 250
8eea9dcb 251 if (c == '\r' && !bitset(EF_CRLF_NOT_EOL, e->e_flags))
a909e430 252 {
0d721ffb
EA
253 istate = IS_CR;
254 continue;
a909e430 255 }
0d721ffb
EA
256 else if (c == '\n' && !bitset(EF_NL_NOT_EOL, e->e_flags))
257 istate = IS_BOL;
258 else
259 istate = IS_NORM;
a909e430 260
8eea9dcb 261bufferchar:
0d721ffb
EA
262 if (mstate == MS_BODY)
263 {
264 /* just put the character out */
2ccb80de
EA
265 if (MaxMessageSize <= 0 ||
266 e->e_msgsize <= MaxMessageSize)
267 fputc(c, tf);
0d721ffb
EA
268 continue;
269 }
c23930c0 270
0d721ffb
EA
271 /* header -- buffer up */
272 if (bp >= &buf[buflen - 2])
273 {
274 char *obuf;
275
276 if (mstate != MS_HEADER)
277 break;
278
279 /* out of space for header */
280 obuf = buf;
281 if (buflen < MEMCHUNKSIZE)
282 buflen *= 2;
283 else
284 buflen += MEMCHUNKSIZE;
285 buf = xalloc(buflen);
286 bcopy(obuf, buf, bp - obuf);
287 bp = &buf[bp - obuf];
288 if (obuf != bufbuf)
289 free(obuf);
290 }
291 *bp++ = c;
292 if (istate == IS_BOL)
293 break;
294 }
295 *bp = '\0';
1a12c7d6 296
0d721ffb
EA
297nextstate:
298 if (tTd(30, 35))
299 printf("nextstate, istate=%d, mstate=%d, line = \"%s\"\n",
300 istate, mstate, buf);
301 switch (mstate)
302 {
303 case MS_UFROM:
304 mstate = MS_HEADER;
305 if (strncmp(buf, "From ", 5) == 0)
306 {
c9845a7c 307 bp = buf;
0d721ffb
EA
308 eatfrom(buf, e);
309 continue;
310 }
311 /* fall through */
cb590f52 312
0d721ffb
EA
313 case MS_HEADER:
314 if (!isheader(buf))
315 {
316 mstate = MS_BODY;
317 goto nextstate;
318 }
71326571 319
0d721ffb
EA
320 /* check for possible continuation line */
321 do
322 {
323 clearerr(InChannel);
324 errno = 0;
325 c = fgetc(InChannel);
326 } while (errno == EINTR);
327 if (c != EOF)
328 ungetc(c, InChannel);
329 if (c == ' ' || c == '\t')
330 {
331 /* yep -- defer this */
332 continue;
333 }
2768afe3 334
0d721ffb
EA
335 /* trim off trailing CRLF or NL */
336 if (*--bp != '\n' || *--bp != '\r')
337 bp++;
338 *bp = '\0';
339 if (bitset(H_EOH, chompheader(buf, FALSE, e)))
340 mstate = MS_BODY;
1a12c7d6
EA
341 break;
342
0d721ffb
EA
343 case MS_BODY:
344 if (tTd(30, 1))
345 printf("EOH\n");
346 if (headeronly)
347 goto readerr;
348 bp = buf;
cbdb7357 349
0d721ffb
EA
350 /* toss blank line */
351 if ((!bitset(EF_CRLF_NOT_EOL, e->e_flags) &&
352 bp[0] == '\r' && bp[1] == '\n') ||
353 (!bitset(EF_NL_NOT_EOL, e->e_flags) &&
354 bp[0] == '\n'))
355 {
356 break;
357 }
71326571 358
0d721ffb 359 /* if not a blank separator, write it out */
2ccb80de
EA
360 if (MaxMessageSize <= 0 ||
361 e->e_msgsize <= MaxMessageSize)
362 {
363 while (*bp != '\0')
364 fputc(*bp++, tf);
365 }
0d721ffb
EA
366 break;
367 }
368 bp = buf;
fe3849ea 369 }
a909e430
KB
370
371readerr:
c23930c0
EA
372 if ((feof(fp) && smtpmode) || ferror(fp))
373 {
7378b013
EA
374 if (tTd(30, 1))
375 printf("collect: read error\n");
fe3849ea
EA
376 inputerr = TRUE;
377 }
378
0ab15a80 379 /* reset global timer */
0d721ffb 380 clrevent(CollectTimeout);
0ab15a80 381
c23930c0
EA
382 if (headeronly)
383 return;
384
385 if (tf != NULL)
eb1ada3c 386 {
c23930c0
EA
387 if (fflush(tf) != 0)
388 tferror(tf, e);
389 if (fsync(fileno(tf)) < 0 || fclose(tf) < 0)
390 {
391 tferror(tf, e);
392 finis();
393 }
eb1ada3c 394 }
1a12c7d6 395
fd540fcc 396 if (CollectErrorMessage != NULL && Errors <= 0)
b65a52af 397 {
fd540fcc
EA
398 if (CollectErrno != 0)
399 {
400 errno = CollectErrno;
401 syserr(CollectErrorMessage, e->e_df);
402 finis();
403 }
404 usrerr(CollectErrorMessage);
405 }
406 else if (inputerr && (OpMode == MD_SMTP || OpMode == MD_DAEMON))
407 {
408 /* An EOF when running SMTP is an error */
5973222c 409 char *host;
fe3849ea 410 char *problem;
1c7897ef 411
5973222c
EA
412 host = RealHostName;
413 if (host == NULL)
414 host = "localhost";
415
c23930c0 416 if (feof(fp))
fe3849ea 417 problem = "unexpected close";
c23930c0 418 else if (ferror(fp))
fe3849ea
EA
419 problem = "I/O error";
420 else
421 problem = "read timeout";
2e15a2d8 422# ifdef LOG
c23930c0 423 if (LogLevel > 0 && feof(fp))
ab4889ea 424 syslog(LOG_NOTICE,
67d76dc4
EA
425 "collect: %s on connection from %s, sender=%s: %s\n",
426 problem, host, e->e_from.q_paddr, errstring(errno));
2e15a2d8 427# endif
c23930c0 428 if (feof(fp))
90f66104
EA
429 usrerr("451 collect: %s on connection from %s, from=%s",
430 problem, host, e->e_from.q_paddr);
431 else
432 syserr("451 collect: %s on connection from %s, from=%s",
fe3849ea 433 problem, host, e->e_from.q_paddr);
b65a52af
EA
434
435 /* don't return an error indication */
a4076aed
EA
436 e->e_to = NULL;
437 e->e_flags &= ~EF_FATALERRS;
1023828f 438 e->e_flags |= EF_CLRQUEUE;
b65a52af
EA
439
440 /* and don't try to deliver the partial message either */
fe3849ea
EA
441 if (InChild)
442 ExitStat = EX_QUIT;
b65a52af
EA
443 finis();
444 }
0e33b011 445
1a12c7d6
EA
446 /*
447 ** Find out some information from the headers.
2f0c5bd8 448 ** Examples are who is the from person & the date.
1a12c7d6
EA
449 */
450
959cf51d 451 eatheader(e, !requeueflag);
9678c96d 452
f50419dd
EA
453 /* collect statistics */
454 if (OpMode != MD_VERIFY)
455 markstats(e, (ADDRESS *) NULL);
456
75a2bcaf
EA
457 /*
458 ** Add an Apparently-To: line if we have no recipient lines.
459 */
1a12c7d6 460
c23930c0
EA
461 if (hvalue("to", e->e_header) == NULL &&
462 hvalue("cc", e->e_header) == NULL &&
463 hvalue("bcc", e->e_header) == NULL &&
464 hvalue("apparently-to", e->e_header) == NULL)
f0375650
EA
465 {
466 register ADDRESS *q;
467
468 /* create an Apparently-To: field */
469 /* that or reject the message.... */
a4076aed 470 for (q = e->e_sendqueue; q != NULL; q = q->q_next)
f0375650 471 {
755d533c
EA
472 if (q->q_alias != NULL)
473 continue;
9678c96d 474 if (tTd(30, 3))
f0375650 475 printf("Adding Apparently-To: %s\n", q->q_paddr);
c23930c0 476 addheader("Apparently-To", q->q_paddr, &e->e_header);
f0375650
EA
477 }
478 }
479
213020d2
EA
480 /* check for message too large */
481 if (MaxMessageSize > 0 && e->e_msgsize > MaxMessageSize)
482 {
483 usrerr("552 Message exceeds maximum fixed size (%ld)",
484 MaxMessageSize);
485 }
486
e3c84ea8
EA
487 /* check for illegal 8-bit data */
488 if (HasEightBits)
489 {
490 e->e_flags |= EF_HAS8BIT;
7d858063 491 if (!bitset(MM_PASS8BIT|MM_MIME8BIT, MimeMode))
e3c84ea8
EA
492 usrerr("554 Eight bit data not allowed");
493 }
494
a4076aed 495 if ((e->e_dfp = fopen(e->e_df, "r")) == NULL)
e0539260
EA
496 {
497 /* we haven't acked receipt yet, so just chuck this */
a4076aed 498 syserr("Cannot reopen %s", e->e_df);
e0539260
EA
499 finis();
500 }
1a12c7d6 501}
a909e430 502
a909e430 503
0d721ffb
EA
504static
505collecttimeout(timeout)
506 time_t timeout;
507{
508 /* if no progress was made, die now */
509 if (!CollectProgress)
510 longjmp(CtxCollectTimeout, 1);
a909e430 511
0d721ffb
EA
512 /* otherwise reset the timeout */
513 CollectTimeout = setevent(timeout, collecttimeout, timeout);
514 CollectProgress = FALSE;
a909e430
KB
515}
516\f/*
83f057f2
EA
517** TFERROR -- signal error on writing the temporary file.
518**
519** Parameters:
520** tf -- the file pointer for the temporary file.
521**
522** Returns:
523** none.
524**
525** Side Effects:
526** Gives an error message.
527** Arranges for following output to go elsewhere.
528*/
529
a4076aed 530tferror(tf, e)
83f057f2 531 FILE *tf;
a4076aed 532 register ENVELOPE *e;
83f057f2 533{
fd540fcc 534 CollectErrno = errno;
83f057f2
EA
535 if (errno == ENOSPC)
536 {
5825c126
EA
537 struct stat st;
538 long avail;
539 long bsize;
540
2bade550 541 e->e_flags |= EF_NORETURN;
5825c126
EA
542 if (fstat(fileno(tf), &st) < 0)
543 st.st_size = 0;
a4076aed 544 (void) freopen(e->e_df, "w", tf);
5825c126
EA
545 if (st.st_size <= 0)
546 fprintf(tf, "\n*** Mail could not be accepted");
547 else if (sizeof st.st_size > sizeof (long))
548 fprintf(tf, "\n*** Mail of at least %qd bytes could not be accepted\n",
549 st.st_size);
550 else
551 fprintf(tf, "\n*** Mail of at least %ld bytes could not be accepted\n",
552 st.st_size);
553 fprintf(tf, "*** at %s due to lack of disk space for temp file.\n",
554 MyHostName);
555 avail = freespace(QueueDir, &bsize);
556 if (avail > 0)
557 {
558 if (bsize > 1024)
559 avail *= bsize / 1024;
560 else if (bsize < 1024)
561 avail /= 1024 / bsize;
562 fprintf(tf, "*** Currently, %ld kilobytes are available for mail temp files.\n",
563 avail);
564 }
fd540fcc 565 CollectErrorMessage = "452 Out of disk space for temp file";
83f057f2
EA
566 }
567 else
fd540fcc
EA
568 {
569 CollectErrorMessage = "cannot write message body to disk (%s)";
570 }
83f057f2
EA
571 (void) freopen("/dev/null", "w", tf);
572}
573\f/*
1a12c7d6
EA
574** EATFROM -- chew up a UNIX style from line and process
575**
576** This does indeed make some assumptions about the format
577** of UNIX messages.
578**
579** Parameters:
580** fm -- the from line.
581**
582** Returns:
583** none.
584**
585** Side Effects:
586** extracts what information it can from the header,
2f0c5bd8 587** such as the date.
1a12c7d6
EA
588*/
589
a4758674
EA
590# ifndef NOTUNIX
591
823c5e31
EA
592char *DowList[] =
593{
594 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", NULL
595};
596
1a12c7d6
EA
597char *MonthList[] =
598{
599 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
600 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
601 NULL
602};
603
a4076aed 604eatfrom(fm, e)
1a12c7d6 605 char *fm;
a4076aed 606 register ENVELOPE *e;
1a12c7d6
EA
607{
608 register char *p;
609 register char **dt;
610
9678c96d 611 if (tTd(30, 2))
823c5e31 612 printf("eatfrom(%s)\n", fm);
823c5e31 613
1a12c7d6
EA
614 /* find the date part */
615 p = fm;
616 while (*p != '\0')
617 {
618 /* skip a word */
619 while (*p != '\0' && *p != ' ')
34fe0a9b 620 p++;
1a12c7d6 621 while (*p == ' ')
34fe0a9b 622 p++;
2bee003d
EA
623 if (!(isascii(*p) && isupper(*p)) ||
624 p[3] != ' ' || p[13] != ':' || p[16] != ':')
1a12c7d6
EA
625 continue;
626
627 /* we have a possible date */
823c5e31 628 for (dt = DowList; *dt != NULL; dt++)
1a12c7d6
EA
629 if (strncmp(*dt, p, 3) == 0)
630 break;
823c5e31
EA
631 if (*dt == NULL)
632 continue;
1a12c7d6 633
823c5e31
EA
634 for (dt = MonthList; *dt != NULL; dt++)
635 if (strncmp(*dt, &p[4], 3) == 0)
636 break;
1a12c7d6
EA
637 if (*dt != NULL)
638 break;
639 }
640
54d35e43 641 if (*p != '\0')
1a12c7d6 642 {
2f0c5bd8 643 char *q;
e863b1fa 644 extern char *arpadate();
2f0c5bd8 645
1a12c7d6 646 /* we have found a date */
2f0c5bd8 647 q = xalloc(25);
03388044 648 (void) strncpy(q, p, 25);
2f0c5bd8 649 q[24] = '\0';
e863b1fa 650 q = arpadate(q);
a4076aed 651 define('a', newstr(q), e);
1a12c7d6
EA
652 }
653}
a4758674 654
f3d8f6d6 655# endif /* NOTUNIX */