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.17 (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 if ((e->e_dfp = fopen(e->e_df, "r")) == NULL) 400 { 401 /* we haven't acked receipt yet, so just chuck this */ 402 syserr("Cannot reopen %s", e->e_df); 403 finis(); 404 } 405 } 406 /* 407 ** FLUSHEOL -- if not at EOL, throw away rest of input line. 408 ** 409 ** Parameters: 410 ** buf -- last line read in (checked for '\n'), 411 ** fp -- file to be read from. 412 ** 413 ** Returns: 414 ** FALSE on error from sfgets(), TRUE otherwise. 415 ** 416 ** Side Effects: 417 ** none. 418 */ 419 420 bool 421 flusheol(buf, fp, dbto) 422 char *buf; 423 FILE *fp; 424 time_t dbto; 425 { 426 register char *p = buf; 427 char junkbuf[MAXLINE]; 428 429 while (strchr(p, '\n') == NULL) 430 { 431 CollectErrorMessage = "553 header line too long"; 432 CollectErrno = 0; 433 if (sfgets(junkbuf, MAXLINE, fp, dbto, 434 "long line flush") == NULL) 435 return (FALSE); 436 p = junkbuf; 437 } 438 439 return (TRUE); 440 } 441 /* 442 ** TFERROR -- signal error on writing the temporary file. 443 ** 444 ** Parameters: 445 ** tf -- the file pointer for the temporary file. 446 ** 447 ** Returns: 448 ** none. 449 ** 450 ** Side Effects: 451 ** Gives an error message. 452 ** Arranges for following output to go elsewhere. 453 */ 454 455 tferror(tf, e) 456 FILE *tf; 457 register ENVELOPE *e; 458 { 459 CollectErrno = errno; 460 if (errno == ENOSPC) 461 { 462 struct stat st; 463 long avail; 464 long bsize; 465 466 e->e_flags |= EF_NORETURN; 467 if (fstat(fileno(tf), &st) < 0) 468 st.st_size = 0; 469 (void) freopen(e->e_df, "w", tf); 470 if (st.st_size <= 0) 471 fprintf(tf, "\n*** Mail could not be accepted"); 472 else if (sizeof st.st_size > sizeof (long)) 473 fprintf(tf, "\n*** Mail of at least %qd bytes could not be accepted\n", 474 st.st_size); 475 else 476 fprintf(tf, "\n*** Mail of at least %ld bytes could not be accepted\n", 477 st.st_size); 478 fprintf(tf, "*** at %s due to lack of disk space for temp file.\n", 479 MyHostName); 480 avail = freespace(QueueDir, &bsize); 481 if (avail > 0) 482 { 483 if (bsize > 1024) 484 avail *= bsize / 1024; 485 else if (bsize < 1024) 486 avail /= 1024 / bsize; 487 fprintf(tf, "*** Currently, %ld kilobytes are available for mail temp files.\n", 488 avail); 489 } 490 CollectErrorMessage = "452 Out of disk space for temp file"; 491 } 492 else 493 { 494 CollectErrorMessage = "cannot write message body to disk (%s)"; 495 } 496 (void) freopen("/dev/null", "w", tf); 497 } 498 /* 499 ** EATFROM -- chew up a UNIX style from line and process 500 ** 501 ** This does indeed make some assumptions about the format 502 ** of UNIX messages. 503 ** 504 ** Parameters: 505 ** fm -- the from line. 506 ** 507 ** Returns: 508 ** none. 509 ** 510 ** Side Effects: 511 ** extracts what information it can from the header, 512 ** such as the date. 513 */ 514 515 # ifndef NOTUNIX 516 517 char *DowList[] = 518 { 519 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", NULL 520 }; 521 522 char *MonthList[] = 523 { 524 "Jan", "Feb", "Mar", "Apr", "May", "Jun", 525 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", 526 NULL 527 }; 528 529 eatfrom(fm, e) 530 char *fm; 531 register ENVELOPE *e; 532 { 533 register char *p; 534 register char **dt; 535 536 if (tTd(30, 2)) 537 printf("eatfrom(%s)\n", fm); 538 539 /* find the date part */ 540 p = fm; 541 while (*p != '\0') 542 { 543 /* skip a word */ 544 while (*p != '\0' && *p != ' ') 545 p++; 546 while (*p == ' ') 547 p++; 548 if (!(isascii(*p) && isupper(*p)) || 549 p[3] != ' ' || p[13] != ':' || p[16] != ':') 550 continue; 551 552 /* we have a possible date */ 553 for (dt = DowList; *dt != NULL; dt++) 554 if (strncmp(*dt, p, 3) == 0) 555 break; 556 if (*dt == NULL) 557 continue; 558 559 for (dt = MonthList; *dt != NULL; dt++) 560 if (strncmp(*dt, &p[4], 3) == 0) 561 break; 562 if (*dt != NULL) 563 break; 564 } 565 566 if (*p != '\0') 567 { 568 char *q; 569 extern char *arpadate(); 570 571 /* we have found a date */ 572 q = xalloc(25); 573 (void) strncpy(q, p, 25); 574 q[24] = '\0'; 575 q = arpadate(q); 576 define('a', newstr(q), e); 577 } 578 } 579 580 # endif /* NOTUNIX */ 581