14091Seric # include <errno.h> 24091Seric # include "sendmail.h" 34091Seric 4*9059Seric SCCSID(@(#)headers.c 3.36 11/04/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; 33*9059Seric bool cond = FALSE; 344627Seric u_long mopts; 354627Seric extern u_long mfencode(); 367890Seric extern char *crackaddr(); 374091Seric 387677Seric # ifdef DEBUG 397677Seric if (tTd(31, 6)) 407677Seric printf("chompheader: %s\n", line); 417677Seric # endif DEBUG 427677Seric 434627Seric /* strip off options */ 444627Seric mopts = 0; 454627Seric p = line; 464627Seric if (*p == '?') 474627Seric { 484627Seric /* have some */ 494627Seric register char *q = index(p + 1, *p); 504627Seric 514627Seric if (q != NULL) 524627Seric { 534627Seric *q++ = '\0'; 544627Seric mopts = mfencode(p + 1); 554627Seric p = q; 564627Seric } 574627Seric else 584627Seric syserr("chompheader: syntax error, line \"%s\"", line); 59*9059Seric cond = TRUE; 604627Seric } 614627Seric 624091Seric /* find canonical name */ 634627Seric fname = p; 644627Seric p = index(p, ':'); 654091Seric fvalue = &p[1]; 664091Seric while (isspace(*--p)) 674091Seric continue; 684091Seric *++p = '\0'; 694091Seric makelower(fname); 704091Seric 714091Seric /* strip field value on front */ 724091Seric if (*fvalue == ' ') 734091Seric fvalue++; 744091Seric 754091Seric /* search header list for this header */ 766908Seric for (hp = &CurEnv->e_header, h = CurEnv->e_header; h != NULL; hp = &h->h_link, h = h->h_link) 774091Seric { 785187Seric if (strcmp(fname, h->h_field) == 0 && bitset(H_DEFAULT, h->h_flags)) 794091Seric break; 804091Seric } 814091Seric 824091Seric /* see if it is a known type */ 834091Seric for (hi = HdrInfo; hi->hi_field != NULL; hi++) 844091Seric { 854091Seric if (strcmp(hi->hi_field, fname) == 0) 864091Seric break; 874091Seric } 884091Seric 894091Seric /* if this means "end of header" quit now */ 904091Seric if (bitset(H_EOH, hi->hi_flags)) 914091Seric return (hi->hi_flags); 924091Seric 937789Seric /* count Received: lines to avoid loops (simulate hop counts) */ 948066Seric if (bitset(H_TRACE, hi->hi_flags)) 957365Seric HopCount++; 967365Seric 974091Seric /* create/fill in a new node */ 985187Seric if (h == NULL || bitset(H_FORCE, h->h_flags)) 994091Seric { 1004091Seric /* create a new node */ 1015187Seric h = (HDR *) xalloc(sizeof *h); 1024091Seric h->h_field = newstr(fname); 1034091Seric h->h_value = NULL; 1045187Seric h->h_link = *hp; 105*9059Seric h->h_mflags = mopts; 1065187Seric *hp = h; 1074091Seric } 1088066Seric h->h_flags = hi->hi_flags; 1094091Seric if (def) 1104091Seric h->h_flags |= H_DEFAULT; 111*9059Seric if (cond) 112*9059Seric h->h_flags |= H_CHECK; 1134091Seric if (h->h_value != NULL) 1144091Seric free(h->h_value); 1158066Seric h->h_value = newstr(fvalue); 1164091Seric 1175937Seric /* hack to see if this is a new format message */ 1188095Seric if (!def && bitset(H_RCPT|H_FROM, h->h_flags) && 1195937Seric (index(fvalue, ',') != NULL || index(fvalue, '(') != NULL || 1208089Seric index(fvalue, '<') != NULL || index(fvalue, ';') != NULL)) 1218089Seric { 1226908Seric CurEnv->e_oldstyle = FALSE; 1238089Seric } 1245937Seric 1258066Seric /* send to this person if we so desire */ 1268066Seric if (!def && GrabTo && bitset(H_RCPT, h->h_flags)) 1278082Seric sendto(h->h_value, (ADDRESS *) NULL, &CurEnv->e_sendqueue); 1288066Seric 1294091Seric return (h->h_flags); 1304091Seric } 1314091Seric /* 1326980Seric ** ADDHEADER -- add a header entry to the end of the queue. 1336980Seric ** 1346980Seric ** This bypasses the special checking of chompheader. 1356980Seric ** 1366980Seric ** Parameters: 1376980Seric ** field -- the name of the header field. 1386980Seric ** value -- the value of the field. It must be lower-cased. 1396980Seric ** e -- the envelope to add them to. 1406980Seric ** 1416980Seric ** Returns: 1426980Seric ** none. 1436980Seric ** 1446980Seric ** Side Effects: 1456980Seric ** adds the field on the list of headers for this envelope. 1466980Seric */ 1476980Seric 1486980Seric addheader(field, value, e) 1496980Seric char *field; 1506980Seric char *value; 1516980Seric ENVELOPE *e; 1526980Seric { 1536980Seric register HDR *h; 1546980Seric register struct hdrinfo *hi; 1556980Seric HDR **hp; 1566980Seric 1576980Seric /* find info struct */ 1586980Seric for (hi = HdrInfo; hi->hi_field != NULL; hi++) 1596980Seric { 1606980Seric if (strcmp(field, hi->hi_field) == 0) 1616980Seric break; 1626980Seric } 1636980Seric 1646980Seric /* find current place in list -- keep back pointer? */ 1656980Seric for (hp = &e->e_header; (h = *hp) != NULL; hp = &h->h_link) 1666980Seric { 1676980Seric if (strcmp(field, h->h_field) == 0) 1686980Seric break; 1696980Seric } 1706980Seric 1716980Seric /* allocate space for new header */ 1726980Seric h = (HDR *) xalloc(sizeof *h); 1736980Seric h->h_field = field; 1746980Seric h->h_value = newstr(value); 1757368Seric h->h_link = *hp; 1766980Seric h->h_flags = hi->hi_flags | H_DEFAULT; 177*9059Seric h->h_mflags = 0; 1786980Seric *hp = h; 1796980Seric } 1806980Seric /* 1814091Seric ** HVALUE -- return value of a header. 1824091Seric ** 1834091Seric ** Only "real" fields (i.e., ones that have not been supplied 1844091Seric ** as a default) are used. 1854091Seric ** 1864091Seric ** Parameters: 1874091Seric ** field -- the field name. 1884091Seric ** 1894091Seric ** Returns: 1904091Seric ** pointer to the value part. 1914091Seric ** NULL if not found. 1924091Seric ** 1934091Seric ** Side Effects: 1944091Seric ** sets the H_USED bit in the header if found. 1954091Seric */ 1964091Seric 1974091Seric char * 1984091Seric hvalue(field) 1994091Seric char *field; 2004091Seric { 2014091Seric register HDR *h; 2024091Seric 2036908Seric for (h = CurEnv->e_header; h != NULL; h = h->h_link) 2044091Seric { 2054091Seric if (!bitset(H_DEFAULT, h->h_flags) && strcmp(h->h_field, field) == 0) 2064091Seric { 2074091Seric h->h_flags |= H_USED; 2084091Seric return (h->h_value); 2094091Seric } 2104091Seric } 2114091Seric return (NULL); 2124091Seric } 2134091Seric /* 2147677Seric ** HRVALUE -- return pointer to header descriptor. 2157677Seric ** 2167677Seric ** Like hvalue except returns header descriptor block and isn't 2177677Seric ** picky about "real" headers. 2187677Seric ** 2197677Seric ** Parameters: 2207677Seric ** field -- name of field we are interested in. 2217677Seric ** 2227677Seric ** Returns: 2237677Seric ** pointer to header descriptor. 2247677Seric ** 2257677Seric ** Side Effects: 2267677Seric ** none. 2277677Seric */ 2287677Seric 2297677Seric HDR * 2307677Seric hrvalue(field) 2317677Seric char *field; 2327677Seric { 2337677Seric register HDR *h; 2347677Seric 2357677Seric for (h = CurEnv->e_header; h != NULL; h = h->h_link) 2367677Seric { 2377677Seric if (strcmp(h->h_field, field) == 0) 2387677Seric return (h); 2397677Seric } 2407677Seric return (NULL); 2417677Seric } 2427677Seric /* 2434091Seric ** ISHEADER -- predicate telling if argument is a header. 2444091Seric ** 2454319Seric ** A line is a header if it has a single word followed by 2464319Seric ** optional white space followed by a colon. 2474319Seric ** 2484091Seric ** Parameters: 2494091Seric ** s -- string to check for possible headerness. 2504091Seric ** 2514091Seric ** Returns: 2524091Seric ** TRUE if s is a header. 2534091Seric ** FALSE otherwise. 2544091Seric ** 2554091Seric ** Side Effects: 2564091Seric ** none. 2574091Seric */ 2584091Seric 2594091Seric bool 2604091Seric isheader(s) 2614091Seric register char *s; 2624091Seric { 2634091Seric if (!isalnum(*s)) 2644091Seric return (FALSE); 2657855Seric while (!isspace(*s) && *s != ':' && *s != '\0') 2664091Seric s++; 2674091Seric while (isspace(*s)) 2684091Seric s++; 2694091Seric return (*s == ':'); 2704091Seric } 2715919Seric /* 2725919Seric ** GETXPART -- extract the "signature" part of an address line. 2735919Seric ** 2745919Seric ** Try to extract the full name from a general address 2755919Seric ** field. We take anything which is a comment as a 2765919Seric ** first choice. Failing in that, we see if there is 2775919Seric ** a "machine readable" name (in <angle brackets>); if 2785919Seric ** so we take anything preceeding that clause. 2795919Seric ** 2805919Seric ** If we blow it here it's not all that serious. 2815919Seric ** 2825919Seric ** Parameters: 2835919Seric ** p -- line to crack. 2845919Seric ** 2855919Seric ** Returns: 2865919Seric ** signature part. 2875919Seric ** NULL if no signature part. 2885919Seric ** 2895919Seric ** Side Effects: 2905919Seric ** none. 2915919Seric */ 2925919Seric 2935919Seric char * 2945919Seric getxpart(p) 2955919Seric register char *p; 2965919Seric { 2975919Seric register char *q; 2985919Seric register char *rval = NULL; 2995919Seric 3005919Seric q = index(p, '('); 3015919Seric if (q != NULL) 3025919Seric { 3035919Seric int parenlev = 0; 3045919Seric 3055919Seric for (p = q; *p != '\0'; p++) 3065919Seric { 3075919Seric if (*p == '(') 3085919Seric parenlev++; 3095919Seric else if (*p == ')' && --parenlev <= 0) 3105919Seric break; 3115919Seric } 3125919Seric if (*p == ')') 3135919Seric { 3145919Seric *p = '\0'; 3155919Seric if (*++q != '\0') 3165919Seric rval = newstr(q); 3175919Seric *p = ')'; 3185919Seric } 3195919Seric } 3205919Seric else if ((q = index(p, '<')) != NULL) 3215919Seric { 3225919Seric char savec; 3235919Seric 3245919Seric while (*--q == ' ') 3255919Seric continue; 3265919Seric while (isspace(*p)) 3275919Seric p++; 3285919Seric savec = *++q; 3295919Seric *q = '\0'; 3305919Seric if (*p != '\0') 3315919Seric rval = newstr(p); 3325919Seric *q = savec; 3335919Seric } 3345919Seric 3355919Seric return (rval); 3365919Seric } 3377783Seric /* 3387783Seric ** EATHEADER -- run through the stored header and extract info. 3397783Seric ** 3407783Seric ** Parameters: 3417783Seric ** none. 3427783Seric ** 3437783Seric ** Returns: 3447783Seric ** none. 3457783Seric ** 3467783Seric ** Side Effects: 3477783Seric ** Sets a bunch of global variables from information 3487783Seric ** in the collected header. 3497783Seric */ 3507783Seric 3517783Seric eatheader() 3527783Seric { 3537783Seric register HDR *h; 3547783Seric register char *p; 3557783Seric char buf[MAXLINE]; 3567783Seric 3577783Seric # ifdef DEBUG 3587783Seric if (tTd(32, 2)) 3597783Seric { 3607783Seric extern char *capitalize(); 3617783Seric 3627783Seric printf("----- collected header -----\n"); 3637783Seric for (h = CurEnv->e_header; h != NULL; h = h->h_link) 3647783Seric printf("%s: %s\n", capitalize(h->h_field), h->h_value); 3657783Seric printf("----------------------------\n"); 3667783Seric } 3677783Seric # endif DEBUG 3687783Seric 3697783Seric /* message priority */ 3707783Seric if (!QueueRun) 3717783Seric { 3727783Seric /* adjust total priority by message priority */ 3737783Seric CurEnv->e_msgpriority = CurEnv->e_msgsize; 3748066Seric p = hvalue("precedence"); 3757783Seric if (p != NULL) 3767783Seric CurEnv->e_class = priencode(p); 3777783Seric CurEnv->e_msgpriority -= CurEnv->e_class * WKPRIFACT; 3787783Seric } 3797783Seric 3808066Seric /* return receipt to */ 3818066Seric p = hvalue("return-receipt-to"); 3827783Seric if (p != NULL) 3838066Seric CurEnv->e_receiptto = p; 3847783Seric 3858253Seric /* errors to */ 3868253Seric p = hvalue("errors-to"); 3878253Seric if (p != NULL) 3888253Seric sendto(p, (ADDRESS *) NULL, &CurEnv->e_errorqueue); 3898253Seric 3907783Seric /* from person */ 3917783Seric if (ArpaMode) 3928066Seric { 3938066Seric register struct hdrinfo *hi = HdrInfo; 3947783Seric 3958066Seric for (p = NULL; p == NULL && hi->hi_field != NULL; hi++) 3968066Seric { 3978066Seric if (bitset(H_FROM, hi->hi_flags)) 3988066Seric p = hvalue(hi->hi_field); 3998066Seric } 4008066Seric if (p != NULL) 4018066Seric setfrom(p, (char *) NULL); 4028066Seric } 4038066Seric 4047783Seric /* full name of from person */ 4057783Seric p = hvalue("full-name"); 4067783Seric if (p != NULL) 4077783Seric define('x', p); 4087783Seric 4097783Seric /* date message originated */ 4107783Seric p = hvalue("posted-date"); 4117783Seric if (p == NULL) 4127783Seric p = hvalue("date"); 4137783Seric if (p != NULL) 4147783Seric { 4157783Seric define('a', p); 4167783Seric /* we don't have a good way to do canonical conversion .... 4177783Seric define('d', newstr(arpatounix(p))); 4187783Seric .... so we will ignore the problem for the time being */ 4197783Seric } 4207783Seric } 4217783Seric /* 4227783Seric ** PRIENCODE -- encode external priority names into internal values. 4237783Seric ** 4247783Seric ** Parameters: 4257783Seric ** p -- priority in ascii. 4267783Seric ** 4277783Seric ** Returns: 4287783Seric ** priority as a numeric level. 4297783Seric ** 4307783Seric ** Side Effects: 4317783Seric ** none. 4327783Seric */ 4337783Seric 4347783Seric priencode(p) 4357783Seric char *p; 4367783Seric { 4378253Seric register int i; 4387783Seric extern bool sameword(); 4397783Seric 4408253Seric for (i = 0; i < NumPriorities; i++) 4417783Seric { 4428253Seric if (sameword(p, Priorities[i].pri_name)) 4438253Seric return (Priorities[i].pri_val); 4447783Seric } 4458253Seric 4468253Seric /* unknown priority */ 4478253Seric return (0); 4487783Seric } 4497783Seric /* 4507890Seric ** CRACKADDR -- parse an address and turn it into a macro 4517783Seric ** 4527783Seric ** This doesn't actually parse the address -- it just extracts 4537783Seric ** it and replaces it with "$g". The parse is totally ad hoc 4547783Seric ** and isn't even guaranteed to leave something syntactically 4557783Seric ** identical to what it started with. However, it does leave 4567783Seric ** something semantically identical. 4577783Seric ** 4587783Seric ** The process is kind of strange. There are a number of 4597783Seric ** interesting cases: 4607783Seric ** 1. comment <address> comment ==> comment <$g> comment 4617783Seric ** 2. address ==> address 4627783Seric ** 3. address (comment) ==> $g (comment) 4637783Seric ** 4. (comment) address ==> (comment) $g 4647783Seric ** And then there are the hard cases.... 4657783Seric ** 5. add (comment) ress ==> $g (comment) 4667783Seric ** 6. comment <address (comment)> ==> comment <$g (comment)> 4677783Seric ** 7. .... etc .... 4687783Seric ** 4697783Seric ** Parameters: 4707890Seric ** addr -- the address to be cracked. 4717783Seric ** 4727783Seric ** Returns: 4737783Seric ** a pointer to the new version. 4747783Seric ** 4757783Seric ** Side Effects: 4767890Seric ** none. 4777783Seric ** 4787783Seric ** Warning: 4797783Seric ** The return value is saved in local storage and should 4807783Seric ** be copied if it is to be reused. 4817783Seric */ 4827783Seric 4837783Seric char * 4847890Seric crackaddr(addr) 4857890Seric register char *addr; 4867783Seric { 4877783Seric register char *p; 4887783Seric register int i; 4897783Seric static char buf[MAXNAME]; 4907783Seric char *rhs; 4917783Seric bool gotaddr; 4927783Seric register char *bp; 4937783Seric 4947783Seric # ifdef DEBUG 4957783Seric if (tTd(33, 1)) 4967890Seric printf("crackaddr(%s)\n", addr); 4977783Seric # endif DEBUG 4987783Seric 4997783Seric strcpy(buf, ""); 5007783Seric rhs = NULL; 5017783Seric 5028082Seric /* strip leading spaces */ 5038082Seric while (*addr != '\0' && isspace(*addr)) 5048082Seric addr++; 5058082Seric 5067783Seric /* 5077783Seric ** See if we have anything in angle brackets. If so, that is 5087783Seric ** the address part, and the rest is the comment. 5097783Seric */ 5107783Seric 5117890Seric p = index(addr, '<'); 5127783Seric if (p != NULL) 5137783Seric { 5147890Seric /* copy the beginning of the addr field to the buffer */ 5157783Seric *p = '\0'; 5167890Seric strcpy(buf, addr); 5177783Seric strcat(buf, "<"); 5188082Seric *p++ = '<'; 5197783Seric 5208082Seric /* skip spaces */ 5218082Seric while (isspace(*p)) 5228082Seric p++; 5238082Seric 5247783Seric /* find the matching right angle bracket */ 5258082Seric addr = p; 5267783Seric for (i = 0; *p != '\0'; p++) 5277783Seric { 5287783Seric switch (*p) 5297783Seric { 5307783Seric case '<': 5317783Seric i++; 5327783Seric break; 5337783Seric 5347783Seric case '>': 5357783Seric i--; 5367783Seric break; 5377783Seric } 5387783Seric if (i < 0) 5397783Seric break; 5407783Seric } 5417783Seric 5427783Seric /* p now points to the closing quote (or a null byte) */ 5437783Seric if (*p != '\0') 5447783Seric { 5457783Seric /* make rhs point to the extra stuff at the end */ 5467783Seric rhs = p; 5477783Seric *p++ = '\0'; 5487783Seric } 5497783Seric } 5507783Seric 5517783Seric /* 5527944Seric ** Now parse the real address part. "addr" points to the (null 5537783Seric ** terminated) version of what we are inerested in; rhs points 5547783Seric ** to the extra stuff at the end of the line, if any. 5557783Seric */ 5567783Seric 5577890Seric p = addr; 5587783Seric 5597783Seric /* now strip out comments */ 5607783Seric bp = &buf[strlen(buf)]; 5617783Seric gotaddr = FALSE; 5627783Seric for (; *p != '\0'; p++) 5637783Seric { 5647783Seric if (*p == '(') 5657783Seric { 5667783Seric /* copy to matching close paren */ 5677783Seric *bp++ = *p++; 5687783Seric for (i = 0; *p != '\0'; p++) 5697783Seric { 5707783Seric *bp++ = *p; 5717783Seric switch (*p) 5727783Seric { 5737783Seric case '(': 5747783Seric i++; 5757783Seric break; 5767783Seric 5777783Seric case ')': 5787783Seric i--; 5797783Seric break; 5807783Seric } 5817783Seric if (i < 0) 5827783Seric break; 5837783Seric } 5847783Seric continue; 5857783Seric } 5867783Seric 5877783Seric /* 5887783Seric ** If this is the first "real" character we have seen, 5897783Seric ** then we put the "$g" in the buffer now. 5907783Seric */ 5917783Seric 5927783Seric if (isspace(*p)) 5937783Seric *bp++ = *p; 5947783Seric else if (!gotaddr) 5957783Seric { 5967783Seric strcpy(bp, "$g"); 5977783Seric bp += 2; 5987783Seric gotaddr = TRUE; 5997783Seric } 6007783Seric } 6017783Seric 6027944Seric /* hack, hack.... strip trailing blanks */ 6037944Seric do 6047944Seric { 6057944Seric *bp-- = '\0'; 6067944Seric } while (isspace(*bp)); 6077944Seric bp++; 6087783Seric 6097944Seric /* put any right hand side back on */ 6107783Seric if (rhs != NULL) 6117783Seric { 6127783Seric *rhs = '>'; 6137783Seric strcpy(bp, rhs); 6147783Seric } 6157783Seric 6167783Seric # ifdef DEBUG 6177783Seric if (tTd(33, 1)) 6187944Seric printf("crackaddr=>`%s'\n", buf); 6197783Seric # endif DEBUG 6207783Seric 6217783Seric return (buf); 6227783Seric } 623