122706Sdist /* 234921Sbostic * Copyright (c) 1983 Eric P. Allman 333729Sbostic * Copyright (c) 1988 Regents of the University of California. 433729Sbostic * All rights reserved. 533729Sbostic * 633729Sbostic * Redistribution and use in source and binary forms are permitted 734921Sbostic * provided that the above copyright notice and this paragraph are 834921Sbostic * duplicated in all such forms and that any documentation, 934921Sbostic * advertising materials, and other materials related to such 1034921Sbostic * distribution and use acknowledge that the software was developed 1134921Sbostic * by the University of California, Berkeley. The name of the 1234921Sbostic * University may not be used to endorse or promote products derived 1334921Sbostic * from this software without specific prior written permission. 1434921Sbostic * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 1534921Sbostic * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 1634921Sbostic * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 1733729Sbostic */ 1822706Sdist 1922706Sdist #ifndef lint 20*40960Sbostic static char sccsid[] = "@(#)headers.c 5.14 (Berkeley) 04/18/90"; 2133729Sbostic #endif /* not lint */ 2222706Sdist 23*40960Sbostic # include <sys/param.h> 244091Seric # include <errno.h> 254091Seric # include "sendmail.h" 264091Seric 274091Seric /* 284091Seric ** CHOMPHEADER -- process and save a header line. 294091Seric ** 304091Seric ** Called by collect and by readcf to deal with header lines. 314091Seric ** 324091Seric ** Parameters: 334091Seric ** line -- header as a text line. 344091Seric ** def -- if set, this is a default value. 354091Seric ** 364091Seric ** Returns: 374091Seric ** flags for this header. 384091Seric ** 394091Seric ** Side Effects: 404091Seric ** The header is saved on the header list. 414319Seric ** Contents of 'line' are destroyed. 424091Seric */ 434091Seric 444091Seric chompheader(line, def) 454091Seric char *line; 464091Seric bool def; 474091Seric { 484091Seric register char *p; 494091Seric register HDR *h; 504091Seric HDR **hp; 514091Seric char *fname; 524091Seric char *fvalue; 534091Seric struct hdrinfo *hi; 549059Seric bool cond = FALSE; 5510689Seric BITMAP mopts; 567890Seric extern char *crackaddr(); 574091Seric 587677Seric if (tTd(31, 6)) 597677Seric printf("chompheader: %s\n", line); 607677Seric 614627Seric /* strip off options */ 6210689Seric clrbitmap(mopts); 634627Seric p = line; 644627Seric if (*p == '?') 654627Seric { 664627Seric /* have some */ 674627Seric register char *q = index(p + 1, *p); 684627Seric 694627Seric if (q != NULL) 704627Seric { 714627Seric *q++ = '\0'; 7210689Seric while (*++p != '\0') 7310689Seric setbitn(*p, mopts); 744627Seric p = q; 754627Seric } 764627Seric else 7736233Skarels usrerr("chompheader: syntax error, line \"%s\"", line); 789059Seric cond = TRUE; 794627Seric } 804627Seric 814091Seric /* find canonical name */ 824627Seric fname = p; 834627Seric p = index(p, ':'); 8410118Seric if (p == NULL) 8510118Seric { 8610118Seric syserr("chompheader: syntax error, line \"%s\"", line); 8710118Seric return (0); 8810118Seric } 894091Seric fvalue = &p[1]; 904091Seric while (isspace(*--p)) 914091Seric continue; 924091Seric *++p = '\0'; 934091Seric makelower(fname); 944091Seric 954091Seric /* strip field value on front */ 964091Seric if (*fvalue == ' ') 974091Seric fvalue++; 984091Seric 994091Seric /* see if it is a known type */ 1004091Seric for (hi = HdrInfo; hi->hi_field != NULL; hi++) 1014091Seric { 1024091Seric if (strcmp(hi->hi_field, fname) == 0) 1034091Seric break; 1044091Seric } 1054091Seric 10611414Seric /* see if this is a resent message */ 10711930Seric if (!def && bitset(H_RESENT, hi->hi_flags)) 10811414Seric CurEnv->e_flags |= EF_RESENT; 10911414Seric 1104091Seric /* if this means "end of header" quit now */ 1114091Seric if (bitset(H_EOH, hi->hi_flags)) 1124091Seric return (hi->hi_flags); 1134091Seric 11411414Seric /* drop explicit From: if same as what we would generate -- for MH */ 11514784Seric p = "resent-from"; 11614784Seric if (!bitset(EF_RESENT, CurEnv->e_flags)) 11714784Seric p += 7; 11814784Seric if (!def && !QueueRun && strcmp(fname, p) == 0) 11911414Seric { 12024941Seric if (CurEnv->e_from.q_paddr != NULL && 12124941Seric strcmp(fvalue, CurEnv->e_from.q_paddr) == 0) 12211414Seric return (hi->hi_flags); 12311414Seric } 12411414Seric 12514784Seric /* delete default value for this header */ 12614784Seric for (hp = &CurEnv->e_header; (h = *hp) != NULL; hp = &h->h_link) 12714784Seric { 12814784Seric if (strcmp(fname, h->h_field) == 0 && 12914784Seric bitset(H_DEFAULT, h->h_flags) && 13014784Seric !bitset(H_FORCE, h->h_flags)) 13114784Seric h->h_value = NULL; 13214784Seric } 13314784Seric 13413012Seric /* create a new node */ 13513012Seric h = (HDR *) xalloc(sizeof *h); 13613012Seric h->h_field = newstr(fname); 13713012Seric h->h_value = NULL; 13813012Seric h->h_link = NULL; 13923117Seric bcopy((char *) mopts, (char *) h->h_mflags, sizeof mopts); 14013012Seric *hp = h; 1418066Seric h->h_flags = hi->hi_flags; 1424091Seric if (def) 1434091Seric h->h_flags |= H_DEFAULT; 1449059Seric if (cond) 1459059Seric h->h_flags |= H_CHECK; 1464091Seric if (h->h_value != NULL) 1479351Seric free((char *) h->h_value); 1488066Seric h->h_value = newstr(fvalue); 1494091Seric 1505937Seric /* hack to see if this is a new format message */ 1518095Seric if (!def && bitset(H_RCPT|H_FROM, h->h_flags) && 1525937Seric (index(fvalue, ',') != NULL || index(fvalue, '(') != NULL || 1538089Seric index(fvalue, '<') != NULL || index(fvalue, ';') != NULL)) 1548089Seric { 1559342Seric CurEnv->e_flags &= ~EF_OLDSTYLE; 1568089Seric } 1575937Seric 1584091Seric return (h->h_flags); 1594091Seric } 1604091Seric /* 1616980Seric ** ADDHEADER -- add a header entry to the end of the queue. 1626980Seric ** 1636980Seric ** This bypasses the special checking of chompheader. 1646980Seric ** 1656980Seric ** Parameters: 1666980Seric ** field -- the name of the header field. 1676980Seric ** value -- the value of the field. It must be lower-cased. 1686980Seric ** e -- the envelope to add them to. 1696980Seric ** 1706980Seric ** Returns: 1716980Seric ** none. 1726980Seric ** 1736980Seric ** Side Effects: 1746980Seric ** adds the field on the list of headers for this envelope. 1756980Seric */ 1766980Seric 1776980Seric addheader(field, value, e) 1786980Seric char *field; 1796980Seric char *value; 1806980Seric ENVELOPE *e; 1816980Seric { 1826980Seric register HDR *h; 1836980Seric register struct hdrinfo *hi; 1846980Seric HDR **hp; 1856980Seric 1866980Seric /* find info struct */ 1876980Seric for (hi = HdrInfo; hi->hi_field != NULL; hi++) 1886980Seric { 1896980Seric if (strcmp(field, hi->hi_field) == 0) 1906980Seric break; 1916980Seric } 1926980Seric 1936980Seric /* find current place in list -- keep back pointer? */ 1946980Seric for (hp = &e->e_header; (h = *hp) != NULL; hp = &h->h_link) 1956980Seric { 1966980Seric if (strcmp(field, h->h_field) == 0) 1976980Seric break; 1986980Seric } 1996980Seric 2006980Seric /* allocate space for new header */ 2016980Seric h = (HDR *) xalloc(sizeof *h); 2026980Seric h->h_field = field; 2036980Seric h->h_value = newstr(value); 2047368Seric h->h_link = *hp; 2056980Seric h->h_flags = hi->hi_flags | H_DEFAULT; 20610689Seric clrbitmap(h->h_mflags); 2076980Seric *hp = h; 2086980Seric } 2096980Seric /* 2104091Seric ** HVALUE -- return value of a header. 2114091Seric ** 2124091Seric ** Only "real" fields (i.e., ones that have not been supplied 2134091Seric ** as a default) are used. 2144091Seric ** 2154091Seric ** Parameters: 2164091Seric ** field -- the field name. 2174091Seric ** 2184091Seric ** Returns: 2194091Seric ** pointer to the value part. 2204091Seric ** NULL if not found. 2214091Seric ** 2224091Seric ** Side Effects: 2239382Seric ** none. 2244091Seric */ 2254091Seric 2264091Seric char * 2274091Seric hvalue(field) 2284091Seric char *field; 2294091Seric { 2304091Seric register HDR *h; 2314091Seric 2326908Seric for (h = CurEnv->e_header; h != NULL; h = h->h_link) 2334091Seric { 2344091Seric if (!bitset(H_DEFAULT, h->h_flags) && strcmp(h->h_field, field) == 0) 2354091Seric return (h->h_value); 2364091Seric } 2374091Seric return (NULL); 2384091Seric } 2394091Seric /* 2404091Seric ** ISHEADER -- predicate telling if argument is a header. 2414091Seric ** 2424319Seric ** A line is a header if it has a single word followed by 2434319Seric ** optional white space followed by a colon. 2444319Seric ** 2454091Seric ** Parameters: 2464091Seric ** s -- string to check for possible headerness. 2474091Seric ** 2484091Seric ** Returns: 2494091Seric ** TRUE if s is a header. 2504091Seric ** FALSE otherwise. 2514091Seric ** 2524091Seric ** Side Effects: 2534091Seric ** none. 2544091Seric */ 2554091Seric 2564091Seric bool 2574091Seric isheader(s) 2584091Seric register char *s; 2594091Seric { 2609382Seric while (*s > ' ' && *s != ':' && *s != '\0') 2614091Seric s++; 2629382Seric 2639382Seric /* following technically violates RFC822 */ 2644091Seric while (isspace(*s)) 2654091Seric s++; 2669382Seric 2674091Seric return (*s == ':'); 2684091Seric } 2695919Seric /* 2707783Seric ** EATHEADER -- run through the stored header and extract info. 2717783Seric ** 2727783Seric ** Parameters: 2739382Seric ** e -- the envelope to process. 2747783Seric ** 2757783Seric ** Returns: 2767783Seric ** none. 2777783Seric ** 2787783Seric ** Side Effects: 2797783Seric ** Sets a bunch of global variables from information 2809382Seric ** in the collected header. 2819382Seric ** Aborts the message if the hop count is exceeded. 2827783Seric */ 2837783Seric 2849382Seric eatheader(e) 2859382Seric register ENVELOPE *e; 2867783Seric { 2877783Seric register HDR *h; 2887783Seric register char *p; 2899382Seric int hopcnt = 0; 2907783Seric 2919382Seric if (tTd(32, 1)) 2929382Seric printf("----- collected header -----\n"); 2939382Seric for (h = e->e_header; h != NULL; h = h->h_link) 2947783Seric { 2957783Seric extern char *capitalize(); 2967783Seric 2979382Seric if (tTd(32, 1)) 2987783Seric printf("%s: %s\n", capitalize(h->h_field), h->h_value); 29911414Seric /* count the number of times it has been processed */ 3009382Seric if (bitset(H_TRACE, h->h_flags)) 3019382Seric hopcnt++; 30211414Seric 30311414Seric /* send to this person if we so desire */ 30411414Seric if (GrabTo && bitset(H_RCPT, h->h_flags) && 30511414Seric !bitset(H_DEFAULT, h->h_flags) && 30611414Seric (!bitset(EF_RESENT, CurEnv->e_flags) || bitset(H_RESENT, h->h_flags))) 30711414Seric { 30811414Seric sendtolist(h->h_value, (ADDRESS *) NULL, &CurEnv->e_sendqueue); 30911414Seric } 31011414Seric 31111414Seric /* log the message-id */ 31211290Seric #ifdef LOG 31313103Seric if (!QueueRun && LogLevel > 8 && h->h_value != NULL && 31411299Seric strcmp(h->h_field, "message-id") == 0) 31511290Seric { 31611290Seric char buf[MAXNAME]; 31711290Seric 31811290Seric p = h->h_value; 31911290Seric if (bitset(H_DEFAULT, h->h_flags)) 32011290Seric { 32111290Seric expand(p, buf, &buf[sizeof buf], e); 32211290Seric p = buf; 32311290Seric } 32411290Seric syslog(LOG_INFO, "%s: message-id=%s", e->e_id, p); 32511290Seric } 32611290Seric #endif LOG 3279382Seric } 3289382Seric if (tTd(32, 1)) 3297783Seric printf("----------------------------\n"); 3307783Seric 3319382Seric /* store hop count */ 3329382Seric if (hopcnt > e->e_hopcount) 3339382Seric e->e_hopcount = hopcnt; 3349382Seric 3357783Seric /* message priority */ 3369382Seric p = hvalue("precedence"); 3379382Seric if (p != NULL) 3389382Seric e->e_class = priencode(p); 3397783Seric if (!QueueRun) 34025013Seric e->e_msgpriority = e->e_msgsize 34124981Seric - e->e_class * WkClassFact 34224981Seric + e->e_nrcpts * WkRecipFact; 3437783Seric 3448066Seric /* return receipt to */ 3458066Seric p = hvalue("return-receipt-to"); 3467783Seric if (p != NULL) 3479382Seric e->e_receiptto = p; 3487783Seric 3498253Seric /* errors to */ 3508253Seric p = hvalue("errors-to"); 3518253Seric if (p != NULL) 3529620Seric sendtolist(p, (ADDRESS *) NULL, &e->e_errorqueue); 3538253Seric 3547783Seric /* from person */ 3559285Seric if (OpMode == MD_ARPAFTP) 3568066Seric { 3578066Seric register struct hdrinfo *hi = HdrInfo; 3587783Seric 3598066Seric for (p = NULL; p == NULL && hi->hi_field != NULL; hi++) 3608066Seric { 3618066Seric if (bitset(H_FROM, hi->hi_flags)) 3628066Seric p = hvalue(hi->hi_field); 3638066Seric } 3648066Seric if (p != NULL) 3659382Seric setsender(p); 3668066Seric } 3678066Seric 3687783Seric /* full name of from person */ 3697783Seric p = hvalue("full-name"); 3707783Seric if (p != NULL) 3719382Seric define('x', p, e); 3727783Seric 3737783Seric /* date message originated */ 3747783Seric p = hvalue("posted-date"); 3757783Seric if (p == NULL) 3767783Seric p = hvalue("date"); 3777783Seric if (p != NULL) 3787783Seric { 3799382Seric define('a', p, e); 3807783Seric /* we don't have a good way to do canonical conversion .... 3819382Seric define('d', newstr(arpatounix(p)), e); 3827783Seric .... so we will ignore the problem for the time being */ 3837783Seric } 38411290Seric 38511290Seric /* 38611290Seric ** Log collection information. 38711290Seric */ 38811290Seric 38911290Seric # ifdef LOG 39011299Seric if (!QueueRun && LogLevel > 1) 39111290Seric { 39236230Skarels char hbuf[100], *name = hbuf; 39336230Skarels 39436230Skarels if (RealHostName == NULL) 39536230Skarels name = "local"; 39636230Skarels else if (RealHostName[0] == '[') 39736230Skarels name = RealHostName; 39836230Skarels else 39936230Skarels (void)sprintf(hbuf, "%.90s (%s)", 40036230Skarels RealHostName, inet_ntoa(RealHostAddr.sin_addr)); 40136230Skarels syslog(LOG_INFO, 40236230Skarels "%s: from=%s, size=%ld, class=%d, received from %s\n", 40336230Skarels CurEnv->e_id, CurEnv->e_from.q_paddr, CurEnv->e_msgsize, 40436230Skarels CurEnv->e_class, name); 40511290Seric } 40611290Seric # endif LOG 4077783Seric } 4087783Seric /* 4097783Seric ** PRIENCODE -- encode external priority names into internal values. 4107783Seric ** 4117783Seric ** Parameters: 4127783Seric ** p -- priority in ascii. 4137783Seric ** 4147783Seric ** Returns: 4157783Seric ** priority as a numeric level. 4167783Seric ** 4177783Seric ** Side Effects: 4187783Seric ** none. 4197783Seric */ 4207783Seric 4217783Seric priencode(p) 4227783Seric char *p; 4237783Seric { 4248253Seric register int i; 4257783Seric 4268253Seric for (i = 0; i < NumPriorities; i++) 4277783Seric { 42833725Sbostic if (!strcasecmp(p, Priorities[i].pri_name)) 4298253Seric return (Priorities[i].pri_val); 4307783Seric } 4318253Seric 4328253Seric /* unknown priority */ 4338253Seric return (0); 4347783Seric } 4357783Seric /* 4367890Seric ** CRACKADDR -- parse an address and turn it into a macro 4377783Seric ** 4387783Seric ** This doesn't actually parse the address -- it just extracts 4397783Seric ** it and replaces it with "$g". The parse is totally ad hoc 4407783Seric ** and isn't even guaranteed to leave something syntactically 4417783Seric ** identical to what it started with. However, it does leave 4427783Seric ** something semantically identical. 4437783Seric ** 4447783Seric ** The process is kind of strange. There are a number of 4457783Seric ** interesting cases: 4467783Seric ** 1. comment <address> comment ==> comment <$g> comment 4477783Seric ** 2. address ==> address 4487783Seric ** 3. address (comment) ==> $g (comment) 4497783Seric ** 4. (comment) address ==> (comment) $g 4507783Seric ** And then there are the hard cases.... 4517783Seric ** 5. add (comment) ress ==> $g (comment) 4527783Seric ** 6. comment <address (comment)> ==> comment <$g (comment)> 4537783Seric ** 7. .... etc .... 4547783Seric ** 4557783Seric ** Parameters: 4567890Seric ** addr -- the address to be cracked. 4577783Seric ** 4587783Seric ** Returns: 4597783Seric ** a pointer to the new version. 4607783Seric ** 4617783Seric ** Side Effects: 4627890Seric ** none. 4637783Seric ** 4647783Seric ** Warning: 4657783Seric ** The return value is saved in local storage and should 4667783Seric ** be copied if it is to be reused. 4677783Seric */ 4687783Seric 4697783Seric char * 4707890Seric crackaddr(addr) 4717890Seric register char *addr; 4727783Seric { 4737783Seric register char *p; 4747783Seric register int i; 4757783Seric static char buf[MAXNAME]; 4767783Seric char *rhs; 4777783Seric bool gotaddr; 4787783Seric register char *bp; 4797783Seric 4807783Seric if (tTd(33, 1)) 4817890Seric printf("crackaddr(%s)\n", addr); 4827783Seric 48323099Seric (void) strcpy(buf, ""); 4847783Seric rhs = NULL; 4857783Seric 4868082Seric /* strip leading spaces */ 4878082Seric while (*addr != '\0' && isspace(*addr)) 4888082Seric addr++; 4898082Seric 4907783Seric /* 4917783Seric ** See if we have anything in angle brackets. If so, that is 4927783Seric ** the address part, and the rest is the comment. 4937783Seric */ 4947783Seric 4957890Seric p = index(addr, '<'); 4967783Seric if (p != NULL) 4977783Seric { 4987890Seric /* copy the beginning of the addr field to the buffer */ 4997783Seric *p = '\0'; 50023099Seric (void) strcpy(buf, addr); 50123099Seric (void) strcat(buf, "<"); 5028082Seric *p++ = '<'; 5037783Seric 5048082Seric /* skip spaces */ 5058082Seric while (isspace(*p)) 5068082Seric p++; 5078082Seric 5087783Seric /* find the matching right angle bracket */ 5098082Seric addr = p; 5107783Seric for (i = 0; *p != '\0'; p++) 5117783Seric { 5127783Seric switch (*p) 5137783Seric { 5147783Seric case '<': 5157783Seric i++; 5167783Seric break; 5177783Seric 5187783Seric case '>': 5197783Seric i--; 5207783Seric break; 5217783Seric } 5227783Seric if (i < 0) 5237783Seric break; 5247783Seric } 5257783Seric 5267783Seric /* p now points to the closing quote (or a null byte) */ 5277783Seric if (*p != '\0') 5287783Seric { 5297783Seric /* make rhs point to the extra stuff at the end */ 5307783Seric rhs = p; 5317783Seric *p++ = '\0'; 5327783Seric } 5337783Seric } 5347783Seric 5357783Seric /* 5367944Seric ** Now parse the real address part. "addr" points to the (null 5377783Seric ** terminated) version of what we are inerested in; rhs points 5387783Seric ** to the extra stuff at the end of the line, if any. 5397783Seric */ 5407783Seric 5417890Seric p = addr; 5427783Seric 5437783Seric /* now strip out comments */ 5447783Seric bp = &buf[strlen(buf)]; 5457783Seric gotaddr = FALSE; 5467783Seric for (; *p != '\0'; p++) 5477783Seric { 5487783Seric if (*p == '(') 5497783Seric { 5507783Seric /* copy to matching close paren */ 5517783Seric *bp++ = *p++; 5527783Seric for (i = 0; *p != '\0'; p++) 5537783Seric { 5547783Seric *bp++ = *p; 5557783Seric switch (*p) 5567783Seric { 5577783Seric case '(': 5587783Seric i++; 5597783Seric break; 5607783Seric 5617783Seric case ')': 5627783Seric i--; 5637783Seric break; 5647783Seric } 5657783Seric if (i < 0) 5667783Seric break; 5677783Seric } 5687783Seric continue; 5697783Seric } 5707783Seric 5717783Seric /* 5727783Seric ** If this is the first "real" character we have seen, 5737783Seric ** then we put the "$g" in the buffer now. 5747783Seric */ 5757783Seric 5767783Seric if (isspace(*p)) 5777783Seric *bp++ = *p; 5787783Seric else if (!gotaddr) 5797783Seric { 58023099Seric (void) strcpy(bp, "\001g"); 5817783Seric bp += 2; 5827783Seric gotaddr = TRUE; 5837783Seric } 5847783Seric } 5857783Seric 5867944Seric /* hack, hack.... strip trailing blanks */ 5877944Seric do 5887944Seric { 5897944Seric *bp-- = '\0'; 5907944Seric } while (isspace(*bp)); 5917944Seric bp++; 5927783Seric 5937944Seric /* put any right hand side back on */ 5947783Seric if (rhs != NULL) 5957783Seric { 5967783Seric *rhs = '>'; 59723099Seric (void) strcpy(bp, rhs); 5987783Seric } 5997783Seric 6007783Seric if (tTd(33, 1)) 6017944Seric printf("crackaddr=>`%s'\n", buf); 6027783Seric 6037783Seric return (buf); 6047783Seric } 6059382Seric /* 6069382Seric ** PUTHEADER -- put the header part of a message from the in-core copy 6079382Seric ** 6089382Seric ** Parameters: 6099382Seric ** fp -- file to put it on. 6109382Seric ** m -- mailer to use. 6119382Seric ** e -- envelope to use. 6129382Seric ** 6139382Seric ** Returns: 6149382Seric ** none. 6159382Seric ** 6169382Seric ** Side Effects: 6179382Seric ** none. 6189382Seric */ 6199382Seric 62010176Seric putheader(fp, m, e) 6219382Seric register FILE *fp; 6229382Seric register MAILER *m; 6239382Seric register ENVELOPE *e; 6249382Seric { 625*40960Sbostic char buf[MAX(MAXFIELD,BUFSIZ)]; 6269382Seric register HDR *h; 6279382Seric extern char *arpadate(); 6289382Seric extern char *capitalize(); 629*40960Sbostic char obuf[MAX(MAXFIELD,MAXLINE)]; 6309382Seric 6319382Seric for (h = e->e_header; h != NULL; h = h->h_link) 6329382Seric { 6339382Seric register char *p; 63410689Seric extern bool bitintersect(); 6359382Seric 6369382Seric if (bitset(H_CHECK|H_ACHECK, h->h_flags) && 63710689Seric !bitintersect(h->h_mflags, m->m_flags)) 6389382Seric continue; 6399382Seric 64011414Seric /* handle Resent-... headers specially */ 64111414Seric if (bitset(H_RESENT, h->h_flags) && !bitset(EF_RESENT, e->e_flags)) 64211414Seric continue; 64311414Seric 6449382Seric p = h->h_value; 6459382Seric if (bitset(H_DEFAULT, h->h_flags)) 6469382Seric { 6479382Seric /* macro expand value if generated internally */ 6489382Seric expand(p, buf, &buf[sizeof buf], e); 6499382Seric p = buf; 6509382Seric if (p == NULL || *p == '\0') 6519382Seric continue; 6529382Seric } 6539382Seric 6549382Seric if (bitset(H_FROM|H_RCPT, h->h_flags)) 6559382Seric { 6569382Seric /* address field */ 6579382Seric bool oldstyle = bitset(EF_OLDSTYLE, e->e_flags); 6589382Seric 6599382Seric if (bitset(H_FROM, h->h_flags)) 6609382Seric oldstyle = FALSE; 66110176Seric commaize(h, p, fp, oldstyle, m); 6629382Seric } 6639382Seric else 6649382Seric { 6659382Seric /* vanilla header line */ 66612159Seric register char *nlp; 66712159Seric 66812159Seric (void) sprintf(obuf, "%s: ", capitalize(h->h_field)); 66912159Seric while ((nlp = index(p, '\n')) != NULL) 67012159Seric { 67112159Seric *nlp = '\0'; 67212159Seric (void) strcat(obuf, p); 67312159Seric *nlp = '\n'; 67412159Seric putline(obuf, fp, m); 67512159Seric p = ++nlp; 67612161Seric obuf[0] = '\0'; 67712159Seric } 67812159Seric (void) strcat(obuf, p); 67910176Seric putline(obuf, fp, m); 6809382Seric } 6819382Seric } 6829382Seric } 6839382Seric /* 6849382Seric ** COMMAIZE -- output a header field, making a comma-translated list. 6859382Seric ** 6869382Seric ** Parameters: 6879382Seric ** h -- the header field to output. 6889382Seric ** p -- the value to put in it. 6899382Seric ** fp -- file to put it to. 6909382Seric ** oldstyle -- TRUE if this is an old style header. 6919382Seric ** m -- a pointer to the mailer descriptor. If NULL, 6929382Seric ** don't transform the name at all. 6939382Seric ** 6949382Seric ** Returns: 6959382Seric ** none. 6969382Seric ** 6979382Seric ** Side Effects: 6989382Seric ** outputs "p" to file "fp". 6999382Seric */ 7009382Seric 70110176Seric commaize(h, p, fp, oldstyle, m) 7029382Seric register HDR *h; 7039382Seric register char *p; 7049382Seric FILE *fp; 7059382Seric bool oldstyle; 7069382Seric register MAILER *m; 7079382Seric { 7089382Seric register char *obp; 7099382Seric int opos; 7109382Seric bool firstone = TRUE; 71111157Seric char obuf[MAXLINE + 3]; 7129382Seric 7139382Seric /* 7149382Seric ** Output the address list translated by the 7159382Seric ** mailer and with commas. 7169382Seric */ 7179382Seric 7189382Seric if (tTd(14, 2)) 7199382Seric printf("commaize(%s: %s)\n", h->h_field, p); 7209382Seric 7219382Seric obp = obuf; 7229382Seric (void) sprintf(obp, "%s: ", capitalize(h->h_field)); 7239382Seric opos = strlen(h->h_field) + 2; 7249382Seric obp += opos; 7259382Seric 7269382Seric /* 7279382Seric ** Run through the list of values. 7289382Seric */ 7299382Seric 7309382Seric while (*p != '\0') 7319382Seric { 7329382Seric register char *name; 7339382Seric char savechar; 7349382Seric extern char *remotename(); 7359382Seric extern char *DelimChar; /* defined in prescan */ 7369382Seric 7379382Seric /* 7389382Seric ** Find the end of the name. New style names 7399382Seric ** end with a comma, old style names end with 7409382Seric ** a space character. However, spaces do not 7419382Seric ** necessarily delimit an old-style name -- at 7429382Seric ** signs mean keep going. 7439382Seric */ 7449382Seric 7459382Seric /* find end of name */ 7469382Seric while (isspace(*p) || *p == ',') 7479382Seric p++; 7489382Seric name = p; 7499382Seric for (;;) 7509382Seric { 7519382Seric char *oldp; 75216909Seric char pvpbuf[PSBUFSIZE]; 7539382Seric extern bool isatword(); 7549382Seric extern char **prescan(); 7559382Seric 75616909Seric (void) prescan(p, oldstyle ? ' ' : ',', pvpbuf); 7579382Seric p = DelimChar; 7589382Seric 7599382Seric /* look to see if we have an at sign */ 7609382Seric oldp = p; 7619382Seric while (*p != '\0' && isspace(*p)) 7629382Seric p++; 7639382Seric 7649382Seric if (*p != '@' && !isatword(p)) 7659382Seric { 7669382Seric p = oldp; 7679382Seric break; 7689382Seric } 7699382Seric p += *p == '@' ? 1 : 2; 7709382Seric while (*p != '\0' && isspace(*p)) 7719382Seric p++; 7729382Seric } 7739382Seric /* at the end of one complete name */ 7749382Seric 7759382Seric /* strip off trailing white space */ 7769382Seric while (p >= name && (isspace(*p) || *p == ',' || *p == '\0')) 7779382Seric p--; 7789382Seric if (++p == name) 7799382Seric continue; 7809382Seric savechar = *p; 7819382Seric *p = '\0'; 7829382Seric 7839382Seric /* translate the name to be relative */ 78410309Seric name = remotename(name, m, bitset(H_FROM, h->h_flags), FALSE); 7859382Seric if (*name == '\0') 7869382Seric { 7879382Seric *p = savechar; 7889382Seric continue; 7899382Seric } 7909382Seric 7919382Seric /* output the name with nice formatting */ 7929382Seric opos += qstrlen(name); 7939382Seric if (!firstone) 7949382Seric opos += 2; 7959382Seric if (opos > 78 && !firstone) 7969382Seric { 79710178Seric (void) strcpy(obp, ",\n"); 79810176Seric putline(obuf, fp, m); 7999382Seric obp = obuf; 8009382Seric (void) sprintf(obp, " "); 80110161Seric opos = strlen(obp); 80210161Seric obp += opos; 80310161Seric opos += qstrlen(name); 8049382Seric } 8059382Seric else if (!firstone) 8069382Seric { 8079382Seric (void) sprintf(obp, ", "); 8089382Seric obp += 2; 8099382Seric } 8109382Seric 8119382Seric /* strip off quote bits as we output */ 81211157Seric while (*name != '\0' && obp < &obuf[MAXLINE]) 8139382Seric { 8149382Seric if (bitset(0200, *name)) 8159382Seric *obp++ = '\\'; 8169382Seric *obp++ = *name++ & ~0200; 8179382Seric } 8189382Seric firstone = FALSE; 8199382Seric *p = savechar; 8209382Seric } 8219382Seric (void) strcpy(obp, "\n"); 82210176Seric putline(obuf, fp, m); 8239382Seric } 8249382Seric /* 8259382Seric ** ISATWORD -- tell if the word we are pointing to is "at". 8269382Seric ** 8279382Seric ** Parameters: 8289382Seric ** p -- word to check. 8299382Seric ** 8309382Seric ** Returns: 8319382Seric ** TRUE -- if p is the word at. 8329382Seric ** FALSE -- otherwise. 8339382Seric ** 8349382Seric ** Side Effects: 8359382Seric ** none. 8369382Seric */ 8379382Seric 8389382Seric bool 8399382Seric isatword(p) 8409382Seric register char *p; 8419382Seric { 8429382Seric extern char lower(); 8439382Seric 8449382Seric if (lower(p[0]) == 'a' && lower(p[1]) == 't' && 8459382Seric p[2] != '\0' && isspace(p[2])) 8469382Seric return (TRUE); 8479382Seric return (FALSE); 8489382Seric } 849