122706Sdist /* 234921Sbostic * Copyright (c) 1983 Eric P. Allman 333729Sbostic * Copyright (c) 1988 Regents of the University of California. 433729Sbostic * All rights reserved. 533729Sbostic * 642826Sbostic * %sccs.include.redist.c% 733729Sbostic */ 822706Sdist 922706Sdist #ifndef lint 10*56678Seric static char sccsid[] = "@(#)headers.c 5.22 (Berkeley) 11/04/92"; 1133729Sbostic #endif /* not lint */ 1222706Sdist 1340960Sbostic # include <sys/param.h> 144091Seric # include <errno.h> 154091Seric # include "sendmail.h" 164091Seric 174091Seric /* 184091Seric ** CHOMPHEADER -- process and save a header line. 194091Seric ** 204091Seric ** Called by collect and by readcf to deal with header lines. 214091Seric ** 224091Seric ** Parameters: 234091Seric ** line -- header as a text line. 244091Seric ** def -- if set, this is a default value. 2555012Seric ** e -- the envelope including this header. 264091Seric ** 274091Seric ** Returns: 284091Seric ** flags for this header. 294091Seric ** 304091Seric ** Side Effects: 314091Seric ** The header is saved on the header list. 324319Seric ** Contents of 'line' are destroyed. 334091Seric */ 344091Seric 3555012Seric chompheader(line, def, e) 364091Seric char *line; 374091Seric bool def; 3855012Seric register ENVELOPE *e; 394091Seric { 404091Seric register char *p; 414091Seric register HDR *h; 424091Seric HDR **hp; 434091Seric char *fname; 444091Seric char *fvalue; 454091Seric struct hdrinfo *hi; 469059Seric bool cond = FALSE; 4710689Seric BITMAP mopts; 487890Seric extern char *crackaddr(); 494091Seric 507677Seric if (tTd(31, 6)) 517677Seric printf("chompheader: %s\n", line); 527677Seric 534627Seric /* strip off options */ 5410689Seric clrbitmap(mopts); 554627Seric p = line; 56*56678Seric if (def && *p == '?') 574627Seric { 584627Seric /* have some */ 594627Seric register char *q = index(p + 1, *p); 604627Seric 614627Seric if (q != NULL) 624627Seric { 634627Seric *q++ = '\0'; 6410689Seric while (*++p != '\0') 6510689Seric setbitn(*p, mopts); 664627Seric p = q; 674627Seric } 684627Seric else 6936233Skarels usrerr("chompheader: syntax error, line \"%s\"", line); 709059Seric cond = TRUE; 714627Seric } 724627Seric 734091Seric /* find canonical name */ 744627Seric fname = p; 754627Seric p = index(p, ':'); 7610118Seric if (p == NULL) 7710118Seric { 7810118Seric syserr("chompheader: syntax error, line \"%s\"", line); 7910118Seric return (0); 8010118Seric } 814091Seric fvalue = &p[1]; 824091Seric while (isspace(*--p)) 834091Seric continue; 844091Seric *++p = '\0'; 854091Seric makelower(fname); 864091Seric 874091Seric /* strip field value on front */ 884091Seric if (*fvalue == ' ') 894091Seric fvalue++; 904091Seric 914091Seric /* see if it is a known type */ 924091Seric for (hi = HdrInfo; hi->hi_field != NULL; hi++) 934091Seric { 944091Seric if (strcmp(hi->hi_field, fname) == 0) 954091Seric break; 964091Seric } 974091Seric 9811414Seric /* see if this is a resent message */ 9911930Seric if (!def && bitset(H_RESENT, hi->hi_flags)) 10055012Seric e->e_flags |= EF_RESENT; 10111414Seric 1024091Seric /* if this means "end of header" quit now */ 1034091Seric if (bitset(H_EOH, hi->hi_flags)) 1044091Seric return (hi->hi_flags); 1054091Seric 10611414Seric /* drop explicit From: if same as what we would generate -- for MH */ 10714784Seric p = "resent-from"; 10855012Seric if (!bitset(EF_RESENT, e->e_flags)) 10914784Seric p += 7; 11014784Seric if (!def && !QueueRun && strcmp(fname, p) == 0) 11111414Seric { 11255012Seric if (e->e_from.q_paddr != NULL && 11355012Seric strcmp(fvalue, e->e_from.q_paddr) == 0) 11411414Seric return (hi->hi_flags); 11511414Seric } 11611414Seric 11714784Seric /* delete default value for this header */ 11855012Seric for (hp = &e->e_header; (h = *hp) != NULL; hp = &h->h_link) 11914784Seric { 12014784Seric if (strcmp(fname, h->h_field) == 0 && 12114784Seric bitset(H_DEFAULT, h->h_flags) && 12214784Seric !bitset(H_FORCE, h->h_flags)) 12314784Seric h->h_value = NULL; 12414784Seric } 12514784Seric 12613012Seric /* create a new node */ 12713012Seric h = (HDR *) xalloc(sizeof *h); 12813012Seric h->h_field = newstr(fname); 12913012Seric h->h_value = NULL; 13013012Seric h->h_link = NULL; 13123117Seric bcopy((char *) mopts, (char *) h->h_mflags, sizeof mopts); 13213012Seric *hp = h; 1338066Seric h->h_flags = hi->hi_flags; 1344091Seric if (def) 1354091Seric h->h_flags |= H_DEFAULT; 1369059Seric if (cond) 1379059Seric h->h_flags |= H_CHECK; 1384091Seric if (h->h_value != NULL) 1399351Seric free((char *) h->h_value); 1408066Seric h->h_value = newstr(fvalue); 1414091Seric 1425937Seric /* hack to see if this is a new format message */ 1438095Seric if (!def && bitset(H_RCPT|H_FROM, h->h_flags) && 1445937Seric (index(fvalue, ',') != NULL || index(fvalue, '(') != NULL || 1458089Seric index(fvalue, '<') != NULL || index(fvalue, ';') != NULL)) 1468089Seric { 14755012Seric e->e_flags &= ~EF_OLDSTYLE; 1488089Seric } 1495937Seric 1504091Seric return (h->h_flags); 1514091Seric } 1524091Seric /* 1536980Seric ** ADDHEADER -- add a header entry to the end of the queue. 1546980Seric ** 1556980Seric ** This bypasses the special checking of chompheader. 1566980Seric ** 1576980Seric ** Parameters: 1586980Seric ** field -- the name of the header field. 1596980Seric ** value -- the value of the field. It must be lower-cased. 1606980Seric ** e -- the envelope to add them to. 1616980Seric ** 1626980Seric ** Returns: 1636980Seric ** none. 1646980Seric ** 1656980Seric ** Side Effects: 1666980Seric ** adds the field on the list of headers for this envelope. 1676980Seric */ 1686980Seric 1696980Seric addheader(field, value, e) 1706980Seric char *field; 1716980Seric char *value; 1726980Seric ENVELOPE *e; 1736980Seric { 1746980Seric register HDR *h; 1756980Seric register struct hdrinfo *hi; 1766980Seric HDR **hp; 1776980Seric 1786980Seric /* find info struct */ 1796980Seric for (hi = HdrInfo; hi->hi_field != NULL; hi++) 1806980Seric { 1816980Seric if (strcmp(field, hi->hi_field) == 0) 1826980Seric break; 1836980Seric } 1846980Seric 1856980Seric /* find current place in list -- keep back pointer? */ 1866980Seric for (hp = &e->e_header; (h = *hp) != NULL; hp = &h->h_link) 1876980Seric { 1886980Seric if (strcmp(field, h->h_field) == 0) 1896980Seric break; 1906980Seric } 1916980Seric 1926980Seric /* allocate space for new header */ 1936980Seric h = (HDR *) xalloc(sizeof *h); 1946980Seric h->h_field = field; 1956980Seric h->h_value = newstr(value); 1967368Seric h->h_link = *hp; 1976980Seric h->h_flags = hi->hi_flags | H_DEFAULT; 19810689Seric clrbitmap(h->h_mflags); 1996980Seric *hp = h; 2006980Seric } 2016980Seric /* 2024091Seric ** HVALUE -- return value of a header. 2034091Seric ** 2044091Seric ** Only "real" fields (i.e., ones that have not been supplied 2054091Seric ** as a default) are used. 2064091Seric ** 2074091Seric ** Parameters: 2084091Seric ** field -- the field name. 20955012Seric ** e -- the envelope containing the header. 2104091Seric ** 2114091Seric ** Returns: 2124091Seric ** pointer to the value part. 2134091Seric ** NULL if not found. 2144091Seric ** 2154091Seric ** Side Effects: 2169382Seric ** none. 2174091Seric */ 2184091Seric 2194091Seric char * 22055012Seric hvalue(field, e) 2214091Seric char *field; 22255012Seric register ENVELOPE *e; 2234091Seric { 2244091Seric register HDR *h; 2254091Seric 22655012Seric for (h = e->e_header; h != NULL; h = h->h_link) 2274091Seric { 2284091Seric if (!bitset(H_DEFAULT, h->h_flags) && strcmp(h->h_field, field) == 0) 2294091Seric return (h->h_value); 2304091Seric } 2314091Seric return (NULL); 2324091Seric } 2334091Seric /* 2344091Seric ** ISHEADER -- predicate telling if argument is a header. 2354091Seric ** 2364319Seric ** A line is a header if it has a single word followed by 2374319Seric ** optional white space followed by a colon. 2384319Seric ** 2394091Seric ** Parameters: 2404091Seric ** s -- string to check for possible headerness. 2414091Seric ** 2424091Seric ** Returns: 2434091Seric ** TRUE if s is a header. 2444091Seric ** FALSE otherwise. 2454091Seric ** 2464091Seric ** Side Effects: 2474091Seric ** none. 2484091Seric */ 2494091Seric 2504091Seric bool 2514091Seric isheader(s) 2524091Seric register char *s; 2534091Seric { 2549382Seric while (*s > ' ' && *s != ':' && *s != '\0') 2554091Seric s++; 2569382Seric 2579382Seric /* following technically violates RFC822 */ 2584091Seric while (isspace(*s)) 2594091Seric s++; 2609382Seric 2614091Seric return (*s == ':'); 2624091Seric } 2635919Seric /* 2647783Seric ** EATHEADER -- run through the stored header and extract info. 2657783Seric ** 2667783Seric ** Parameters: 2679382Seric ** e -- the envelope to process. 2687783Seric ** 2697783Seric ** Returns: 2707783Seric ** none. 2717783Seric ** 2727783Seric ** Side Effects: 2737783Seric ** Sets a bunch of global variables from information 2749382Seric ** in the collected header. 2759382Seric ** Aborts the message if the hop count is exceeded. 2767783Seric */ 2777783Seric 2789382Seric eatheader(e) 2799382Seric register ENVELOPE *e; 2807783Seric { 2817783Seric register HDR *h; 2827783Seric register char *p; 2839382Seric int hopcnt = 0; 2847783Seric 2859382Seric if (tTd(32, 1)) 2869382Seric printf("----- collected header -----\n"); 2879382Seric for (h = e->e_header; h != NULL; h = h->h_link) 2887783Seric { 2897783Seric extern char *capitalize(); 2907783Seric 2919382Seric if (tTd(32, 1)) 2927783Seric printf("%s: %s\n", capitalize(h->h_field), h->h_value); 29311414Seric /* count the number of times it has been processed */ 2949382Seric if (bitset(H_TRACE, h->h_flags)) 2959382Seric hopcnt++; 29611414Seric 29711414Seric /* send to this person if we so desire */ 29811414Seric if (GrabTo && bitset(H_RCPT, h->h_flags) && 29911414Seric !bitset(H_DEFAULT, h->h_flags) && 30055012Seric (!bitset(EF_RESENT, e->e_flags) || bitset(H_RESENT, h->h_flags))) 30111414Seric { 30255012Seric sendtolist(h->h_value, (ADDRESS *) NULL, 30355012Seric &e->e_sendqueue, e); 30411414Seric } 30511414Seric 30611414Seric /* log the message-id */ 30711290Seric #ifdef LOG 30813103Seric if (!QueueRun && LogLevel > 8 && h->h_value != NULL && 30911299Seric strcmp(h->h_field, "message-id") == 0) 31011290Seric { 31111290Seric char buf[MAXNAME]; 31211290Seric 31311290Seric p = h->h_value; 31411290Seric if (bitset(H_DEFAULT, h->h_flags)) 31511290Seric { 31611290Seric expand(p, buf, &buf[sizeof buf], e); 31711290Seric p = buf; 31811290Seric } 31911290Seric syslog(LOG_INFO, "%s: message-id=%s", e->e_id, p); 32011290Seric } 32111290Seric #endif LOG 3229382Seric } 3239382Seric if (tTd(32, 1)) 3247783Seric printf("----------------------------\n"); 3257783Seric 3269382Seric /* store hop count */ 3279382Seric if (hopcnt > e->e_hopcount) 3289382Seric e->e_hopcount = hopcnt; 3299382Seric 3307783Seric /* message priority */ 33155012Seric p = hvalue("precedence", e); 3329382Seric if (p != NULL) 3339382Seric e->e_class = priencode(p); 3347783Seric if (!QueueRun) 33525013Seric e->e_msgpriority = e->e_msgsize 33624981Seric - e->e_class * WkClassFact 33724981Seric + e->e_nrcpts * WkRecipFact; 3387783Seric 3398066Seric /* return receipt to */ 34055012Seric p = hvalue("return-receipt-to", e); 3417783Seric if (p != NULL) 3429382Seric e->e_receiptto = p; 3437783Seric 3448253Seric /* errors to */ 34555012Seric p = hvalue("errors-to", e); 3468253Seric if (p != NULL) 34755012Seric sendtolist(p, (ADDRESS *) NULL, &e->e_errorqueue, e); 3488253Seric 3497783Seric /* full name of from person */ 35055012Seric p = hvalue("full-name", e); 3517783Seric if (p != NULL) 3529382Seric define('x', p, e); 3537783Seric 3547783Seric /* date message originated */ 35555012Seric p = hvalue("posted-date", e); 3567783Seric if (p == NULL) 35755012Seric p = hvalue("date", e); 3587783Seric if (p != NULL) 3597783Seric { 3609382Seric define('a', p, e); 3617783Seric /* we don't have a good way to do canonical conversion .... 3629382Seric define('d', newstr(arpatounix(p)), e); 3637783Seric .... so we will ignore the problem for the time being */ 3647783Seric } 36511290Seric 36611290Seric /* 36711290Seric ** Log collection information. 36811290Seric */ 36911290Seric 37011290Seric # ifdef LOG 37111299Seric if (!QueueRun && LogLevel > 1) 37211290Seric { 37355019Seric char hbuf[100]; 37455019Seric char *name = hbuf; 37555019Seric extern char *inet_ntoa(); 37636230Skarels 37736230Skarels if (RealHostName == NULL) 37836230Skarels name = "local"; 37936230Skarels else if (RealHostName[0] == '[') 38036230Skarels name = RealHostName; 38136230Skarels else 38254999Seric (void)sprintf(hbuf, "%.80s (%s)", 38336230Skarels RealHostName, inet_ntoa(RealHostAddr.sin_addr)); 38436230Skarels syslog(LOG_INFO, 38536230Skarels "%s: from=%s, size=%ld, class=%d, received from %s\n", 38655012Seric e->e_id, e->e_from.q_paddr, e->e_msgsize, 38755012Seric e->e_class, name); 38811290Seric } 38911290Seric # endif LOG 3907783Seric } 3917783Seric /* 3927783Seric ** PRIENCODE -- encode external priority names into internal values. 3937783Seric ** 3947783Seric ** Parameters: 3957783Seric ** p -- priority in ascii. 3967783Seric ** 3977783Seric ** Returns: 3987783Seric ** priority as a numeric level. 3997783Seric ** 4007783Seric ** Side Effects: 4017783Seric ** none. 4027783Seric */ 4037783Seric 4047783Seric priencode(p) 4057783Seric char *p; 4067783Seric { 4078253Seric register int i; 4087783Seric 4098253Seric for (i = 0; i < NumPriorities; i++) 4107783Seric { 41133725Sbostic if (!strcasecmp(p, Priorities[i].pri_name)) 4128253Seric return (Priorities[i].pri_val); 4137783Seric } 4148253Seric 4158253Seric /* unknown priority */ 4168253Seric return (0); 4177783Seric } 4187783Seric /* 4197890Seric ** CRACKADDR -- parse an address and turn it into a macro 4207783Seric ** 4217783Seric ** This doesn't actually parse the address -- it just extracts 4227783Seric ** it and replaces it with "$g". The parse is totally ad hoc 4237783Seric ** and isn't even guaranteed to leave something syntactically 4247783Seric ** identical to what it started with. However, it does leave 4257783Seric ** something semantically identical. 4267783Seric ** 42751379Seric ** This algorithm has been cleaned up to handle a wider range 42851379Seric ** of cases -- notably quoted and backslash escaped strings. 42951379Seric ** This modification makes it substantially better at preserving 43051379Seric ** the original syntax. 4317783Seric ** 4327783Seric ** Parameters: 4337890Seric ** addr -- the address to be cracked. 4347783Seric ** 4357783Seric ** Returns: 4367783Seric ** a pointer to the new version. 4377783Seric ** 4387783Seric ** Side Effects: 4397890Seric ** none. 4407783Seric ** 4417783Seric ** Warning: 4427783Seric ** The return value is saved in local storage and should 4437783Seric ** be copied if it is to be reused. 4447783Seric */ 4457783Seric 4467783Seric char * 4477890Seric crackaddr(addr) 4487890Seric register char *addr; 4497783Seric { 4507783Seric register char *p; 45151379Seric register char c; 45251379Seric int cmtlev; 45351379Seric int copylev; 45451379Seric bool qmode; 45551379Seric bool putgmac = FALSE; 45651379Seric register char *bp; 4577783Seric static char buf[MAXNAME]; 4587783Seric 4597783Seric if (tTd(33, 1)) 4607890Seric printf("crackaddr(%s)\n", addr); 4617783Seric 4628082Seric /* strip leading spaces */ 4638082Seric while (*addr != '\0' && isspace(*addr)) 4648082Seric addr++; 4658082Seric 4667783Seric /* 46751379Seric ** Start by assuming we have no angle brackets. This will be 46851379Seric ** adjusted later if we find them. 4697783Seric */ 4707783Seric 47151379Seric bp = buf; 47251379Seric p = addr; 47351379Seric copylev = cmtlev = 0; 47451379Seric qmode = FALSE; 47551379Seric 47651379Seric while ((c = *p++) != '\0') 4777783Seric { 47851379Seric if (copylev > 0 || c == ' ') 47951379Seric *bp++ = c; 4807783Seric 48151379Seric /* check for backslash escapes */ 48251379Seric if (c == '\\') 4837783Seric { 48451379Seric if ((c = *p++) == '\0') 4857783Seric { 48651379Seric /* too far */ 48751379Seric p--; 48851379Seric goto putg; 4897783Seric } 49051379Seric if (copylev > 0) 49151379Seric *bp++ = c; 49251379Seric goto putg; 4937783Seric } 4947783Seric 49551379Seric /* check for quoted strings */ 49651379Seric if (c == '"') 4977783Seric { 49851379Seric qmode = !qmode; 49951379Seric continue; 5007783Seric } 50151379Seric if (qmode) 50251379Seric goto putg; 5037783Seric 50451379Seric /* check for comments */ 50551379Seric if (c == '(') 5067783Seric { 50751379Seric cmtlev++; 50851379Seric if (copylev++ <= 0) 50951379Seric *bp++ = c; 51051379Seric } 51151379Seric if (cmtlev > 0) 51251379Seric { 51351379Seric if (c == ')') 5147783Seric { 51551379Seric cmtlev--; 51651379Seric copylev--; 5177783Seric } 5187783Seric continue; 5197783Seric } 5207783Seric 52151379Seric /* check for angle brackets */ 52251379Seric if (c == '<') 52351379Seric { 52451379Seric /* oops -- have to change our mind */ 52551379Seric bcopy(addr, buf, p - addr); 52651379Seric bp = &buf[p - addr]; 52751379Seric copylev = 0; 52851379Seric putgmac = FALSE; 52951379Seric continue; 53051379Seric } 5317783Seric 53251379Seric if (c == '>') 5337783Seric { 53451379Seric if (copylev++ <= 0) 53551379Seric *bp++ = c; 53651379Seric continue; 5377783Seric } 53851379Seric 53951379Seric /* must be a real address character */ 54051379Seric putg: 54151379Seric if (copylev <= 0 && !putgmac) 54251379Seric { 54351379Seric *bp++ = '\001'; 54451379Seric *bp++ = 'g'; 54551379Seric putgmac = TRUE; 54651379Seric } 5477783Seric } 5487783Seric 54951379Seric *bp++ = '\0'; 5507783Seric 5517783Seric if (tTd(33, 1)) 5527944Seric printf("crackaddr=>`%s'\n", buf); 5537783Seric 5547783Seric return (buf); 5557783Seric } 5569382Seric /* 5579382Seric ** PUTHEADER -- put the header part of a message from the in-core copy 5589382Seric ** 5599382Seric ** Parameters: 5609382Seric ** fp -- file to put it on. 5619382Seric ** m -- mailer to use. 5629382Seric ** e -- envelope to use. 5639382Seric ** 5649382Seric ** Returns: 5659382Seric ** none. 5669382Seric ** 5679382Seric ** Side Effects: 5689382Seric ** none. 5699382Seric */ 5709382Seric 57110176Seric putheader(fp, m, e) 5729382Seric register FILE *fp; 5739382Seric register MAILER *m; 5749382Seric register ENVELOPE *e; 5759382Seric { 57640960Sbostic char buf[MAX(MAXFIELD,BUFSIZ)]; 5779382Seric register HDR *h; 5789382Seric extern char *arpadate(); 5799382Seric extern char *capitalize(); 58040960Sbostic char obuf[MAX(MAXFIELD,MAXLINE)]; 5819382Seric 5829382Seric for (h = e->e_header; h != NULL; h = h->h_link) 5839382Seric { 5849382Seric register char *p; 58510689Seric extern bool bitintersect(); 5869382Seric 5879382Seric if (bitset(H_CHECK|H_ACHECK, h->h_flags) && 58810689Seric !bitintersect(h->h_mflags, m->m_flags)) 5899382Seric continue; 5909382Seric 59111414Seric /* handle Resent-... headers specially */ 59211414Seric if (bitset(H_RESENT, h->h_flags) && !bitset(EF_RESENT, e->e_flags)) 59311414Seric continue; 59411414Seric 5959382Seric p = h->h_value; 5969382Seric if (bitset(H_DEFAULT, h->h_flags)) 5979382Seric { 5989382Seric /* macro expand value if generated internally */ 5999382Seric expand(p, buf, &buf[sizeof buf], e); 6009382Seric p = buf; 6019382Seric if (p == NULL || *p == '\0') 6029382Seric continue; 6039382Seric } 6049382Seric 6059382Seric if (bitset(H_FROM|H_RCPT, h->h_flags)) 6069382Seric { 6079382Seric /* address field */ 6089382Seric bool oldstyle = bitset(EF_OLDSTYLE, e->e_flags); 6099382Seric 6109382Seric if (bitset(H_FROM, h->h_flags)) 6119382Seric oldstyle = FALSE; 61255012Seric commaize(h, p, fp, oldstyle, m, e); 6139382Seric } 6149382Seric else 6159382Seric { 6169382Seric /* vanilla header line */ 61712159Seric register char *nlp; 61812159Seric 61912159Seric (void) sprintf(obuf, "%s: ", capitalize(h->h_field)); 62012159Seric while ((nlp = index(p, '\n')) != NULL) 62112159Seric { 62212159Seric *nlp = '\0'; 62312159Seric (void) strcat(obuf, p); 62412159Seric *nlp = '\n'; 62512159Seric putline(obuf, fp, m); 62612159Seric p = ++nlp; 62712161Seric obuf[0] = '\0'; 62812159Seric } 62912159Seric (void) strcat(obuf, p); 63010176Seric putline(obuf, fp, m); 6319382Seric } 6329382Seric } 6339382Seric } 6349382Seric /* 6359382Seric ** COMMAIZE -- output a header field, making a comma-translated list. 6369382Seric ** 6379382Seric ** Parameters: 6389382Seric ** h -- the header field to output. 6399382Seric ** p -- the value to put in it. 6409382Seric ** fp -- file to put it to. 6419382Seric ** oldstyle -- TRUE if this is an old style header. 6429382Seric ** m -- a pointer to the mailer descriptor. If NULL, 6439382Seric ** don't transform the name at all. 64455012Seric ** e -- the envelope containing the message. 6459382Seric ** 6469382Seric ** Returns: 6479382Seric ** none. 6489382Seric ** 6499382Seric ** Side Effects: 6509382Seric ** outputs "p" to file "fp". 6519382Seric */ 6529382Seric 65355012Seric commaize(h, p, fp, oldstyle, m, e) 6549382Seric register HDR *h; 6559382Seric register char *p; 6569382Seric FILE *fp; 6579382Seric bool oldstyle; 6589382Seric register MAILER *m; 65955012Seric register ENVELOPE *e; 6609382Seric { 6619382Seric register char *obp; 6629382Seric int opos; 6639382Seric bool firstone = TRUE; 66411157Seric char obuf[MAXLINE + 3]; 6659382Seric 6669382Seric /* 6679382Seric ** Output the address list translated by the 6689382Seric ** mailer and with commas. 6699382Seric */ 6709382Seric 6719382Seric if (tTd(14, 2)) 6729382Seric printf("commaize(%s: %s)\n", h->h_field, p); 6739382Seric 6749382Seric obp = obuf; 6759382Seric (void) sprintf(obp, "%s: ", capitalize(h->h_field)); 6769382Seric opos = strlen(h->h_field) + 2; 6779382Seric obp += opos; 6789382Seric 6799382Seric /* 6809382Seric ** Run through the list of values. 6819382Seric */ 6829382Seric 6839382Seric while (*p != '\0') 6849382Seric { 6859382Seric register char *name; 68654983Seric register int c; 6879382Seric char savechar; 6889382Seric extern char *remotename(); 6899382Seric extern char *DelimChar; /* defined in prescan */ 6909382Seric 6919382Seric /* 6929382Seric ** Find the end of the name. New style names 6939382Seric ** end with a comma, old style names end with 6949382Seric ** a space character. However, spaces do not 6959382Seric ** necessarily delimit an old-style name -- at 6969382Seric ** signs mean keep going. 6979382Seric */ 6989382Seric 6999382Seric /* find end of name */ 7009382Seric while (isspace(*p) || *p == ',') 7019382Seric p++; 7029382Seric name = p; 7039382Seric for (;;) 7049382Seric { 7059382Seric char *oldp; 70616909Seric char pvpbuf[PSBUFSIZE]; 7079382Seric extern bool isatword(); 7089382Seric extern char **prescan(); 7099382Seric 71016909Seric (void) prescan(p, oldstyle ? ' ' : ',', pvpbuf); 7119382Seric p = DelimChar; 7129382Seric 7139382Seric /* look to see if we have an at sign */ 7149382Seric oldp = p; 7159382Seric while (*p != '\0' && isspace(*p)) 7169382Seric p++; 7179382Seric 7189382Seric if (*p != '@' && !isatword(p)) 7199382Seric { 7209382Seric p = oldp; 7219382Seric break; 7229382Seric } 7239382Seric p += *p == '@' ? 1 : 2; 7249382Seric while (*p != '\0' && isspace(*p)) 7259382Seric p++; 7269382Seric } 7279382Seric /* at the end of one complete name */ 7289382Seric 7299382Seric /* strip off trailing white space */ 7309382Seric while (p >= name && (isspace(*p) || *p == ',' || *p == '\0')) 7319382Seric p--; 7329382Seric if (++p == name) 7339382Seric continue; 7349382Seric savechar = *p; 7359382Seric *p = '\0'; 7369382Seric 7379382Seric /* translate the name to be relative */ 73855012Seric name = remotename(name, m, bitset(H_FROM, h->h_flags), FALSE, e); 7399382Seric if (*name == '\0') 7409382Seric { 7419382Seric *p = savechar; 7429382Seric continue; 7439382Seric } 7449382Seric 7459382Seric /* output the name with nice formatting */ 74654983Seric opos += strlen(name); 7479382Seric if (!firstone) 7489382Seric opos += 2; 7499382Seric if (opos > 78 && !firstone) 7509382Seric { 75110178Seric (void) strcpy(obp, ",\n"); 75210176Seric putline(obuf, fp, m); 7539382Seric obp = obuf; 7549382Seric (void) sprintf(obp, " "); 75510161Seric opos = strlen(obp); 75610161Seric obp += opos; 75754983Seric opos += strlen(name); 7589382Seric } 7599382Seric else if (!firstone) 7609382Seric { 7619382Seric (void) sprintf(obp, ", "); 7629382Seric obp += 2; 7639382Seric } 7649382Seric 7659382Seric /* strip off quote bits as we output */ 76654983Seric while ((c = *name++) != '\0' && obp < &obuf[MAXLINE]) 7679382Seric { 76854983Seric if (bitnset(M_7BITS, m->m_flags)) 76954983Seric c &= 0177; 77054983Seric *obp++ = c; 7719382Seric } 7729382Seric firstone = FALSE; 7739382Seric *p = savechar; 7749382Seric } 7759382Seric (void) strcpy(obp, "\n"); 77610176Seric putline(obuf, fp, m); 7779382Seric } 7789382Seric /* 7799382Seric ** ISATWORD -- tell if the word we are pointing to is "at". 7809382Seric ** 7819382Seric ** Parameters: 7829382Seric ** p -- word to check. 7839382Seric ** 7849382Seric ** Returns: 7859382Seric ** TRUE -- if p is the word at. 7869382Seric ** FALSE -- otherwise. 7879382Seric ** 7889382Seric ** Side Effects: 7899382Seric ** none. 7909382Seric */ 7919382Seric 7929382Seric bool 7939382Seric isatword(p) 7949382Seric register char *p; 7959382Seric { 7969382Seric extern char lower(); 7979382Seric 7989382Seric if (lower(p[0]) == 'a' && lower(p[1]) == 't' && 7999382Seric p[2] != '\0' && isspace(p[2])) 8009382Seric return (TRUE); 8019382Seric return (FALSE); 8029382Seric } 803