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.44 (Berkeley) 06/10/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 (dbto != 0) 142 { 143 /* handle possible input timeout */ 144 if (setjmp(CtxCollectTimeout) != 0) 145 { 146 #ifdef LOG 147 syslog(LOG_NOTICE, 148 "timeout waiting for input from %s during message collect", 149 CurHostName ? CurHostName : "<local machine>"); 150 #endif 151 errno = 0; 152 usrerr("451 timeout waiting for input during message collect"); 153 goto readerr; 154 } 155 CollectTimeout = setevent(dbto, collecttimeout, dbto); 156 } 157 158 for (;;) 159 { 160 if (tTd(30, 35)) 161 printf("top, istate=%d, mstate=%d\n", istate, mstate); 162 for (;;) 163 { 164 if (pbp > peekbuf) 165 c = *--pbp; 166 else 167 { 168 while (!feof(fp) && !ferror(fp)) 169 { 170 errno = 0; 171 c = getc(fp); 172 if (errno != EINTR) 173 break; 174 clearerr(fp); 175 } 176 CollectProgress = TRUE; 177 if (TrafficLogFile != NULL && !headeronly) 178 { 179 if (istate == IS_BOL) 180 fprintf(TrafficLogFile, "%05d <<< ", 181 getpid()); 182 if (c == EOF) 183 fprintf(TrafficLogFile, "[EOF]\n"); 184 else 185 putc(c, TrafficLogFile); 186 } 187 if (c == EOF) 188 goto readerr; 189 if (SevenBitInput) 190 c &= 0x7f; 191 else 192 HasEightBits |= bitset(0x80, c); 193 if (!headeronly) 194 e->e_msgsize++; 195 } 196 if (tTd(30, 94)) 197 printf("istate=%d, c=%c (0x%x)\n", 198 istate, c, c); 199 switch (istate) 200 { 201 case IS_BOL: 202 if (c == '.') 203 { 204 istate = IS_DOT; 205 continue; 206 } 207 break; 208 209 case IS_DOT: 210 if (c == '\n' && !ignrdot && 211 !bitset(EF_NL_NOT_EOL, e->e_flags)) 212 goto readerr; 213 else if (c == '\r' && 214 !bitset(EF_CRLF_NOT_EOL, e->e_flags)) 215 { 216 istate = IS_DOTCR; 217 continue; 218 } 219 else if (c != '.' || 220 (OpMode != MD_SMTP && 221 OpMode != MD_DAEMON && 222 OpMode != MD_ARPAFTP)) 223 { 224 *pbp++ = c; 225 c = '.'; 226 } 227 break; 228 229 case IS_DOTCR: 230 if (c == '\n') 231 goto readerr; 232 else 233 { 234 /* push back the ".\rx" */ 235 *pbp++ = c; 236 *pbp++ = '\r'; 237 c = '.'; 238 } 239 break; 240 241 case IS_CR: 242 if (c == '\n') 243 istate = IS_BOL; 244 else 245 { 246 ungetc(c, fp); 247 c = '\r'; 248 istate = IS_NORM; 249 } 250 goto bufferchar; 251 } 252 253 if (c == '\r' && !bitset(EF_CRLF_NOT_EOL, e->e_flags)) 254 { 255 istate = IS_CR; 256 continue; 257 } 258 else if (c == '\n' && !bitset(EF_NL_NOT_EOL, e->e_flags)) 259 istate = IS_BOL; 260 else 261 istate = IS_NORM; 262 263 bufferchar: 264 if (mstate == MS_BODY) 265 { 266 /* just put the character out */ 267 if (MaxMessageSize <= 0 || 268 e->e_msgsize <= MaxMessageSize) 269 putc(c, tf); 270 continue; 271 } 272 273 /* header -- buffer up */ 274 if (bp >= &buf[buflen - 2]) 275 { 276 char *obuf; 277 278 if (mstate != MS_HEADER) 279 break; 280 281 /* out of space for header */ 282 obuf = buf; 283 if (buflen < MEMCHUNKSIZE) 284 buflen *= 2; 285 else 286 buflen += MEMCHUNKSIZE; 287 buf = xalloc(buflen); 288 bcopy(obuf, buf, bp - obuf); 289 bp = &buf[bp - obuf]; 290 if (obuf != bufbuf) 291 free(obuf); 292 } 293 if (c != '\0') 294 *bp++ = c; 295 if (istate == IS_BOL) 296 break; 297 } 298 *bp = '\0'; 299 300 nextstate: 301 if (tTd(30, 35)) 302 printf("nextstate, istate=%d, mstate=%d, line = \"%s\"\n", 303 istate, mstate, buf); 304 switch (mstate) 305 { 306 extern int chompheader(); 307 308 case MS_UFROM: 309 mstate = MS_HEADER; 310 if (strncmp(buf, "From ", 5) == 0) 311 { 312 extern void eatfrom(); 313 314 bp = buf; 315 eatfrom(buf, e); 316 continue; 317 } 318 /* fall through */ 319 320 case MS_HEADER: 321 if (!isheader(buf)) 322 { 323 mstate = MS_BODY; 324 goto nextstate; 325 } 326 327 /* check for possible continuation line */ 328 do 329 { 330 clearerr(fp); 331 errno = 0; 332 c = getc(fp); 333 } while (errno == EINTR); 334 if (c != EOF) 335 ungetc(c, fp); 336 if (c == ' ' || c == '\t') 337 { 338 /* yep -- defer this */ 339 continue; 340 } 341 342 /* trim off trailing CRLF or NL */ 343 if (*--bp != '\n' || *--bp != '\r') 344 bp++; 345 *bp = '\0'; 346 if (bitset(H_EOH, chompheader(buf, FALSE, hdrp, e))) 347 mstate = MS_BODY; 348 break; 349 350 case MS_BODY: 351 if (tTd(30, 1)) 352 printf("EOH\n"); 353 if (headeronly) 354 goto readerr; 355 bp = buf; 356 357 /* toss blank line */ 358 if ((!bitset(EF_CRLF_NOT_EOL, e->e_flags) && 359 bp[0] == '\r' && bp[1] == '\n') || 360 (!bitset(EF_NL_NOT_EOL, e->e_flags) && 361 bp[0] == '\n')) 362 { 363 break; 364 } 365 366 /* if not a blank separator, write it out */ 367 if (MaxMessageSize <= 0 || 368 e->e_msgsize <= MaxMessageSize) 369 { 370 while (*bp != '\0') 371 putc(*bp++, tf); 372 } 373 break; 374 } 375 bp = buf; 376 } 377 378 readerr: 379 if ((feof(fp) && smtpmode) || ferror(fp)) 380 { 381 const char *errmsg = errstring(errno); 382 383 if (tTd(30, 1)) 384 printf("collect: premature EOM: %s\n", errmsg); 385 #ifdef LOG 386 if (LogLevel >= 2) 387 syslog(LOG_WARNING, "collect: premature EOM: %s", errmsg); 388 #endif 389 inputerr = TRUE; 390 } 391 392 /* reset global timer */ 393 clrevent(CollectTimeout); 394 395 if (headeronly) 396 return; 397 398 if (tf != NULL) 399 { 400 if (fflush(tf) != 0) 401 tferror(tf, e); 402 if (fsync(fileno(tf)) < 0 || fclose(tf) < 0) 403 { 404 tferror(tf, e); 405 finis(); 406 } 407 } 408 409 /* An EOF when running SMTP is an error */ 410 if (inputerr && (OpMode == MD_SMTP || OpMode == MD_DAEMON)) 411 { 412 char *host; 413 char *problem; 414 415 host = RealHostName; 416 if (host == NULL) 417 host = "localhost"; 418 419 if (feof(fp)) 420 problem = "unexpected close"; 421 else if (ferror(fp)) 422 problem = "I/O error"; 423 else 424 problem = "read timeout"; 425 # ifdef LOG 426 if (LogLevel > 0 && feof(fp)) 427 syslog(LOG_NOTICE, 428 "collect: %s on connection from %s, sender=%s: %s\n", 429 problem, host, e->e_from.q_paddr, errstring(errno)); 430 # endif 431 if (feof(fp)) 432 usrerr("451 collect: %s on connection from %s, from=%s", 433 problem, host, e->e_from.q_paddr); 434 else 435 syserr("451 collect: %s on connection from %s, from=%s", 436 problem, host, e->e_from.q_paddr); 437 438 /* don't return an error indication */ 439 e->e_to = NULL; 440 e->e_flags &= ~EF_FATALERRS; 441 e->e_flags |= EF_CLRQUEUE; 442 443 /* and don't try to deliver the partial message either */ 444 if (InChild) 445 ExitStat = EX_QUIT; 446 finis(); 447 } 448 449 /* 450 ** Find out some information from the headers. 451 ** Examples are who is the from person & the date. 452 */ 453 454 eatheader(e, !requeueflag); 455 456 if (GrabTo && e->e_sendqueue == NULL) 457 usrerr("No recipient addresses found in header"); 458 459 /* collect statistics */ 460 if (OpMode != MD_VERIFY) 461 { 462 extern void markstats(); 463 464 markstats(e, (ADDRESS *) NULL); 465 } 466 467 /* 468 ** Add an Apparently-To: line if we have no recipient lines. 469 */ 470 471 if (hvalue("to", e->e_header) == NULL && 472 hvalue("cc", e->e_header) == NULL && 473 hvalue("bcc", e->e_header) == NULL && 474 hvalue("apparently-to", e->e_header) == NULL) 475 { 476 register ADDRESS *q; 477 char *hdr = NULL; 478 extern void addheader(); 479 480 /* create an Apparently-To: field */ 481 /* that or reject the message.... */ 482 switch (NoRecipientAction) 483 { 484 case NRA_ADD_APPARENTLY_TO: 485 hdr = "Apparently-To"; 486 break; 487 488 case NRA_ADD_TO: 489 hdr = "To"; 490 break; 491 492 case NRA_ADD_BCC: 493 addheader("Bcc", "", &e->e_header); 494 break; 495 496 case NRA_ADD_TO_UNDISCLOSED: 497 addheader("To", "undisclosed-recipients:;", &e->e_header); 498 break; 499 } 500 501 if (hdr != NULL) 502 { 503 for (q = e->e_sendqueue; q != NULL; q = q->q_next) 504 { 505 if (q->q_alias != NULL) 506 continue; 507 if (tTd(30, 3)) 508 printf("Adding %s: %s\n", 509 hdr, q->q_paddr); 510 addheader(hdr, q->q_paddr, &e->e_header); 511 } 512 } 513 } 514 515 /* check for message too large */ 516 if (MaxMessageSize > 0 && e->e_msgsize > MaxMessageSize) 517 { 518 e->e_status = "5.2.3"; 519 usrerr("552 Message exceeds maximum fixed size (%ld)", 520 MaxMessageSize); 521 # ifdef LOG 522 if (LogLevel > 6) 523 syslog(LOG_NOTICE, "%s: message size (%ld) exceeds maximum (%ld)", 524 e->e_id, e->e_msgsize, MaxMessageSize); 525 # endif 526 } 527 528 /* check for illegal 8-bit data */ 529 if (HasEightBits) 530 { 531 e->e_flags |= EF_HAS8BIT; 532 if (!bitset(MM_PASS8BIT|MM_MIME8BIT, MimeMode)) 533 { 534 e->e_status = "5.6.1"; 535 usrerr("554 Eight bit data not allowed"); 536 } 537 } 538 else 539 { 540 /* if it claimed to be 8 bits, well, it lied.... */ 541 if (e->e_bodytype != NULL && 542 strcasecmp(e->e_bodytype, "8BITMIME") == 0) 543 e->e_bodytype = "7BIT"; 544 } 545 546 if ((e->e_dfp = fopen(dfname, "r")) == NULL) 547 { 548 /* we haven't acked receipt yet, so just chuck this */ 549 syserr("Cannot reopen %s", dfname); 550 finis(); 551 } 552 } 553 554 555 static void 556 collecttimeout(timeout) 557 time_t timeout; 558 { 559 /* if no progress was made, die now */ 560 if (!CollectProgress) 561 longjmp(CtxCollectTimeout, 1); 562 563 /* otherwise reset the timeout */ 564 CollectTimeout = setevent(timeout, collecttimeout, timeout); 565 CollectProgress = FALSE; 566 } 567 /* 568 ** TFERROR -- signal error on writing the temporary file. 569 ** 570 ** Parameters: 571 ** tf -- the file pointer for the temporary file. 572 ** e -- the current envelope. 573 ** 574 ** Returns: 575 ** none. 576 ** 577 ** Side Effects: 578 ** Gives an error message. 579 ** Arranges for following output to go elsewhere. 580 */ 581 582 void 583 tferror(tf, e) 584 FILE *tf; 585 register ENVELOPE *e; 586 { 587 if (errno == ENOSPC) 588 { 589 struct stat st; 590 long avail; 591 long bsize; 592 593 e->e_flags |= EF_NO_BODY_RETN; 594 if (fstat(fileno(tf), &st) < 0) 595 st.st_size = 0; 596 (void) freopen(queuename(e, 'd'), "w", tf); 597 if (st.st_size <= 0) 598 fprintf(tf, "\n*** Mail could not be accepted"); 599 else if (sizeof st.st_size > sizeof (long)) 600 fprintf(tf, "\n*** Mail of at least %qd bytes could not be accepted\n", 601 st.st_size); 602 else 603 fprintf(tf, "\n*** Mail of at least %ld bytes could not be accepted\n", 604 st.st_size); 605 fprintf(tf, "*** at %s due to lack of disk space for temp file.\n", 606 MyHostName); 607 avail = freediskspace(QueueDir, &bsize); 608 if (avail > 0) 609 { 610 if (bsize > 1024) 611 avail *= bsize / 1024; 612 else if (bsize < 1024) 613 avail /= 1024 / bsize; 614 fprintf(tf, "*** Currently, %ld kilobytes are available for mail temp files.\n", 615 avail); 616 } 617 e->e_status = "4.3.1"; 618 usrerr("452 Out of disk space for temp file"); 619 } 620 else 621 syserr("collect: Cannot write tf%s", e->e_id); 622 (void) freopen("/dev/null", "w", tf); 623 } 624 /* 625 ** EATFROM -- chew up a UNIX style from line and process 626 ** 627 ** This does indeed make some assumptions about the format 628 ** of UNIX messages. 629 ** 630 ** Parameters: 631 ** fm -- the from line. 632 ** 633 ** Returns: 634 ** none. 635 ** 636 ** Side Effects: 637 ** extracts what information it can from the header, 638 ** such as the date. 639 */ 640 641 # ifndef NOTUNIX 642 643 char *DowList[] = 644 { 645 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", NULL 646 }; 647 648 char *MonthList[] = 649 { 650 "Jan", "Feb", "Mar", "Apr", "May", "Jun", 651 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", 652 NULL 653 }; 654 655 void 656 eatfrom(fm, e) 657 char *fm; 658 register ENVELOPE *e; 659 { 660 register char *p; 661 register char **dt; 662 663 if (tTd(30, 2)) 664 printf("eatfrom(%s)\n", fm); 665 666 /* find the date part */ 667 p = fm; 668 while (*p != '\0') 669 { 670 /* skip a word */ 671 while (*p != '\0' && *p != ' ') 672 p++; 673 while (*p == ' ') 674 p++; 675 if (!(isascii(*p) && isupper(*p)) || 676 p[3] != ' ' || p[13] != ':' || p[16] != ':') 677 continue; 678 679 /* we have a possible date */ 680 for (dt = DowList; *dt != NULL; dt++) 681 if (strncmp(*dt, p, 3) == 0) 682 break; 683 if (*dt == NULL) 684 continue; 685 686 for (dt = MonthList; *dt != NULL; dt++) 687 if (strncmp(*dt, &p[4], 3) == 0) 688 break; 689 if (*dt != NULL) 690 break; 691 } 692 693 if (*p != '\0') 694 { 695 char *q; 696 extern char *arpadate(); 697 698 /* we have found a date */ 699 q = xalloc(25); 700 (void) strncpy(q, p, 25); 701 q[24] = '\0'; 702 q = arpadate(q); 703 define('a', newstr(q), e); 704 } 705 } 706 707 # endif /* NOTUNIX */ 708