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