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