1 # include <errno.h> 2 # include "sendmail.h" 3 4 SCCSID(@(#)collect.c 3.46 08/15/82); 5 6 /* 7 ** COLLECT -- read & parse message header & make temp file. 8 ** 9 ** Creates a temporary file name and copies the standard 10 ** input to that file. While it is doing it, it looks for 11 ** "From:" and "Sender:" fields to use as the from-person 12 ** (but only if the -a flag is specified). It prefers to 13 ** to use the "Sender:" field. 14 ** 15 ** MIT seems to like to produce "Sent-By:" fields instead 16 ** of "Sender:" fields. We used to catch this, but it turns 17 ** out that the "Sent-By:" field doesn't always correspond 18 ** to someone real ("___057", for instance), as required by 19 ** the protocol. So we limp by..... 20 ** 21 ** Parameters: 22 ** sayok -- if set, give an ARPANET style message 23 ** to say we are ready to collect input. 24 ** 25 ** Returns: 26 ** none. 27 ** 28 ** Side Effects: 29 ** Temp file is created and filled. 30 ** The from person may be set. 31 */ 32 33 collect(sayok) 34 bool sayok; 35 { 36 register FILE *tf; 37 char buf[MAXFIELD+1]; 38 register char *p; 39 char *xfrom; 40 extern char *hvalue(); 41 extern char *mktemp(); 42 static char tempfname[40]; 43 extern char *macvalue(); 44 register HDR *h; 45 extern HDR *hrvalue(); 46 47 /* 48 ** Create the temp file name and create the file. 49 */ 50 51 (void) strcpy(tempfname, QueueDir); 52 (void) strcat(tempfname, "/dfXXXXXX"); 53 (void) mktemp(tempfname); 54 if ((tf = dfopen(tempfname, "w")) == NULL) 55 { 56 syserr("Cannot create %s", tempfname); 57 NoReturn = TRUE; 58 finis(); 59 } 60 (void) chmod(tempfname, 0600); 61 CurEnv->e_df = tempfname; 62 63 /* 64 ** Create the Mail-From line if we want to. 65 */ 66 67 if (Smtp && macvalue('s') != NULL) 68 { 69 char xbuf[50]; 70 71 (void) sprintf(xbuf, "Mail-From: %s$s received by $i at $b", 72 macvalue('r') == NULL ? "" : "$r host "); 73 expand(xbuf, buf, &buf[sizeof buf - 1], CurEnv); 74 (void) chompheader(buf, FALSE); 75 } 76 77 /* 78 ** Tell ARPANET to go ahead. 79 */ 80 81 if (sayok) 82 message("354", "Enter mail, end with \".\" on a line by itself"); 83 84 /* 85 ** Try to read a UNIX-style From line 86 */ 87 88 if (fgets(buf, sizeof buf, InChannel) == NULL) 89 return; 90 fixcrlf(buf, FALSE); 91 # ifndef NOTUNIX 92 if (!SaveFrom && strncmp(buf, "From ", 5) == 0) 93 { 94 eatfrom(buf); 95 (void) fgets(buf, sizeof buf, InChannel); 96 fixcrlf(buf, FALSE); 97 } 98 # endif NOTUNIX 99 100 /* 101 ** Copy InChannel to temp file & do message editing. 102 ** To keep certain mailers from getting confused, 103 ** and to keep the output clean, lines that look 104 ** like UNIX "From" lines are deleted in the header, 105 ** and prepended with ">" in the body. 106 */ 107 108 for (; !feof(InChannel); !feof(InChannel) && fgets(buf, sizeof buf, InChannel) != NULL) 109 { 110 register char c; 111 extern bool isheader(); 112 113 /* if the line is too long, throw the rest away */ 114 if (index(buf, '\n') == NULL) 115 { 116 while ((c = getc(InChannel)) != '\n') 117 continue; 118 /* give an error? */ 119 } 120 121 fixcrlf(buf, FALSE); 122 123 /* see if the header is over */ 124 if (!isheader(buf)) 125 break; 126 127 /* get the rest of this field */ 128 while ((c = getc(InChannel)) == ' ' || c == '\t') 129 { 130 p = &buf[strlen(buf)]; 131 *p++ = c; 132 if (fgets(p, sizeof buf - (p - buf), InChannel) == NULL) 133 break; 134 fixcrlf(p, FALSE); 135 } 136 if (!feof(InChannel)) 137 (void) ungetc(c, InChannel); 138 139 CurEnv->e_msgsize += strlen(buf); 140 141 /* 142 ** Snarf header away. 143 */ 144 145 if (bitset(H_EOH, chompheader(buf, FALSE))) 146 break; 147 } 148 149 # ifdef DEBUG 150 if (tTd(30, 1)) 151 printf("EOH\n"); 152 # endif DEBUG 153 154 /* throw away a blank line */ 155 if (buf[0] == '\n') 156 { 157 (void) fgets(buf, sizeof buf, InChannel); 158 fixcrlf(buf, FALSE); 159 } 160 161 /* 162 ** Collect the body of the message. 163 */ 164 165 for (; !feof(InChannel); !feof(InChannel) && fgets(buf, sizeof buf, InChannel) != NULL) 166 { 167 register int i; 168 register char *bp = buf; 169 170 fixcrlf(buf, FALSE); 171 172 /* check for end-of-message */ 173 if (!IgnrDot && buf[0] == '.' && (buf[1] == '\n' || buf[1] == '\0')) 174 break; 175 176 /* check for transparent dot */ 177 if (Smtp && *bp == '.') 178 bp++; 179 180 # ifndef NOTUNIX 181 /* Hide UNIX-like From lines */ 182 if (strncmp(bp, "From ", 5) == 0) 183 { 184 fputs(">", tf); 185 CurEnv->e_msgsize++; 186 } 187 # endif NOTUNIX 188 189 /* 190 ** Figure message length, output the line to the temp 191 ** file, and insert a newline if missing. 192 */ 193 194 i = strlen(bp); 195 CurEnv->e_msgsize += i; 196 fputs(bp, tf); 197 if (bp[i - 1] != '\n') 198 fputs("\n", tf); 199 if (ferror(tf)) 200 { 201 if (errno == ENOSPC) 202 { 203 (void) freopen(CurEnv->e_df, "w", tf); 204 fputs("\nMAIL DELETED BECAUSE OF LACK OF DISK SPACE\n\n", tf); 205 usrerr("452 Out of disk space for temp file"); 206 } 207 else 208 syserr("collect: Cannot write %s", CurEnv->e_df); 209 (void) freopen("/dev/null", "w", tf); 210 } 211 } 212 (void) fclose(tf); 213 214 /* 215 ** Find out some information from the headers. 216 ** Examples are who is the from person & the date. 217 */ 218 219 /* message id */ 220 h = hrvalue("message-id"); 221 if (h == NULL) 222 syserr("No Message-Id spec"); 223 else if (bitset(H_DEFAULT, h->h_flags)) 224 { 225 (void) expand(h->h_value, buf, &buf[sizeof buf - 1], CurEnv); 226 MsgId = newstr(buf); 227 } 228 else 229 MsgId = h->h_value; 230 # ifdef DEBUG 231 if (tTd(30, 1)) 232 printf("Message-Id: %s\n", MsgId); 233 # endif DEBUG 234 235 /* message priority */ 236 if (!QueueRun) 237 { 238 /* adjust total priority by message priority */ 239 CurEnv->e_msgpriority = CurEnv->e_msgsize; 240 p = hvalue("priority"); 241 if (p != NULL) 242 CurEnv->e_class = priencode(p); 243 else 244 CurEnv->e_class = PRI_NORMAL; 245 CurEnv->e_msgpriority -= CurEnv->e_class * WKPRIFACT; 246 } 247 248 /* special handling */ 249 p = hvalue("special-handling"); 250 if (p != NULL) 251 spechandling(p); 252 253 /* from person */ 254 xfrom = hvalue("sender"); 255 if (xfrom == NULL) 256 xfrom = CurEnv->e_origfrom; 257 if (ArpaMode) 258 setfrom(xfrom, (char *) NULL); 259 260 /* full name of from person */ 261 p = hvalue("full-name"); 262 if (p != NULL) 263 define('x', p); 264 else 265 { 266 extern char *getxpart(); 267 268 /* 269 ** Try to extract the full name from a general From: 270 ** field. We take anything which is a comment as a 271 ** first choice. Failing in that, we see if there is 272 ** a "machine readable" name (in <angle brackets>); if 273 ** so we take anything preceeding that clause. 274 ** 275 ** If we blow it here it's not all that serious. 276 */ 277 278 p = hvalue("original-from"); 279 if (p == NULL) 280 p = CurEnv->e_origfrom; 281 p = getxpart(p); 282 if (p != NULL) 283 define('x', newstr(p)); 284 } 285 286 /* date message originated */ 287 p = hvalue("posted-date"); 288 if (p == NULL) 289 p = hvalue("date"); 290 if (p != NULL) 291 { 292 define('a', p); 293 /* we don't have a good way to do canonical conversion .... 294 define('d', newstr(arpatounix(p))); 295 .... so we will ignore the problem for the time being */ 296 } 297 298 if (hvalue("to") == NULL && hvalue("cc") == NULL && 299 hvalue("bcc") == NULL && hvalue("apparently-to") == NULL) 300 { 301 register ADDRESS *q; 302 303 /* create an Apparently-To: field */ 304 /* that or reject the message.... */ 305 for (q = CurEnv->e_sendqueue; q != NULL; q = q->q_next) 306 { 307 if (q->q_alias != NULL) 308 continue; 309 # ifdef DEBUG 310 if (tTd(30, 3)) 311 printf("Adding Apparently-To: %s\n", q->q_paddr); 312 # endif DEBUG 313 addheader("apparently-to", q->q_paddr, CurEnv); 314 } 315 } 316 317 /* check for hop count overflow */ 318 if (HopCount > MAXHOP) 319 syserr("Too many hops (%d max); probably forwarding loop", MAXHOP); 320 321 if ((TempFile = fopen(CurEnv->e_df, "r")) == NULL) 322 syserr("Cannot reopen %s", CurEnv->e_df); 323 324 # ifdef DEBUG 325 if (tTd(30, 2)) 326 { 327 HDR *h; 328 extern char *capitalize(); 329 330 printf("----- collected header -----\n"); 331 for (h = CurEnv->e_header; h != NULL; h = h->h_link) 332 printf("%s: %s\n", capitalize(h->h_field), h->h_value); 333 printf("----------------------------\n"); 334 } 335 # endif DEBUG 336 337 /* 338 ** Log collection information. 339 */ 340 341 # ifdef LOG 342 if (LogLevel > 1) 343 syslog(LOG_INFO, "%s: from=%s, size=%ld, class=%d\n", MsgId, 344 CurEnv->e_from.q_paddr, CurEnv->e_msgsize, 345 CurEnv->e_class); 346 # endif LOG 347 return; 348 } 349 /* 350 ** EATFROM -- chew up a UNIX style from line and process 351 ** 352 ** This does indeed make some assumptions about the format 353 ** of UNIX messages. 354 ** 355 ** Parameters: 356 ** fm -- the from line. 357 ** 358 ** Returns: 359 ** none. 360 ** 361 ** Side Effects: 362 ** extracts what information it can from the header, 363 ** such as the date. 364 */ 365 366 # ifndef NOTUNIX 367 368 char *DowList[] = 369 { 370 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", NULL 371 }; 372 373 char *MonthList[] = 374 { 375 "Jan", "Feb", "Mar", "Apr", "May", "Jun", 376 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", 377 NULL 378 }; 379 380 eatfrom(fm) 381 char *fm; 382 { 383 register char *p; 384 register char **dt; 385 386 # ifdef DEBUG 387 if (tTd(30, 2)) 388 printf("eatfrom(%s)\n", fm); 389 # endif DEBUG 390 391 /* find the date part */ 392 p = fm; 393 while (*p != '\0') 394 { 395 /* skip a word */ 396 while (*p != '\0' && *p != ' ') 397 *p++; 398 while (*p == ' ') 399 *p++; 400 if (!isupper(*p) || p[3] != ' ' || p[13] != ':' || p[16] != ':') 401 continue; 402 403 /* we have a possible date */ 404 for (dt = DowList; *dt != NULL; dt++) 405 if (strncmp(*dt, p, 3) == 0) 406 break; 407 if (*dt == NULL) 408 continue; 409 410 for (dt = MonthList; *dt != NULL; dt++) 411 if (strncmp(*dt, &p[4], 3) == 0) 412 break; 413 if (*dt != NULL) 414 break; 415 } 416 417 if (*p != NULL) 418 { 419 char *q; 420 extern char *arpadate(); 421 422 /* we have found a date */ 423 q = xalloc(25); 424 strncpy(q, p, 25); 425 q[24] = '\0'; 426 define('d', q); 427 q = arpadate(q); 428 define('a', newstr(q)); 429 } 430 } 431 432 # endif NOTUNIX 433 /* 434 ** PRIENCODE -- encode external priority names into internal values. 435 ** 436 ** Parameters: 437 ** p -- priority in ascii. 438 ** 439 ** Returns: 440 ** priority as a numeric level. 441 ** 442 ** Side Effects: 443 ** none. 444 */ 445 446 struct prio 447 { 448 char *pri_name; /* external name of priority */ 449 int pri_val; /* internal value for same */ 450 }; 451 452 static struct prio Prio[] = 453 { 454 "alert", PRI_ALERT, 455 "quick", PRI_QUICK, 456 "first-class", PRI_FIRSTCL, 457 "normal", PRI_NORMAL, 458 "second-class", PRI_SECONDCL, 459 "third-class", PRI_THIRDCL, 460 "junk", PRI_JUNK, 461 NULL, PRI_NORMAL, 462 }; 463 464 priencode(p) 465 char *p; 466 { 467 register struct prio *pl; 468 extern bool sameword(); 469 470 for (pl = Prio; pl->pri_name != NULL; pl++) 471 { 472 if (sameword(p, pl->pri_name)) 473 break; 474 } 475 return (pl->pri_val); 476 } 477 /* 478 ** SPECHANDLE -- do special handling 479 ** 480 ** Parameters: 481 ** p -- pointer to list of special handling words. 482 ** 483 ** Returns: 484 ** none. 485 ** 486 ** Side Effects: 487 ** Sets flags as indicated by p. 488 */ 489 490 struct handling 491 { 492 char *han_name; /* word to get this magic */ 493 int han_what; /* what to do, see below */ 494 }; 495 496 /* modes for han_what */ 497 # define HAN_NONE 0 /* nothing special */ 498 # define HAN_RRECEIPT 1 /* give return receipt */ 499 500 struct handling Handling[] = 501 { 502 "return-receipt-requested", HAN_RRECEIPT, 503 NULL, HAN_NONE 504 }; 505 506 spechandling(p) 507 register char *p; 508 { 509 register char *w; 510 register struct handling *h; 511 extern bool sameword(); 512 513 while (*p != '\0') 514 { 515 /* collect a word to compare to */ 516 while (*p != '\0' && (*p == ',' || isspace(*p))) 517 p++; 518 if (*p == '\0') 519 break; 520 w = p; 521 while (*p != '\0' && *p != ',' && !isspace(*p)) 522 p++; 523 if (*p != '\0') 524 *p++ = '\0'; 525 526 /* scan the special handling table */ 527 for (h = Handling; h->han_name != NULL; h++) 528 if (sameword(h->han_name, w)) 529 break; 530 531 /* see if we can do anything interesting */ 532 switch (h->han_what) 533 { 534 case HAN_NONE: /* nothing to be done */ 535 break; 536 537 case HAN_RRECEIPT: /* give return receipt */ 538 CurEnv->e_retreceipt = TRUE; 539 # ifdef DEBUG 540 if (tTd(30, 3)) 541 printf(">>> Return receipt requested\n"); 542 # endif DEBUG 543 break; 544 545 default: 546 syserr("spechandling: handling %d (%s)", h->han_what, w); 547 } 548 } 549 } 550