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