1 # include <errno.h> 2 # include "sendmail.h" 3 4 SCCSID(@(#)headers.c 3.39 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((char *) 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 ** ISHEADER -- predicate telling if argument is a header. 215 ** 216 ** A line is a header if it has a single word followed by 217 ** optional white space followed by a colon. 218 ** 219 ** Parameters: 220 ** s -- string to check for possible headerness. 221 ** 222 ** Returns: 223 ** TRUE if s is a header. 224 ** FALSE otherwise. 225 ** 226 ** Side Effects: 227 ** none. 228 */ 229 230 bool 231 isheader(s) 232 register char *s; 233 { 234 if (!isalnum(*s)) 235 return (FALSE); 236 while (!isspace(*s) && *s != ':' && *s != '\0') 237 s++; 238 while (isspace(*s)) 239 s++; 240 return (*s == ':'); 241 } 242 /* 243 ** EATHEADER -- run through the stored header and extract info. 244 ** 245 ** Parameters: 246 ** none. 247 ** 248 ** Returns: 249 ** none. 250 ** 251 ** Side Effects: 252 ** Sets a bunch of global variables from information 253 ** in the collected header. 254 */ 255 256 eatheader() 257 { 258 register HDR *h; 259 register char *p; 260 261 # ifdef DEBUG 262 if (tTd(32, 2)) 263 { 264 extern char *capitalize(); 265 266 printf("----- collected header -----\n"); 267 for (h = CurEnv->e_header; h != NULL; h = h->h_link) 268 printf("%s: %s\n", capitalize(h->h_field), h->h_value); 269 printf("----------------------------\n"); 270 } 271 # endif DEBUG 272 273 /* message priority */ 274 if (!QueueRun) 275 { 276 /* adjust total priority by message priority */ 277 CurEnv->e_msgpriority = CurEnv->e_msgsize; 278 p = hvalue("precedence"); 279 if (p != NULL) 280 CurEnv->e_class = priencode(p); 281 CurEnv->e_msgpriority -= CurEnv->e_class * WKPRIFACT; 282 } 283 284 /* return receipt to */ 285 p = hvalue("return-receipt-to"); 286 if (p != NULL) 287 CurEnv->e_receiptto = p; 288 289 /* errors to */ 290 p = hvalue("errors-to"); 291 if (p != NULL) 292 sendto(p, (ADDRESS *) NULL, &CurEnv->e_errorqueue); 293 294 /* from person */ 295 if (OpMode == MD_ARPAFTP) 296 { 297 register struct hdrinfo *hi = HdrInfo; 298 299 for (p = NULL; p == NULL && hi->hi_field != NULL; hi++) 300 { 301 if (bitset(H_FROM, hi->hi_flags)) 302 p = hvalue(hi->hi_field); 303 } 304 if (p != NULL) 305 setfrom(p, (char *) NULL); 306 } 307 308 /* full name of from person */ 309 p = hvalue("full-name"); 310 if (p != NULL) 311 define('x', p); 312 313 /* date message originated */ 314 p = hvalue("posted-date"); 315 if (p == NULL) 316 p = hvalue("date"); 317 if (p != NULL) 318 { 319 define('a', p); 320 /* we don't have a good way to do canonical conversion .... 321 define('d', newstr(arpatounix(p))); 322 .... so we will ignore the problem for the time being */ 323 } 324 } 325 /* 326 ** PRIENCODE -- encode external priority names into internal values. 327 ** 328 ** Parameters: 329 ** p -- priority in ascii. 330 ** 331 ** Returns: 332 ** priority as a numeric level. 333 ** 334 ** Side Effects: 335 ** none. 336 */ 337 338 priencode(p) 339 char *p; 340 { 341 register int i; 342 extern bool sameword(); 343 344 for (i = 0; i < NumPriorities; i++) 345 { 346 if (sameword(p, Priorities[i].pri_name)) 347 return (Priorities[i].pri_val); 348 } 349 350 /* unknown priority */ 351 return (0); 352 } 353 /* 354 ** CRACKADDR -- parse an address and turn it into a macro 355 ** 356 ** This doesn't actually parse the address -- it just extracts 357 ** it and replaces it with "$g". The parse is totally ad hoc 358 ** and isn't even guaranteed to leave something syntactically 359 ** identical to what it started with. However, it does leave 360 ** something semantically identical. 361 ** 362 ** The process is kind of strange. There are a number of 363 ** interesting cases: 364 ** 1. comment <address> comment ==> comment <$g> comment 365 ** 2. address ==> address 366 ** 3. address (comment) ==> $g (comment) 367 ** 4. (comment) address ==> (comment) $g 368 ** And then there are the hard cases.... 369 ** 5. add (comment) ress ==> $g (comment) 370 ** 6. comment <address (comment)> ==> comment <$g (comment)> 371 ** 7. .... etc .... 372 ** 373 ** Parameters: 374 ** addr -- the address to be cracked. 375 ** 376 ** Returns: 377 ** a pointer to the new version. 378 ** 379 ** Side Effects: 380 ** none. 381 ** 382 ** Warning: 383 ** The return value is saved in local storage and should 384 ** be copied if it is to be reused. 385 */ 386 387 char * 388 crackaddr(addr) 389 register char *addr; 390 { 391 register char *p; 392 register int i; 393 static char buf[MAXNAME]; 394 char *rhs; 395 bool gotaddr; 396 register char *bp; 397 398 # ifdef DEBUG 399 if (tTd(33, 1)) 400 printf("crackaddr(%s)\n", addr); 401 # endif DEBUG 402 403 strcpy(buf, ""); 404 rhs = NULL; 405 406 /* strip leading spaces */ 407 while (*addr != '\0' && isspace(*addr)) 408 addr++; 409 410 /* 411 ** See if we have anything in angle brackets. If so, that is 412 ** the address part, and the rest is the comment. 413 */ 414 415 p = index(addr, '<'); 416 if (p != NULL) 417 { 418 /* copy the beginning of the addr field to the buffer */ 419 *p = '\0'; 420 strcpy(buf, addr); 421 strcat(buf, "<"); 422 *p++ = '<'; 423 424 /* skip spaces */ 425 while (isspace(*p)) 426 p++; 427 428 /* find the matching right angle bracket */ 429 addr = p; 430 for (i = 0; *p != '\0'; p++) 431 { 432 switch (*p) 433 { 434 case '<': 435 i++; 436 break; 437 438 case '>': 439 i--; 440 break; 441 } 442 if (i < 0) 443 break; 444 } 445 446 /* p now points to the closing quote (or a null byte) */ 447 if (*p != '\0') 448 { 449 /* make rhs point to the extra stuff at the end */ 450 rhs = p; 451 *p++ = '\0'; 452 } 453 } 454 455 /* 456 ** Now parse the real address part. "addr" points to the (null 457 ** terminated) version of what we are inerested in; rhs points 458 ** to the extra stuff at the end of the line, if any. 459 */ 460 461 p = addr; 462 463 /* now strip out comments */ 464 bp = &buf[strlen(buf)]; 465 gotaddr = FALSE; 466 for (; *p != '\0'; p++) 467 { 468 if (*p == '(') 469 { 470 /* copy to matching close paren */ 471 *bp++ = *p++; 472 for (i = 0; *p != '\0'; p++) 473 { 474 *bp++ = *p; 475 switch (*p) 476 { 477 case '(': 478 i++; 479 break; 480 481 case ')': 482 i--; 483 break; 484 } 485 if (i < 0) 486 break; 487 } 488 continue; 489 } 490 491 /* 492 ** If this is the first "real" character we have seen, 493 ** then we put the "$g" in the buffer now. 494 */ 495 496 if (isspace(*p)) 497 *bp++ = *p; 498 else if (!gotaddr) 499 { 500 strcpy(bp, "$g"); 501 bp += 2; 502 gotaddr = TRUE; 503 } 504 } 505 506 /* hack, hack.... strip trailing blanks */ 507 do 508 { 509 *bp-- = '\0'; 510 } while (isspace(*bp)); 511 bp++; 512 513 /* put any right hand side back on */ 514 if (rhs != NULL) 515 { 516 *rhs = '>'; 517 strcpy(bp, rhs); 518 } 519 520 # ifdef DEBUG 521 if (tTd(33, 1)) 522 printf("crackaddr=>`%s'\n", buf); 523 # endif DEBUG 524 525 return (buf); 526 } 527