1 # include <errno.h> 2 # include "sendmail.h" 3 4 SCCSID(@(#)collect.c 3.44 08/08/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 fixcrlf(buf, FALSE); 114 115 /* see if the header is over */ 116 if (!isheader(buf)) 117 break; 118 119 /* get the rest of this field */ 120 while ((c = getc(InChannel)) == ' ' || c == '\t') 121 { 122 p = &buf[strlen(buf)]; 123 *p++ = c; 124 if (fgets(p, sizeof buf - (p - buf), InChannel) == NULL) 125 break; 126 fixcrlf(p, FALSE); 127 } 128 if (!feof(InChannel)) 129 (void) ungetc(c, InChannel); 130 131 CurEnv->e_msgsize += strlen(buf); 132 133 /* 134 ** Snarf header away. 135 */ 136 137 if (bitset(H_EOH, chompheader(buf, FALSE))) 138 break; 139 } 140 141 # ifdef DEBUG 142 if (tTd(30, 1)) 143 printf("EOH\n"); 144 # endif DEBUG 145 146 /* throw away a blank line */ 147 if (buf[0] == '\n') 148 { 149 (void) fgets(buf, sizeof buf, InChannel); 150 fixcrlf(buf, FALSE); 151 } 152 153 /* 154 ** Collect the body of the message. 155 */ 156 157 for (; !feof(InChannel); !feof(InChannel) && fgets(buf, sizeof buf, InChannel) != NULL) 158 { 159 register int i; 160 register char *bp = buf; 161 162 fixcrlf(buf, FALSE); 163 164 /* check for end-of-message */ 165 if (!IgnrDot && buf[0] == '.' && (buf[1] == '\n' || buf[1] == '\0')) 166 break; 167 168 /* check for transparent dot */ 169 if (Smtp && *bp == '.') 170 bp++; 171 172 # ifndef NOTUNIX 173 /* Hide UNIX-like From lines */ 174 if (strncmp(bp, "From ", 5) == 0) 175 { 176 fputs(">", tf); 177 CurEnv->e_msgsize++; 178 } 179 # endif NOTUNIX 180 181 /* 182 ** Figure message length, output the line to the temp 183 ** file, and insert a newline if missing. 184 */ 185 186 i = strlen(bp); 187 CurEnv->e_msgsize += i; 188 fputs(bp, tf); 189 if (bp[i - 1] != '\n') 190 fputs("\n", tf); 191 if (ferror(tf)) 192 { 193 if (errno == ENOSPC) 194 { 195 (void) freopen(CurEnv->e_df, "w", tf); 196 fputs("\nMAIL DELETED BECAUSE OF LACK OF DISK SPACE\n\n", tf); 197 usrerr("452 Out of disk space for temp file"); 198 } 199 else 200 syserr("collect: Cannot write %s", CurEnv->e_df); 201 (void) freopen("/dev/null", "w", tf); 202 } 203 } 204 (void) fclose(tf); 205 206 /* 207 ** Find out some information from the headers. 208 ** Examples are who is the from person & the date. 209 */ 210 211 /* message id */ 212 h = hrvalue("message-id"); 213 if (h == NULL) 214 syserr("No Message-Id spec"); 215 else if (bitset(H_DEFAULT, h->h_flags)) 216 { 217 (void) expand(h->h_value, buf, &buf[sizeof buf - 1], CurEnv); 218 MsgId = newstr(buf); 219 } 220 else 221 MsgId = h->h_value; 222 # ifdef DEBUG 223 if (tTd(30, 1)) 224 printf("Message-Id: %s\n", MsgId); 225 # endif DEBUG 226 227 /* message priority */ 228 if (!QueueRun) 229 { 230 /* adjust total priority by message priority */ 231 CurEnv->e_msgpriority = CurEnv->e_msgsize; 232 p = hvalue("priority"); 233 if (p != NULL) 234 CurEnv->e_class = priencode(p); 235 else 236 CurEnv->e_class = PRI_NORMAL; 237 CurEnv->e_msgpriority -= CurEnv->e_class * WKPRIFACT; 238 } 239 240 /* special handling */ 241 p = hvalue("special-handling"); 242 if (p != NULL) 243 spechandling(p); 244 245 /* from person */ 246 xfrom = hvalue("sender"); 247 if (xfrom == NULL) 248 xfrom = CurEnv->e_origfrom; 249 if (ArpaMode) 250 setfrom(xfrom, (char *) NULL); 251 252 /* full name of from person */ 253 p = hvalue("full-name"); 254 if (p != NULL) 255 define('x', p); 256 else 257 { 258 extern char *getxpart(); 259 260 /* 261 ** Try to extract the full name from a general From: 262 ** field. We take anything which is a comment as a 263 ** first choice. Failing in that, we see if there is 264 ** a "machine readable" name (in <angle brackets>); if 265 ** so we take anything preceeding that clause. 266 ** 267 ** If we blow it here it's not all that serious. 268 */ 269 270 p = hvalue("original-from"); 271 if (p == NULL) 272 p = CurEnv->e_origfrom; 273 p = getxpart(p); 274 if (p != NULL) 275 define('x', newstr(p)); 276 } 277 278 /* date message originated */ 279 p = hvalue("posted-date"); 280 if (p == NULL) 281 p = hvalue("date"); 282 if (p != NULL) 283 { 284 define('a', p); 285 /* we don't have a good way to do canonical conversion .... 286 define('d', newstr(arpatounix(p))); 287 .... so we will ignore the problem for the time being */ 288 } 289 290 if (hvalue("to") == NULL && hvalue("cc") == NULL && 291 hvalue("bcc") == NULL && hvalue("apparently-to") == NULL) 292 { 293 register ADDRESS *q; 294 295 /* create an Apparently-To: field */ 296 /* that or reject the message.... */ 297 for (q = CurEnv->e_sendqueue; q != NULL; q = q->q_next) 298 { 299 if (q->q_alias != NULL) 300 continue; 301 # ifdef DEBUG 302 if (tTd(30, 3)) 303 printf("Adding Apparently-To: %s\n", q->q_paddr); 304 # endif DEBUG 305 addheader("apparently-to", q->q_paddr, CurEnv); 306 } 307 } 308 309 /* check for hop count overflow */ 310 if (HopCount > MAXHOP) 311 syserr("Too many hops (%d max); probably forwarding loop", MAXHOP); 312 313 if ((TempFile = fopen(CurEnv->e_df, "r")) == NULL) 314 syserr("Cannot reopen %s", CurEnv->e_df); 315 316 # ifdef DEBUG 317 if (tTd(30, 2)) 318 { 319 HDR *h; 320 extern char *capitalize(); 321 322 printf("----- collected header -----\n"); 323 for (h = CurEnv->e_header; h != NULL; h = h->h_link) 324 printf("%s: %s\n", capitalize(h->h_field), h->h_value); 325 printf("----------------------------\n"); 326 } 327 # endif DEBUG 328 329 /* 330 ** Log collection information. 331 */ 332 333 # ifdef LOG 334 if (LogLevel > 1) 335 syslog(LOG_INFO, "%s: from=%s, size=%ld, class=%d\n", MsgId, 336 CurEnv->e_from.q_paddr, CurEnv->e_msgsize, 337 CurEnv->e_msgpriority); 338 # endif LOG 339 return; 340 } 341 /* 342 ** EATFROM -- chew up a UNIX style from line and process 343 ** 344 ** This does indeed make some assumptions about the format 345 ** of UNIX messages. 346 ** 347 ** Parameters: 348 ** fm -- the from line. 349 ** 350 ** Returns: 351 ** none. 352 ** 353 ** Side Effects: 354 ** extracts what information it can from the header, 355 ** such as the date. 356 */ 357 358 # ifndef NOTUNIX 359 360 char *DowList[] = 361 { 362 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", NULL 363 }; 364 365 char *MonthList[] = 366 { 367 "Jan", "Feb", "Mar", "Apr", "May", "Jun", 368 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", 369 NULL 370 }; 371 372 eatfrom(fm) 373 char *fm; 374 { 375 register char *p; 376 register char **dt; 377 378 # ifdef DEBUG 379 if (tTd(30, 2)) 380 printf("eatfrom(%s)\n", fm); 381 # endif DEBUG 382 383 /* find the date part */ 384 p = fm; 385 while (*p != '\0') 386 { 387 /* skip a word */ 388 while (*p != '\0' && *p != ' ') 389 *p++; 390 while (*p == ' ') 391 *p++; 392 if (!isupper(*p) || p[3] != ' ' || p[13] != ':' || p[16] != ':') 393 continue; 394 395 /* we have a possible date */ 396 for (dt = DowList; *dt != NULL; dt++) 397 if (strncmp(*dt, p, 3) == 0) 398 break; 399 if (*dt == NULL) 400 continue; 401 402 for (dt = MonthList; *dt != NULL; dt++) 403 if (strncmp(*dt, &p[4], 3) == 0) 404 break; 405 if (*dt != NULL) 406 break; 407 } 408 409 if (*p != NULL) 410 { 411 char *q; 412 extern char *arpadate(); 413 414 /* we have found a date */ 415 q = xalloc(25); 416 strncpy(q, p, 25); 417 q[24] = '\0'; 418 define('d', q); 419 q = arpadate(q); 420 define('a', newstr(q)); 421 } 422 } 423 424 # endif NOTUNIX 425 /* 426 ** PRIENCODE -- encode external priority names into internal values. 427 ** 428 ** Parameters: 429 ** p -- priority in ascii. 430 ** 431 ** Returns: 432 ** priority as a numeric level. 433 ** 434 ** Side Effects: 435 ** none. 436 */ 437 438 struct prio 439 { 440 char *pri_name; /* external name of priority */ 441 int pri_val; /* internal value for same */ 442 }; 443 444 static struct prio Prio[] = 445 { 446 "alert", PRI_ALERT, 447 "quick", PRI_QUICK, 448 "first-class", PRI_FIRSTCL, 449 "normal", PRI_NORMAL, 450 "second-class", PRI_SECONDCL, 451 "third-class", PRI_THIRDCL, 452 "junk", PRI_JUNK, 453 NULL, PRI_NORMAL, 454 }; 455 456 priencode(p) 457 char *p; 458 { 459 register struct prio *pl; 460 extern bool sameword(); 461 462 for (pl = Prio; pl->pri_name != NULL; pl++) 463 { 464 if (sameword(p, pl->pri_name)) 465 break; 466 } 467 return (pl->pri_val); 468 } 469 /* 470 ** SPECHANDLE -- do special handling 471 ** 472 ** Parameters: 473 ** p -- pointer to list of special handling words. 474 ** 475 ** Returns: 476 ** none. 477 ** 478 ** Side Effects: 479 ** Sets flags as indicated by p. 480 */ 481 482 struct handling 483 { 484 char *han_name; /* word to get this magic */ 485 int han_what; /* what to do, see below */ 486 }; 487 488 /* modes for han_what */ 489 # define HAN_NONE 0 /* nothing special */ 490 # define HAN_RRECEIPT 1 /* give return receipt */ 491 492 struct handling Handling[] = 493 { 494 "return-receipt-requested", HAN_RRECEIPT, 495 NULL, HAN_NONE 496 }; 497 498 spechandling(p) 499 register char *p; 500 { 501 register char *w; 502 register struct handling *h; 503 extern bool sameword(); 504 505 while (*p != '\0') 506 { 507 /* collect a word to compare to */ 508 while (*p != '\0' && (*p == ',' || isspace(*p))) 509 p++; 510 if (*p == '\0') 511 break; 512 w = p; 513 while (*p != '\0' && *p != ',' && !isspace(*p)) 514 p++; 515 if (*p != '\0') 516 *p++ = '\0'; 517 518 /* scan the special handling table */ 519 for (h = Handling; h->han_name != NULL; h++) 520 if (sameword(h->han_name, w)) 521 break; 522 523 /* see if we can do anything interesting */ 524 switch (h->han_what) 525 { 526 case HAN_NONE: /* nothing to be done */ 527 break; 528 529 case HAN_RRECEIPT: /* give return receipt */ 530 CurEnv->e_retreceipt = TRUE; 531 # ifdef DEBUG 532 if (tTd(30, 3)) 533 printf(">>> Return receipt requested\n"); 534 # endif DEBUG 535 break; 536 537 default: 538 syserr("spechandling: handling %d (%s)", h->han_what, w); 539 } 540 } 541 } 542