14091Seric # include <errno.h> 24091Seric # include "sendmail.h" 34091Seric 4*8082Seric SCCSID(@(#)headers.c 3.32 09/06/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 */ 1165937Seric if (bitset(H_RCPT, h->h_flags) && 1175937Seric (index(fvalue, ',') != NULL || index(fvalue, '(') != NULL || 1188066Seric index(fvalue, '<') != NULL) || index(fvalue, ';') != NULL) 1196908Seric CurEnv->e_oldstyle = FALSE; 1205937Seric 1218066Seric /* send to this person if we so desire */ 1228066Seric if (!def && GrabTo && bitset(H_RCPT, h->h_flags)) 123*8082Seric sendto(h->h_value, (ADDRESS *) NULL, &CurEnv->e_sendqueue); 1248066Seric 1254091Seric return (h->h_flags); 1264091Seric } 1274091Seric /* 1286980Seric ** ADDHEADER -- add a header entry to the end of the queue. 1296980Seric ** 1306980Seric ** This bypasses the special checking of chompheader. 1316980Seric ** 1326980Seric ** Parameters: 1336980Seric ** field -- the name of the header field. 1346980Seric ** value -- the value of the field. It must be lower-cased. 1356980Seric ** e -- the envelope to add them to. 1366980Seric ** 1376980Seric ** Returns: 1386980Seric ** none. 1396980Seric ** 1406980Seric ** Side Effects: 1416980Seric ** adds the field on the list of headers for this envelope. 1426980Seric */ 1436980Seric 1446980Seric addheader(field, value, e) 1456980Seric char *field; 1466980Seric char *value; 1476980Seric ENVELOPE *e; 1486980Seric { 1496980Seric register HDR *h; 1506980Seric register struct hdrinfo *hi; 1516980Seric HDR **hp; 1526980Seric 1536980Seric /* find info struct */ 1546980Seric for (hi = HdrInfo; hi->hi_field != NULL; hi++) 1556980Seric { 1566980Seric if (strcmp(field, hi->hi_field) == 0) 1576980Seric break; 1586980Seric } 1596980Seric 1606980Seric /* find current place in list -- keep back pointer? */ 1616980Seric for (hp = &e->e_header; (h = *hp) != NULL; hp = &h->h_link) 1626980Seric { 1636980Seric if (strcmp(field, h->h_field) == 0) 1646980Seric break; 1656980Seric } 1666980Seric 1676980Seric /* allocate space for new header */ 1686980Seric h = (HDR *) xalloc(sizeof *h); 1696980Seric h->h_field = field; 1706980Seric h->h_value = newstr(value); 1717368Seric h->h_link = *hp; 1726980Seric h->h_flags = hi->hi_flags | H_DEFAULT; 1736980Seric h->h_mflags = hi->hi_mflags; 1746980Seric *hp = h; 1756980Seric } 1766980Seric /* 1774091Seric ** HVALUE -- return value of a header. 1784091Seric ** 1794091Seric ** Only "real" fields (i.e., ones that have not been supplied 1804091Seric ** as a default) are used. 1814091Seric ** 1824091Seric ** Parameters: 1834091Seric ** field -- the field name. 1844091Seric ** 1854091Seric ** Returns: 1864091Seric ** pointer to the value part. 1874091Seric ** NULL if not found. 1884091Seric ** 1894091Seric ** Side Effects: 1904091Seric ** sets the H_USED bit in the header if found. 1914091Seric */ 1924091Seric 1934091Seric char * 1944091Seric hvalue(field) 1954091Seric char *field; 1964091Seric { 1974091Seric register HDR *h; 1984091Seric 1996908Seric for (h = CurEnv->e_header; h != NULL; h = h->h_link) 2004091Seric { 2014091Seric if (!bitset(H_DEFAULT, h->h_flags) && strcmp(h->h_field, field) == 0) 2024091Seric { 2034091Seric h->h_flags |= H_USED; 2044091Seric return (h->h_value); 2054091Seric } 2064091Seric } 2074091Seric return (NULL); 2084091Seric } 2094091Seric /* 2107677Seric ** HRVALUE -- return pointer to header descriptor. 2117677Seric ** 2127677Seric ** Like hvalue except returns header descriptor block and isn't 2137677Seric ** picky about "real" headers. 2147677Seric ** 2157677Seric ** Parameters: 2167677Seric ** field -- name of field we are interested in. 2177677Seric ** 2187677Seric ** Returns: 2197677Seric ** pointer to header descriptor. 2207677Seric ** 2217677Seric ** Side Effects: 2227677Seric ** none. 2237677Seric */ 2247677Seric 2257677Seric HDR * 2267677Seric hrvalue(field) 2277677Seric char *field; 2287677Seric { 2297677Seric register HDR *h; 2307677Seric 2317677Seric for (h = CurEnv->e_header; h != NULL; h = h->h_link) 2327677Seric { 2337677Seric if (strcmp(h->h_field, field) == 0) 2347677Seric return (h); 2357677Seric } 2367677Seric return (NULL); 2377677Seric } 2387677Seric /* 2394091Seric ** ISHEADER -- predicate telling if argument is a header. 2404091Seric ** 2414319Seric ** A line is a header if it has a single word followed by 2424319Seric ** optional white space followed by a colon. 2434319Seric ** 2444091Seric ** Parameters: 2454091Seric ** s -- string to check for possible headerness. 2464091Seric ** 2474091Seric ** Returns: 2484091Seric ** TRUE if s is a header. 2494091Seric ** FALSE otherwise. 2504091Seric ** 2514091Seric ** Side Effects: 2524091Seric ** none. 2534091Seric */ 2544091Seric 2554091Seric bool 2564091Seric isheader(s) 2574091Seric register char *s; 2584091Seric { 2594091Seric if (!isalnum(*s)) 2604091Seric return (FALSE); 2617855Seric while (!isspace(*s) && *s != ':' && *s != '\0') 2624091Seric s++; 2634091Seric while (isspace(*s)) 2644091Seric s++; 2654091Seric return (*s == ':'); 2664091Seric } 2675919Seric /* 2685919Seric ** GETXPART -- extract the "signature" part of an address line. 2695919Seric ** 2705919Seric ** Try to extract the full name from a general address 2715919Seric ** field. We take anything which is a comment as a 2725919Seric ** first choice. Failing in that, we see if there is 2735919Seric ** a "machine readable" name (in <angle brackets>); if 2745919Seric ** so we take anything preceeding that clause. 2755919Seric ** 2765919Seric ** If we blow it here it's not all that serious. 2775919Seric ** 2785919Seric ** Parameters: 2795919Seric ** p -- line to crack. 2805919Seric ** 2815919Seric ** Returns: 2825919Seric ** signature part. 2835919Seric ** NULL if no signature part. 2845919Seric ** 2855919Seric ** Side Effects: 2865919Seric ** none. 2875919Seric */ 2885919Seric 2895919Seric char * 2905919Seric getxpart(p) 2915919Seric register char *p; 2925919Seric { 2935919Seric register char *q; 2945919Seric register char *rval = NULL; 2955919Seric 2965919Seric q = index(p, '('); 2975919Seric if (q != NULL) 2985919Seric { 2995919Seric int parenlev = 0; 3005919Seric 3015919Seric for (p = q; *p != '\0'; p++) 3025919Seric { 3035919Seric if (*p == '(') 3045919Seric parenlev++; 3055919Seric else if (*p == ')' && --parenlev <= 0) 3065919Seric break; 3075919Seric } 3085919Seric if (*p == ')') 3095919Seric { 3105919Seric *p = '\0'; 3115919Seric if (*++q != '\0') 3125919Seric rval = newstr(q); 3135919Seric *p = ')'; 3145919Seric } 3155919Seric } 3165919Seric else if ((q = index(p, '<')) != NULL) 3175919Seric { 3185919Seric char savec; 3195919Seric 3205919Seric while (*--q == ' ') 3215919Seric continue; 3225919Seric while (isspace(*p)) 3235919Seric p++; 3245919Seric savec = *++q; 3255919Seric *q = '\0'; 3265919Seric if (*p != '\0') 3275919Seric rval = newstr(p); 3285919Seric *q = savec; 3295919Seric } 3305919Seric 3315919Seric return (rval); 3325919Seric } 3337783Seric /* 3347783Seric ** EATHEADER -- run through the stored header and extract info. 3357783Seric ** 3367783Seric ** Parameters: 3377783Seric ** none. 3387783Seric ** 3397783Seric ** Returns: 3407783Seric ** none. 3417783Seric ** 3427783Seric ** Side Effects: 3437783Seric ** Sets a bunch of global variables from information 3447783Seric ** in the collected header. 3457783Seric */ 3467783Seric 3477783Seric eatheader() 3487783Seric { 3497783Seric register HDR *h; 3507783Seric register char *p; 3517783Seric char buf[MAXLINE]; 3527783Seric 3537783Seric # ifdef DEBUG 3547783Seric if (tTd(32, 2)) 3557783Seric { 3567783Seric extern char *capitalize(); 3577783Seric 3587783Seric printf("----- collected header -----\n"); 3597783Seric for (h = CurEnv->e_header; h != NULL; h = h->h_link) 3607783Seric printf("%s: %s\n", capitalize(h->h_field), h->h_value); 3617783Seric printf("----------------------------\n"); 3627783Seric } 3637783Seric # endif DEBUG 3647783Seric 3657783Seric /* message priority */ 3667783Seric if (!QueueRun) 3677783Seric { 3687783Seric /* adjust total priority by message priority */ 3697783Seric CurEnv->e_msgpriority = CurEnv->e_msgsize; 3708066Seric p = hvalue("precedence"); 3717783Seric if (p != NULL) 3727783Seric CurEnv->e_class = priencode(p); 3737783Seric else 3747783Seric CurEnv->e_class = PRI_NORMAL; 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 3837783Seric /* from person */ 3847783Seric if (ArpaMode) 3858066Seric { 3868066Seric register struct hdrinfo *hi = HdrInfo; 3877783Seric 3888066Seric for (p = NULL; p == NULL && hi->hi_field != NULL; hi++) 3898066Seric { 3908066Seric if (bitset(H_FROM, hi->hi_flags)) 3918066Seric p = hvalue(hi->hi_field); 3928066Seric } 3938066Seric if (p != NULL) 3948066Seric setfrom(p, (char *) NULL); 3958066Seric } 3968066Seric 3977783Seric /* full name of from person */ 3987783Seric p = hvalue("full-name"); 3997783Seric if (p != NULL) 4007783Seric define('x', p); 4017783Seric 4027783Seric /* date message originated */ 4037783Seric p = hvalue("posted-date"); 4047783Seric if (p == NULL) 4057783Seric p = hvalue("date"); 4067783Seric if (p != NULL) 4077783Seric { 4087783Seric define('a', p); 4097783Seric /* we don't have a good way to do canonical conversion .... 4107783Seric define('d', newstr(arpatounix(p))); 4117783Seric .... so we will ignore the problem for the time being */ 4127783Seric } 4137783Seric } 4147783Seric /* 4157783Seric ** PRIENCODE -- encode external priority names into internal values. 4167783Seric ** 4177783Seric ** Parameters: 4187783Seric ** p -- priority in ascii. 4197783Seric ** 4207783Seric ** Returns: 4217783Seric ** priority as a numeric level. 4227783Seric ** 4237783Seric ** Side Effects: 4247783Seric ** none. 4257783Seric */ 4267783Seric 4277783Seric struct prio 4287783Seric { 4297783Seric char *pri_name; /* external name of priority */ 4307783Seric int pri_val; /* internal value for same */ 4317783Seric }; 4327783Seric 4337783Seric static struct prio Prio[] = 4347783Seric { 4357783Seric "alert", PRI_ALERT, 4367783Seric "quick", PRI_QUICK, 4377783Seric "first-class", PRI_FIRSTCL, 4387783Seric "normal", PRI_NORMAL, 4397783Seric "second-class", PRI_SECONDCL, 4407783Seric "third-class", PRI_THIRDCL, 4417783Seric "junk", PRI_JUNK, 4427783Seric NULL, PRI_NORMAL, 4437783Seric }; 4447783Seric 4457783Seric priencode(p) 4467783Seric char *p; 4477783Seric { 4487783Seric register struct prio *pl; 4497783Seric extern bool sameword(); 4507783Seric 4517783Seric for (pl = Prio; pl->pri_name != NULL; pl++) 4527783Seric { 4537783Seric if (sameword(p, pl->pri_name)) 4547783Seric break; 4557783Seric } 4567783Seric return (pl->pri_val); 4577783Seric } 4587783Seric /* 4597890Seric ** CRACKADDR -- parse an address and turn it into a macro 4607783Seric ** 4617783Seric ** This doesn't actually parse the address -- it just extracts 4627783Seric ** it and replaces it with "$g". The parse is totally ad hoc 4637783Seric ** and isn't even guaranteed to leave something syntactically 4647783Seric ** identical to what it started with. However, it does leave 4657783Seric ** something semantically identical. 4667783Seric ** 4677783Seric ** The process is kind of strange. There are a number of 4687783Seric ** interesting cases: 4697783Seric ** 1. comment <address> comment ==> comment <$g> comment 4707783Seric ** 2. address ==> address 4717783Seric ** 3. address (comment) ==> $g (comment) 4727783Seric ** 4. (comment) address ==> (comment) $g 4737783Seric ** And then there are the hard cases.... 4747783Seric ** 5. add (comment) ress ==> $g (comment) 4757783Seric ** 6. comment <address (comment)> ==> comment <$g (comment)> 4767783Seric ** 7. .... etc .... 4777783Seric ** 4787783Seric ** Parameters: 4797890Seric ** addr -- the address to be cracked. 4807783Seric ** 4817783Seric ** Returns: 4827783Seric ** a pointer to the new version. 4837783Seric ** 4847783Seric ** Side Effects: 4857890Seric ** none. 4867783Seric ** 4877783Seric ** Warning: 4887783Seric ** The return value is saved in local storage and should 4897783Seric ** be copied if it is to be reused. 4907783Seric */ 4917783Seric 4927783Seric char * 4937890Seric crackaddr(addr) 4947890Seric register char *addr; 4957783Seric { 4967783Seric register char *p; 4977783Seric register int i; 4987783Seric static char buf[MAXNAME]; 4997783Seric char *rhs; 5007783Seric bool gotaddr; 5017783Seric register char *bp; 5027783Seric 5037783Seric # ifdef DEBUG 5047783Seric if (tTd(33, 1)) 5057890Seric printf("crackaddr(%s)\n", addr); 5067783Seric # endif DEBUG 5077783Seric 5087783Seric strcpy(buf, ""); 5097783Seric rhs = NULL; 5107783Seric 511*8082Seric /* strip leading spaces */ 512*8082Seric while (*addr != '\0' && isspace(*addr)) 513*8082Seric addr++; 514*8082Seric 5157783Seric /* 5167783Seric ** See if we have anything in angle brackets. If so, that is 5177783Seric ** the address part, and the rest is the comment. 5187783Seric */ 5197783Seric 5207890Seric p = index(addr, '<'); 5217783Seric if (p != NULL) 5227783Seric { 5237890Seric /* copy the beginning of the addr field to the buffer */ 5247783Seric *p = '\0'; 5257890Seric strcpy(buf, addr); 5267783Seric strcat(buf, "<"); 527*8082Seric *p++ = '<'; 5287783Seric 529*8082Seric /* skip spaces */ 530*8082Seric while (isspace(*p)) 531*8082Seric p++; 532*8082Seric 5337783Seric /* find the matching right angle bracket */ 534*8082Seric addr = p; 5357783Seric for (i = 0; *p != '\0'; p++) 5367783Seric { 5377783Seric switch (*p) 5387783Seric { 5397783Seric case '<': 5407783Seric i++; 5417783Seric break; 5427783Seric 5437783Seric case '>': 5447783Seric i--; 5457783Seric break; 5467783Seric } 5477783Seric if (i < 0) 5487783Seric break; 5497783Seric } 5507783Seric 5517783Seric /* p now points to the closing quote (or a null byte) */ 5527783Seric if (*p != '\0') 5537783Seric { 5547783Seric /* make rhs point to the extra stuff at the end */ 5557783Seric rhs = p; 5567783Seric *p++ = '\0'; 5577783Seric } 5587783Seric } 5597783Seric 5607783Seric /* 5617944Seric ** Now parse the real address part. "addr" points to the (null 5627783Seric ** terminated) version of what we are inerested in; rhs points 5637783Seric ** to the extra stuff at the end of the line, if any. 5647783Seric */ 5657783Seric 5667890Seric p = addr; 5677783Seric 5687783Seric /* now strip out comments */ 5697783Seric bp = &buf[strlen(buf)]; 5707783Seric gotaddr = FALSE; 5717783Seric for (; *p != '\0'; p++) 5727783Seric { 5737783Seric if (*p == '(') 5747783Seric { 5757783Seric /* copy to matching close paren */ 5767783Seric *bp++ = *p++; 5777783Seric for (i = 0; *p != '\0'; p++) 5787783Seric { 5797783Seric *bp++ = *p; 5807783Seric switch (*p) 5817783Seric { 5827783Seric case '(': 5837783Seric i++; 5847783Seric break; 5857783Seric 5867783Seric case ')': 5877783Seric i--; 5887783Seric break; 5897783Seric } 5907783Seric if (i < 0) 5917783Seric break; 5927783Seric } 5937783Seric continue; 5947783Seric } 5957783Seric 5967783Seric /* 5977783Seric ** If this is the first "real" character we have seen, 5987783Seric ** then we put the "$g" in the buffer now. 5997783Seric */ 6007783Seric 6017783Seric if (isspace(*p)) 6027783Seric *bp++ = *p; 6037783Seric else if (!gotaddr) 6047783Seric { 6057783Seric strcpy(bp, "$g"); 6067783Seric bp += 2; 6077783Seric gotaddr = TRUE; 6087783Seric } 6097783Seric } 6107783Seric 6117944Seric /* hack, hack.... strip trailing blanks */ 6127944Seric do 6137944Seric { 6147944Seric *bp-- = '\0'; 6157944Seric } while (isspace(*bp)); 6167944Seric bp++; 6177783Seric 6187944Seric /* put any right hand side back on */ 6197783Seric if (rhs != NULL) 6207783Seric { 6217783Seric *rhs = '>'; 6227783Seric strcpy(bp, rhs); 6237783Seric } 6247783Seric 6257783Seric # ifdef DEBUG 6267783Seric if (tTd(33, 1)) 6277944Seric printf("crackaddr=>`%s'\n", buf); 6287783Seric # endif DEBUG 6297783Seric 6307783Seric return (buf); 6317783Seric } 632