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*55012Seric static char sccsid[] = "@(#)headers.c 5.20 (Berkeley) 07/12/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. 25*55012Seric ** 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 35*55012Seric chompheader(line, def, e) 364091Seric char *line; 374091Seric bool def; 38*55012Seric 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; 564627Seric if (*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)) 100*55012Seric 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"; 108*55012Seric if (!bitset(EF_RESENT, e->e_flags)) 10914784Seric p += 7; 11014784Seric if (!def && !QueueRun && strcmp(fname, p) == 0) 11111414Seric { 112*55012Seric if (e->e_from.q_paddr != NULL && 113*55012Seric strcmp(fvalue, e->e_from.q_paddr) == 0) 11411414Seric return (hi->hi_flags); 11511414Seric } 11611414Seric 11714784Seric /* delete default value for this header */ 118*55012Seric 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 { 147*55012Seric 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. 209*55012Seric ** 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 * 220*55012Seric hvalue(field, e) 2214091Seric char *field; 222*55012Seric register ENVELOPE *e; 2234091Seric { 2244091Seric register HDR *h; 2254091Seric 226*55012Seric 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) && 300*55012Seric (!bitset(EF_RESENT, e->e_flags) || bitset(H_RESENT, h->h_flags))) 30111414Seric { 302*55012Seric sendtolist(h->h_value, (ADDRESS *) NULL, 303*55012Seric &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 */ 331*55012Seric 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 */ 340*55012Seric p = hvalue("return-receipt-to", e); 3417783Seric if (p != NULL) 3429382Seric e->e_receiptto = p; 3437783Seric 3448253Seric /* errors to */ 345*55012Seric p = hvalue("errors-to", e); 3468253Seric if (p != NULL) 347*55012Seric sendtolist(p, (ADDRESS *) NULL, &e->e_errorqueue, e); 3488253Seric 3497783Seric /* full name of from person */ 350*55012Seric p = hvalue("full-name", e); 3517783Seric if (p != NULL) 3529382Seric define('x', p, e); 3537783Seric 3547783Seric /* date message originated */ 355*55012Seric p = hvalue("posted-date", e); 3567783Seric if (p == NULL) 357*55012Seric 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 { 37336230Skarels char hbuf[100], *name = hbuf; 37436230Skarels 37536230Skarels if (RealHostName == NULL) 37636230Skarels name = "local"; 37736230Skarels else if (RealHostName[0] == '[') 37836230Skarels name = RealHostName; 37936230Skarels else 38054999Seric (void)sprintf(hbuf, "%.80s (%s)", 38136230Skarels RealHostName, inet_ntoa(RealHostAddr.sin_addr)); 38236230Skarels syslog(LOG_INFO, 38336230Skarels "%s: from=%s, size=%ld, class=%d, received from %s\n", 384*55012Seric e->e_id, e->e_from.q_paddr, e->e_msgsize, 385*55012Seric e->e_class, name); 38611290Seric } 38711290Seric # endif LOG 3887783Seric } 3897783Seric /* 3907783Seric ** PRIENCODE -- encode external priority names into internal values. 3917783Seric ** 3927783Seric ** Parameters: 3937783Seric ** p -- priority in ascii. 3947783Seric ** 3957783Seric ** Returns: 3967783Seric ** priority as a numeric level. 3977783Seric ** 3987783Seric ** Side Effects: 3997783Seric ** none. 4007783Seric */ 4017783Seric 4027783Seric priencode(p) 4037783Seric char *p; 4047783Seric { 4058253Seric register int i; 4067783Seric 4078253Seric for (i = 0; i < NumPriorities; i++) 4087783Seric { 40933725Sbostic if (!strcasecmp(p, Priorities[i].pri_name)) 4108253Seric return (Priorities[i].pri_val); 4117783Seric } 4128253Seric 4138253Seric /* unknown priority */ 4148253Seric return (0); 4157783Seric } 4167783Seric /* 4177890Seric ** CRACKADDR -- parse an address and turn it into a macro 4187783Seric ** 4197783Seric ** This doesn't actually parse the address -- it just extracts 4207783Seric ** it and replaces it with "$g". The parse is totally ad hoc 4217783Seric ** and isn't even guaranteed to leave something syntactically 4227783Seric ** identical to what it started with. However, it does leave 4237783Seric ** something semantically identical. 4247783Seric ** 42551379Seric ** This algorithm has been cleaned up to handle a wider range 42651379Seric ** of cases -- notably quoted and backslash escaped strings. 42751379Seric ** This modification makes it substantially better at preserving 42851379Seric ** the original syntax. 4297783Seric ** 4307783Seric ** Parameters: 4317890Seric ** addr -- the address to be cracked. 4327783Seric ** 4337783Seric ** Returns: 4347783Seric ** a pointer to the new version. 4357783Seric ** 4367783Seric ** Side Effects: 4377890Seric ** none. 4387783Seric ** 4397783Seric ** Warning: 4407783Seric ** The return value is saved in local storage and should 4417783Seric ** be copied if it is to be reused. 4427783Seric */ 4437783Seric 4447783Seric char * 4457890Seric crackaddr(addr) 4467890Seric register char *addr; 4477783Seric { 4487783Seric register char *p; 44951379Seric register char c; 45051379Seric int cmtlev; 45151379Seric int copylev; 45251379Seric bool qmode; 45351379Seric bool putgmac = FALSE; 45451379Seric register char *bp; 4557783Seric static char buf[MAXNAME]; 4567783Seric 4577783Seric if (tTd(33, 1)) 4587890Seric printf("crackaddr(%s)\n", addr); 4597783Seric 4608082Seric /* strip leading spaces */ 4618082Seric while (*addr != '\0' && isspace(*addr)) 4628082Seric addr++; 4638082Seric 4647783Seric /* 46551379Seric ** Start by assuming we have no angle brackets. This will be 46651379Seric ** adjusted later if we find them. 4677783Seric */ 4687783Seric 46951379Seric bp = buf; 47051379Seric p = addr; 47151379Seric copylev = cmtlev = 0; 47251379Seric qmode = FALSE; 47351379Seric 47451379Seric while ((c = *p++) != '\0') 4757783Seric { 47651379Seric if (copylev > 0 || c == ' ') 47751379Seric *bp++ = c; 4787783Seric 47951379Seric /* check for backslash escapes */ 48051379Seric if (c == '\\') 4817783Seric { 48251379Seric if ((c = *p++) == '\0') 4837783Seric { 48451379Seric /* too far */ 48551379Seric p--; 48651379Seric goto putg; 4877783Seric } 48851379Seric if (copylev > 0) 48951379Seric *bp++ = c; 49051379Seric goto putg; 4917783Seric } 4927783Seric 49351379Seric /* check for quoted strings */ 49451379Seric if (c == '"') 4957783Seric { 49651379Seric qmode = !qmode; 49751379Seric continue; 4987783Seric } 49951379Seric if (qmode) 50051379Seric goto putg; 5017783Seric 50251379Seric /* check for comments */ 50351379Seric if (c == '(') 5047783Seric { 50551379Seric cmtlev++; 50651379Seric if (copylev++ <= 0) 50751379Seric *bp++ = c; 50851379Seric } 50951379Seric if (cmtlev > 0) 51051379Seric { 51151379Seric if (c == ')') 5127783Seric { 51351379Seric cmtlev--; 51451379Seric copylev--; 5157783Seric } 5167783Seric continue; 5177783Seric } 5187783Seric 51951379Seric /* check for angle brackets */ 52051379Seric if (c == '<') 52151379Seric { 52251379Seric /* oops -- have to change our mind */ 52351379Seric bcopy(addr, buf, p - addr); 52451379Seric bp = &buf[p - addr]; 52551379Seric copylev = 0; 52651379Seric putgmac = FALSE; 52751379Seric continue; 52851379Seric } 5297783Seric 53051379Seric if (c == '>') 5317783Seric { 53251379Seric if (copylev++ <= 0) 53351379Seric *bp++ = c; 53451379Seric continue; 5357783Seric } 53651379Seric 53751379Seric /* must be a real address character */ 53851379Seric putg: 53951379Seric if (copylev <= 0 && !putgmac) 54051379Seric { 54151379Seric *bp++ = '\001'; 54251379Seric *bp++ = 'g'; 54351379Seric putgmac = TRUE; 54451379Seric } 5457783Seric } 5467783Seric 54751379Seric *bp++ = '\0'; 5487783Seric 5497783Seric if (tTd(33, 1)) 5507944Seric printf("crackaddr=>`%s'\n", buf); 5517783Seric 5527783Seric return (buf); 5537783Seric } 5549382Seric /* 5559382Seric ** PUTHEADER -- put the header part of a message from the in-core copy 5569382Seric ** 5579382Seric ** Parameters: 5589382Seric ** fp -- file to put it on. 5599382Seric ** m -- mailer to use. 5609382Seric ** e -- envelope to use. 5619382Seric ** 5629382Seric ** Returns: 5639382Seric ** none. 5649382Seric ** 5659382Seric ** Side Effects: 5669382Seric ** none. 5679382Seric */ 5689382Seric 56910176Seric putheader(fp, m, e) 5709382Seric register FILE *fp; 5719382Seric register MAILER *m; 5729382Seric register ENVELOPE *e; 5739382Seric { 57440960Sbostic char buf[MAX(MAXFIELD,BUFSIZ)]; 5759382Seric register HDR *h; 5769382Seric extern char *arpadate(); 5779382Seric extern char *capitalize(); 57840960Sbostic char obuf[MAX(MAXFIELD,MAXLINE)]; 5799382Seric 5809382Seric for (h = e->e_header; h != NULL; h = h->h_link) 5819382Seric { 5829382Seric register char *p; 58310689Seric extern bool bitintersect(); 5849382Seric 5859382Seric if (bitset(H_CHECK|H_ACHECK, h->h_flags) && 58610689Seric !bitintersect(h->h_mflags, m->m_flags)) 5879382Seric continue; 5889382Seric 58911414Seric /* handle Resent-... headers specially */ 59011414Seric if (bitset(H_RESENT, h->h_flags) && !bitset(EF_RESENT, e->e_flags)) 59111414Seric continue; 59211414Seric 5939382Seric p = h->h_value; 5949382Seric if (bitset(H_DEFAULT, h->h_flags)) 5959382Seric { 5969382Seric /* macro expand value if generated internally */ 5979382Seric expand(p, buf, &buf[sizeof buf], e); 5989382Seric p = buf; 5999382Seric if (p == NULL || *p == '\0') 6009382Seric continue; 6019382Seric } 6029382Seric 6039382Seric if (bitset(H_FROM|H_RCPT, h->h_flags)) 6049382Seric { 6059382Seric /* address field */ 6069382Seric bool oldstyle = bitset(EF_OLDSTYLE, e->e_flags); 6079382Seric 6089382Seric if (bitset(H_FROM, h->h_flags)) 6099382Seric oldstyle = FALSE; 610*55012Seric commaize(h, p, fp, oldstyle, m, e); 6119382Seric } 6129382Seric else 6139382Seric { 6149382Seric /* vanilla header line */ 61512159Seric register char *nlp; 61612159Seric 61712159Seric (void) sprintf(obuf, "%s: ", capitalize(h->h_field)); 61812159Seric while ((nlp = index(p, '\n')) != NULL) 61912159Seric { 62012159Seric *nlp = '\0'; 62112159Seric (void) strcat(obuf, p); 62212159Seric *nlp = '\n'; 62312159Seric putline(obuf, fp, m); 62412159Seric p = ++nlp; 62512161Seric obuf[0] = '\0'; 62612159Seric } 62712159Seric (void) strcat(obuf, p); 62810176Seric putline(obuf, fp, m); 6299382Seric } 6309382Seric } 6319382Seric } 6329382Seric /* 6339382Seric ** COMMAIZE -- output a header field, making a comma-translated list. 6349382Seric ** 6359382Seric ** Parameters: 6369382Seric ** h -- the header field to output. 6379382Seric ** p -- the value to put in it. 6389382Seric ** fp -- file to put it to. 6399382Seric ** oldstyle -- TRUE if this is an old style header. 6409382Seric ** m -- a pointer to the mailer descriptor. If NULL, 6419382Seric ** don't transform the name at all. 642*55012Seric ** e -- the envelope containing the message. 6439382Seric ** 6449382Seric ** Returns: 6459382Seric ** none. 6469382Seric ** 6479382Seric ** Side Effects: 6489382Seric ** outputs "p" to file "fp". 6499382Seric */ 6509382Seric 651*55012Seric commaize(h, p, fp, oldstyle, m, e) 6529382Seric register HDR *h; 6539382Seric register char *p; 6549382Seric FILE *fp; 6559382Seric bool oldstyle; 6569382Seric register MAILER *m; 657*55012Seric register ENVELOPE *e; 6589382Seric { 6599382Seric register char *obp; 6609382Seric int opos; 6619382Seric bool firstone = TRUE; 66211157Seric char obuf[MAXLINE + 3]; 6639382Seric 6649382Seric /* 6659382Seric ** Output the address list translated by the 6669382Seric ** mailer and with commas. 6679382Seric */ 6689382Seric 6699382Seric if (tTd(14, 2)) 6709382Seric printf("commaize(%s: %s)\n", h->h_field, p); 6719382Seric 6729382Seric obp = obuf; 6739382Seric (void) sprintf(obp, "%s: ", capitalize(h->h_field)); 6749382Seric opos = strlen(h->h_field) + 2; 6759382Seric obp += opos; 6769382Seric 6779382Seric /* 6789382Seric ** Run through the list of values. 6799382Seric */ 6809382Seric 6819382Seric while (*p != '\0') 6829382Seric { 6839382Seric register char *name; 68454983Seric register int c; 6859382Seric char savechar; 6869382Seric extern char *remotename(); 6879382Seric extern char *DelimChar; /* defined in prescan */ 6889382Seric 6899382Seric /* 6909382Seric ** Find the end of the name. New style names 6919382Seric ** end with a comma, old style names end with 6929382Seric ** a space character. However, spaces do not 6939382Seric ** necessarily delimit an old-style name -- at 6949382Seric ** signs mean keep going. 6959382Seric */ 6969382Seric 6979382Seric /* find end of name */ 6989382Seric while (isspace(*p) || *p == ',') 6999382Seric p++; 7009382Seric name = p; 7019382Seric for (;;) 7029382Seric { 7039382Seric char *oldp; 70416909Seric char pvpbuf[PSBUFSIZE]; 7059382Seric extern bool isatword(); 7069382Seric extern char **prescan(); 7079382Seric 70816909Seric (void) prescan(p, oldstyle ? ' ' : ',', pvpbuf); 7099382Seric p = DelimChar; 7109382Seric 7119382Seric /* look to see if we have an at sign */ 7129382Seric oldp = p; 7139382Seric while (*p != '\0' && isspace(*p)) 7149382Seric p++; 7159382Seric 7169382Seric if (*p != '@' && !isatword(p)) 7179382Seric { 7189382Seric p = oldp; 7199382Seric break; 7209382Seric } 7219382Seric p += *p == '@' ? 1 : 2; 7229382Seric while (*p != '\0' && isspace(*p)) 7239382Seric p++; 7249382Seric } 7259382Seric /* at the end of one complete name */ 7269382Seric 7279382Seric /* strip off trailing white space */ 7289382Seric while (p >= name && (isspace(*p) || *p == ',' || *p == '\0')) 7299382Seric p--; 7309382Seric if (++p == name) 7319382Seric continue; 7329382Seric savechar = *p; 7339382Seric *p = '\0'; 7349382Seric 7359382Seric /* translate the name to be relative */ 736*55012Seric name = remotename(name, m, bitset(H_FROM, h->h_flags), FALSE, e); 7379382Seric if (*name == '\0') 7389382Seric { 7399382Seric *p = savechar; 7409382Seric continue; 7419382Seric } 7429382Seric 7439382Seric /* output the name with nice formatting */ 74454983Seric opos += strlen(name); 7459382Seric if (!firstone) 7469382Seric opos += 2; 7479382Seric if (opos > 78 && !firstone) 7489382Seric { 74910178Seric (void) strcpy(obp, ",\n"); 75010176Seric putline(obuf, fp, m); 7519382Seric obp = obuf; 7529382Seric (void) sprintf(obp, " "); 75310161Seric opos = strlen(obp); 75410161Seric obp += opos; 75554983Seric opos += strlen(name); 7569382Seric } 7579382Seric else if (!firstone) 7589382Seric { 7599382Seric (void) sprintf(obp, ", "); 7609382Seric obp += 2; 7619382Seric } 7629382Seric 7639382Seric /* strip off quote bits as we output */ 76454983Seric while ((c = *name++) != '\0' && obp < &obuf[MAXLINE]) 7659382Seric { 76654983Seric if (bitnset(M_7BITS, m->m_flags)) 76754983Seric c &= 0177; 76854983Seric *obp++ = c; 7699382Seric } 7709382Seric firstone = FALSE; 7719382Seric *p = savechar; 7729382Seric } 7739382Seric (void) strcpy(obp, "\n"); 77410176Seric putline(obuf, fp, m); 7759382Seric } 7769382Seric /* 7779382Seric ** ISATWORD -- tell if the word we are pointing to is "at". 7789382Seric ** 7799382Seric ** Parameters: 7809382Seric ** p -- word to check. 7819382Seric ** 7829382Seric ** Returns: 7839382Seric ** TRUE -- if p is the word at. 7849382Seric ** FALSE -- otherwise. 7859382Seric ** 7869382Seric ** Side Effects: 7879382Seric ** none. 7889382Seric */ 7899382Seric 7909382Seric bool 7919382Seric isatword(p) 7929382Seric register char *p; 7939382Seric { 7949382Seric extern char lower(); 7959382Seric 7969382Seric if (lower(p[0]) == 'a' && lower(p[1]) == 't' && 7979382Seric p[2] != '\0' && isspace(p[2])) 7989382Seric return (TRUE); 7999382Seric return (FALSE); 8009382Seric } 801