14091Seric # include <errno.h> 24091Seric # include "sendmail.h" 34091Seric 4*10091Seric SCCSID(@(#)headers.c 3.43 01/03/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; 344627Seric u_long mopts; 354627Seric extern u_long mfencode(); 367890Seric extern char *crackaddr(); 374091Seric 387677Seric # ifdef DEBUG 397677Seric if (tTd(31, 6)) 407677Seric printf("chompheader: %s\n", line); 417677Seric # endif DEBUG 427677Seric 434627Seric /* strip off options */ 444627Seric mopts = 0; 454627Seric p = line; 464627Seric if (*p == '?') 474627Seric { 484627Seric /* have some */ 494627Seric register char *q = index(p + 1, *p); 504627Seric 514627Seric if (q != NULL) 524627Seric { 534627Seric *q++ = '\0'; 544627Seric mopts = mfencode(p + 1); 554627Seric p = q; 564627Seric } 574627Seric else 584627Seric syserr("chompheader: syntax error, line \"%s\"", line); 599059Seric cond = TRUE; 604627Seric } 614627Seric 624091Seric /* find canonical name */ 634627Seric fname = p; 644627Seric p = index(p, ':'); 654091Seric fvalue = &p[1]; 664091Seric while (isspace(*--p)) 674091Seric continue; 684091Seric *++p = '\0'; 694091Seric makelower(fname); 704091Seric 714091Seric /* strip field value on front */ 724091Seric if (*fvalue == ' ') 734091Seric fvalue++; 744091Seric 754091Seric /* search header list for this header */ 769382Seric for (hp = &CurEnv->e_header, h = CurEnv->e_header; h != NULL; 779382Seric hp = &h->h_link, h = h->h_link) 784091Seric { 795187Seric if (strcmp(fname, h->h_field) == 0 && bitset(H_DEFAULT, h->h_flags)) 804091Seric break; 814091Seric } 824091Seric 834091Seric /* see if it is a known type */ 844091Seric for (hi = HdrInfo; hi->hi_field != NULL; hi++) 854091Seric { 864091Seric if (strcmp(hi->hi_field, fname) == 0) 874091Seric break; 884091Seric } 894091Seric 904091Seric /* if this means "end of header" quit now */ 914091Seric if (bitset(H_EOH, hi->hi_flags)) 924091Seric return (hi->hi_flags); 934091Seric 944091Seric /* create/fill in a new node */ 955187Seric if (h == NULL || bitset(H_FORCE, h->h_flags)) 964091Seric { 974091Seric /* create a new node */ 985187Seric h = (HDR *) xalloc(sizeof *h); 994091Seric h->h_field = newstr(fname); 1004091Seric h->h_value = NULL; 1015187Seric h->h_link = *hp; 1029059Seric h->h_mflags = mopts; 1035187Seric *hp = h; 1044091Seric } 1058066Seric h->h_flags = hi->hi_flags; 1064091Seric if (def) 1074091Seric h->h_flags |= H_DEFAULT; 1089059Seric if (cond) 1099059Seric h->h_flags |= H_CHECK; 1104091Seric if (h->h_value != NULL) 1119351Seric free((char *) h->h_value); 1128066Seric h->h_value = newstr(fvalue); 1134091Seric 1145937Seric /* hack to see if this is a new format message */ 1158095Seric if (!def && bitset(H_RCPT|H_FROM, h->h_flags) && 1165937Seric (index(fvalue, ',') != NULL || index(fvalue, '(') != NULL || 1178089Seric index(fvalue, '<') != NULL || index(fvalue, ';') != NULL)) 1188089Seric { 1199342Seric CurEnv->e_flags &= ~EF_OLDSTYLE; 1208089Seric } 1215937Seric 1228066Seric /* send to this person if we so desire */ 1238066Seric if (!def && GrabTo && bitset(H_RCPT, h->h_flags)) 1249620Seric sendtolist(h->h_value, (ADDRESS *) NULL, &CurEnv->e_sendqueue); 1258066Seric 1264091Seric return (h->h_flags); 1274091Seric } 1284091Seric /* 1296980Seric ** ADDHEADER -- add a header entry to the end of the queue. 1306980Seric ** 1316980Seric ** This bypasses the special checking of chompheader. 1326980Seric ** 1336980Seric ** Parameters: 1346980Seric ** field -- the name of the header field. 1356980Seric ** value -- the value of the field. It must be lower-cased. 1366980Seric ** e -- the envelope to add them to. 1376980Seric ** 1386980Seric ** Returns: 1396980Seric ** none. 1406980Seric ** 1416980Seric ** Side Effects: 1426980Seric ** adds the field on the list of headers for this envelope. 1436980Seric */ 1446980Seric 1456980Seric addheader(field, value, e) 1466980Seric char *field; 1476980Seric char *value; 1486980Seric ENVELOPE *e; 1496980Seric { 1506980Seric register HDR *h; 1516980Seric register struct hdrinfo *hi; 1526980Seric HDR **hp; 1536980Seric 1546980Seric /* find info struct */ 1556980Seric for (hi = HdrInfo; hi->hi_field != NULL; hi++) 1566980Seric { 1576980Seric if (strcmp(field, hi->hi_field) == 0) 1586980Seric break; 1596980Seric } 1606980Seric 1616980Seric /* find current place in list -- keep back pointer? */ 1626980Seric for (hp = &e->e_header; (h = *hp) != NULL; hp = &h->h_link) 1636980Seric { 1646980Seric if (strcmp(field, h->h_field) == 0) 1656980Seric break; 1666980Seric } 1676980Seric 1686980Seric /* allocate space for new header */ 1696980Seric h = (HDR *) xalloc(sizeof *h); 1706980Seric h->h_field = field; 1716980Seric h->h_value = newstr(value); 1727368Seric h->h_link = *hp; 1736980Seric h->h_flags = hi->hi_flags | H_DEFAULT; 1749059Seric h->h_mflags = 0; 1756980Seric *hp = h; 1766980Seric } 1776980Seric /* 1784091Seric ** HVALUE -- return value of a header. 1794091Seric ** 1804091Seric ** Only "real" fields (i.e., ones that have not been supplied 1814091Seric ** as a default) are used. 1824091Seric ** 1834091Seric ** Parameters: 1844091Seric ** field -- the field name. 1854091Seric ** 1864091Seric ** Returns: 1874091Seric ** pointer to the value part. 1884091Seric ** NULL if not found. 1894091Seric ** 1904091Seric ** Side Effects: 1919382Seric ** none. 1924091Seric */ 1934091Seric 1944091Seric char * 1954091Seric hvalue(field) 1964091Seric char *field; 1974091Seric { 1984091Seric register HDR *h; 1994091Seric 2006908Seric for (h = CurEnv->e_header; h != NULL; h = h->h_link) 2014091Seric { 2024091Seric if (!bitset(H_DEFAULT, h->h_flags) && strcmp(h->h_field, field) == 0) 2034091Seric return (h->h_value); 2044091Seric } 2054091Seric return (NULL); 2064091Seric } 2074091Seric /* 2084091Seric ** ISHEADER -- predicate telling if argument is a header. 2094091Seric ** 2104319Seric ** A line is a header if it has a single word followed by 2114319Seric ** optional white space followed by a colon. 2124319Seric ** 2134091Seric ** Parameters: 2144091Seric ** s -- string to check for possible headerness. 2154091Seric ** 2164091Seric ** Returns: 2174091Seric ** TRUE if s is a header. 2184091Seric ** FALSE otherwise. 2194091Seric ** 2204091Seric ** Side Effects: 2214091Seric ** none. 2224091Seric */ 2234091Seric 2244091Seric bool 2254091Seric isheader(s) 2264091Seric register char *s; 2274091Seric { 2289382Seric while (*s > ' ' && *s != ':' && *s != '\0') 2294091Seric s++; 2309382Seric 2319382Seric /* following technically violates RFC822 */ 2324091Seric while (isspace(*s)) 2334091Seric s++; 2349382Seric 2354091Seric return (*s == ':'); 2364091Seric } 2375919Seric /* 2387783Seric ** EATHEADER -- run through the stored header and extract info. 2397783Seric ** 2407783Seric ** Parameters: 2419382Seric ** e -- the envelope to process. 2427783Seric ** 2437783Seric ** Returns: 2447783Seric ** none. 2457783Seric ** 2467783Seric ** Side Effects: 2477783Seric ** Sets a bunch of global variables from information 2489382Seric ** in the collected header. 2499382Seric ** Aborts the message if the hop count is exceeded. 2507783Seric */ 2517783Seric 2529382Seric eatheader(e) 2539382Seric register ENVELOPE *e; 2547783Seric { 2557783Seric register HDR *h; 2567783Seric register char *p; 2579382Seric int hopcnt = 0; 2587783Seric 2599382Seric #ifdef DEBUG 2609382Seric if (tTd(32, 1)) 2619382Seric printf("----- collected header -----\n"); 2629382Seric #endif DEBUG 2639382Seric for (h = e->e_header; h != NULL; h = h->h_link) 2647783Seric { 2659382Seric #ifdef DEBUG 2667783Seric extern char *capitalize(); 2677783Seric 2689382Seric if (tTd(32, 1)) 2697783Seric printf("%s: %s\n", capitalize(h->h_field), h->h_value); 2709382Seric #endif DEBUG 2719382Seric if (bitset(H_TRACE, h->h_flags)) 2729382Seric hopcnt++; 2739382Seric } 2749382Seric #ifdef DEBUG 2759382Seric if (tTd(32, 1)) 2767783Seric printf("----------------------------\n"); 2779382Seric #endif DEBUG 2787783Seric 2799382Seric /* store hop count */ 2809382Seric if (hopcnt > e->e_hopcount) 2819382Seric e->e_hopcount = hopcnt; 2829382Seric 2837783Seric /* message priority */ 2849382Seric p = hvalue("precedence"); 2859382Seric if (p != NULL) 2869382Seric e->e_class = priencode(p); 2877783Seric if (!QueueRun) 2889382Seric e->e_msgpriority = e->e_msgsize - e->e_class * WKPRIFACT; 2897783Seric 2908066Seric /* return receipt to */ 2918066Seric p = hvalue("return-receipt-to"); 2927783Seric if (p != NULL) 2939382Seric e->e_receiptto = p; 2947783Seric 2958253Seric /* errors to */ 2968253Seric p = hvalue("errors-to"); 2978253Seric if (p != NULL) 2989620Seric sendtolist(p, (ADDRESS *) NULL, &e->e_errorqueue); 2998253Seric 3007783Seric /* from person */ 3019285Seric if (OpMode == MD_ARPAFTP) 3028066Seric { 3038066Seric register struct hdrinfo *hi = HdrInfo; 3047783Seric 3058066Seric for (p = NULL; p == NULL && hi->hi_field != NULL; hi++) 3068066Seric { 3078066Seric if (bitset(H_FROM, hi->hi_flags)) 3088066Seric p = hvalue(hi->hi_field); 3098066Seric } 3108066Seric if (p != NULL) 3119382Seric setsender(p); 3128066Seric } 3138066Seric 3147783Seric /* full name of from person */ 3157783Seric p = hvalue("full-name"); 3167783Seric if (p != NULL) 3179382Seric define('x', p, e); 3187783Seric 3197783Seric /* date message originated */ 3207783Seric p = hvalue("posted-date"); 3217783Seric if (p == NULL) 3227783Seric p = hvalue("date"); 3237783Seric if (p != NULL) 3247783Seric { 3259382Seric define('a', p, e); 3267783Seric /* we don't have a good way to do canonical conversion .... 3279382Seric define('d', newstr(arpatounix(p)), e); 3287783Seric .... so we will ignore the problem for the time being */ 3297783Seric } 3307783Seric } 3317783Seric /* 3327783Seric ** PRIENCODE -- encode external priority names into internal values. 3337783Seric ** 3347783Seric ** Parameters: 3357783Seric ** p -- priority in ascii. 3367783Seric ** 3377783Seric ** Returns: 3387783Seric ** priority as a numeric level. 3397783Seric ** 3407783Seric ** Side Effects: 3417783Seric ** none. 3427783Seric */ 3437783Seric 3447783Seric priencode(p) 3457783Seric char *p; 3467783Seric { 3478253Seric register int i; 3487783Seric extern bool sameword(); 3497783Seric 3508253Seric for (i = 0; i < NumPriorities; i++) 3517783Seric { 3528253Seric if (sameword(p, Priorities[i].pri_name)) 3538253Seric return (Priorities[i].pri_val); 3547783Seric } 3558253Seric 3568253Seric /* unknown priority */ 3578253Seric return (0); 3587783Seric } 3597783Seric /* 3607890Seric ** CRACKADDR -- parse an address and turn it into a macro 3617783Seric ** 3627783Seric ** This doesn't actually parse the address -- it just extracts 3637783Seric ** it and replaces it with "$g". The parse is totally ad hoc 3647783Seric ** and isn't even guaranteed to leave something syntactically 3657783Seric ** identical to what it started with. However, it does leave 3667783Seric ** something semantically identical. 3677783Seric ** 3687783Seric ** The process is kind of strange. There are a number of 3697783Seric ** interesting cases: 3707783Seric ** 1. comment <address> comment ==> comment <$g> comment 3717783Seric ** 2. address ==> address 3727783Seric ** 3. address (comment) ==> $g (comment) 3737783Seric ** 4. (comment) address ==> (comment) $g 3747783Seric ** And then there are the hard cases.... 3757783Seric ** 5. add (comment) ress ==> $g (comment) 3767783Seric ** 6. comment <address (comment)> ==> comment <$g (comment)> 3777783Seric ** 7. .... etc .... 3787783Seric ** 3797783Seric ** Parameters: 3807890Seric ** addr -- the address to be cracked. 3817783Seric ** 3827783Seric ** Returns: 3837783Seric ** a pointer to the new version. 3847783Seric ** 3857783Seric ** Side Effects: 3867890Seric ** none. 3877783Seric ** 3887783Seric ** Warning: 3897783Seric ** The return value is saved in local storage and should 3907783Seric ** be copied if it is to be reused. 3917783Seric */ 3927783Seric 3937783Seric char * 3947890Seric crackaddr(addr) 3957890Seric register char *addr; 3967783Seric { 3977783Seric register char *p; 3987783Seric register int i; 3997783Seric static char buf[MAXNAME]; 4007783Seric char *rhs; 4017783Seric bool gotaddr; 4027783Seric register char *bp; 4037783Seric 4047783Seric # ifdef DEBUG 4057783Seric if (tTd(33, 1)) 4067890Seric printf("crackaddr(%s)\n", addr); 4077783Seric # endif DEBUG 4087783Seric 4097783Seric strcpy(buf, ""); 4107783Seric rhs = NULL; 4117783Seric 4128082Seric /* strip leading spaces */ 4138082Seric while (*addr != '\0' && isspace(*addr)) 4148082Seric addr++; 4158082Seric 4167783Seric /* 4177783Seric ** See if we have anything in angle brackets. If so, that is 4187783Seric ** the address part, and the rest is the comment. 4197783Seric */ 4207783Seric 4217890Seric p = index(addr, '<'); 4227783Seric if (p != NULL) 4237783Seric { 4247890Seric /* copy the beginning of the addr field to the buffer */ 4257783Seric *p = '\0'; 4267890Seric strcpy(buf, addr); 4277783Seric strcat(buf, "<"); 4288082Seric *p++ = '<'; 4297783Seric 4308082Seric /* skip spaces */ 4318082Seric while (isspace(*p)) 4328082Seric p++; 4338082Seric 4347783Seric /* find the matching right angle bracket */ 4358082Seric addr = p; 4367783Seric for (i = 0; *p != '\0'; p++) 4377783Seric { 4387783Seric switch (*p) 4397783Seric { 4407783Seric case '<': 4417783Seric i++; 4427783Seric break; 4437783Seric 4447783Seric case '>': 4457783Seric i--; 4467783Seric break; 4477783Seric } 4487783Seric if (i < 0) 4497783Seric break; 4507783Seric } 4517783Seric 4527783Seric /* p now points to the closing quote (or a null byte) */ 4537783Seric if (*p != '\0') 4547783Seric { 4557783Seric /* make rhs point to the extra stuff at the end */ 4567783Seric rhs = p; 4577783Seric *p++ = '\0'; 4587783Seric } 4597783Seric } 4607783Seric 4617783Seric /* 4627944Seric ** Now parse the real address part. "addr" points to the (null 4637783Seric ** terminated) version of what we are inerested in; rhs points 4647783Seric ** to the extra stuff at the end of the line, if any. 4657783Seric */ 4667783Seric 4677890Seric p = addr; 4687783Seric 4697783Seric /* now strip out comments */ 4707783Seric bp = &buf[strlen(buf)]; 4717783Seric gotaddr = FALSE; 4727783Seric for (; *p != '\0'; p++) 4737783Seric { 4747783Seric if (*p == '(') 4757783Seric { 4767783Seric /* copy to matching close paren */ 4777783Seric *bp++ = *p++; 4787783Seric for (i = 0; *p != '\0'; p++) 4797783Seric { 4807783Seric *bp++ = *p; 4817783Seric switch (*p) 4827783Seric { 4837783Seric case '(': 4847783Seric i++; 4857783Seric break; 4867783Seric 4877783Seric case ')': 4887783Seric i--; 4897783Seric break; 4907783Seric } 4917783Seric if (i < 0) 4927783Seric break; 4937783Seric } 4947783Seric continue; 4957783Seric } 4967783Seric 4977783Seric /* 4987783Seric ** If this is the first "real" character we have seen, 4997783Seric ** then we put the "$g" in the buffer now. 5007783Seric */ 5017783Seric 5027783Seric if (isspace(*p)) 5037783Seric *bp++ = *p; 5047783Seric else if (!gotaddr) 5057783Seric { 5067783Seric strcpy(bp, "$g"); 5077783Seric bp += 2; 5087783Seric gotaddr = TRUE; 5097783Seric } 5107783Seric } 5117783Seric 5127944Seric /* hack, hack.... strip trailing blanks */ 5137944Seric do 5147944Seric { 5157944Seric *bp-- = '\0'; 5167944Seric } while (isspace(*bp)); 5177944Seric bp++; 5187783Seric 5197944Seric /* put any right hand side back on */ 5207783Seric if (rhs != NULL) 5217783Seric { 5227783Seric *rhs = '>'; 5237783Seric strcpy(bp, rhs); 5247783Seric } 5257783Seric 5267783Seric # ifdef DEBUG 5277783Seric if (tTd(33, 1)) 5287944Seric printf("crackaddr=>`%s'\n", buf); 5297783Seric # endif DEBUG 5307783Seric 5317783Seric return (buf); 5327783Seric } 5339382Seric /* 5349382Seric ** PUTHEADER -- put the header part of a message from the in-core copy 5359382Seric ** 5369382Seric ** Parameters: 5379382Seric ** fp -- file to put it on. 5389382Seric ** m -- mailer to use. 5399382Seric ** e -- envelope to use. 54010067Seric ** crlf -- if set, output CRLF on the end of lines. 5419382Seric ** 5429382Seric ** Returns: 5439382Seric ** none. 5449382Seric ** 5459382Seric ** Side Effects: 5469382Seric ** none. 5479382Seric */ 5489382Seric 54910067Seric putheader(fp, m, e, crlf) 5509382Seric register FILE *fp; 5519382Seric register MAILER *m; 5529382Seric register ENVELOPE *e; 5539382Seric { 5549382Seric char buf[BUFSIZ]; 5559382Seric register HDR *h; 5569382Seric extern char *arpadate(); 5579382Seric extern char *capitalize(); 5589382Seric char obuf[MAXLINE]; 5599382Seric bool fullsmtp = bitset(M_FULLSMTP, m->m_flags); 5609382Seric 5619382Seric for (h = e->e_header; h != NULL; h = h->h_link) 5629382Seric { 5639382Seric register char *p; 5649382Seric 5659382Seric if (bitset(H_CHECK|H_ACHECK, h->h_flags) && 5669382Seric !bitset(h->h_mflags, m->m_flags)) 5679382Seric continue; 5689382Seric 5699382Seric p = h->h_value; 5709382Seric if (bitset(H_DEFAULT, h->h_flags)) 5719382Seric { 5729382Seric /* macro expand value if generated internally */ 5739382Seric expand(p, buf, &buf[sizeof buf], e); 5749382Seric p = buf; 5759382Seric if (p == NULL || *p == '\0') 5769382Seric continue; 5779382Seric } 5789382Seric 5799382Seric if (bitset(H_FROM|H_RCPT, h->h_flags)) 5809382Seric { 5819382Seric /* address field */ 5829382Seric bool oldstyle = bitset(EF_OLDSTYLE, e->e_flags); 5839382Seric 5849382Seric if (bitset(H_FROM, h->h_flags)) 5859382Seric oldstyle = FALSE; 58610067Seric commaize(h, p, fp, oldstyle, m, crlf); 5879382Seric } 5889382Seric else 5899382Seric { 5909382Seric /* vanilla header line */ 5919382Seric (void) sprintf(obuf, "%s: %s\n", capitalize(h->h_field), p); 59210067Seric putline(obuf, fp, crlf, fullsmtp); 5939382Seric } 5949382Seric } 5959382Seric } 5969382Seric /* 5979382Seric ** COMMAIZE -- output a header field, making a comma-translated list. 5989382Seric ** 5999382Seric ** Parameters: 6009382Seric ** h -- the header field to output. 6019382Seric ** p -- the value to put in it. 6029382Seric ** fp -- file to put it to. 6039382Seric ** oldstyle -- TRUE if this is an old style header. 6049382Seric ** m -- a pointer to the mailer descriptor. If NULL, 6059382Seric ** don't transform the name at all. 60610067Seric ** crlf -- set if we want CRLF's on the end of lines. 6079382Seric ** 6089382Seric ** Returns: 6099382Seric ** none. 6109382Seric ** 6119382Seric ** Side Effects: 6129382Seric ** outputs "p" to file "fp". 6139382Seric */ 6149382Seric 61510067Seric commaize(h, p, fp, oldstyle, m, crlf) 6169382Seric register HDR *h; 6179382Seric register char *p; 6189382Seric FILE *fp; 6199382Seric bool oldstyle; 6209382Seric register MAILER *m; 62110067Seric bool crlf; 6229382Seric { 6239382Seric register char *obp; 6249382Seric int opos; 6259382Seric bool fullsmtp = FALSE; 6269382Seric bool firstone = TRUE; 6279382Seric char obuf[MAXLINE]; 6289382Seric 6299382Seric /* 6309382Seric ** Output the address list translated by the 6319382Seric ** mailer and with commas. 6329382Seric */ 6339382Seric 6349382Seric # ifdef DEBUG 6359382Seric if (tTd(14, 2)) 6369382Seric printf("commaize(%s: %s)\n", h->h_field, p); 6379382Seric # endif DEBUG 6389382Seric 6399382Seric if (m != NULL && bitset(M_FULLSMTP, m->m_flags)) 6409382Seric fullsmtp = TRUE; 6419382Seric 6429382Seric obp = obuf; 6439382Seric (void) sprintf(obp, "%s: ", capitalize(h->h_field)); 6449382Seric opos = strlen(h->h_field) + 2; 6459382Seric obp += opos; 6469382Seric 6479382Seric /* 6489382Seric ** Run through the list of values. 6499382Seric */ 6509382Seric 6519382Seric while (*p != '\0') 6529382Seric { 6539382Seric register char *name; 6549382Seric char savechar; 6559382Seric extern char *remotename(); 6569382Seric extern char *DelimChar; /* defined in prescan */ 6579382Seric 6589382Seric /* 6599382Seric ** Find the end of the name. New style names 6609382Seric ** end with a comma, old style names end with 6619382Seric ** a space character. However, spaces do not 6629382Seric ** necessarily delimit an old-style name -- at 6639382Seric ** signs mean keep going. 6649382Seric */ 6659382Seric 6669382Seric /* find end of name */ 6679382Seric while (isspace(*p) || *p == ',') 6689382Seric p++; 6699382Seric name = p; 6709382Seric for (;;) 6719382Seric { 6729382Seric char *oldp; 6739382Seric extern bool isatword(); 6749382Seric extern char **prescan(); 6759382Seric 6769382Seric (void) prescan(p, oldstyle ? ' ' : ','); 6779382Seric p = DelimChar; 6789382Seric 6799382Seric /* look to see if we have an at sign */ 6809382Seric oldp = p; 6819382Seric while (*p != '\0' && isspace(*p)) 6829382Seric p++; 6839382Seric 6849382Seric if (*p != '@' && !isatword(p)) 6859382Seric { 6869382Seric p = oldp; 6879382Seric break; 6889382Seric } 6899382Seric p += *p == '@' ? 1 : 2; 6909382Seric while (*p != '\0' && isspace(*p)) 6919382Seric p++; 6929382Seric } 6939382Seric /* at the end of one complete name */ 6949382Seric 6959382Seric /* strip off trailing white space */ 6969382Seric while (p >= name && (isspace(*p) || *p == ',' || *p == '\0')) 6979382Seric p--; 6989382Seric if (++p == name) 6999382Seric continue; 7009382Seric savechar = *p; 7019382Seric *p = '\0'; 7029382Seric 7039382Seric /* translate the name to be relative */ 7049382Seric if (m != NULL) 7059382Seric name = remotename(name, m, bitset(H_FROM, h->h_flags)); 7069382Seric if (*name == '\0') 7079382Seric { 7089382Seric *p = savechar; 7099382Seric continue; 7109382Seric } 7119382Seric 7129382Seric /* output the name with nice formatting */ 7139382Seric opos += qstrlen(name); 7149382Seric if (!firstone) 7159382Seric opos += 2; 7169382Seric if (opos > 78 && !firstone) 7179382Seric { 718*10091Seric fputc(',', fp); 71910067Seric if (crlf) 720*10091Seric fputc('\r', fp); 721*10091Seric fputc('\n', fp); 72210067Seric putline(obuf, fp, crlf, fullsmtp); 7239382Seric obp = obuf; 7249382Seric (void) sprintf(obp, " "); 7259382Seric obp += strlen(obp); 7269382Seric opos = 8 + strlen(name); 7279382Seric } 7289382Seric else if (!firstone) 7299382Seric { 7309382Seric (void) sprintf(obp, ", "); 7319382Seric obp += 2; 7329382Seric } 7339382Seric 7349382Seric /* strip off quote bits as we output */ 7359382Seric while (*name != '\0') 7369382Seric { 7379382Seric if (bitset(0200, *name)) 7389382Seric *obp++ = '\\'; 7399382Seric *obp++ = *name++ & ~0200; 7409382Seric } 7419382Seric firstone = FALSE; 7429382Seric *p = savechar; 7439382Seric } 7449382Seric (void) strcpy(obp, "\n"); 74510067Seric putline(obuf, fp, crlf, fullsmtp); 7469382Seric } 7479382Seric /* 7489382Seric ** ISATWORD -- tell if the word we are pointing to is "at". 7499382Seric ** 7509382Seric ** Parameters: 7519382Seric ** p -- word to check. 7529382Seric ** 7539382Seric ** Returns: 7549382Seric ** TRUE -- if p is the word at. 7559382Seric ** FALSE -- otherwise. 7569382Seric ** 7579382Seric ** Side Effects: 7589382Seric ** none. 7599382Seric */ 7609382Seric 7619382Seric bool 7629382Seric isatword(p) 7639382Seric register char *p; 7649382Seric { 7659382Seric extern char lower(); 7669382Seric 7679382Seric if (lower(p[0]) == 'a' && lower(p[1]) == 't' && 7689382Seric p[2] != '\0' && isspace(p[2])) 7699382Seric return (TRUE); 7709382Seric return (FALSE); 7719382Seric } 772