14091Seric # include <errno.h> 24091Seric # include "sendmail.h" 34091Seric 4*9351Seric SCCSID(@(#)headers.c 3.39 11/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; 339059Seric 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); 599059Seric 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; 1059059Seric h->h_mflags = mopts; 1065187Seric *hp = h; 1074091Seric } 1088066Seric h->h_flags = hi->hi_flags; 1094091Seric if (def) 1104091Seric h->h_flags |= H_DEFAULT; 1119059Seric if (cond) 1129059Seric h->h_flags |= H_CHECK; 1134091Seric if (h->h_value != NULL) 114*9351Seric free((char *) 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 { 1229342Seric CurEnv->e_flags &= ~EF_OLDSTYLE; 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; 1779059Seric 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 /* 2144091Seric ** ISHEADER -- predicate telling if argument is a header. 2154091Seric ** 2164319Seric ** A line is a header if it has a single word followed by 2174319Seric ** optional white space followed by a colon. 2184319Seric ** 2194091Seric ** Parameters: 2204091Seric ** s -- string to check for possible headerness. 2214091Seric ** 2224091Seric ** Returns: 2234091Seric ** TRUE if s is a header. 2244091Seric ** FALSE otherwise. 2254091Seric ** 2264091Seric ** Side Effects: 2274091Seric ** none. 2284091Seric */ 2294091Seric 2304091Seric bool 2314091Seric isheader(s) 2324091Seric register char *s; 2334091Seric { 2344091Seric if (!isalnum(*s)) 2354091Seric return (FALSE); 2367855Seric while (!isspace(*s) && *s != ':' && *s != '\0') 2374091Seric s++; 2384091Seric while (isspace(*s)) 2394091Seric s++; 2404091Seric return (*s == ':'); 2414091Seric } 2425919Seric /* 2437783Seric ** EATHEADER -- run through the stored header and extract info. 2447783Seric ** 2457783Seric ** Parameters: 2467783Seric ** none. 2477783Seric ** 2487783Seric ** Returns: 2497783Seric ** none. 2507783Seric ** 2517783Seric ** Side Effects: 2527783Seric ** Sets a bunch of global variables from information 2537783Seric ** in the collected header. 2547783Seric */ 2557783Seric 2567783Seric eatheader() 2577783Seric { 2587783Seric register HDR *h; 2597783Seric register char *p; 2607783Seric 2617783Seric # ifdef DEBUG 2627783Seric if (tTd(32, 2)) 2637783Seric { 2647783Seric extern char *capitalize(); 2657783Seric 2667783Seric printf("----- collected header -----\n"); 2677783Seric for (h = CurEnv->e_header; h != NULL; h = h->h_link) 2687783Seric printf("%s: %s\n", capitalize(h->h_field), h->h_value); 2697783Seric printf("----------------------------\n"); 2707783Seric } 2717783Seric # endif DEBUG 2727783Seric 2737783Seric /* message priority */ 2747783Seric if (!QueueRun) 2757783Seric { 2767783Seric /* adjust total priority by message priority */ 2777783Seric CurEnv->e_msgpriority = CurEnv->e_msgsize; 2788066Seric p = hvalue("precedence"); 2797783Seric if (p != NULL) 2807783Seric CurEnv->e_class = priencode(p); 2817783Seric CurEnv->e_msgpriority -= CurEnv->e_class * WKPRIFACT; 2827783Seric } 2837783Seric 2848066Seric /* return receipt to */ 2858066Seric p = hvalue("return-receipt-to"); 2867783Seric if (p != NULL) 2878066Seric CurEnv->e_receiptto = p; 2887783Seric 2898253Seric /* errors to */ 2908253Seric p = hvalue("errors-to"); 2918253Seric if (p != NULL) 2928253Seric sendto(p, (ADDRESS *) NULL, &CurEnv->e_errorqueue); 2938253Seric 2947783Seric /* from person */ 2959285Seric if (OpMode == MD_ARPAFTP) 2968066Seric { 2978066Seric register struct hdrinfo *hi = HdrInfo; 2987783Seric 2998066Seric for (p = NULL; p == NULL && hi->hi_field != NULL; hi++) 3008066Seric { 3018066Seric if (bitset(H_FROM, hi->hi_flags)) 3028066Seric p = hvalue(hi->hi_field); 3038066Seric } 3048066Seric if (p != NULL) 3058066Seric setfrom(p, (char *) NULL); 3068066Seric } 3078066Seric 3087783Seric /* full name of from person */ 3097783Seric p = hvalue("full-name"); 3107783Seric if (p != NULL) 3117783Seric define('x', p); 3127783Seric 3137783Seric /* date message originated */ 3147783Seric p = hvalue("posted-date"); 3157783Seric if (p == NULL) 3167783Seric p = hvalue("date"); 3177783Seric if (p != NULL) 3187783Seric { 3197783Seric define('a', p); 3207783Seric /* we don't have a good way to do canonical conversion .... 3217783Seric define('d', newstr(arpatounix(p))); 3227783Seric .... so we will ignore the problem for the time being */ 3237783Seric } 3247783Seric } 3257783Seric /* 3267783Seric ** PRIENCODE -- encode external priority names into internal values. 3277783Seric ** 3287783Seric ** Parameters: 3297783Seric ** p -- priority in ascii. 3307783Seric ** 3317783Seric ** Returns: 3327783Seric ** priority as a numeric level. 3337783Seric ** 3347783Seric ** Side Effects: 3357783Seric ** none. 3367783Seric */ 3377783Seric 3387783Seric priencode(p) 3397783Seric char *p; 3407783Seric { 3418253Seric register int i; 3427783Seric extern bool sameword(); 3437783Seric 3448253Seric for (i = 0; i < NumPriorities; i++) 3457783Seric { 3468253Seric if (sameword(p, Priorities[i].pri_name)) 3478253Seric return (Priorities[i].pri_val); 3487783Seric } 3498253Seric 3508253Seric /* unknown priority */ 3518253Seric return (0); 3527783Seric } 3537783Seric /* 3547890Seric ** CRACKADDR -- parse an address and turn it into a macro 3557783Seric ** 3567783Seric ** This doesn't actually parse the address -- it just extracts 3577783Seric ** it and replaces it with "$g". The parse is totally ad hoc 3587783Seric ** and isn't even guaranteed to leave something syntactically 3597783Seric ** identical to what it started with. However, it does leave 3607783Seric ** something semantically identical. 3617783Seric ** 3627783Seric ** The process is kind of strange. There are a number of 3637783Seric ** interesting cases: 3647783Seric ** 1. comment <address> comment ==> comment <$g> comment 3657783Seric ** 2. address ==> address 3667783Seric ** 3. address (comment) ==> $g (comment) 3677783Seric ** 4. (comment) address ==> (comment) $g 3687783Seric ** And then there are the hard cases.... 3697783Seric ** 5. add (comment) ress ==> $g (comment) 3707783Seric ** 6. comment <address (comment)> ==> comment <$g (comment)> 3717783Seric ** 7. .... etc .... 3727783Seric ** 3737783Seric ** Parameters: 3747890Seric ** addr -- the address to be cracked. 3757783Seric ** 3767783Seric ** Returns: 3777783Seric ** a pointer to the new version. 3787783Seric ** 3797783Seric ** Side Effects: 3807890Seric ** none. 3817783Seric ** 3827783Seric ** Warning: 3837783Seric ** The return value is saved in local storage and should 3847783Seric ** be copied if it is to be reused. 3857783Seric */ 3867783Seric 3877783Seric char * 3887890Seric crackaddr(addr) 3897890Seric register char *addr; 3907783Seric { 3917783Seric register char *p; 3927783Seric register int i; 3937783Seric static char buf[MAXNAME]; 3947783Seric char *rhs; 3957783Seric bool gotaddr; 3967783Seric register char *bp; 3977783Seric 3987783Seric # ifdef DEBUG 3997783Seric if (tTd(33, 1)) 4007890Seric printf("crackaddr(%s)\n", addr); 4017783Seric # endif DEBUG 4027783Seric 4037783Seric strcpy(buf, ""); 4047783Seric rhs = NULL; 4057783Seric 4068082Seric /* strip leading spaces */ 4078082Seric while (*addr != '\0' && isspace(*addr)) 4088082Seric addr++; 4098082Seric 4107783Seric /* 4117783Seric ** See if we have anything in angle brackets. If so, that is 4127783Seric ** the address part, and the rest is the comment. 4137783Seric */ 4147783Seric 4157890Seric p = index(addr, '<'); 4167783Seric if (p != NULL) 4177783Seric { 4187890Seric /* copy the beginning of the addr field to the buffer */ 4197783Seric *p = '\0'; 4207890Seric strcpy(buf, addr); 4217783Seric strcat(buf, "<"); 4228082Seric *p++ = '<'; 4237783Seric 4248082Seric /* skip spaces */ 4258082Seric while (isspace(*p)) 4268082Seric p++; 4278082Seric 4287783Seric /* find the matching right angle bracket */ 4298082Seric addr = p; 4307783Seric for (i = 0; *p != '\0'; p++) 4317783Seric { 4327783Seric switch (*p) 4337783Seric { 4347783Seric case '<': 4357783Seric i++; 4367783Seric break; 4377783Seric 4387783Seric case '>': 4397783Seric i--; 4407783Seric break; 4417783Seric } 4427783Seric if (i < 0) 4437783Seric break; 4447783Seric } 4457783Seric 4467783Seric /* p now points to the closing quote (or a null byte) */ 4477783Seric if (*p != '\0') 4487783Seric { 4497783Seric /* make rhs point to the extra stuff at the end */ 4507783Seric rhs = p; 4517783Seric *p++ = '\0'; 4527783Seric } 4537783Seric } 4547783Seric 4557783Seric /* 4567944Seric ** Now parse the real address part. "addr" points to the (null 4577783Seric ** terminated) version of what we are inerested in; rhs points 4587783Seric ** to the extra stuff at the end of the line, if any. 4597783Seric */ 4607783Seric 4617890Seric p = addr; 4627783Seric 4637783Seric /* now strip out comments */ 4647783Seric bp = &buf[strlen(buf)]; 4657783Seric gotaddr = FALSE; 4667783Seric for (; *p != '\0'; p++) 4677783Seric { 4687783Seric if (*p == '(') 4697783Seric { 4707783Seric /* copy to matching close paren */ 4717783Seric *bp++ = *p++; 4727783Seric for (i = 0; *p != '\0'; p++) 4737783Seric { 4747783Seric *bp++ = *p; 4757783Seric switch (*p) 4767783Seric { 4777783Seric case '(': 4787783Seric i++; 4797783Seric break; 4807783Seric 4817783Seric case ')': 4827783Seric i--; 4837783Seric break; 4847783Seric } 4857783Seric if (i < 0) 4867783Seric break; 4877783Seric } 4887783Seric continue; 4897783Seric } 4907783Seric 4917783Seric /* 4927783Seric ** If this is the first "real" character we have seen, 4937783Seric ** then we put the "$g" in the buffer now. 4947783Seric */ 4957783Seric 4967783Seric if (isspace(*p)) 4977783Seric *bp++ = *p; 4987783Seric else if (!gotaddr) 4997783Seric { 5007783Seric strcpy(bp, "$g"); 5017783Seric bp += 2; 5027783Seric gotaddr = TRUE; 5037783Seric } 5047783Seric } 5057783Seric 5067944Seric /* hack, hack.... strip trailing blanks */ 5077944Seric do 5087944Seric { 5097944Seric *bp-- = '\0'; 5107944Seric } while (isspace(*bp)); 5117944Seric bp++; 5127783Seric 5137944Seric /* put any right hand side back on */ 5147783Seric if (rhs != NULL) 5157783Seric { 5167783Seric *rhs = '>'; 5177783Seric strcpy(bp, rhs); 5187783Seric } 5197783Seric 5207783Seric # ifdef DEBUG 5217783Seric if (tTd(33, 1)) 5227944Seric printf("crackaddr=>`%s'\n", buf); 5237783Seric # endif DEBUG 5247783Seric 5257783Seric return (buf); 5267783Seric } 527