14091Seric # include <errno.h> 24091Seric # include "sendmail.h" 34091Seric 4*12159Seric SCCSID(@(#)headers.c 3.56 05/01/83); 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; 3410689Seric BITMAP mopts; 357890Seric extern char *crackaddr(); 364091Seric 377677Seric # ifdef DEBUG 387677Seric if (tTd(31, 6)) 397677Seric printf("chompheader: %s\n", line); 407677Seric # endif DEBUG 417677Seric 424627Seric /* strip off options */ 4310689Seric clrbitmap(mopts); 444627Seric p = line; 454627Seric if (*p == '?') 464627Seric { 474627Seric /* have some */ 484627Seric register char *q = index(p + 1, *p); 494627Seric 504627Seric if (q != NULL) 514627Seric { 524627Seric *q++ = '\0'; 5310689Seric while (*++p != '\0') 5410689Seric setbitn(*p, mopts); 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, ':'); 6510118Seric if (p == NULL) 6610118Seric { 6710118Seric syserr("chompheader: syntax error, line \"%s\"", line); 6810118Seric return (0); 6910118Seric } 704091Seric fvalue = &p[1]; 714091Seric while (isspace(*--p)) 724091Seric continue; 734091Seric *++p = '\0'; 744091Seric makelower(fname); 754091Seric 764091Seric /* strip field value on front */ 774091Seric if (*fvalue == ' ') 784091Seric fvalue++; 794091Seric 804091Seric /* search header list for this header */ 819382Seric for (hp = &CurEnv->e_header, h = CurEnv->e_header; h != NULL; 829382Seric hp = &h->h_link, h = h->h_link) 834091Seric { 845187Seric if (strcmp(fname, h->h_field) == 0 && bitset(H_DEFAULT, h->h_flags)) 854091Seric break; 864091Seric } 874091Seric 884091Seric /* see if it is a known type */ 894091Seric for (hi = HdrInfo; hi->hi_field != NULL; hi++) 904091Seric { 914091Seric if (strcmp(hi->hi_field, fname) == 0) 924091Seric break; 934091Seric } 944091Seric 9511414Seric /* see if this is a resent message */ 9611930Seric if (!def && bitset(H_RESENT, hi->hi_flags)) 9711414Seric CurEnv->e_flags |= EF_RESENT; 9811414Seric 994091Seric /* if this means "end of header" quit now */ 1004091Seric if (bitset(H_EOH, hi->hi_flags)) 1014091Seric return (hi->hi_flags); 1024091Seric 10311414Seric /* drop explicit From: if same as what we would generate -- for MH */ 10411541Seric if (!def && !QueueRun && strcmp(fvalue, CurEnv->e_from.q_paddr) == 0) 10511414Seric { 10611414Seric p = "resent-from"; 10711414Seric if (!bitset(EF_RESENT, CurEnv->e_flags)) 10811414Seric p += 7; 10911414Seric if (strcmp(fname, p) == 0) 11011414Seric return (hi->hi_flags); 11111414Seric } 11211414Seric 1134091Seric /* create/fill in a new node */ 1145187Seric if (h == NULL || bitset(H_FORCE, h->h_flags)) 1154091Seric { 1164091Seric /* create a new node */ 1175187Seric h = (HDR *) xalloc(sizeof *h); 1184091Seric h->h_field = newstr(fname); 1194091Seric h->h_value = NULL; 1205187Seric h->h_link = *hp; 12110689Seric bcopy(mopts, h->h_mflags, sizeof mopts); 1225187Seric *hp = h; 1234091Seric } 1248066Seric h->h_flags = hi->hi_flags; 1254091Seric if (def) 1264091Seric h->h_flags |= H_DEFAULT; 1279059Seric if (cond) 1289059Seric h->h_flags |= H_CHECK; 1294091Seric if (h->h_value != NULL) 1309351Seric free((char *) h->h_value); 1318066Seric h->h_value = newstr(fvalue); 1324091Seric 1335937Seric /* hack to see if this is a new format message */ 1348095Seric if (!def && bitset(H_RCPT|H_FROM, h->h_flags) && 1355937Seric (index(fvalue, ',') != NULL || index(fvalue, '(') != NULL || 1368089Seric index(fvalue, '<') != NULL || index(fvalue, ';') != NULL)) 1378089Seric { 1389342Seric CurEnv->e_flags &= ~EF_OLDSTYLE; 1398089Seric } 1405937Seric 1414091Seric return (h->h_flags); 1424091Seric } 1434091Seric /* 1446980Seric ** ADDHEADER -- add a header entry to the end of the queue. 1456980Seric ** 1466980Seric ** This bypasses the special checking of chompheader. 1476980Seric ** 1486980Seric ** Parameters: 1496980Seric ** field -- the name of the header field. 1506980Seric ** value -- the value of the field. It must be lower-cased. 1516980Seric ** e -- the envelope to add them to. 1526980Seric ** 1536980Seric ** Returns: 1546980Seric ** none. 1556980Seric ** 1566980Seric ** Side Effects: 1576980Seric ** adds the field on the list of headers for this envelope. 1586980Seric */ 1596980Seric 1606980Seric addheader(field, value, e) 1616980Seric char *field; 1626980Seric char *value; 1636980Seric ENVELOPE *e; 1646980Seric { 1656980Seric register HDR *h; 1666980Seric register struct hdrinfo *hi; 1676980Seric HDR **hp; 1686980Seric 1696980Seric /* find info struct */ 1706980Seric for (hi = HdrInfo; hi->hi_field != NULL; hi++) 1716980Seric { 1726980Seric if (strcmp(field, hi->hi_field) == 0) 1736980Seric break; 1746980Seric } 1756980Seric 1766980Seric /* find current place in list -- keep back pointer? */ 1776980Seric for (hp = &e->e_header; (h = *hp) != NULL; hp = &h->h_link) 1786980Seric { 1796980Seric if (strcmp(field, h->h_field) == 0) 1806980Seric break; 1816980Seric } 1826980Seric 1836980Seric /* allocate space for new header */ 1846980Seric h = (HDR *) xalloc(sizeof *h); 1856980Seric h->h_field = field; 1866980Seric h->h_value = newstr(value); 1877368Seric h->h_link = *hp; 1886980Seric h->h_flags = hi->hi_flags | H_DEFAULT; 18910689Seric clrbitmap(h->h_mflags); 1906980Seric *hp = h; 1916980Seric } 1926980Seric /* 1934091Seric ** HVALUE -- return value of a header. 1944091Seric ** 1954091Seric ** Only "real" fields (i.e., ones that have not been supplied 1964091Seric ** as a default) are used. 1974091Seric ** 1984091Seric ** Parameters: 1994091Seric ** field -- the field name. 2004091Seric ** 2014091Seric ** Returns: 2024091Seric ** pointer to the value part. 2034091Seric ** NULL if not found. 2044091Seric ** 2054091Seric ** Side Effects: 2069382Seric ** none. 2074091Seric */ 2084091Seric 2094091Seric char * 2104091Seric hvalue(field) 2114091Seric char *field; 2124091Seric { 2134091Seric register HDR *h; 2144091Seric 2156908Seric for (h = CurEnv->e_header; h != NULL; h = h->h_link) 2164091Seric { 2174091Seric if (!bitset(H_DEFAULT, h->h_flags) && strcmp(h->h_field, field) == 0) 2184091Seric return (h->h_value); 2194091Seric } 2204091Seric return (NULL); 2214091Seric } 2224091Seric /* 2234091Seric ** ISHEADER -- predicate telling if argument is a header. 2244091Seric ** 2254319Seric ** A line is a header if it has a single word followed by 2264319Seric ** optional white space followed by a colon. 2274319Seric ** 2284091Seric ** Parameters: 2294091Seric ** s -- string to check for possible headerness. 2304091Seric ** 2314091Seric ** Returns: 2324091Seric ** TRUE if s is a header. 2334091Seric ** FALSE otherwise. 2344091Seric ** 2354091Seric ** Side Effects: 2364091Seric ** none. 2374091Seric */ 2384091Seric 2394091Seric bool 2404091Seric isheader(s) 2414091Seric register char *s; 2424091Seric { 2439382Seric while (*s > ' ' && *s != ':' && *s != '\0') 2444091Seric s++; 2459382Seric 2469382Seric /* following technically violates RFC822 */ 2474091Seric while (isspace(*s)) 2484091Seric s++; 2499382Seric 2504091Seric return (*s == ':'); 2514091Seric } 2525919Seric /* 2537783Seric ** EATHEADER -- run through the stored header and extract info. 2547783Seric ** 2557783Seric ** Parameters: 2569382Seric ** e -- the envelope to process. 2577783Seric ** 2587783Seric ** Returns: 2597783Seric ** none. 2607783Seric ** 2617783Seric ** Side Effects: 2627783Seric ** Sets a bunch of global variables from information 2639382Seric ** in the collected header. 2649382Seric ** Aborts the message if the hop count is exceeded. 2657783Seric */ 2667783Seric 2679382Seric eatheader(e) 2689382Seric register ENVELOPE *e; 2697783Seric { 2707783Seric register HDR *h; 2717783Seric register char *p; 2729382Seric int hopcnt = 0; 2737783Seric 2749382Seric #ifdef DEBUG 2759382Seric if (tTd(32, 1)) 2769382Seric printf("----- collected header -----\n"); 2779382Seric #endif DEBUG 2789382Seric for (h = e->e_header; h != NULL; h = h->h_link) 2797783Seric { 2809382Seric #ifdef DEBUG 2817783Seric extern char *capitalize(); 2827783Seric 2839382Seric if (tTd(32, 1)) 2847783Seric printf("%s: %s\n", capitalize(h->h_field), h->h_value); 2859382Seric #endif DEBUG 28611414Seric /* count the number of times it has been processed */ 2879382Seric if (bitset(H_TRACE, h->h_flags)) 2889382Seric hopcnt++; 28911414Seric 29011414Seric /* send to this person if we so desire */ 29111414Seric if (GrabTo && bitset(H_RCPT, h->h_flags) && 29211414Seric !bitset(H_DEFAULT, h->h_flags) && 29311414Seric (!bitset(EF_RESENT, CurEnv->e_flags) || bitset(H_RESENT, h->h_flags))) 29411414Seric { 29511414Seric sendtolist(h->h_value, (ADDRESS *) NULL, &CurEnv->e_sendqueue); 29611414Seric } 29711414Seric 29811414Seric /* log the message-id */ 29911290Seric #ifdef LOG 30011299Seric if (!QueueRun && LogLevel > 8 && 30111299Seric strcmp(h->h_field, "message-id") == 0) 30211290Seric { 30311290Seric char buf[MAXNAME]; 30411290Seric 30511290Seric p = h->h_value; 30611290Seric if (bitset(H_DEFAULT, h->h_flags)) 30711290Seric { 30811290Seric expand(p, buf, &buf[sizeof buf], e); 30911290Seric p = buf; 31011290Seric } 31111290Seric syslog(LOG_INFO, "%s: message-id=%s", e->e_id, p); 31211290Seric } 31311290Seric #endif LOG 3149382Seric } 3159382Seric #ifdef DEBUG 3169382Seric if (tTd(32, 1)) 3177783Seric printf("----------------------------\n"); 3189382Seric #endif DEBUG 3197783Seric 3209382Seric /* store hop count */ 3219382Seric if (hopcnt > e->e_hopcount) 3229382Seric e->e_hopcount = hopcnt; 3239382Seric 3247783Seric /* message priority */ 3259382Seric p = hvalue("precedence"); 3269382Seric if (p != NULL) 3279382Seric e->e_class = priencode(p); 3287783Seric if (!QueueRun) 3299382Seric e->e_msgpriority = e->e_msgsize - e->e_class * WKPRIFACT; 3307783Seric 3318066Seric /* return receipt to */ 3328066Seric p = hvalue("return-receipt-to"); 3337783Seric if (p != NULL) 3349382Seric e->e_receiptto = p; 3357783Seric 3368253Seric /* errors to */ 3378253Seric p = hvalue("errors-to"); 3388253Seric if (p != NULL) 3399620Seric sendtolist(p, (ADDRESS *) NULL, &e->e_errorqueue); 3408253Seric 3417783Seric /* from person */ 3429285Seric if (OpMode == MD_ARPAFTP) 3438066Seric { 3448066Seric register struct hdrinfo *hi = HdrInfo; 3457783Seric 3468066Seric for (p = NULL; p == NULL && hi->hi_field != NULL; hi++) 3478066Seric { 3488066Seric if (bitset(H_FROM, hi->hi_flags)) 3498066Seric p = hvalue(hi->hi_field); 3508066Seric } 3518066Seric if (p != NULL) 3529382Seric setsender(p); 3538066Seric } 3548066Seric 3557783Seric /* full name of from person */ 3567783Seric p = hvalue("full-name"); 3577783Seric if (p != NULL) 3589382Seric define('x', p, e); 3597783Seric 3607783Seric /* date message originated */ 3617783Seric p = hvalue("posted-date"); 3627783Seric if (p == NULL) 3637783Seric p = hvalue("date"); 3647783Seric if (p != NULL) 3657783Seric { 3669382Seric define('a', p, e); 3677783Seric /* we don't have a good way to do canonical conversion .... 3689382Seric define('d', newstr(arpatounix(p)), e); 3697783Seric .... so we will ignore the problem for the time being */ 3707783Seric } 37111290Seric 37211290Seric /* 37311290Seric ** Log collection information. 37411290Seric */ 37511290Seric 37611290Seric # ifdef LOG 37711299Seric if (!QueueRun && LogLevel > 1) 37811290Seric { 37911290Seric syslog(LOG_INFO, "%s: from=%s, size=%ld, class=%d\n", 38011290Seric CurEnv->e_id, CurEnv->e_from.q_paddr, CurEnv->e_msgsize, 38111290Seric CurEnv->e_class); 38211290Seric } 38311290Seric # endif LOG 3847783Seric } 3857783Seric /* 3867783Seric ** PRIENCODE -- encode external priority names into internal values. 3877783Seric ** 3887783Seric ** Parameters: 3897783Seric ** p -- priority in ascii. 3907783Seric ** 3917783Seric ** Returns: 3927783Seric ** priority as a numeric level. 3937783Seric ** 3947783Seric ** Side Effects: 3957783Seric ** none. 3967783Seric */ 3977783Seric 3987783Seric priencode(p) 3997783Seric char *p; 4007783Seric { 4018253Seric register int i; 4027783Seric extern bool sameword(); 4037783Seric 4048253Seric for (i = 0; i < NumPriorities; i++) 4057783Seric { 4068253Seric if (sameword(p, Priorities[i].pri_name)) 4078253Seric return (Priorities[i].pri_val); 4087783Seric } 4098253Seric 4108253Seric /* unknown priority */ 4118253Seric return (0); 4127783Seric } 4137783Seric /* 4147890Seric ** CRACKADDR -- parse an address and turn it into a macro 4157783Seric ** 4167783Seric ** This doesn't actually parse the address -- it just extracts 4177783Seric ** it and replaces it with "$g". The parse is totally ad hoc 4187783Seric ** and isn't even guaranteed to leave something syntactically 4197783Seric ** identical to what it started with. However, it does leave 4207783Seric ** something semantically identical. 4217783Seric ** 4227783Seric ** The process is kind of strange. There are a number of 4237783Seric ** interesting cases: 4247783Seric ** 1. comment <address> comment ==> comment <$g> comment 4257783Seric ** 2. address ==> address 4267783Seric ** 3. address (comment) ==> $g (comment) 4277783Seric ** 4. (comment) address ==> (comment) $g 4287783Seric ** And then there are the hard cases.... 4297783Seric ** 5. add (comment) ress ==> $g (comment) 4307783Seric ** 6. comment <address (comment)> ==> comment <$g (comment)> 4317783Seric ** 7. .... etc .... 4327783Seric ** 4337783Seric ** Parameters: 4347890Seric ** addr -- the address to be cracked. 4357783Seric ** 4367783Seric ** Returns: 4377783Seric ** a pointer to the new version. 4387783Seric ** 4397783Seric ** Side Effects: 4407890Seric ** none. 4417783Seric ** 4427783Seric ** Warning: 4437783Seric ** The return value is saved in local storage and should 4447783Seric ** be copied if it is to be reused. 4457783Seric */ 4467783Seric 4477783Seric char * 4487890Seric crackaddr(addr) 4497890Seric register char *addr; 4507783Seric { 4517783Seric register char *p; 4527783Seric register int i; 4537783Seric static char buf[MAXNAME]; 4547783Seric char *rhs; 4557783Seric bool gotaddr; 4567783Seric register char *bp; 4577783Seric 4587783Seric # ifdef DEBUG 4597783Seric if (tTd(33, 1)) 4607890Seric printf("crackaddr(%s)\n", addr); 4617783Seric # endif DEBUG 4627783Seric 4637783Seric strcpy(buf, ""); 4647783Seric rhs = NULL; 4657783Seric 4668082Seric /* strip leading spaces */ 4678082Seric while (*addr != '\0' && isspace(*addr)) 4688082Seric addr++; 4698082Seric 4707783Seric /* 4717783Seric ** See if we have anything in angle brackets. If so, that is 4727783Seric ** the address part, and the rest is the comment. 4737783Seric */ 4747783Seric 4757890Seric p = index(addr, '<'); 4767783Seric if (p != NULL) 4777783Seric { 4787890Seric /* copy the beginning of the addr field to the buffer */ 4797783Seric *p = '\0'; 4807890Seric strcpy(buf, addr); 4817783Seric strcat(buf, "<"); 4828082Seric *p++ = '<'; 4837783Seric 4848082Seric /* skip spaces */ 4858082Seric while (isspace(*p)) 4868082Seric p++; 4878082Seric 4887783Seric /* find the matching right angle bracket */ 4898082Seric addr = p; 4907783Seric for (i = 0; *p != '\0'; p++) 4917783Seric { 4927783Seric switch (*p) 4937783Seric { 4947783Seric case '<': 4957783Seric i++; 4967783Seric break; 4977783Seric 4987783Seric case '>': 4997783Seric i--; 5007783Seric break; 5017783Seric } 5027783Seric if (i < 0) 5037783Seric break; 5047783Seric } 5057783Seric 5067783Seric /* p now points to the closing quote (or a null byte) */ 5077783Seric if (*p != '\0') 5087783Seric { 5097783Seric /* make rhs point to the extra stuff at the end */ 5107783Seric rhs = p; 5117783Seric *p++ = '\0'; 5127783Seric } 5137783Seric } 5147783Seric 5157783Seric /* 5167944Seric ** Now parse the real address part. "addr" points to the (null 5177783Seric ** terminated) version of what we are inerested in; rhs points 5187783Seric ** to the extra stuff at the end of the line, if any. 5197783Seric */ 5207783Seric 5217890Seric p = addr; 5227783Seric 5237783Seric /* now strip out comments */ 5247783Seric bp = &buf[strlen(buf)]; 5257783Seric gotaddr = FALSE; 5267783Seric for (; *p != '\0'; p++) 5277783Seric { 5287783Seric if (*p == '(') 5297783Seric { 5307783Seric /* copy to matching close paren */ 5317783Seric *bp++ = *p++; 5327783Seric for (i = 0; *p != '\0'; p++) 5337783Seric { 5347783Seric *bp++ = *p; 5357783Seric switch (*p) 5367783Seric { 5377783Seric case '(': 5387783Seric i++; 5397783Seric break; 5407783Seric 5417783Seric case ')': 5427783Seric i--; 5437783Seric break; 5447783Seric } 5457783Seric if (i < 0) 5467783Seric break; 5477783Seric } 5487783Seric continue; 5497783Seric } 5507783Seric 5517783Seric /* 5527783Seric ** If this is the first "real" character we have seen, 5537783Seric ** then we put the "$g" in the buffer now. 5547783Seric */ 5557783Seric 5567783Seric if (isspace(*p)) 5577783Seric *bp++ = *p; 5587783Seric else if (!gotaddr) 5597783Seric { 5607783Seric strcpy(bp, "$g"); 5617783Seric bp += 2; 5627783Seric gotaddr = TRUE; 5637783Seric } 5647783Seric } 5657783Seric 5667944Seric /* hack, hack.... strip trailing blanks */ 5677944Seric do 5687944Seric { 5697944Seric *bp-- = '\0'; 5707944Seric } while (isspace(*bp)); 5717944Seric bp++; 5727783Seric 5737944Seric /* put any right hand side back on */ 5747783Seric if (rhs != NULL) 5757783Seric { 5767783Seric *rhs = '>'; 5777783Seric strcpy(bp, rhs); 5787783Seric } 5797783Seric 5807783Seric # ifdef DEBUG 5817783Seric if (tTd(33, 1)) 5827944Seric printf("crackaddr=>`%s'\n", buf); 5837783Seric # endif DEBUG 5847783Seric 5857783Seric return (buf); 5867783Seric } 5879382Seric /* 5889382Seric ** PUTHEADER -- put the header part of a message from the in-core copy 5899382Seric ** 5909382Seric ** Parameters: 5919382Seric ** fp -- file to put it on. 5929382Seric ** m -- mailer to use. 5939382Seric ** e -- envelope to use. 5949382Seric ** 5959382Seric ** Returns: 5969382Seric ** none. 5979382Seric ** 5989382Seric ** Side Effects: 5999382Seric ** none. 6009382Seric */ 6019382Seric 60210176Seric putheader(fp, m, e) 6039382Seric register FILE *fp; 6049382Seric register MAILER *m; 6059382Seric register ENVELOPE *e; 6069382Seric { 6079382Seric char buf[BUFSIZ]; 6089382Seric register HDR *h; 6099382Seric extern char *arpadate(); 6109382Seric extern char *capitalize(); 6119382Seric char obuf[MAXLINE]; 6129382Seric 6139382Seric for (h = e->e_header; h != NULL; h = h->h_link) 6149382Seric { 6159382Seric register char *p; 61610689Seric extern bool bitintersect(); 6179382Seric 6189382Seric if (bitset(H_CHECK|H_ACHECK, h->h_flags) && 61910689Seric !bitintersect(h->h_mflags, m->m_flags)) 6209382Seric continue; 6219382Seric 62211414Seric /* handle Resent-... headers specially */ 62311414Seric if (bitset(H_RESENT, h->h_flags) && !bitset(EF_RESENT, e->e_flags)) 62411414Seric continue; 62511414Seric 6269382Seric p = h->h_value; 6279382Seric if (bitset(H_DEFAULT, h->h_flags)) 6289382Seric { 6299382Seric /* macro expand value if generated internally */ 6309382Seric expand(p, buf, &buf[sizeof buf], e); 6319382Seric p = buf; 6329382Seric if (p == NULL || *p == '\0') 6339382Seric continue; 6349382Seric } 6359382Seric 6369382Seric if (bitset(H_FROM|H_RCPT, h->h_flags)) 6379382Seric { 6389382Seric /* address field */ 6399382Seric bool oldstyle = bitset(EF_OLDSTYLE, e->e_flags); 6409382Seric 6419382Seric if (bitset(H_FROM, h->h_flags)) 6429382Seric oldstyle = FALSE; 64310176Seric commaize(h, p, fp, oldstyle, m); 6449382Seric } 6459382Seric else 6469382Seric { 6479382Seric /* vanilla header line */ 648*12159Seric register char *nlp; 649*12159Seric 650*12159Seric (void) sprintf(obuf, "%s: ", capitalize(h->h_field)); 651*12159Seric while ((nlp = index(p, '\n')) != NULL) 652*12159Seric { 653*12159Seric *nlp = '\0'; 654*12159Seric (void) strcat(obuf, p); 655*12159Seric *nlp = '\n'; 656*12159Seric putline(obuf, fp, m); 657*12159Seric p = ++nlp; 658*12159Seric (void) strcpy(obuf, "\t"); 659*12159Seric } 660*12159Seric (void) strcat(obuf, p); 66110176Seric putline(obuf, fp, m); 6629382Seric } 6639382Seric } 6649382Seric } 6659382Seric /* 6669382Seric ** COMMAIZE -- output a header field, making a comma-translated list. 6679382Seric ** 6689382Seric ** Parameters: 6699382Seric ** h -- the header field to output. 6709382Seric ** p -- the value to put in it. 6719382Seric ** fp -- file to put it to. 6729382Seric ** oldstyle -- TRUE if this is an old style header. 6739382Seric ** m -- a pointer to the mailer descriptor. If NULL, 6749382Seric ** don't transform the name at all. 6759382Seric ** 6769382Seric ** Returns: 6779382Seric ** none. 6789382Seric ** 6799382Seric ** Side Effects: 6809382Seric ** outputs "p" to file "fp". 6819382Seric */ 6829382Seric 68310176Seric commaize(h, p, fp, oldstyle, m) 6849382Seric register HDR *h; 6859382Seric register char *p; 6869382Seric FILE *fp; 6879382Seric bool oldstyle; 6889382Seric register MAILER *m; 6899382Seric { 6909382Seric register char *obp; 6919382Seric int opos; 6929382Seric bool firstone = TRUE; 69311157Seric char obuf[MAXLINE + 3]; 6949382Seric 6959382Seric /* 6969382Seric ** Output the address list translated by the 6979382Seric ** mailer and with commas. 6989382Seric */ 6999382Seric 7009382Seric # ifdef DEBUG 7019382Seric if (tTd(14, 2)) 7029382Seric printf("commaize(%s: %s)\n", h->h_field, p); 7039382Seric # endif DEBUG 7049382Seric 7059382Seric obp = obuf; 7069382Seric (void) sprintf(obp, "%s: ", capitalize(h->h_field)); 7079382Seric opos = strlen(h->h_field) + 2; 7089382Seric obp += opos; 7099382Seric 7109382Seric /* 7119382Seric ** Run through the list of values. 7129382Seric */ 7139382Seric 7149382Seric while (*p != '\0') 7159382Seric { 7169382Seric register char *name; 7179382Seric char savechar; 7189382Seric extern char *remotename(); 7199382Seric extern char *DelimChar; /* defined in prescan */ 7209382Seric 7219382Seric /* 7229382Seric ** Find the end of the name. New style names 7239382Seric ** end with a comma, old style names end with 7249382Seric ** a space character. However, spaces do not 7259382Seric ** necessarily delimit an old-style name -- at 7269382Seric ** signs mean keep going. 7279382Seric */ 7289382Seric 7299382Seric /* find end of name */ 7309382Seric while (isspace(*p) || *p == ',') 7319382Seric p++; 7329382Seric name = p; 7339382Seric for (;;) 7349382Seric { 7359382Seric char *oldp; 7369382Seric extern bool isatword(); 7379382Seric extern char **prescan(); 7389382Seric 7399382Seric (void) prescan(p, oldstyle ? ' ' : ','); 7409382Seric p = DelimChar; 7419382Seric 7429382Seric /* look to see if we have an at sign */ 7439382Seric oldp = p; 7449382Seric while (*p != '\0' && isspace(*p)) 7459382Seric p++; 7469382Seric 7479382Seric if (*p != '@' && !isatword(p)) 7489382Seric { 7499382Seric p = oldp; 7509382Seric break; 7519382Seric } 7529382Seric p += *p == '@' ? 1 : 2; 7539382Seric while (*p != '\0' && isspace(*p)) 7549382Seric p++; 7559382Seric } 7569382Seric /* at the end of one complete name */ 7579382Seric 7589382Seric /* strip off trailing white space */ 7599382Seric while (p >= name && (isspace(*p) || *p == ',' || *p == '\0')) 7609382Seric p--; 7619382Seric if (++p == name) 7629382Seric continue; 7639382Seric savechar = *p; 7649382Seric *p = '\0'; 7659382Seric 7669382Seric /* translate the name to be relative */ 76710309Seric name = remotename(name, m, bitset(H_FROM, h->h_flags), FALSE); 7689382Seric if (*name == '\0') 7699382Seric { 7709382Seric *p = savechar; 7719382Seric continue; 7729382Seric } 7739382Seric 7749382Seric /* output the name with nice formatting */ 7759382Seric opos += qstrlen(name); 7769382Seric if (!firstone) 7779382Seric opos += 2; 7789382Seric if (opos > 78 && !firstone) 7799382Seric { 78010178Seric (void) strcpy(obp, ",\n"); 78110176Seric putline(obuf, fp, m); 7829382Seric obp = obuf; 7839382Seric (void) sprintf(obp, " "); 78410161Seric opos = strlen(obp); 78510161Seric obp += opos; 78610161Seric opos += qstrlen(name); 7879382Seric } 7889382Seric else if (!firstone) 7899382Seric { 7909382Seric (void) sprintf(obp, ", "); 7919382Seric obp += 2; 7929382Seric } 7939382Seric 7949382Seric /* strip off quote bits as we output */ 79511157Seric while (*name != '\0' && obp < &obuf[MAXLINE]) 7969382Seric { 7979382Seric if (bitset(0200, *name)) 7989382Seric *obp++ = '\\'; 7999382Seric *obp++ = *name++ & ~0200; 8009382Seric } 8019382Seric firstone = FALSE; 8029382Seric *p = savechar; 8039382Seric } 8049382Seric (void) strcpy(obp, "\n"); 80510176Seric putline(obuf, fp, m); 8069382Seric } 8079382Seric /* 8089382Seric ** ISATWORD -- tell if the word we are pointing to is "at". 8099382Seric ** 8109382Seric ** Parameters: 8119382Seric ** p -- word to check. 8129382Seric ** 8139382Seric ** Returns: 8149382Seric ** TRUE -- if p is the word at. 8159382Seric ** FALSE -- otherwise. 8169382Seric ** 8179382Seric ** Side Effects: 8189382Seric ** none. 8199382Seric */ 8209382Seric 8219382Seric bool 8229382Seric isatword(p) 8239382Seric register char *p; 8249382Seric { 8259382Seric extern char lower(); 8269382Seric 8279382Seric if (lower(p[0]) == 'a' && lower(p[1]) == 't' && 8289382Seric p[2] != '\0' && isspace(p[2])) 8299382Seric return (TRUE); 8309382Seric return (FALSE); 8319382Seric } 832