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.18 (Berkeley) 07/23/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 collect(fp, smtpmode, requeueflag, hdrp, e) 46 FILE *fp; 47 bool smtpmode; 48 bool requeueflag; 49 HDR **hdrp; 50 register ENVELOPE *e; 51 { 52 register FILE *tf; 53 bool ignrdot = smtpmode ? FALSE : IgnrDot; 54 time_t dbto = smtpmode ? TimeOuts.to_datablock : 0; 55 register char *workbuf, *freebuf; 56 bool inputerr = FALSE; 57 bool headeronly = FALSE; 58 char buf[MAXLINE], buf2[MAXLINE]; 59 extern char *hvalue(); 60 extern bool isheader(), flusheol(); 61 62 CollectErrorMessage = NULL; 63 CollectErrno = 0; 64 if (hdrp == NULL) 65 hdrp = &e->e_header; 66 else 67 headeronly = TRUE; 68 69 /* 70 ** Create the temp file name and create the file. 71 */ 72 73 if (!headeronly) 74 { 75 e->e_df = queuename(e, 'd'); 76 e->e_df = newstr(e->e_df); 77 if ((tf = dfopen(e->e_df, O_WRONLY|O_CREAT|O_TRUNC, FileMode)) == NULL) 78 { 79 syserr("Cannot create %s", e->e_df); 80 e->e_flags |= EF_NORETURN; 81 finis(); 82 } 83 HasEightBits = FALSE; 84 } 85 86 /* 87 ** Tell ARPANET to go ahead. 88 */ 89 90 if (smtpmode) 91 message("354 Enter mail, end with \".\" on a line by itself"); 92 93 /* set global timer to monitor progress */ 94 sfgetset(dbto); 95 96 /* 97 ** Try to read a UNIX-style From line 98 */ 99 100 if (sfgets(buf, MAXLINE, fp, dbto, "initial message read") == NULL) 101 goto readerr; 102 fixcrlf(buf, FALSE); 103 # ifndef NOTUNIX 104 if (!headeronly && !SaveFrom && strncmp(buf, "From ", 5) == 0) 105 { 106 if (!flusheol(buf, fp, dbto)) 107 goto readerr; 108 eatfrom(buf, e); 109 if (sfgets(buf, MAXLINE, fp, dbto, 110 "message header read") == NULL) 111 goto readerr; 112 fixcrlf(buf, FALSE); 113 } 114 # endif /* NOTUNIX */ 115 116 /* 117 ** Copy fp to temp file & do message editing. 118 ** To keep certain mailers from getting confused, 119 ** and to keep the output clean, lines that look 120 ** like UNIX "From" lines are deleted in the header. 121 */ 122 123 workbuf = buf; /* `workbuf' contains a header field */ 124 freebuf = buf2; /* `freebuf' can be used for read-ahead */ 125 for (;;) 126 { 127 char *curbuf; 128 int curbuffree; 129 register int curbuflen; 130 char *p; 131 132 /* first, see if the header is over */ 133 if (!isheader(workbuf)) 134 { 135 fixcrlf(workbuf, TRUE); 136 break; 137 } 138 139 /* if the line is too long, throw the rest away */ 140 if (!flusheol(workbuf, fp, dbto)) 141 goto readerr; 142 143 /* it's okay to toss '\n' now (flusheol() needed it) */ 144 fixcrlf(workbuf, TRUE); 145 146 curbuf = workbuf; 147 curbuflen = strlen(curbuf); 148 curbuffree = MAXLINE - curbuflen; 149 p = curbuf + curbuflen; 150 151 /* get the rest of this field */ 152 for (;;) 153 { 154 int clen; 155 156 if (sfgets(freebuf, MAXLINE, fp, dbto, 157 "message header read") == NULL) 158 { 159 freebuf[0] = '\0'; 160 break; 161 } 162 163 /* is this a continuation line? */ 164 if (*freebuf != ' ' && *freebuf != '\t') 165 break; 166 167 if (!flusheol(freebuf, fp, dbto)) 168 goto readerr; 169 170 fixcrlf(freebuf, TRUE); 171 clen = strlen(freebuf) + 1; 172 173 /* if insufficient room, dynamically allocate buffer */ 174 if (clen >= curbuffree) 175 { 176 /* reallocate buffer */ 177 int nbuflen = ((p - curbuf) + clen) * 2; 178 char *nbuf = xalloc(nbuflen); 179 180 p = nbuf + curbuflen; 181 curbuffree = nbuflen - curbuflen; 182 bcopy(curbuf, nbuf, curbuflen); 183 if (curbuf != buf && curbuf != buf2) 184 free(curbuf); 185 curbuf = nbuf; 186 } 187 *p++ = '\n'; 188 bcopy(freebuf, p, clen - 1); 189 p += clen - 1; 190 curbuffree -= clen; 191 curbuflen += clen; 192 } 193 *p++ = '\0'; 194 195 e->e_msgsize += curbuflen; 196 197 /* 198 ** The working buffer now becomes the free buffer, since 199 ** the free buffer contains a new header field. 200 ** 201 ** This is premature, since we still havent called 202 ** chompheader() to process the field we just created 203 ** (so the call to chompheader() will use `freebuf'). 204 ** This convolution is necessary so that if we break out 205 ** of the loop due to H_EOH, `workbuf' will always be 206 ** the next unprocessed buffer. 207 */ 208 209 { 210 register char *tmp = workbuf; 211 workbuf = freebuf; 212 freebuf = tmp; 213 } 214 215 /* 216 ** Snarf header away. 217 */ 218 219 if (bitset(H_EOH, chompheader(curbuf, FALSE, e))) 220 break; 221 222 /* 223 ** If the buffer was dynamically allocated, free it. 224 */ 225 226 if (curbuf != buf && curbuf != buf2) 227 free(curbuf); 228 } 229 230 if (tTd(30, 1)) 231 printf("EOH\n"); 232 233 if (headeronly) 234 { 235 if (*workbuf != '\0') 236 syserr("collect: lost first line of message"); 237 goto readerr; 238 } 239 240 if (*workbuf == '\0') 241 { 242 /* throw away a blank line */ 243 if (sfgets(buf, MAXLINE, fp, dbto, 244 "message separator read") == NULL) 245 goto readerr; 246 } 247 else if (workbuf == buf2) /* guarantee `buf' contains data */ 248 (void) strcpy(buf, buf2); 249 250 /* 251 ** Collect the body of the message. 252 */ 253 254 for (;;) 255 { 256 register char *bp = buf; 257 258 fixcrlf(buf, TRUE); 259 260 /* check for end-of-message */ 261 if (!ignrdot && buf[0] == '.' && (buf[1] == '\n' || buf[1] == '\0')) 262 break; 263 264 /* check for transparent dot */ 265 if ((OpMode == MD_SMTP || OpMode == MD_DAEMON) && 266 bp[0] == '.' && bp[1] == '.') 267 bp++; 268 269 /* 270 ** Figure message length, output the line to the temp 271 ** file, and insert a newline if missing. 272 */ 273 274 e->e_msgsize += strlen(bp) + 1; 275 fputs(bp, tf); 276 fputs("\n", tf); 277 if (ferror(tf)) 278 tferror(tf, e); 279 if (sfgets(buf, MAXLINE, fp, dbto, "message body read") == NULL) 280 goto readerr; 281 } 282 283 readerr: 284 if ((feof(fp) && smtpmode) || ferror(fp)) 285 { 286 if (tTd(30, 1)) 287 printf("collect: read error\n"); 288 inputerr = TRUE; 289 } 290 291 /* reset global timer */ 292 sfgetset((time_t) 0); 293 294 if (headeronly) 295 return; 296 297 if (tf != NULL) 298 { 299 if (fflush(tf) != 0) 300 tferror(tf, e); 301 if (fsync(fileno(tf)) < 0 || fclose(tf) < 0) 302 { 303 tferror(tf, e); 304 finis(); 305 } 306 } 307 308 if (CollectErrorMessage != NULL && Errors <= 0) 309 { 310 if (CollectErrno != 0) 311 { 312 errno = CollectErrno; 313 syserr(CollectErrorMessage, e->e_df); 314 finis(); 315 } 316 usrerr(CollectErrorMessage); 317 } 318 else if (inputerr && (OpMode == MD_SMTP || OpMode == MD_DAEMON)) 319 { 320 /* An EOF when running SMTP is an error */ 321 char *host; 322 char *problem; 323 324 host = RealHostName; 325 if (host == NULL) 326 host = "localhost"; 327 328 if (feof(fp)) 329 problem = "unexpected close"; 330 else if (ferror(fp)) 331 problem = "I/O error"; 332 else 333 problem = "read timeout"; 334 # ifdef LOG 335 if (LogLevel > 0 && feof(fp)) 336 syslog(LOG_NOTICE, 337 "collect: %s on connection from %s, sender=%s: %s\n", 338 problem, host, e->e_from.q_paddr, errstring(errno)); 339 # endif 340 if (feof(fp)) 341 usrerr("451 collect: %s on connection from %s, from=%s", 342 problem, host, e->e_from.q_paddr); 343 else 344 syserr("451 collect: %s on connection from %s, from=%s", 345 problem, host, e->e_from.q_paddr); 346 347 /* don't return an error indication */ 348 e->e_to = NULL; 349 e->e_flags &= ~EF_FATALERRS; 350 e->e_flags |= EF_CLRQUEUE; 351 352 /* and don't try to deliver the partial message either */ 353 if (InChild) 354 ExitStat = EX_QUIT; 355 finis(); 356 } 357 358 /* 359 ** Find out some information from the headers. 360 ** Examples are who is the from person & the date. 361 */ 362 363 eatheader(e, !requeueflag); 364 365 /* collect statistics */ 366 if (OpMode != MD_VERIFY) 367 markstats(e, (ADDRESS *) NULL); 368 369 /* 370 ** Add an Apparently-To: line if we have no recipient lines. 371 */ 372 373 if (hvalue("to", e->e_header) == NULL && 374 hvalue("cc", e->e_header) == NULL && 375 hvalue("bcc", e->e_header) == NULL && 376 hvalue("apparently-to", e->e_header) == NULL) 377 { 378 register ADDRESS *q; 379 380 /* create an Apparently-To: field */ 381 /* that or reject the message.... */ 382 for (q = e->e_sendqueue; q != NULL; q = q->q_next) 383 { 384 if (q->q_alias != NULL) 385 continue; 386 if (tTd(30, 3)) 387 printf("Adding Apparently-To: %s\n", q->q_paddr); 388 addheader("Apparently-To", q->q_paddr, &e->e_header); 389 } 390 } 391 392 /* check for message too large */ 393 if (MaxMessageSize > 0 && e->e_msgsize > MaxMessageSize) 394 { 395 usrerr("552 Message exceeds maximum fixed size (%ld)", 396 MaxMessageSize); 397 } 398 399 /* check for illegal 8-bit data */ 400 if (HasEightBits) 401 { 402 e->e_flags |= EF_HAS8BIT; 403 if (bitset(MM_MIME8BIT, MimeMode)) 404 { 405 /* convert it to MIME */ 406 if (hvalue("MIME-Version", e->e_header) == NULL) 407 { 408 char mimebuf[20]; 409 410 strcpy(mimebuf, "MIME-Version: 1.0"); 411 chompheader(mimebuf, FALSE, e); 412 } 413 if (e->e_bodytype == NULL) 414 e->e_bodytype = "8BITMIME"; 415 } 416 else if (!bitset(MM_PASS8BIT, MimeMode)) 417 usrerr("554 Eight bit data not allowed"); 418 } 419 420 if ((e->e_dfp = fopen(e->e_df, "r")) == NULL) 421 { 422 /* we haven't acked receipt yet, so just chuck this */ 423 syserr("Cannot reopen %s", e->e_df); 424 finis(); 425 } 426 } 427 /* 428 ** FLUSHEOL -- if not at EOL, throw away rest of input line. 429 ** 430 ** Parameters: 431 ** buf -- last line read in (checked for '\n'), 432 ** fp -- file to be read from. 433 ** 434 ** Returns: 435 ** FALSE on error from sfgets(), TRUE otherwise. 436 ** 437 ** Side Effects: 438 ** none. 439 */ 440 441 bool 442 flusheol(buf, fp, dbto) 443 char *buf; 444 FILE *fp; 445 time_t dbto; 446 { 447 register char *p = buf; 448 char junkbuf[MAXLINE]; 449 450 while (strchr(p, '\n') == NULL) 451 { 452 CollectErrorMessage = "553 header line too long"; 453 CollectErrno = 0; 454 if (sfgets(junkbuf, MAXLINE, fp, dbto, 455 "long line flush") == NULL) 456 return (FALSE); 457 p = junkbuf; 458 } 459 460 return (TRUE); 461 } 462 /* 463 ** TFERROR -- signal error on writing the temporary file. 464 ** 465 ** Parameters: 466 ** tf -- the file pointer for the temporary file. 467 ** 468 ** Returns: 469 ** none. 470 ** 471 ** Side Effects: 472 ** Gives an error message. 473 ** Arranges for following output to go elsewhere. 474 */ 475 476 tferror(tf, e) 477 FILE *tf; 478 register ENVELOPE *e; 479 { 480 CollectErrno = errno; 481 if (errno == ENOSPC) 482 { 483 struct stat st; 484 long avail; 485 long bsize; 486 487 e->e_flags |= EF_NORETURN; 488 if (fstat(fileno(tf), &st) < 0) 489 st.st_size = 0; 490 (void) freopen(e->e_df, "w", tf); 491 if (st.st_size <= 0) 492 fprintf(tf, "\n*** Mail could not be accepted"); 493 else if (sizeof st.st_size > sizeof (long)) 494 fprintf(tf, "\n*** Mail of at least %qd bytes could not be accepted\n", 495 st.st_size); 496 else 497 fprintf(tf, "\n*** Mail of at least %ld bytes could not be accepted\n", 498 st.st_size); 499 fprintf(tf, "*** at %s due to lack of disk space for temp file.\n", 500 MyHostName); 501 avail = freespace(QueueDir, &bsize); 502 if (avail > 0) 503 { 504 if (bsize > 1024) 505 avail *= bsize / 1024; 506 else if (bsize < 1024) 507 avail /= 1024 / bsize; 508 fprintf(tf, "*** Currently, %ld kilobytes are available for mail temp files.\n", 509 avail); 510 } 511 CollectErrorMessage = "452 Out of disk space for temp file"; 512 } 513 else 514 { 515 CollectErrorMessage = "cannot write message body to disk (%s)"; 516 } 517 (void) freopen("/dev/null", "w", tf); 518 } 519 /* 520 ** EATFROM -- chew up a UNIX style from line and process 521 ** 522 ** This does indeed make some assumptions about the format 523 ** of UNIX messages. 524 ** 525 ** Parameters: 526 ** fm -- the from line. 527 ** 528 ** Returns: 529 ** none. 530 ** 531 ** Side Effects: 532 ** extracts what information it can from the header, 533 ** such as the date. 534 */ 535 536 # ifndef NOTUNIX 537 538 char *DowList[] = 539 { 540 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", NULL 541 }; 542 543 char *MonthList[] = 544 { 545 "Jan", "Feb", "Mar", "Apr", "May", "Jun", 546 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", 547 NULL 548 }; 549 550 eatfrom(fm, e) 551 char *fm; 552 register ENVELOPE *e; 553 { 554 register char *p; 555 register char **dt; 556 557 if (tTd(30, 2)) 558 printf("eatfrom(%s)\n", fm); 559 560 /* find the date part */ 561 p = fm; 562 while (*p != '\0') 563 { 564 /* skip a word */ 565 while (*p != '\0' && *p != ' ') 566 p++; 567 while (*p == ' ') 568 p++; 569 if (!(isascii(*p) && isupper(*p)) || 570 p[3] != ' ' || p[13] != ':' || p[16] != ':') 571 continue; 572 573 /* we have a possible date */ 574 for (dt = DowList; *dt != NULL; dt++) 575 if (strncmp(*dt, p, 3) == 0) 576 break; 577 if (*dt == NULL) 578 continue; 579 580 for (dt = MonthList; *dt != NULL; dt++) 581 if (strncmp(*dt, &p[4], 3) == 0) 582 break; 583 if (*dt != NULL) 584 break; 585 } 586 587 if (*p != '\0') 588 { 589 char *q; 590 extern char *arpadate(); 591 592 /* we have found a date */ 593 q = xalloc(25); 594 (void) strncpy(q, p, 25); 595 q[24] = '\0'; 596 q = arpadate(q); 597 define('a', newstr(q), e); 598 } 599 } 600 601 # endif /* NOTUNIX */ 602