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