14091Seric # include <errno.h> 24091Seric # include "sendmail.h" 34091Seric 4*8253Seric SCCSID(@(#)headers.c 3.35 09/24/82); 54091Seric 64091Seric /* 74091Seric ** CHOMPHEADER -- process and save a header line. 84091Seric ** 94091Seric ** Called by collect and by readcf to deal with header lines. 104091Seric ** 114091Seric ** Parameters: 124091Seric ** line -- header as a text line. 134091Seric ** def -- if set, this is a default value. 144091Seric ** 154091Seric ** Returns: 164091Seric ** flags for this header. 174091Seric ** 184091Seric ** Side Effects: 194091Seric ** The header is saved on the header list. 204319Seric ** Contents of 'line' are destroyed. 214091Seric */ 224091Seric 234091Seric chompheader(line, def) 244091Seric char *line; 254091Seric bool def; 264091Seric { 274091Seric register char *p; 284091Seric register HDR *h; 294091Seric HDR **hp; 304091Seric char *fname; 314091Seric char *fvalue; 324091Seric struct hdrinfo *hi; 334627Seric u_long mopts; 344627Seric extern u_long mfencode(); 357890Seric extern char *crackaddr(); 364091Seric 377677Seric # ifdef DEBUG 387677Seric if (tTd(31, 6)) 397677Seric printf("chompheader: %s\n", line); 407677Seric # endif DEBUG 417677Seric 424627Seric /* strip off options */ 434627Seric mopts = 0; 444627Seric p = line; 454627Seric if (*p == '?') 464627Seric { 474627Seric /* have some */ 484627Seric register char *q = index(p + 1, *p); 494627Seric 504627Seric if (q != NULL) 514627Seric { 524627Seric *q++ = '\0'; 534627Seric mopts = mfencode(p + 1); 544627Seric p = q; 554627Seric } 564627Seric else 574627Seric syserr("chompheader: syntax error, line \"%s\"", line); 584627Seric } 594627Seric 604091Seric /* find canonical name */ 614627Seric fname = p; 624627Seric p = index(p, ':'); 634091Seric fvalue = &p[1]; 644091Seric while (isspace(*--p)) 654091Seric continue; 664091Seric *++p = '\0'; 674091Seric makelower(fname); 684091Seric 694091Seric /* strip field value on front */ 704091Seric if (*fvalue == ' ') 714091Seric fvalue++; 724091Seric 734091Seric /* search header list for this header */ 746908Seric for (hp = &CurEnv->e_header, h = CurEnv->e_header; h != NULL; hp = &h->h_link, h = h->h_link) 754091Seric { 765187Seric if (strcmp(fname, h->h_field) == 0 && bitset(H_DEFAULT, h->h_flags)) 774091Seric break; 784091Seric } 794091Seric 804091Seric /* see if it is a known type */ 814091Seric for (hi = HdrInfo; hi->hi_field != NULL; hi++) 824091Seric { 834091Seric if (strcmp(hi->hi_field, fname) == 0) 844091Seric break; 854091Seric } 864091Seric 874091Seric /* if this means "end of header" quit now */ 884091Seric if (bitset(H_EOH, hi->hi_flags)) 894091Seric return (hi->hi_flags); 904091Seric 917789Seric /* count Received: lines to avoid loops (simulate hop counts) */ 928066Seric if (bitset(H_TRACE, hi->hi_flags)) 937365Seric HopCount++; 947365Seric 954091Seric /* create/fill in a new node */ 965187Seric if (h == NULL || bitset(H_FORCE, h->h_flags)) 974091Seric { 984091Seric /* create a new node */ 995187Seric h = (HDR *) xalloc(sizeof *h); 1004091Seric h->h_field = newstr(fname); 1014091Seric h->h_value = NULL; 1025187Seric h->h_link = *hp; 1034627Seric h->h_mflags = mopts | hi->hi_mflags; 1045187Seric *hp = h; 1054091Seric } 1068066Seric h->h_flags = hi->hi_flags; 1074091Seric if (def) 1084091Seric h->h_flags |= H_DEFAULT; 1094627Seric else if (mopts == 0) 1104091Seric h->h_flags &= ~H_CHECK; 1114091Seric if (h->h_value != NULL) 1124091Seric free(h->h_value); 1138066Seric h->h_value = newstr(fvalue); 1144091Seric 1155937Seric /* hack to see if this is a new format message */ 1168095Seric if (!def && bitset(H_RCPT|H_FROM, h->h_flags) && 1175937Seric (index(fvalue, ',') != NULL || index(fvalue, '(') != NULL || 1188089Seric index(fvalue, '<') != NULL || index(fvalue, ';') != NULL)) 1198089Seric { 1206908Seric CurEnv->e_oldstyle = FALSE; 1218089Seric } 1225937Seric 1238066Seric /* send to this person if we so desire */ 1248066Seric if (!def && GrabTo && bitset(H_RCPT, h->h_flags)) 1258082Seric sendto(h->h_value, (ADDRESS *) NULL, &CurEnv->e_sendqueue); 1268066Seric 1274091Seric return (h->h_flags); 1284091Seric } 1294091Seric /* 1306980Seric ** ADDHEADER -- add a header entry to the end of the queue. 1316980Seric ** 1326980Seric ** This bypasses the special checking of chompheader. 1336980Seric ** 1346980Seric ** Parameters: 1356980Seric ** field -- the name of the header field. 1366980Seric ** value -- the value of the field. It must be lower-cased. 1376980Seric ** e -- the envelope to add them to. 1386980Seric ** 1396980Seric ** Returns: 1406980Seric ** none. 1416980Seric ** 1426980Seric ** Side Effects: 1436980Seric ** adds the field on the list of headers for this envelope. 1446980Seric */ 1456980Seric 1466980Seric addheader(field, value, e) 1476980Seric char *field; 1486980Seric char *value; 1496980Seric ENVELOPE *e; 1506980Seric { 1516980Seric register HDR *h; 1526980Seric register struct hdrinfo *hi; 1536980Seric HDR **hp; 1546980Seric 1556980Seric /* find info struct */ 1566980Seric for (hi = HdrInfo; hi->hi_field != NULL; hi++) 1576980Seric { 1586980Seric if (strcmp(field, hi->hi_field) == 0) 1596980Seric break; 1606980Seric } 1616980Seric 1626980Seric /* find current place in list -- keep back pointer? */ 1636980Seric for (hp = &e->e_header; (h = *hp) != NULL; hp = &h->h_link) 1646980Seric { 1656980Seric if (strcmp(field, h->h_field) == 0) 1666980Seric break; 1676980Seric } 1686980Seric 1696980Seric /* allocate space for new header */ 1706980Seric h = (HDR *) xalloc(sizeof *h); 1716980Seric h->h_field = field; 1726980Seric h->h_value = newstr(value); 1737368Seric h->h_link = *hp; 1746980Seric h->h_flags = hi->hi_flags | H_DEFAULT; 1756980Seric h->h_mflags = hi->hi_mflags; 1766980Seric *hp = h; 1776980Seric } 1786980Seric /* 1794091Seric ** HVALUE -- return value of a header. 1804091Seric ** 1814091Seric ** Only "real" fields (i.e., ones that have not been supplied 1824091Seric ** as a default) are used. 1834091Seric ** 1844091Seric ** Parameters: 1854091Seric ** field -- the field name. 1864091Seric ** 1874091Seric ** Returns: 1884091Seric ** pointer to the value part. 1894091Seric ** NULL if not found. 1904091Seric ** 1914091Seric ** Side Effects: 1924091Seric ** sets the H_USED bit in the header if found. 1934091Seric */ 1944091Seric 1954091Seric char * 1964091Seric hvalue(field) 1974091Seric char *field; 1984091Seric { 1994091Seric register HDR *h; 2004091Seric 2016908Seric for (h = CurEnv->e_header; h != NULL; h = h->h_link) 2024091Seric { 2034091Seric if (!bitset(H_DEFAULT, h->h_flags) && strcmp(h->h_field, field) == 0) 2044091Seric { 2054091Seric h->h_flags |= H_USED; 2064091Seric return (h->h_value); 2074091Seric } 2084091Seric } 2094091Seric return (NULL); 2104091Seric } 2114091Seric /* 2127677Seric ** HRVALUE -- return pointer to header descriptor. 2137677Seric ** 2147677Seric ** Like hvalue except returns header descriptor block and isn't 2157677Seric ** picky about "real" headers. 2167677Seric ** 2177677Seric ** Parameters: 2187677Seric ** field -- name of field we are interested in. 2197677Seric ** 2207677Seric ** Returns: 2217677Seric ** pointer to header descriptor. 2227677Seric ** 2237677Seric ** Side Effects: 2247677Seric ** none. 2257677Seric */ 2267677Seric 2277677Seric HDR * 2287677Seric hrvalue(field) 2297677Seric char *field; 2307677Seric { 2317677Seric register HDR *h; 2327677Seric 2337677Seric for (h = CurEnv->e_header; h != NULL; h = h->h_link) 2347677Seric { 2357677Seric if (strcmp(h->h_field, field) == 0) 2367677Seric return (h); 2377677Seric } 2387677Seric return (NULL); 2397677Seric } 2407677Seric /* 2414091Seric ** ISHEADER -- predicate telling if argument is a header. 2424091Seric ** 2434319Seric ** A line is a header if it has a single word followed by 2444319Seric ** optional white space followed by a colon. 2454319Seric ** 2464091Seric ** Parameters: 2474091Seric ** s -- string to check for possible headerness. 2484091Seric ** 2494091Seric ** Returns: 2504091Seric ** TRUE if s is a header. 2514091Seric ** FALSE otherwise. 2524091Seric ** 2534091Seric ** Side Effects: 2544091Seric ** none. 2554091Seric */ 2564091Seric 2574091Seric bool 2584091Seric isheader(s) 2594091Seric register char *s; 2604091Seric { 2614091Seric if (!isalnum(*s)) 2624091Seric return (FALSE); 2637855Seric while (!isspace(*s) && *s != ':' && *s != '\0') 2644091Seric s++; 2654091Seric while (isspace(*s)) 2664091Seric s++; 2674091Seric return (*s == ':'); 2684091Seric } 2695919Seric /* 2705919Seric ** GETXPART -- extract the "signature" part of an address line. 2715919Seric ** 2725919Seric ** Try to extract the full name from a general address 2735919Seric ** field. We take anything which is a comment as a 2745919Seric ** first choice. Failing in that, we see if there is 2755919Seric ** a "machine readable" name (in <angle brackets>); if 2765919Seric ** so we take anything preceeding that clause. 2775919Seric ** 2785919Seric ** If we blow it here it's not all that serious. 2795919Seric ** 2805919Seric ** Parameters: 2815919Seric ** p -- line to crack. 2825919Seric ** 2835919Seric ** Returns: 2845919Seric ** signature part. 2855919Seric ** NULL if no signature part. 2865919Seric ** 2875919Seric ** Side Effects: 2885919Seric ** none. 2895919Seric */ 2905919Seric 2915919Seric char * 2925919Seric getxpart(p) 2935919Seric register char *p; 2945919Seric { 2955919Seric register char *q; 2965919Seric register char *rval = NULL; 2975919Seric 2985919Seric q = index(p, '('); 2995919Seric if (q != NULL) 3005919Seric { 3015919Seric int parenlev = 0; 3025919Seric 3035919Seric for (p = q; *p != '\0'; p++) 3045919Seric { 3055919Seric if (*p == '(') 3065919Seric parenlev++; 3075919Seric else if (*p == ')' && --parenlev <= 0) 3085919Seric break; 3095919Seric } 3105919Seric if (*p == ')') 3115919Seric { 3125919Seric *p = '\0'; 3135919Seric if (*++q != '\0') 3145919Seric rval = newstr(q); 3155919Seric *p = ')'; 3165919Seric } 3175919Seric } 3185919Seric else if ((q = index(p, '<')) != NULL) 3195919Seric { 3205919Seric char savec; 3215919Seric 3225919Seric while (*--q == ' ') 3235919Seric continue; 3245919Seric while (isspace(*p)) 3255919Seric p++; 3265919Seric savec = *++q; 3275919Seric *q = '\0'; 3285919Seric if (*p != '\0') 3295919Seric rval = newstr(p); 3305919Seric *q = savec; 3315919Seric } 3325919Seric 3335919Seric return (rval); 3345919Seric } 3357783Seric /* 3367783Seric ** EATHEADER -- run through the stored header and extract info. 3377783Seric ** 3387783Seric ** Parameters: 3397783Seric ** none. 3407783Seric ** 3417783Seric ** Returns: 3427783Seric ** none. 3437783Seric ** 3447783Seric ** Side Effects: 3457783Seric ** Sets a bunch of global variables from information 3467783Seric ** in the collected header. 3477783Seric */ 3487783Seric 3497783Seric eatheader() 3507783Seric { 3517783Seric register HDR *h; 3527783Seric register char *p; 3537783Seric char buf[MAXLINE]; 3547783Seric 3557783Seric # ifdef DEBUG 3567783Seric if (tTd(32, 2)) 3577783Seric { 3587783Seric extern char *capitalize(); 3597783Seric 3607783Seric printf("----- collected header -----\n"); 3617783Seric for (h = CurEnv->e_header; h != NULL; h = h->h_link) 3627783Seric printf("%s: %s\n", capitalize(h->h_field), h->h_value); 3637783Seric printf("----------------------------\n"); 3647783Seric } 3657783Seric # endif DEBUG 3667783Seric 3677783Seric /* message priority */ 3687783Seric if (!QueueRun) 3697783Seric { 3707783Seric /* adjust total priority by message priority */ 3717783Seric CurEnv->e_msgpriority = CurEnv->e_msgsize; 3728066Seric p = hvalue("precedence"); 3737783Seric if (p != NULL) 3747783Seric CurEnv->e_class = priencode(p); 3757783Seric CurEnv->e_msgpriority -= CurEnv->e_class * WKPRIFACT; 3767783Seric } 3777783Seric 3788066Seric /* return receipt to */ 3798066Seric p = hvalue("return-receipt-to"); 3807783Seric if (p != NULL) 3818066Seric CurEnv->e_receiptto = p; 3827783Seric 383*8253Seric /* errors to */ 384*8253Seric p = hvalue("errors-to"); 385*8253Seric if (p != NULL) 386*8253Seric sendto(p, (ADDRESS *) NULL, &CurEnv->e_errorqueue); 387*8253Seric 3887783Seric /* from person */ 3897783Seric if (ArpaMode) 3908066Seric { 3918066Seric register struct hdrinfo *hi = HdrInfo; 3927783Seric 3938066Seric for (p = NULL; p == NULL && hi->hi_field != NULL; hi++) 3948066Seric { 3958066Seric if (bitset(H_FROM, hi->hi_flags)) 3968066Seric p = hvalue(hi->hi_field); 3978066Seric } 3988066Seric if (p != NULL) 3998066Seric setfrom(p, (char *) NULL); 4008066Seric } 4018066Seric 4027783Seric /* full name of from person */ 4037783Seric p = hvalue("full-name"); 4047783Seric if (p != NULL) 4057783Seric define('x', p); 4067783Seric 4077783Seric /* date message originated */ 4087783Seric p = hvalue("posted-date"); 4097783Seric if (p == NULL) 4107783Seric p = hvalue("date"); 4117783Seric if (p != NULL) 4127783Seric { 4137783Seric define('a', p); 4147783Seric /* we don't have a good way to do canonical conversion .... 4157783Seric define('d', newstr(arpatounix(p))); 4167783Seric .... so we will ignore the problem for the time being */ 4177783Seric } 4187783Seric } 4197783Seric /* 4207783Seric ** PRIENCODE -- encode external priority names into internal values. 4217783Seric ** 4227783Seric ** Parameters: 4237783Seric ** p -- priority in ascii. 4247783Seric ** 4257783Seric ** Returns: 4267783Seric ** priority as a numeric level. 4277783Seric ** 4287783Seric ** Side Effects: 4297783Seric ** none. 4307783Seric */ 4317783Seric 4327783Seric priencode(p) 4337783Seric char *p; 4347783Seric { 435*8253Seric register int i; 4367783Seric extern bool sameword(); 4377783Seric 438*8253Seric for (i = 0; i < NumPriorities; i++) 4397783Seric { 440*8253Seric if (sameword(p, Priorities[i].pri_name)) 441*8253Seric return (Priorities[i].pri_val); 4427783Seric } 443*8253Seric 444*8253Seric /* unknown priority */ 445*8253Seric return (0); 4467783Seric } 4477783Seric /* 4487890Seric ** CRACKADDR -- parse an address and turn it into a macro 4497783Seric ** 4507783Seric ** This doesn't actually parse the address -- it just extracts 4517783Seric ** it and replaces it with "$g". The parse is totally ad hoc 4527783Seric ** and isn't even guaranteed to leave something syntactically 4537783Seric ** identical to what it started with. However, it does leave 4547783Seric ** something semantically identical. 4557783Seric ** 4567783Seric ** The process is kind of strange. There are a number of 4577783Seric ** interesting cases: 4587783Seric ** 1. comment <address> comment ==> comment <$g> comment 4597783Seric ** 2. address ==> address 4607783Seric ** 3. address (comment) ==> $g (comment) 4617783Seric ** 4. (comment) address ==> (comment) $g 4627783Seric ** And then there are the hard cases.... 4637783Seric ** 5. add (comment) ress ==> $g (comment) 4647783Seric ** 6. comment <address (comment)> ==> comment <$g (comment)> 4657783Seric ** 7. .... etc .... 4667783Seric ** 4677783Seric ** Parameters: 4687890Seric ** addr -- the address to be cracked. 4697783Seric ** 4707783Seric ** Returns: 4717783Seric ** a pointer to the new version. 4727783Seric ** 4737783Seric ** Side Effects: 4747890Seric ** none. 4757783Seric ** 4767783Seric ** Warning: 4777783Seric ** The return value is saved in local storage and should 4787783Seric ** be copied if it is to be reused. 4797783Seric */ 4807783Seric 4817783Seric char * 4827890Seric crackaddr(addr) 4837890Seric register char *addr; 4847783Seric { 4857783Seric register char *p; 4867783Seric register int i; 4877783Seric static char buf[MAXNAME]; 4887783Seric char *rhs; 4897783Seric bool gotaddr; 4907783Seric register char *bp; 4917783Seric 4927783Seric # ifdef DEBUG 4937783Seric if (tTd(33, 1)) 4947890Seric printf("crackaddr(%s)\n", addr); 4957783Seric # endif DEBUG 4967783Seric 4977783Seric strcpy(buf, ""); 4987783Seric rhs = NULL; 4997783Seric 5008082Seric /* strip leading spaces */ 5018082Seric while (*addr != '\0' && isspace(*addr)) 5028082Seric addr++; 5038082Seric 5047783Seric /* 5057783Seric ** See if we have anything in angle brackets. If so, that is 5067783Seric ** the address part, and the rest is the comment. 5077783Seric */ 5087783Seric 5097890Seric p = index(addr, '<'); 5107783Seric if (p != NULL) 5117783Seric { 5127890Seric /* copy the beginning of the addr field to the buffer */ 5137783Seric *p = '\0'; 5147890Seric strcpy(buf, addr); 5157783Seric strcat(buf, "<"); 5168082Seric *p++ = '<'; 5177783Seric 5188082Seric /* skip spaces */ 5198082Seric while (isspace(*p)) 5208082Seric p++; 5218082Seric 5227783Seric /* find the matching right angle bracket */ 5238082Seric addr = p; 5247783Seric for (i = 0; *p != '\0'; p++) 5257783Seric { 5267783Seric switch (*p) 5277783Seric { 5287783Seric case '<': 5297783Seric i++; 5307783Seric break; 5317783Seric 5327783Seric case '>': 5337783Seric i--; 5347783Seric break; 5357783Seric } 5367783Seric if (i < 0) 5377783Seric break; 5387783Seric } 5397783Seric 5407783Seric /* p now points to the closing quote (or a null byte) */ 5417783Seric if (*p != '\0') 5427783Seric { 5437783Seric /* make rhs point to the extra stuff at the end */ 5447783Seric rhs = p; 5457783Seric *p++ = '\0'; 5467783Seric } 5477783Seric } 5487783Seric 5497783Seric /* 5507944Seric ** Now parse the real address part. "addr" points to the (null 5517783Seric ** terminated) version of what we are inerested in; rhs points 5527783Seric ** to the extra stuff at the end of the line, if any. 5537783Seric */ 5547783Seric 5557890Seric p = addr; 5567783Seric 5577783Seric /* now strip out comments */ 5587783Seric bp = &buf[strlen(buf)]; 5597783Seric gotaddr = FALSE; 5607783Seric for (; *p != '\0'; p++) 5617783Seric { 5627783Seric if (*p == '(') 5637783Seric { 5647783Seric /* copy to matching close paren */ 5657783Seric *bp++ = *p++; 5667783Seric for (i = 0; *p != '\0'; p++) 5677783Seric { 5687783Seric *bp++ = *p; 5697783Seric switch (*p) 5707783Seric { 5717783Seric case '(': 5727783Seric i++; 5737783Seric break; 5747783Seric 5757783Seric case ')': 5767783Seric i--; 5777783Seric break; 5787783Seric } 5797783Seric if (i < 0) 5807783Seric break; 5817783Seric } 5827783Seric continue; 5837783Seric } 5847783Seric 5857783Seric /* 5867783Seric ** If this is the first "real" character we have seen, 5877783Seric ** then we put the "$g" in the buffer now. 5887783Seric */ 5897783Seric 5907783Seric if (isspace(*p)) 5917783Seric *bp++ = *p; 5927783Seric else if (!gotaddr) 5937783Seric { 5947783Seric strcpy(bp, "$g"); 5957783Seric bp += 2; 5967783Seric gotaddr = TRUE; 5977783Seric } 5987783Seric } 5997783Seric 6007944Seric /* hack, hack.... strip trailing blanks */ 6017944Seric do 6027944Seric { 6037944Seric *bp-- = '\0'; 6047944Seric } while (isspace(*bp)); 6057944Seric bp++; 6067783Seric 6077944Seric /* put any right hand side back on */ 6087783Seric if (rhs != NULL) 6097783Seric { 6107783Seric *rhs = '>'; 6117783Seric strcpy(bp, rhs); 6127783Seric } 6137783Seric 6147783Seric # ifdef DEBUG 6157783Seric if (tTd(33, 1)) 6167944Seric printf("crackaddr=>`%s'\n", buf); 6177783Seric # endif DEBUG 6187783Seric 6197783Seric return (buf); 6207783Seric } 621