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