1 # include <errno.h> 2 # include "sendmail.h" 3 4 SCCSID(@(#)headers.c 3.31 09/05/82); 5 6 /* 7 ** CHOMPHEADER -- process and save a header line. 8 ** 9 ** Called by collect and by readcf to deal with header lines. 10 ** 11 ** Parameters: 12 ** line -- header as a text line. 13 ** def -- if set, this is a default value. 14 ** 15 ** Returns: 16 ** flags for this header. 17 ** 18 ** Side Effects: 19 ** The header is saved on the header list. 20 ** Contents of 'line' are destroyed. 21 */ 22 23 chompheader(line, def) 24 char *line; 25 bool def; 26 { 27 register char *p; 28 register HDR *h; 29 HDR **hp; 30 char *fname; 31 char *fvalue; 32 struct hdrinfo *hi; 33 u_long mopts; 34 extern u_long mfencode(); 35 extern char *crackaddr(); 36 37 # ifdef DEBUG 38 if (tTd(31, 6)) 39 printf("chompheader: %s\n", line); 40 # endif DEBUG 41 42 /* strip off options */ 43 mopts = 0; 44 p = line; 45 if (*p == '?') 46 { 47 /* have some */ 48 register char *q = index(p + 1, *p); 49 50 if (q != NULL) 51 { 52 *q++ = '\0'; 53 mopts = mfencode(p + 1); 54 p = q; 55 } 56 else 57 syserr("chompheader: syntax error, line \"%s\"", line); 58 } 59 60 /* find canonical name */ 61 fname = p; 62 p = index(p, ':'); 63 fvalue = &p[1]; 64 while (isspace(*--p)) 65 continue; 66 *++p = '\0'; 67 makelower(fname); 68 69 /* strip field value on front */ 70 if (*fvalue == ' ') 71 fvalue++; 72 73 /* search header list for this header */ 74 for (hp = &CurEnv->e_header, h = CurEnv->e_header; h != NULL; hp = &h->h_link, h = h->h_link) 75 { 76 if (strcmp(fname, h->h_field) == 0 && bitset(H_DEFAULT, h->h_flags)) 77 break; 78 } 79 80 /* see if it is a known type */ 81 for (hi = HdrInfo; hi->hi_field != NULL; hi++) 82 { 83 if (strcmp(hi->hi_field, fname) == 0) 84 break; 85 } 86 87 /* if this means "end of header" quit now */ 88 if (bitset(H_EOH, hi->hi_flags)) 89 return (hi->hi_flags); 90 91 /* count Received: lines to avoid loops (simulate hop counts) */ 92 if (bitset(H_TRACE, hi->hi_flags)) 93 HopCount++; 94 95 /* create/fill in a new node */ 96 if (h == NULL || bitset(H_FORCE, h->h_flags)) 97 { 98 /* create a new node */ 99 h = (HDR *) xalloc(sizeof *h); 100 h->h_field = newstr(fname); 101 h->h_value = NULL; 102 h->h_link = *hp; 103 h->h_mflags = mopts | hi->hi_mflags; 104 *hp = h; 105 } 106 h->h_flags = hi->hi_flags; 107 if (def) 108 h->h_flags |= H_DEFAULT; 109 else if (mopts == 0) 110 h->h_flags &= ~H_CHECK; 111 if (h->h_value != NULL) 112 free(h->h_value); 113 h->h_value = newstr(fvalue); 114 115 /* hack to see if this is a new format message */ 116 if (bitset(H_RCPT, h->h_flags) && 117 (index(fvalue, ',') != NULL || index(fvalue, '(') != NULL || 118 index(fvalue, '<') != NULL) || index(fvalue, ';') != NULL) 119 CurEnv->e_oldstyle = FALSE; 120 121 /* send to this person if we so desire */ 122 if (!def && GrabTo && bitset(H_RCPT, h->h_flags)) 123 sendto(h->h_value, 0, (ADDRESS *) NULL, &CurEnv->e_sendqueue); 124 125 return (h->h_flags); 126 } 127 /* 128 ** ADDHEADER -- add a header entry to the end of the queue. 129 ** 130 ** This bypasses the special checking of chompheader. 131 ** 132 ** Parameters: 133 ** field -- the name of the header field. 134 ** value -- the value of the field. It must be lower-cased. 135 ** e -- the envelope to add them to. 136 ** 137 ** Returns: 138 ** none. 139 ** 140 ** Side Effects: 141 ** adds the field on the list of headers for this envelope. 142 */ 143 144 addheader(field, value, e) 145 char *field; 146 char *value; 147 ENVELOPE *e; 148 { 149 register HDR *h; 150 register struct hdrinfo *hi; 151 HDR **hp; 152 153 /* find info struct */ 154 for (hi = HdrInfo; hi->hi_field != NULL; hi++) 155 { 156 if (strcmp(field, hi->hi_field) == 0) 157 break; 158 } 159 160 /* find current place in list -- keep back pointer? */ 161 for (hp = &e->e_header; (h = *hp) != NULL; hp = &h->h_link) 162 { 163 if (strcmp(field, h->h_field) == 0) 164 break; 165 } 166 167 /* allocate space for new header */ 168 h = (HDR *) xalloc(sizeof *h); 169 h->h_field = field; 170 h->h_value = newstr(value); 171 h->h_link = *hp; 172 h->h_flags = hi->hi_flags | H_DEFAULT; 173 h->h_mflags = hi->hi_mflags; 174 *hp = h; 175 } 176 /* 177 ** HVALUE -- return value of a header. 178 ** 179 ** Only "real" fields (i.e., ones that have not been supplied 180 ** as a default) are used. 181 ** 182 ** Parameters: 183 ** field -- the field name. 184 ** 185 ** Returns: 186 ** pointer to the value part. 187 ** NULL if not found. 188 ** 189 ** Side Effects: 190 ** sets the H_USED bit in the header if found. 191 */ 192 193 char * 194 hvalue(field) 195 char *field; 196 { 197 register HDR *h; 198 199 for (h = CurEnv->e_header; h != NULL; h = h->h_link) 200 { 201 if (!bitset(H_DEFAULT, h->h_flags) && strcmp(h->h_field, field) == 0) 202 { 203 h->h_flags |= H_USED; 204 return (h->h_value); 205 } 206 } 207 return (NULL); 208 } 209 /* 210 ** HRVALUE -- return pointer to header descriptor. 211 ** 212 ** Like hvalue except returns header descriptor block and isn't 213 ** picky about "real" headers. 214 ** 215 ** Parameters: 216 ** field -- name of field we are interested in. 217 ** 218 ** Returns: 219 ** pointer to header descriptor. 220 ** 221 ** Side Effects: 222 ** none. 223 */ 224 225 HDR * 226 hrvalue(field) 227 char *field; 228 { 229 register HDR *h; 230 231 for (h = CurEnv->e_header; h != NULL; h = h->h_link) 232 { 233 if (strcmp(h->h_field, field) == 0) 234 return (h); 235 } 236 return (NULL); 237 } 238 /* 239 ** ISHEADER -- predicate telling if argument is a header. 240 ** 241 ** A line is a header if it has a single word followed by 242 ** optional white space followed by a colon. 243 ** 244 ** Parameters: 245 ** s -- string to check for possible headerness. 246 ** 247 ** Returns: 248 ** TRUE if s is a header. 249 ** FALSE otherwise. 250 ** 251 ** Side Effects: 252 ** none. 253 */ 254 255 bool 256 isheader(s) 257 register char *s; 258 { 259 if (!isalnum(*s)) 260 return (FALSE); 261 while (!isspace(*s) && *s != ':' && *s != '\0') 262 s++; 263 while (isspace(*s)) 264 s++; 265 return (*s == ':'); 266 } 267 /* 268 ** GETXPART -- extract the "signature" part of an address line. 269 ** 270 ** Try to extract the full name from a general address 271 ** field. We take anything which is a comment as a 272 ** first choice. Failing in that, we see if there is 273 ** a "machine readable" name (in <angle brackets>); if 274 ** so we take anything preceeding that clause. 275 ** 276 ** If we blow it here it's not all that serious. 277 ** 278 ** Parameters: 279 ** p -- line to crack. 280 ** 281 ** Returns: 282 ** signature part. 283 ** NULL if no signature part. 284 ** 285 ** Side Effects: 286 ** none. 287 */ 288 289 char * 290 getxpart(p) 291 register char *p; 292 { 293 register char *q; 294 register char *rval = NULL; 295 296 q = index(p, '('); 297 if (q != NULL) 298 { 299 int parenlev = 0; 300 301 for (p = q; *p != '\0'; p++) 302 { 303 if (*p == '(') 304 parenlev++; 305 else if (*p == ')' && --parenlev <= 0) 306 break; 307 } 308 if (*p == ')') 309 { 310 *p = '\0'; 311 if (*++q != '\0') 312 rval = newstr(q); 313 *p = ')'; 314 } 315 } 316 else if ((q = index(p, '<')) != NULL) 317 { 318 char savec; 319 320 while (*--q == ' ') 321 continue; 322 while (isspace(*p)) 323 p++; 324 savec = *++q; 325 *q = '\0'; 326 if (*p != '\0') 327 rval = newstr(p); 328 *q = savec; 329 } 330 331 return (rval); 332 } 333 /* 334 ** EATHEADER -- run through the stored header and extract info. 335 ** 336 ** Parameters: 337 ** none. 338 ** 339 ** Returns: 340 ** none. 341 ** 342 ** Side Effects: 343 ** Sets a bunch of global variables from information 344 ** in the collected header. 345 */ 346 347 eatheader() 348 { 349 register HDR *h; 350 register char *p; 351 char buf[MAXLINE]; 352 353 # ifdef DEBUG 354 if (tTd(32, 2)) 355 { 356 extern char *capitalize(); 357 358 printf("----- collected header -----\n"); 359 for (h = CurEnv->e_header; h != NULL; h = h->h_link) 360 printf("%s: %s\n", capitalize(h->h_field), h->h_value); 361 printf("----------------------------\n"); 362 } 363 # endif DEBUG 364 365 /* message priority */ 366 if (!QueueRun) 367 { 368 /* adjust total priority by message priority */ 369 CurEnv->e_msgpriority = CurEnv->e_msgsize; 370 p = hvalue("precedence"); 371 if (p != NULL) 372 CurEnv->e_class = priencode(p); 373 else 374 CurEnv->e_class = PRI_NORMAL; 375 CurEnv->e_msgpriority -= CurEnv->e_class * WKPRIFACT; 376 } 377 378 /* return receipt to */ 379 p = hvalue("return-receipt-to"); 380 if (p != NULL) 381 CurEnv->e_receiptto = p; 382 383 /* from person */ 384 if (ArpaMode) 385 { 386 register struct hdrinfo *hi = HdrInfo; 387 388 for (p = NULL; p == NULL && hi->hi_field != NULL; hi++) 389 { 390 if (bitset(H_FROM, hi->hi_flags)) 391 p = hvalue(hi->hi_field); 392 } 393 if (p != NULL) 394 setfrom(p, (char *) NULL); 395 } 396 397 /* full name of from person */ 398 p = hvalue("full-name"); 399 if (p != NULL) 400 define('x', p); 401 402 /* date message originated */ 403 p = hvalue("posted-date"); 404 if (p == NULL) 405 p = hvalue("date"); 406 if (p != NULL) 407 { 408 define('a', p); 409 /* we don't have a good way to do canonical conversion .... 410 define('d', newstr(arpatounix(p))); 411 .... so we will ignore the problem for the time being */ 412 } 413 } 414 /* 415 ** PRIENCODE -- encode external priority names into internal values. 416 ** 417 ** Parameters: 418 ** p -- priority in ascii. 419 ** 420 ** Returns: 421 ** priority as a numeric level. 422 ** 423 ** Side Effects: 424 ** none. 425 */ 426 427 struct prio 428 { 429 char *pri_name; /* external name of priority */ 430 int pri_val; /* internal value for same */ 431 }; 432 433 static struct prio Prio[] = 434 { 435 "alert", PRI_ALERT, 436 "quick", PRI_QUICK, 437 "first-class", PRI_FIRSTCL, 438 "normal", PRI_NORMAL, 439 "second-class", PRI_SECONDCL, 440 "third-class", PRI_THIRDCL, 441 "junk", PRI_JUNK, 442 NULL, PRI_NORMAL, 443 }; 444 445 priencode(p) 446 char *p; 447 { 448 register struct prio *pl; 449 extern bool sameword(); 450 451 for (pl = Prio; pl->pri_name != NULL; pl++) 452 { 453 if (sameword(p, pl->pri_name)) 454 break; 455 } 456 return (pl->pri_val); 457 } 458 /* 459 ** CRACKADDR -- parse an address and turn it into a macro 460 ** 461 ** This doesn't actually parse the address -- it just extracts 462 ** it and replaces it with "$g". The parse is totally ad hoc 463 ** and isn't even guaranteed to leave something syntactically 464 ** identical to what it started with. However, it does leave 465 ** something semantically identical. 466 ** 467 ** The process is kind of strange. There are a number of 468 ** interesting cases: 469 ** 1. comment <address> comment ==> comment <$g> comment 470 ** 2. address ==> address 471 ** 3. address (comment) ==> $g (comment) 472 ** 4. (comment) address ==> (comment) $g 473 ** And then there are the hard cases.... 474 ** 5. add (comment) ress ==> $g (comment) 475 ** 6. comment <address (comment)> ==> comment <$g (comment)> 476 ** 7. .... etc .... 477 ** 478 ** Parameters: 479 ** addr -- the address to be cracked. 480 ** 481 ** Returns: 482 ** a pointer to the new version. 483 ** 484 ** Side Effects: 485 ** none. 486 ** 487 ** Warning: 488 ** The return value is saved in local storage and should 489 ** be copied if it is to be reused. 490 */ 491 492 char * 493 crackaddr(addr) 494 register char *addr; 495 { 496 register char *p; 497 register int i; 498 static char buf[MAXNAME]; 499 char *rhs; 500 bool gotaddr; 501 register char *bp; 502 503 # ifdef DEBUG 504 if (tTd(33, 1)) 505 printf("crackaddr(%s)\n", addr); 506 # endif DEBUG 507 508 strcpy(buf, ""); 509 rhs = NULL; 510 511 /* 512 ** See if we have anything in angle brackets. If so, that is 513 ** the address part, and the rest is the comment. 514 */ 515 516 p = index(addr, '<'); 517 if (p != NULL) 518 { 519 /* copy the beginning of the addr field to the buffer */ 520 *p = '\0'; 521 strcpy(buf, addr); 522 strcat(buf, "<"); 523 *p = '<'; 524 525 /* find the matching right angle bracket */ 526 addr = ++p; 527 for (i = 0; *p != '\0'; p++) 528 { 529 switch (*p) 530 { 531 case '<': 532 i++; 533 break; 534 535 case '>': 536 i--; 537 break; 538 } 539 if (i < 0) 540 break; 541 } 542 543 /* p now points to the closing quote (or a null byte) */ 544 if (*p != '\0') 545 { 546 /* make rhs point to the extra stuff at the end */ 547 rhs = p; 548 *p++ = '\0'; 549 } 550 } 551 552 /* 553 ** Now parse the real address part. "addr" points to the (null 554 ** terminated) version of what we are inerested in; rhs points 555 ** to the extra stuff at the end of the line, if any. 556 */ 557 558 p = addr; 559 560 /* now strip out comments */ 561 bp = &buf[strlen(buf)]; 562 gotaddr = FALSE; 563 for (; *p != '\0'; p++) 564 { 565 if (*p == '(') 566 { 567 /* copy to matching close paren */ 568 *bp++ = *p++; 569 for (i = 0; *p != '\0'; p++) 570 { 571 *bp++ = *p; 572 switch (*p) 573 { 574 case '(': 575 i++; 576 break; 577 578 case ')': 579 i--; 580 break; 581 } 582 if (i < 0) 583 break; 584 } 585 continue; 586 } 587 588 /* 589 ** If this is the first "real" character we have seen, 590 ** then we put the "$g" in the buffer now. 591 */ 592 593 if (isspace(*p)) 594 *bp++ = *p; 595 else if (!gotaddr) 596 { 597 strcpy(bp, "$g"); 598 bp += 2; 599 gotaddr = TRUE; 600 } 601 } 602 603 /* hack, hack.... strip trailing blanks */ 604 do 605 { 606 *bp-- = '\0'; 607 } while (isspace(*bp)); 608 bp++; 609 610 /* put any right hand side back on */ 611 if (rhs != NULL) 612 { 613 *rhs = '>'; 614 strcpy(bp, rhs); 615 } 616 617 # ifdef DEBUG 618 if (tTd(33, 1)) 619 printf("crackaddr=>`%s'\n", buf); 620 # endif DEBUG 621 622 return (buf); 623 } 624